diff --git a/.github/workflows/go.yml b/.github/workflows/ci-build.yml similarity index 96% rename from .github/workflows/go.yml rename to .github/workflows/ci-build.yml index c53559d..99ec7b0 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/ci-build.yml @@ -56,7 +56,7 @@ jobs: - name: Go test with Exasol version ${{ matrix.db }} env: DB_VERSION: ${{ matrix.db }} - run: go test -v -count 1 -coverprofile=coverage.out ./... + run: go test -v -p 1 -count 1 -coverprofile=coverage.out ./... - name: SonarCloud Scan if: matrix.go == env.DEFAULT_GO && matrix.db == env.DEFAULT_DB && github.repository_owner == 'exasol' && env.SONAR_TOKEN != null diff --git a/.gitignore b/.gitignore index 5100a38..74d5d12 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,5 @@ pom.xml.versionsBackup *.orig *.old *.md.html -*.flattened-pom.xml \ No newline at end of file +*.flattened-pom.xml +__debug_bin* diff --git a/.project-keeper.yml b/.project-keeper.yml index 0c1f1db..e0a4cb5 100644 --- a/.project-keeper.yml +++ b/.project-keeper.yml @@ -1,4 +1,4 @@ sources: - type: golang path: go.mod -version: 1.0.5 +version: 1.0.6 diff --git a/doc/changes/changelog.md b/doc/changes/changelog.md index dacf9e0..69a254e 100644 --- a/doc/changes/changelog.md +++ b/doc/changes/changelog.md @@ -1,5 +1,6 @@ # Changes +* [1.0.6](changes_1.0.6.md) * [1.0.5](changes_1.0.5.md) * [1.0.4](changes_1.0.4.md) * [1.0.3](changes_1.0.3.md) diff --git a/doc/changes/changes_1.0.6.md b/doc/changes/changes_1.0.6.md new file mode 100644 index 0000000..dea98f1 --- /dev/null +++ b/doc/changes/changes_1.0.6.md @@ -0,0 +1,11 @@ +# Exasol Driver go 1.0.6, released 2024-03-13 + +Code name: Fix escaping of URL path + +## Summary + +This release fixes an issue when the user specifies a URL path containing characters like `?` or `%`. These were incorrectly escaped. We fixed this and now the URL path is used without modification when connecting to the database. + +## Bugfix + +* #104: Fixed encoding of URL path diff --git a/internal/version/version.go b/internal/version/version.go index aa050ac..185fe3f 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -1,3 +1,3 @@ package version -const DriverVersion = "v1.0.5" +const DriverVersion = "v1.0.6" diff --git a/pkg/connection/websocket.go b/pkg/connection/websocket.go index 1078acb..3bcfa69 100644 --- a/pkg/connection/websocket.go +++ b/pkg/connection/websocket.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "net/url" + "strings" "github.com/exasol/exasol-driver-go/internal/utils" "github.com/exasol/exasol-driver-go/pkg/connection/wsconn" @@ -32,16 +33,14 @@ func (c *Connection) Connect() error { if err != nil { return err } - utils.ShuffleHosts(hosts) - for _, host := range hosts { - url := url.URL{ - Scheme: c.getURIScheme(), - Host: fmt.Sprintf("%s:%d", host, c.Config.Port), - Path: c.Config.UrlPath, + var url *url.URL + url, err = c.createURL(host) + if err != nil { + return err } - c.websocket, err = c.connectToHost(url) + c.websocket, err = c.connectToHost(*url) if err == nil { return nil } @@ -49,6 +48,14 @@ func (c *Connection) Connect() error { return err } +func (c *Connection) createURL(host string) (*url.URL, error) { + urlPath := c.Config.UrlPath + if len(urlPath) > 0 && !strings.HasPrefix(urlPath, "/") { + urlPath = "/" + urlPath + } + return url.Parse(fmt.Sprintf("%s://%s:%d%s", c.getURIScheme(), host, c.Config.Port, urlPath)) +} + func (c *Connection) connectToHost(url url.URL) (wsconn.WebsocketConnection, error) { skipVerify := !c.Config.ValidateServerCertificate || c.Config.CertificateFingerprint != "" ws, err := wsconn.CreateConnection(c.Ctx, skipVerify, c.Config.CertificateFingerprint, url) diff --git a/pkg/connection/websocket_test.go b/pkg/connection/websocket_test.go index 3bf5a40..5fb69a7 100644 --- a/pkg/connection/websocket_test.go +++ b/pkg/connection/websocket_test.go @@ -135,6 +135,33 @@ func (suite *WebsocketTestSuite) TestSendFailsAtParsingResponseData() { suite.EqualError(err, `failed to parse response data "\"invalid\"": json: cannot unmarshal string into Go value of type types.PublicKeyResponse`) } +func (suite *WebsocketTestSuite) TestCreateURL() { + for i, testCase := range []struct { + description string + urlPath string + expectedURL string + }{ + {"empty url path", "", "ws://hostName:12345"}, + {"url path with slash", "/", "ws://hostName:12345/"}, + {"url path with leading slash", "/path", "ws://hostName:12345/path"}, + {"url path without leading slash", "path", "ws://hostName:12345/path"}, + {"url path with trailing slash", "path/", "ws://hostName:12345/path/"}, + {"url path with leading and trailing slash", "/path/", "ws://hostName:12345/path/"}, + {"url path with query", "path?query=1", "ws://hostName:12345/path?query=1"}, + {"url with multiple query parameters", "path?query1=1&query2=2", "ws://hostName:12345/path?query1=1&query2=2"}, + {"url path with query and fragment", "path?query=1#fragment", "ws://hostName:12345/path?query=1#fragment"}, + {"url path with fragment", "path#fragment", "ws://hostName:12345/path#fragment"}, + } { + suite.Run(fmt.Sprintf("Test%02d %s", i, testCase.description), func() { + connection := suite.createOpenConnection() + connection.Config.UrlPath = testCase.urlPath + url, err := connection.createURL("hostName") + suite.Assert().NoError(err) + suite.Equal(testCase.expectedURL, url.String()) + }) + } +} + func (suite *WebsocketTestSuite) createOpenConnection() *Connection { conn := &Connection{ Config: &config.Config{Host: "invalid", Port: 12345, User: "user", Password: "password", ApiVersion: 42},