diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 62dcf07..36d4a5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,20 +22,20 @@ jobs: - name: Set up stable Go version uses: actions/setup-go@v3 with: - go-version: 'stable' + go-version: '1.22' - name: Get dependencies run: go mod tidy - name: Lint code with golangci-lint uses: golangci/golangci-lint-action@v3 with: - version: v1.50.1 + version: v1.55.2 only-new-issues: true tests: name: Tests runs-on: ubuntu-latest strategy: matrix: - go-version: ['1.18', '1.19', 'stable'] + go-version: ['1.18', '1.19', '1.20', '1.21', '1.22', 'stable'] steps: - name: Set up Go ${{ matrix.go-version }} uses: actions/setup-go@v3 @@ -46,7 +46,7 @@ jobs: - name: Get dependencies run: go mod tidy - name: Tests - run: go test ./... -v -cpu 2 -timeout 10s -race -cover -coverprofile=coverage.txt -covermode=atomic + run: go test ./... -v -cpu 2 -timeout 30s -race -cover -coverprofile=coverage.txt -covermode=atomic - name: Coverage run: | go install github.com/axw/gocov/gocov@latest diff --git a/.golangci.yml b/.golangci.yml index 7b3b338..5932275 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,26 +6,9 @@ output: format: colored-line-number sort-results: true -# Linters below do not support go1.18 yet because of generics. -# See https://github.com/golangci/golangci-lint/issues/2649 -# - bodyclose -# - sqlclosecheck - linters: disable-all: true enable: - - paralleltest - - tparallel - - asciicheck - - asasalint - - varnamelen - - reassign - - nilnil - - nilerr - - nakedret - - goprintffuncname - - typecheck - - errchkjson - errcheck - gosimple - govet @@ -33,6 +16,7 @@ linters: - staticcheck - unused - unparam + - bodyclose - dogsled - dupl - errorlint @@ -46,6 +30,7 @@ linters: - godot - goimports - revive + - gomnd - gosec - lll - makezero @@ -53,9 +38,10 @@ linters: - nestif - prealloc - predeclared - - exportloopref + - sqlclosecheck - unconvert - whitespace + - unused linters-settings: govet: @@ -64,11 +50,9 @@ linters-settings: enable: - assign - atomic - - atomicalign - bools - buildtag - copylocks - - fieldalignment - httpresponse - loopclosure - lostcancel @@ -80,6 +64,7 @@ linters-settings: - unmarshal - unreachable - unsafeptr + - unused settings: printf: funcs: @@ -147,20 +132,18 @@ linters-settings: threshold: 200 errorlint: errorf: true - asserts: false - comparison: false exhaustive: check-generated: false default-signifies-exhaustive: false funlen: - lines: 90 + lines: 60 statements: 40 gocognit: min-complexity: 25 gocyclo: min-complexity: 25 goimports: - local-prefixes: github.com/retailcrm/mg-transport-core + local-prefixes: github.com/retailcrm/mg-transport-api-client-go lll: line-length: 120 misspell: @@ -170,25 +153,23 @@ linters-settings: whitespace: multi-if: false multi-func: false - varnamelen: - max-distance: 10 - ignore-map-index-ok: true - ignore-type-assert-ok: true - ignore-chan-recv-ok: true - ignore-decls: - - t *testing.T - - e error - - i int + issues: exclude-rules: - path: _test\.go linters: + - dupl + - gomnd - lll + - bodyclose - errcheck + - sqlclosecheck - misspell - ineffassign - whitespace - makezero + - maligned + - ifshort - errcheck - funlen - goconst @@ -196,10 +177,6 @@ issues: - gocyclo - godot - unused - - errchkjson - - varnamelen - - path: \.go - text: "Error return value of `io.WriteString` is not checked" exclude-use-default: true exclude-case-sensitive: false max-issues-per-linter: 0 @@ -211,4 +188,4 @@ severity: case-sensitive: false service: - golangci-lint-version: 1.50.x + golangci-lint-version: 1.55.x \ No newline at end of file diff --git a/core/healthcheck/notifier_test.go b/core/healthcheck/notifier_test.go index eb7b415..158a283 100644 --- a/core/healthcheck/notifier_test.go +++ b/core/healthcheck/notifier_test.go @@ -6,11 +6,11 @@ import ( "net/url" "testing" + "github.com/h2non/gock" retailcrm "github.com/retailcrm/api-client-go/v2" "github.com/retailcrm/mg-transport-core/v2/core/util/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/h2non/gock.v1" ) func TestDefaultNotifyFunc(t *testing.T) { // nolint:paralleltest diff --git a/core/util/httputil/http_client_builder.go b/core/util/httputil/http_client_builder.go index 5a7d4f3..3e1a17b 100644 --- a/core/util/httputil/http_client_builder.go +++ b/core/util/httputil/http_client_builder.go @@ -7,6 +7,7 @@ import ( "fmt" "net" "net/http" + "net/url" "time" "github.com/pkg/errors" @@ -16,6 +17,14 @@ import ( "github.com/retailcrm/mg-transport-core/v2/core/logger" ) +const ( + defaultDialerTimeout = 30 * time.Second + defaultIdleConnTimeout = 90 * time.Second + defaultTLSHandshakeTimeout = 10 * time.Second + defaultExpectContinueTimeout = 1 * time.Second + defaultMaxIdleConns = 100 +) + // DefaultClient stores original http.DefaultClient. var DefaultClient = http.DefaultClient @@ -24,28 +33,29 @@ var DefaultTransport = http.DefaultTransport // HTTPClientBuilder builds http client with mocks (if necessary) and timeout. // Example: -// // Build HTTP client with timeout = 10 sec, without SSL certificates verification and with mocked google.com -// client, err := NewHTTPClientBuilder(). -// SetTimeout(10). -// SetMockAddress("api_mock:3004"). -// AddMockedDomain("google.com"). -// SetSSLVerification(false). -// Build() // -// if err != nil { -// fmt.Print(err) -// } +// // Build HTTP client with timeout = 10 sec, without SSL certificates verification and with mocked google.com +// client, err := NewHTTPClientBuilder(). +// SetTimeout(10). +// SetMockAddress("api_mock:3004"). +// AddMockedDomain("google.com"). +// SetSSLVerification(false). +// Build() // -// // Actual response will be returned from "api_mock:3004" (it should provide any ssl certificate) -// if resp, err := client.Get("https://google.com"); err == nil { -// if data, err := ioutil.ReadAll(resp.Body); err == nil { -// fmt.Printf("Data: %s", string(data)) -// } else { -// fmt.Print(err) -// } -// } else { -// fmt.Print(err) -// } +// if err != nil { +// fmt.Print(err) +// } +// +// // Actual response will be returned from "api_mock:3004" (it should provide any ssl certificate) +// if resp, err := client.Get("https://google.com"); err == nil { +// if data, err := ioutil.ReadAll(resp.Body); err == nil { +// fmt.Printf("Data: %s", string(data)) +// } else { +// fmt.Print(err) +// } +// } else { +// fmt.Print(err) +// } type HTTPClientBuilder struct { logger logger.Logger httpClient *http.Client @@ -64,11 +74,22 @@ type HTTPClientBuilder struct { // NewHTTPClientBuilder returns HTTPClientBuilder with default values. func NewHTTPClientBuilder() *HTTPClientBuilder { return &HTTPClientBuilder{ - built: false, - httpClient: &http.Client{}, - httpTransport: &http.Transport{}, + built: false, + httpClient: &http.Client{}, + httpTransport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: (&net.Dialer{ + Timeout: defaultDialerTimeout, + KeepAlive: defaultDialerTimeout, + }).DialContext, + ForceAttemptHTTP2: true, + MaxIdleConns: defaultMaxIdleConns, + IdleConnTimeout: defaultIdleConnTimeout, + TLSHandshakeTimeout: defaultTLSHandshakeTimeout, + ExpectContinueTimeout: defaultExpectContinueTimeout, + }, tlsVersion: tls.VersionTLS12, - timeout: 30 * time.Second, + timeout: defaultDialerTimeout, mockAddress: "", mockedDomains: []string{}, logging: false, @@ -147,6 +168,11 @@ func (b *HTTPClientBuilder) SetLogging(flag bool) *HTTPClientBuilder { return b } +func (b *HTTPClientBuilder) SetProxy(proxy func(*http.Request) (*url.URL, error)) *HTTPClientBuilder { + b.httpTransport.Proxy = proxy + return b +} + // FromConfig fulfills mock configuration from HTTPClientConfig. func (b *HTTPClientBuilder) FromConfig(config *config.HTTPClientConfig) *HTTPClientBuilder { if config == nil { @@ -212,6 +238,7 @@ func (b *HTTPClientBuilder) buildMocks() error { b.logf(" - %s\n", domain) } + b.httpTransport.Proxy = nil b.httpTransport.DialContext = func(ctx context.Context, network, addr string) (conn net.Conn, e error) { var ( host string diff --git a/core/util/httputil/http_client_builder_test.go b/core/util/httputil/http_client_builder_test.go index 75e5125..277e281 100644 --- a/core/util/httputil/http_client_builder_test.go +++ b/core/util/httputil/http_client_builder_test.go @@ -92,6 +92,13 @@ func (t *HTTPClientBuilderTest) Test_SetCertPool() { assert.Equal(t.T(), pool, t.builder.httpTransport.TLSClientConfig.RootCAs) } +func (t *HTTPClientBuilderTest) Test_SetProxy() { + t.builder.SetProxy(nil) + assert.Nil(t.T(), t.builder.httpTransport.Proxy) + t.builder.SetProxy(http.ProxyFromEnvironment) + assert.NotNil(t.T(), t.builder.httpTransport.Proxy) +} + func (t *HTTPClientBuilderTest) Test_FromConfigNil() { defer func() { assert.Nil(t.T(), recover()) @@ -161,6 +168,7 @@ func (t *HTTPClientBuilderTest) Test_Build() { assert.NoError(t.T(), err) assert.NotNil(t.T(), client) + assert.Nil(t.T(), client.Transport.(*http.Transport).Proxy) assert.Equal(t.T(), client, http.DefaultClient) assert.Equal(t.T(), timeout*time.Second, client.Timeout) assert.Equal(t.T(), pool, client.Transport.(*http.Transport).TLSClientConfig.RootCAs) @@ -290,6 +298,7 @@ uf/TQPpjrGW5nxOf94qn6FzV2WSype9BcM5MD7z7rk202Fs7Zqc= SetSSLVerification(false). Build() require.NoError(t.T(), err, "cannot build client") + assert.Nil(t.T(), client.Transport.(*http.Transport).Proxy) resp, err := client.Get(mockProto + mockDomainAddr) if err != nil && strings.Contains(err.Error(), "connection refused") { @@ -314,6 +323,7 @@ func (t *HTTPClientBuilderTest) Test_UseTLS10() { t.Require().NotNil(client.Transport) t.Require().NotNil(client.Transport.(*http.Transport).TLSClientConfig) t.Assert().Equal(uint16(tls.VersionTLS10), client.Transport.(*http.Transport).TLSClientConfig.MinVersion) + t.Assert().NotNil(client.Transport.(*http.Transport).Proxy) } // taken from https://stackoverflow.com/questions/23558425/how-do-i-get-the-local-ip-address-in-go diff --git a/core/util/testutil/gock.go b/core/util/testutil/gock.go index 923cab9..130daf3 100644 --- a/core/util/testutil/gock.go +++ b/core/util/testutil/gock.go @@ -5,7 +5,7 @@ import ( "io" "net/http" - "gopkg.in/h2non/gock.v1" + "github.com/h2non/gock" ) // UnmatchedRequestsTestingT contains all of *testing.T methods which are needed for AssertNoUnmatchedRequests. diff --git a/core/util/testutil/gock_test.go b/core/util/testutil/gock_test.go index 23cd421..e1fcfd2 100644 --- a/core/util/testutil/gock_test.go +++ b/core/util/testutil/gock_test.go @@ -6,8 +6,8 @@ import ( "net/http" "testing" + "github.com/h2non/gock" "github.com/stretchr/testify/suite" - "gopkg.in/h2non/gock.v1" ) type testingTMock struct { diff --git a/core/util/utils_test.go b/core/util/utils_test.go index 5843e12..ce7a015 100644 --- a/core/util/utils_test.go +++ b/core/util/utils_test.go @@ -8,13 +8,13 @@ import ( "testing" "time" + "github.com/h2non/gock" "github.com/op/go-logging" retailcrm "github.com/retailcrm/api-client-go/v2" v1 "github.com/retailcrm/mg-transport-api-client-go/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "gopkg.in/h2non/gock.v1" "github.com/retailcrm/mg-transport-core/v2/core/config" diff --git a/go.mod b/go.mod index 1885163..efb5afb 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/gomarkdown/markdown v0.0.0-20221013030248-663e2500819c github.com/gorilla/securecookie v1.1.1 github.com/gorilla/sessions v1.2.0 + github.com/h2non/gock v1.2.0 github.com/jessevdk/go-flags v1.4.0 github.com/jinzhu/gorm v1.9.11 github.com/nicksnyder/go-i18n/v2 v2.0.2 @@ -29,7 +30,6 @@ require ( go.uber.org/atomic v1.10.0 golang.org/x/text v0.9.0 gopkg.in/gormigrate.v1 v1.6.0 - gopkg.in/h2non/gock.v1 v1.1.2 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 034af35..2725199 100644 --- a/go.sum +++ b/go.sum @@ -237,6 +237,8 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+ github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ= github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= +github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=