diff --git a/.golangci.yml b/.golangci.yml index 1c75557b..8600d679 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,23 +1,26 @@ linters-settings: - govet: - check-shadowing: true - golint: - min-confidence: 0 gocyclo: min-complexity: 45 - maligned: - suggest-new: true dupl: threshold: 200 goconst: min-len: 2 min-occurrences: 3 +issues: + # exclude from linting reports various type conversion code smells in the denco router. + exclude-rules: + - path: middleware/denco + linters: + - gosec + text: G115 + linters: enable-all: true disable: + - err113 # disabled temporarily: there are just too many issues to address - nilerr # nilerr crashes on this repo - - maligned + - recvcheck - unparam - lll - gochecknoinits @@ -30,9 +33,6 @@ linters: - wrapcheck - testpackage - nlreturn - - gomnd - - exhaustivestruct - - goerr113 - errorlint - nestif - godot @@ -40,7 +40,6 @@ linters: - paralleltest - tparallel - thelper - - ifshort - exhaustruct - varnamelen - gci @@ -53,10 +52,15 @@ linters: - forcetypeassert - cyclop # deprecated linters - - deadcode - - interfacer - - scopelint - - varcheck - - structcheck - - golint - - nosnakecase + #- deadcode + #- interfacer + #- scopelint + #- varcheck + #- structcheck + #- golint + #- nosnakecase + #- maligned + #- goerr113 + #- ifshort + #- gomnd + #- exhaustivestruct diff --git a/client/auth_info_test.go b/client/auth_info_test.go index 8299a56f..0816c71b 100644 --- a/client/auth_info_test.go +++ b/client/auth_info_test.go @@ -53,11 +53,11 @@ func TestAPIKeyAuth_Query(t *testing.T) { func TestAPIKeyAuth_Header(t *testing.T) { r := newRequest(http.MethodGet, "/", nil) - writer := APIKeyAuth("x-api-token", "header", "the-shared-key") + writer := APIKeyAuth("X-Api-Token", "header", "the-shared-key") err := writer.AuthenticateRequest(r, nil) require.NoError(t, err) - assert.Equal(t, "the-shared-key", r.header.Get("x-api-token")) + assert.Equal(t, "the-shared-key", r.header.Get("X-Api-Token")) } func TestBearerTokenAuth(t *testing.T) { @@ -73,10 +73,10 @@ func TestBearerTokenAuth(t *testing.T) { func TestCompose(t *testing.T) { r := newRequest(http.MethodGet, "/", nil) - writer := Compose(APIKeyAuth("x-api-key", "header", "the-api-key"), APIKeyAuth("x-secret-key", "header", "the-secret-key")) + writer := Compose(APIKeyAuth("X-Api-Key", "header", "the-api-key"), APIKeyAuth("X-Secret-Key", "header", "the-secret-key")) err := writer.AuthenticateRequest(r, nil) require.NoError(t, err) - assert.Equal(t, "the-api-key", r.header.Get("x-api-key")) - assert.Equal(t, "the-secret-key", r.header.Get("x-secret-key")) + assert.Equal(t, "the-api-key", r.header.Get("X-Api-Key")) + assert.Equal(t, "the-secret-key", r.header.Get("X-Secret-Key")) } diff --git a/client/opentracing.go b/client/opentracing.go index 627286d1..25f046a0 100644 --- a/client/opentracing.go +++ b/client/opentracing.go @@ -50,8 +50,8 @@ func (t *tracingTransport) Submit(op *runtime.ClientOperation) (interface{}, err op.Reader = runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) { if span != nil { code := response.Code() - ext.HTTPStatusCode.Set(span, uint16(code)) - if code >= 400 { + ext.HTTPStatusCode.Set(span, uint16(code)) //nolint:gosec // safe to convert regular HTTP codes, no adverse impact other than a garbled trace when converting a code larger than 65535 + if code >= http.StatusBadRequest { ext.Error.Set(span, true) } } diff --git a/client/request.go b/client/request.go index c4a891d0..bd35f46d 100644 --- a/client/request.go +++ b/client/request.go @@ -169,7 +169,8 @@ func (r *request) buildHTTP(mediaType, basePath string, producers map[string]run fileContentType = p.ContentType() } else { // Need to read the data so that we can detect the content type - buf := make([]byte, 512) + const contentTypeBufferSize = 512 + buf := make([]byte, contentTypeBufferSize) size, err := fi.Read(buf) if err != nil && err != io.EOF { logClose(err, pw) diff --git a/client/request_test.go b/client/request_test.go index 2a90aa72..8689b37d 100644 --- a/client/request_test.go +++ b/client/request_test.go @@ -142,7 +142,7 @@ func TestBuildRequest_BuildHTTP_NoPayload(t *testing.T) { req, err := r.BuildHTTP(runtime.JSONMime, "", testProducers, nil) require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get(strings.ToLower("X-Rate-Limit"))) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) } @@ -162,7 +162,7 @@ func TestBuildRequest_BuildHTTP_Payload(t *testing.T) { req, err := r.BuildHTTP(runtime.JSONMime, "", testProducers, nil) require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get(strings.ToLower("X-Rate-Limit"))) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expectedBody, err := json.Marshal(bd) @@ -197,7 +197,7 @@ func TestBuildRequest_BuildHTTP_SetsInAuth(t *testing.T) { require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get("X-Rate-Limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expectedBody, err := json.Marshal(bd) @@ -227,7 +227,7 @@ func TestBuildRequest_BuildHTTP_XMLPayload(t *testing.T) { require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get("X-Rate-Limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expectedBody, err := xml.Marshal(bd) @@ -254,7 +254,7 @@ func TestBuildRequest_BuildHTTP_TextPayload(t *testing.T) { require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get("X-Rate-Limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expectedBody := []byte(bd) @@ -278,7 +278,7 @@ func TestBuildRequest_BuildHTTP_Form(t *testing.T) { require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get("X-Rate-Limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expected := []byte("something=some+value") @@ -301,7 +301,7 @@ func TestBuildRequest_BuildHTTP_Form_URLEncoded(t *testing.T) { require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get("X-Rate-Limit")) assert.Equal(t, runtime.URLencodedFormMime, req.Header.Get(runtime.HeaderContentType)) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) @@ -325,7 +325,7 @@ func TestBuildRequest_BuildHTTP_Form_Content_Length(t *testing.T) { require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get("X-Rate-Limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) assert.Condition(t, func() bool { return req.ContentLength > 0 }, @@ -350,7 +350,7 @@ func TestBuildRequest_BuildHTTP_FormMultipart(t *testing.T) { require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get("X-Rate-Limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expected1 := []byte("Content-Disposition: form-data; name=\"something\"") @@ -383,7 +383,7 @@ func TestBuildRequest_BuildHTTP_FormMultiples(t *testing.T) { require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get("X-Rate-Limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) expected1 := []byte("Content-Disposition: form-data; name=\"something\"") @@ -410,7 +410,7 @@ func TestBuildRequest_BuildHTTP_Files(t *testing.T) { require.NoError(t, err) cont2, err := os.ReadFile("./request.go") require.NoError(t, err) - emptyFile, err := os.CreateTemp("", "empty") + emptyFile, err := os.CreateTemp("", "empty") //nolint:usetesting require.NoError(t, err) reqWrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error { @@ -429,7 +429,7 @@ func TestBuildRequest_BuildHTTP_Files(t *testing.T) { require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get("X-Rate-Limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) @@ -482,7 +482,7 @@ func TestBuildRequest_BuildHTTP_Files_URLEncoded(t *testing.T) { require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get("X-Rate-Limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/flats/1234/", req.URL.Path) mediaType, params, err := mime.ParseMediaType(req.Header.Get(runtime.HeaderContentType)) @@ -581,7 +581,7 @@ func TestBuildRequest_BuildHTTP_BasePath(t *testing.T) { req, err := r.BuildHTTP(runtime.JSONMime, "/basepath", testProducers, nil) require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get("X-Rate-Limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/basepath/flats/1234/", req.URL.Path) } @@ -600,7 +600,7 @@ func TestBuildRequest_BuildHTTP_EscapedPath(t *testing.T) { require.NoError(t, err) require.NotNil(t, req) - assert.Equal(t, "200", req.Header.Get("x-rate-limit")) + assert.Equal(t, "200", req.Header.Get("X-Rate-Limit")) assert.Equal(t, "world", req.URL.Query().Get("hello")) assert.Equal(t, "/basepath/flats/1234/?*&^%/", req.URL.Path) assert.Equal(t, "/basepath/flats/1234%2F%3F%2A&%5E%25/", req.URL.RawPath) @@ -693,7 +693,7 @@ func TestGetBodyCallsBeforeRoundTrip(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { rw.WriteHeader(http.StatusCreated) _, err := rw.Write([]byte("test result")) - require.NoError(t, err) + assert.NoError(t, err) })) defer server.Close() hu, err := url.Parse(server.URL) diff --git a/client/runtime_test.go b/client/runtime_test.go index 8cb5cec4..b01b7e26 100644 --- a/client/runtime_test.go +++ b/client/runtime_test.go @@ -69,7 +69,7 @@ func TestRuntime_Concurrent(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) - require.NoError(t, jsongen.Encode(result)) + assert.NoError(t, jsongen.Encode(result)) })) defer server.Close() @@ -145,7 +145,7 @@ func TestRuntime_Canary(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) - require.NoError(t, jsongen.Encode(result)) + assert.NoError(t, jsongen.Encode(result)) })) defer server.Close() @@ -196,7 +196,7 @@ func TestRuntime_XMLCanary(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.XMLMime) rw.WriteHeader(http.StatusOK) xmlgen := xml.NewEncoder(rw) - require.NoError(t, xmlgen.Encode(result)) + assert.NoError(t, xmlgen.Encode(result)) })) defer server.Close() @@ -344,7 +344,7 @@ func TestRuntime_CustomTransport(t *testing.T) { var resp http.Response resp.StatusCode = http.StatusOK resp.Header = make(http.Header) - resp.Header.Set("content-type", "application/json") + resp.Header.Set("Content-Type", "application/json") buf := bytes.NewBuffer(nil) enc := json.NewEncoder(buf) require.NoError(t, enc.Encode(result)) @@ -395,7 +395,7 @@ func TestRuntime_CustomCookieJar(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) - require.NoError(t, jsongen.Encode([]task{})) + assert.NoError(t, jsongen.Encode([]task{})) } else { rw.WriteHeader(http.StatusUnauthorized) } @@ -453,7 +453,7 @@ func TestRuntime_AuthCanary(t *testing.T) { rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) - require.NoError(t, jsongen.Encode(result)) + assert.NoError(t, jsongen.Encode(result)) })) defer server.Close() diff --git a/client/runtime_tls_test.go b/client/runtime_tls_test.go index e184e085..44c80e4b 100644 --- a/client/runtime_tls_test.go +++ b/client/runtime_tls_test.go @@ -185,7 +185,7 @@ func testTLSServer(t testing.TB, fixtures *tlsFixtures, expectedResult []task) ( rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime) rw.WriteHeader(http.StatusOK) jsongen := json.NewEncoder(rw) - require.NoError(t, jsongen.Encode(expectedResult)) + assert.NoError(t, jsongen.Encode(expectedResult)) })) // create server tls config diff --git a/client_request_test.go b/client_request_test.go index 9646e82a..c6df3e41 100644 --- a/client_request_test.go +++ b/client_request_test.go @@ -23,13 +23,13 @@ import ( func TestRequestWriterFunc(t *testing.T) { hand := ClientRequestWriterFunc(func(r ClientRequest, _ strfmt.Registry) error { - _ = r.SetHeaderParam("blah", "blahblah") + _ = r.SetHeaderParam("Blah", "blahblah") _ = r.SetBodyParam(struct{ Name string }{"Adriana"}) return nil }) tr := new(TestClientRequest) _ = hand.WriteToRequest(tr, nil) - assert.Equal(t, "blahblah", tr.Headers.Get("blah")) + assert.Equal(t, "blahblah", tr.Headers.Get("Blah")) assert.Equal(t, "Adriana", tr.Body.(struct{ Name string }).Name) } diff --git a/client_response.go b/client_response.go index 0d169114..f89a304b 100644 --- a/client_response.go +++ b/client_response.go @@ -74,27 +74,31 @@ func (o *APIError) String() string { return o.Error() } -// IsSuccess returns true when this elapse o k response returns a 2xx status code +// IsSuccess returns true when this API response returns a 2xx status code func (o *APIError) IsSuccess() bool { - return o.Code/100 == 2 + const statusOK = 2 + return o.Code/100 == statusOK } -// IsRedirect returns true when this elapse o k response returns a 3xx status code +// IsRedirect returns true when this API response returns a 3xx status code func (o *APIError) IsRedirect() bool { - return o.Code/100 == 3 + const statusRedirect = 3 + return o.Code/100 == statusRedirect } -// IsClientError returns true when this elapse o k response returns a 4xx status code +// IsClientError returns true when this API response returns a 4xx status code func (o *APIError) IsClientError() bool { - return o.Code/100 == 4 + const statusClientError = 4 + return o.Code/100 == statusClientError } -// IsServerError returns true when this elapse o k response returns a 5xx status code +// IsServerError returns true when this API response returns a 5xx status code func (o *APIError) IsServerError() bool { - return o.Code/100 == 5 + const statusServerError = 5 + return o.Code/100 == statusServerError } -// IsCode returns true when this elapse o k response returns a 4xx status code +// IsCode returns true when this API response returns a given status code func (o *APIError) IsCode(code int) bool { return o.Code == code } diff --git a/client_response_test.go b/client_response_test.go index c8a70c33..c5b20a37 100644 --- a/client_response_test.go +++ b/client_response_test.go @@ -19,7 +19,6 @@ import ( "errors" "io" "io/fs" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -72,7 +71,7 @@ func TestResponseReaderFuncError(t *testing.T) { }) _, err := reader.ReadResponse(response{}, nil) require.Error(t, err) - assert.True(t, strings.Contains(err.Error(), "writer closed"), err.Error()) + require.ErrorContains(t, err, "writer closed") reader = func(r ClientResponse, _ Consumer) (interface{}, error) { _, _ = io.ReadAll(r.Body()) diff --git a/internal/testing/simplepetstore/api_test.go b/internal/testing/simplepetstore/api_test.go index 58f6e56a..f3181272 100644 --- a/internal/testing/simplepetstore/api_test.go +++ b/internal/testing/simplepetstore/api_test.go @@ -37,7 +37,7 @@ func TestSimplePetstoreSpec(t *testing.T) { rw := httptest.NewRecorder() handler.ServeHTTP(rw, r) assert.Equal(t, http.StatusOK, rw.Code) - assert.Equal(t, swaggerJSON, rw.Body.String()) + assert.JSONEq(t, swaggerJSON, rw.Body.String()) } func TestSimplePetstoreAllPets(t *testing.T) { @@ -51,7 +51,7 @@ func TestSimplePetstoreAllPets(t *testing.T) { rw := httptest.NewRecorder() handler.ServeHTTP(rw, r) assert.Equal(t, http.StatusOK, rw.Code) - assert.Equal(t, "[{\"id\":1,\"name\":\"Dog\",\"status\":\"available\"},{\"id\":2,\"name\":\"Cat\",\"status\":\"pending\"}]\n", rw.Body.String()) + assert.JSONEq(t, "[{\"id\":1,\"name\":\"Dog\",\"status\":\"available\"},{\"id\":2,\"name\":\"Cat\",\"status\":\"pending\"}]\n", rw.Body.String()) } func TestSimplePetstorePetByID(t *testing.T) { @@ -65,7 +65,7 @@ func TestSimplePetstorePetByID(t *testing.T) { rw := httptest.NewRecorder() handler.ServeHTTP(rw, r) assert.Equal(t, http.StatusOK, rw.Code) - assert.Equal(t, "{\"id\":1,\"name\":\"Dog\",\"status\":\"available\"}\n", rw.Body.String()) + assert.JSONEq(t, "{\"id\":1,\"name\":\"Dog\",\"status\":\"available\"}\n", rw.Body.String()) } func TestSimplePetstoreAddPet(t *testing.T) { @@ -79,7 +79,7 @@ func TestSimplePetstoreAddPet(t *testing.T) { rw := httptest.NewRecorder() handler.ServeHTTP(rw, r) assert.Equal(t, http.StatusOK, rw.Code) - assert.Equal(t, "{\"id\":3,\"name\":\"Fish\",\"status\":\"available\"}\n", rw.Body.String()) + assert.JSONEq(t, "{\"id\":3,\"name\":\"Fish\",\"status\":\"available\"}\n", rw.Body.String()) } func TestSimplePetstoreDeletePet(t *testing.T) { @@ -101,5 +101,5 @@ func TestSimplePetstoreDeletePet(t *testing.T) { rw = httptest.NewRecorder() handler.ServeHTTP(rw, r) assert.Equal(t, http.StatusNotFound, rw.Code) - assert.Equal(t, "{\"code\":404,\"message\":\"not found: pet 1\"}", rw.Body.String()) + assert.JSONEq(t, "{\"code\":404,\"message\":\"not found: pet 1\"}", rw.Body.String()) } diff --git a/middleware/context.go b/middleware/context.go index 44cecf11..8718ee40 100644 --- a/middleware/context.go +++ b/middleware/context.go @@ -341,7 +341,7 @@ func (c *Context) BindValidRequest(request *http.Request, route *MatchedRoute, b if len(res) == 0 { cons, ok := route.Consumers[ct] if !ok { - res = append(res, errors.New(500, "no consumer registered for %s", ct)) + res = append(res, errors.New(http.StatusInternalServerError, "no consumer registered for %s", ct)) } else { route.Consumer = cons requestContentType = ct diff --git a/middleware/context_test.go b/middleware/context_test.go index e9ed568c..e59ac8fa 100644 --- a/middleware/context_test.go +++ b/middleware/context_test.go @@ -450,7 +450,7 @@ func TestContextNegotiateContentType(t *testing.T) { request, _ := http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", nil) // request.Header.Add("Accept", "*/*") - request.Header.Add("content-type", "text/html") + request.Header.Add("Content-Type", "text/html") v := request.Context().Value(ctxBoundParams) assert.Nil(t, v) @@ -469,15 +469,15 @@ func TestContextBindValidRequest(t *testing.T) { // invalid content-type value request, err := http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", strings.NewReader(`{"name":"dog"}`)) require.NoError(t, err) - request.Header.Add("content-type", "/json") + request.Header.Add("Content-Type", "/json") ri, request, _ := ctx.RouteInfo(request) - assertAPIError(t, 400, ctx.BindValidRequest(request, ri, new(stubBindRequester))) + assertAPIError(t, http.StatusBadRequest, ctx.BindValidRequest(request, ri, new(stubBindRequester))) // unsupported content-type value request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", strings.NewReader(`{"name":"dog"}`)) require.NoError(t, err) - request.Header.Add("content-type", "text/html") + request.Header.Add("Content-Type", "text/html") ri, request, _ = ctx.RouteInfo(request) assertAPIError(t, http.StatusUnsupportedMediaType, ctx.BindValidRequest(request, ri, new(stubBindRequester))) @@ -486,7 +486,7 @@ func TestContextBindValidRequest(t *testing.T) { request, err = http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", nil) require.NoError(t, err) request.Header.Add("Accept", "application/vnd.cia.v1+json") - request.Header.Add("content-type", applicationJSON) + request.Header.Add("Content-Type", applicationJSON) ri, request, _ = ctx.RouteInfo(request) assertAPIError(t, http.StatusNotAcceptable, ctx.BindValidRequest(request, ri, new(stubBindRequester))) @@ -516,7 +516,7 @@ func TestContextBindAndValidate(t *testing.T) { request, _ := http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", nil) request.Header.Add("Accept", "*/*") - request.Header.Add("content-type", "text/html") + request.Header.Add("Content-Type", "text/html") request.ContentLength = 1 v := request.Context().Value(ctxBoundParams) @@ -553,11 +553,11 @@ func TestContextRender(t *testing.T) { recorder := httptest.NewRecorder() ctx.Respond(recorder, request, []string{ct}, ri, map[string]interface{}{"name": "hello"}) assert.Equal(t, http.StatusOK, recorder.Code) - assert.Equal(t, "{\"name\":\"hello\"}\n", recorder.Body.String()) + assert.JSONEq(t, "{\"name\":\"hello\"}\n", recorder.Body.String()) recorder = httptest.NewRecorder() ctx.Respond(recorder, request, []string{ct}, ri, errors.New("this went wrong")) - assert.Equal(t, 500, recorder.Code) + assert.Equal(t, http.StatusInternalServerError, recorder.Code) // recorder = httptest.NewRecorder() // assert.Panics(t, func() { ctx.Respond(recorder, request, []string{ct}, ri, map[int]interface{}{1: "hello"}) }) @@ -577,11 +577,11 @@ func TestContextRender(t *testing.T) { recorder = httptest.NewRecorder() ctx.Respond(recorder, request, []string{ct}, ri, map[string]interface{}{"name": "hello"}) assert.Equal(t, http.StatusOK, recorder.Code) - assert.Equal(t, "{\"name\":\"hello\"}\n", recorder.Body.String()) + assert.JSONEq(t, "{\"name\":\"hello\"}\n", recorder.Body.String()) recorder = httptest.NewRecorder() ctx.Respond(recorder, request, []string{ct}, ri, errors.New("this went wrong")) - assert.Equal(t, 500, recorder.Code) + assert.Equal(t, http.StatusInternalServerError, recorder.Code) // recorder = httptest.NewRecorder() // assert.Panics(t, func() { ctx.Respond(recorder, request, []string{ct}, ri, map[int]interface{}{1: "hello"}) }) @@ -595,7 +595,7 @@ func TestContextRender(t *testing.T) { require.NoError(t, err) ri, request, _ = ctx.RouteInfo(request) ctx.Respond(recorder, request, ri.Produces, ri, nil) - assert.Equal(t, 204, recorder.Code) + assert.Equal(t, http.StatusNoContent, recorder.Code) } func TestContextValidResponseFormat(t *testing.T) { diff --git a/middleware/denco/router.go b/middleware/denco/router.go index 4377f77a..67b2f668 100644 --- a/middleware/denco/router.go +++ b/middleware/denco/router.go @@ -25,7 +25,7 @@ const ( PathParamCharacter = '=' // MaxSize is max size of records and internal slice. - MaxSize = (1 << 22) - 1 + MaxSize = (1 << 22) - 1 //nolint:mnd ) // Router represents a URL router. @@ -138,12 +138,17 @@ func newDoubleArray() *doubleArray { // 32 10 8 0 type baseCheck uint32 +const ( + flagsBits = 10 + checkBits = 8 +) + func (bc baseCheck) Base() int { - return int(bc >> 10) + return int(bc >> flagsBits) } func (bc *baseCheck) SetBase(base int) { - *bc |= baseCheck(base) << 10 + *bc |= baseCheck(base) << flagsBits } func (bc baseCheck) Check() byte { @@ -171,24 +176,27 @@ func (bc baseCheck) IsAnyParam() bool { } func (bc *baseCheck) SetSingleParam() { - *bc |= (1 << 8) + *bc |= (1 << checkBits) } func (bc *baseCheck) SetWildcardParam() { - *bc |= (1 << 9) + *bc |= (1 << (checkBits + 1)) } const ( paramTypeSingle = 0x0100 paramTypeWildcard = 0x0200 paramTypeAny = 0x0300 + + indexOffset = 32 + indexMask = uint64(0xffffffff) ) func (da *doubleArray) lookup(path string, params []Param, idx int) (*node, []Param, bool) { indices := make([]uint64, 0, 1) for i := 0; i < len(path); i++ { if da.bc[idx].IsAnyParam() { - indices = append(indices, (uint64(i)<<32)|(uint64(idx)&0xffffffff)) + indices = append(indices, (uint64(i)<= len(da.bc) || da.bc[idx].Check() != c { @@ -201,7 +209,7 @@ func (da *doubleArray) lookup(path string, params []Param, idx int) (*node, []Pa BACKTRACKING: for j := len(indices) - 1; j >= 0; j-- { - i, idx := int(indices[j]>>32), int(indices[j]&0xffffffff) + i, idx := int(indices[j]>>indexOffset), int(indices[j]&indexMask) if da.bc[idx].IsSingleParam() { nextIdx := nextIndex(da.bc[idx].Base(), ParamCharacter) if nextIdx >= len(da.bc) { diff --git a/middleware/header/header.go b/middleware/header/header.go index df073c87..10350291 100644 --- a/middleware/header/header.go +++ b/middleware/header/header.go @@ -25,6 +25,11 @@ const ( isSpace ) +const ( + asciiMaxControlChar = 31 + asciiMaxChar = 127 +) + func init() { // OCTET = // CHAR = @@ -44,8 +49,8 @@ func init() { for c := 0; c < 256; c++ { var t octetType - isCtl := c <= 31 || c == 127 - isChar := 0 <= c && c <= 127 + isCtl := c <= asciiMaxControlChar || c == asciiMaxChar + isChar := 0 <= c && c <= asciiMaxChar isSeparator := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) if strings.ContainsRune(" \t\r\n", rune(c)) { t |= isSpace diff --git a/middleware/negotiate.go b/middleware/negotiate.go index a9b6f27d..d25d58cf 100644 --- a/middleware/negotiate.go +++ b/middleware/negotiate.go @@ -94,5 +94,6 @@ func normalizeOffers(orig []string) (norm []string) { } func normalizeOffer(orig string) string { - return strings.SplitN(orig, ";", 2)[0] + const maxParts = 2 + return strings.SplitN(orig, ";", maxParts)[0] } diff --git a/middleware/not_implemented_test.go b/middleware/not_implemented_test.go index 213a5499..44517528 100644 --- a/middleware/not_implemented_test.go +++ b/middleware/not_implemented_test.go @@ -16,5 +16,5 @@ func TestErrorResponder(t *testing.T) { resp.WriteResponse(rec, runtime.JSONProducer()) require.Equal(t, http.StatusBadRequest, rec.Code) - require.Equal(t, "{\"message\":\"this is the error body\"}\n", rec.Body.String()) + require.JSONEq(t, "{\"message\":\"this is the error body\"}\n", rec.Body.String()) } diff --git a/middleware/operation_test.go b/middleware/operation_test.go index 09c51247..ddb46590 100644 --- a/middleware/operation_test.go +++ b/middleware/operation_test.go @@ -46,7 +46,7 @@ func TestOperationExecutor(t *testing.T) { request.SetBasicAuth("admin", "admin") mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusOK, recorder.Code) - assert.Equal(t, `[{"id":1,"name":"a dog"}]`+"\n", recorder.Body.String()) + assert.JSONEq(t, `[{"id":1,"name":"a dog"}]`+"\n", recorder.Body.String()) spec, api = petstore.NewAPI(t) api.RegisterOperation("get", "/pets", runtime.OperationHandlerFunc(func(_ interface{}) (interface{}, error) { @@ -64,5 +64,5 @@ func TestOperationExecutor(t *testing.T) { request.SetBasicAuth("admin", "admin") mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusUnprocessableEntity, recorder.Code) - assert.Equal(t, `{"code":422,"message":"expected"}`, recorder.Body.String()) + assert.JSONEq(t, `{"code":422,"message":"expected"}`, recorder.Body.String()) } diff --git a/middleware/parameter.go b/middleware/parameter.go index 9c3353a9..f854a406 100644 --- a/middleware/parameter.go +++ b/middleware/parameter.go @@ -264,7 +264,7 @@ func (p *untypedParamBinder) Bind(request *http.Request, routeParams RouteParams target.Set(reflect.Indirect(newValue)) return nil default: - return errors.New(500, fmt.Sprintf("invalid parameter location %q", p.parameter.In)) + return errors.New(http.StatusInternalServerError, fmt.Sprintf("invalid parameter location %q", p.parameter.In)) } } diff --git a/middleware/request.go b/middleware/request.go index 82e14366..577e4a0f 100644 --- a/middleware/request.go +++ b/middleware/request.go @@ -77,7 +77,7 @@ func (o *UntypedRequestBinder) Bind(request *http.Request, routeParams RoutePara } if !target.IsValid() { - result = append(result, errors.New(500, "parameter name %q is an unknown field", binder.Name)) + result = append(result, errors.New(http.StatusInternalServerError, "parameter name %q is an unknown field", binder.Name)) continue } diff --git a/middleware/router.go b/middleware/router.go index 3a6aee90..249d29f5 100644 --- a/middleware/router.go +++ b/middleware/router.go @@ -377,7 +377,8 @@ func (d *defaultRouter) Lookup(method, path string) (*MatchedRoute, bool) { } // a workaround to handle fragment/composing parameters until they are supported in denco router // check if this parameter is a fragment within a path segment - if xpos := strings.Index(entry.PathPattern, fmt.Sprintf("{%s}", p.Name)) + len(p.Name) + 2; xpos < len(entry.PathPattern) && entry.PathPattern[xpos] != '/' { + const enclosureSize = 2 + if xpos := strings.Index(entry.PathPattern, fmt.Sprintf("{%s}", p.Name)) + len(p.Name) + enclosureSize; xpos < len(entry.PathPattern) && entry.PathPattern[xpos] != '/' { // extract fragment parameters ep := strings.Split(entry.PathPattern[xpos:], "/")[0] pnames, pvalues := decodeCompositParams(p.Name, v, ep, nil, nil) diff --git a/middleware/spec_test.go b/middleware/spec_test.go index efdd9f56..3282461c 100644 --- a/middleware/spec_test.go +++ b/middleware/spec_test.go @@ -43,9 +43,9 @@ func TestServeSpecMiddleware(t *testing.T) { handler.ServeHTTP(recorder, request) assert.Equal(t, http.StatusOK, recorder.Code) - responseHeaders := recorder.Result().Header //nolint:bodyclose // false positive from linter + responseHeaders := recorder.Result().Header responseContentType := responseHeaders.Get("Content-Type") - assert.Equal(t, applicationJSON, responseContentType) + assert.Equal(t, applicationJSON, responseContentType) //nolint:testifylint responseBody := recorder.Body require.NotNil(t, responseBody) diff --git a/middleware/untyped/api.go b/middleware/untyped/api.go index 7b7269bd..6dfbe66e 100644 --- a/middleware/untyped/api.go +++ b/middleware/untyped/api.go @@ -29,6 +29,11 @@ import ( "github.com/go-openapi/runtime" ) +const ( + smallPreallocatedSlots = 10 + mediumPreallocatedSlots = 30 +) + // NewAPI creates the default untyped API func NewAPI(spec *loads.Document) *API { var an *analysis.Spec @@ -38,8 +43,8 @@ func NewAPI(spec *loads.Document) *API { api := &API{ spec: spec, analyzer: an, - consumers: make(map[string]runtime.Consumer, 10), - producers: make(map[string]runtime.Producer, 10), + consumers: make(map[string]runtime.Consumer, smallPreallocatedSlots), + producers: make(map[string]runtime.Producer, smallPreallocatedSlots), authenticators: make(map[string]runtime.Authenticator), operations: make(map[string]map[string]runtime.OperationHandler), ServeError: errors.ServeError, @@ -115,7 +120,7 @@ func (d *API) RegisterAuthorizer(handler runtime.Authorizer) { // RegisterConsumer registers a consumer for a media type. func (d *API) RegisterConsumer(mediaType string, handler runtime.Consumer) { if d.consumers == nil { - d.consumers = make(map[string]runtime.Consumer, 10) + d.consumers = make(map[string]runtime.Consumer, smallPreallocatedSlots) } d.consumers[strings.ToLower(mediaType)] = handler } @@ -123,7 +128,7 @@ func (d *API) RegisterConsumer(mediaType string, handler runtime.Consumer) { // RegisterProducer registers a producer for a media type func (d *API) RegisterProducer(mediaType string, handler runtime.Producer) { if d.producers == nil { - d.producers = make(map[string]runtime.Producer, 10) + d.producers = make(map[string]runtime.Producer, smallPreallocatedSlots) } d.producers[strings.ToLower(mediaType)] = handler } @@ -131,7 +136,7 @@ func (d *API) RegisterProducer(mediaType string, handler runtime.Producer) { // RegisterOperation registers an operation handler for an operation name func (d *API) RegisterOperation(method, path string, handler runtime.OperationHandler) { if d.operations == nil { - d.operations = make(map[string]map[string]runtime.OperationHandler, 30) + d.operations = make(map[string]map[string]runtime.OperationHandler, mediumPreallocatedSlots) } um := strings.ToUpper(method) if b, ok := d.operations[um]; !ok || b == nil { diff --git a/middleware/validation.go b/middleware/validation.go index 0a5356c6..e581cd72 100644 --- a/middleware/validation.go +++ b/middleware/validation.go @@ -111,7 +111,7 @@ func (v *validation) contentType() { if ct != "" && v.route.Consumer == nil { cons, ok := v.route.Consumers[ct] if !ok { - v.result = append(v.result, errors.New(500, "no consumer registered for %s", ct)) + v.result = append(v.result, errors.New(http.StatusInternalServerError, "no consumer registered for %s", ct)) } else { v.route.Consumer = cons } diff --git a/middleware/validation_test.go b/middleware/validation_test.go index 5895055a..bb1b317b 100644 --- a/middleware/validation_test.go +++ b/middleware/validation_test.go @@ -65,53 +65,53 @@ func TestContentTypeValidation(t *testing.T) { recorder = httptest.NewRecorder() request, _ = http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", nil) - request.Header.Add("content-type", "application(") + request.Header.Add("Content-Type", "application(") request.Header.Add("Accept", "application/json") request.ContentLength = 1 mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusBadRequest, recorder.Code) - assert.Equal(t, "application/json", recorder.Header().Get("content-type")) + assert.Equal(t, "application/json", recorder.Header().Get("Content-Type")) recorder = httptest.NewRecorder() request, _ = http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", nil) request.Header.Add("Accept", "application/json") - request.Header.Add("content-type", "text/html") + request.Header.Add("Content-Type", "text/html") request.ContentLength = 1 mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusUnsupportedMediaType, recorder.Code) - assert.Equal(t, "application/json", recorder.Header().Get("content-type")) + assert.Equal(t, "application/json", recorder.Header().Get("Content-Type")) recorder = httptest.NewRecorder() request, _ = http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", strings.NewReader(`{"name":"dog"}`)) request.Header.Add("Accept", "application/json") - request.Header.Add("content-type", "text/html") + request.Header.Add("Content-Type", "text/html") request.TransferEncoding = []string{"chunked"} mw.ServeHTTP(recorder, request) assert.Equal(t, http.StatusUnsupportedMediaType, recorder.Code) - assert.Equal(t, "application/json", recorder.Header().Get("content-type")) + assert.Equal(t, "application/json", recorder.Header().Get("Content-Type")) recorder = httptest.NewRecorder() request, _ = http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", nil) request.Header.Add("Accept", "application/json+special") - request.Header.Add("content-type", "text/html") + request.Header.Add("Content-Type", "text/html") mw.ServeHTTP(recorder, request) assert.Equal(t, 406, recorder.Code) - assert.Equal(t, "application/json", recorder.Header().Get("content-type")) + assert.Equal(t, "application/json", recorder.Header().Get("Content-Type")) // client sends data with unsupported mime recorder = httptest.NewRecorder() request, _ = http.NewRequestWithContext(stdcontext.Background(), http.MethodPost, "/api/pets", nil) request.Header.Add("Accept", "application/json") // this content type is served by default by the API - request.Header.Add("content-type", "application/json+special") + request.Header.Add("Content-Type", "application/json+special") request.ContentLength = 1 mw.ServeHTTP(recorder, request) assert.Equal(t, 415, recorder.Code) // Unsupported media type - assert.Equal(t, "application/json", recorder.Header().Get("content-type")) + assert.Equal(t, "application/json", recorder.Header().Get("Content-Type")) // client sends a body of data with no mime: breaks recorder = httptest.NewRecorder() @@ -121,7 +121,7 @@ func TestContentTypeValidation(t *testing.T) { mw.ServeHTTP(recorder, request) assert.Equal(t, 415, recorder.Code) - assert.Equal(t, "application/json", recorder.Header().Get("content-type")) + assert.Equal(t, "application/json", recorder.Header().Get("Content-Type")) } func TestResponseFormatValidation(t *testing.T) { diff --git a/request.go b/request.go index 9e3e1ecb..3235201a 100644 --- a/request.go +++ b/request.go @@ -50,7 +50,7 @@ func HasBody(r *http.Request) bool { return true } - if r.Header.Get("content-length") != "" { + if r.Header.Get("Content-Length") != "" { // in this case, no Transfer-Encoding should be present // we have a header set but it was explicitly set to 0, so we assume no body return false diff --git a/request_test.go b/request_test.go index 46175912..aab9465d 100644 --- a/request_test.go +++ b/request_test.go @@ -176,8 +176,8 @@ func TestJSONRequest(t *testing.T) { req, err := JSONRequest(http.MethodGet, "/swagger.json", nil) require.NoError(t, err) assert.Equal(t, http.MethodGet, req.Method) - assert.Equal(t, JSONMime, req.Header.Get(HeaderContentType)) - assert.Equal(t, JSONMime, req.Header.Get(HeaderAccept)) + assert.Equal(t, JSONMime, req.Header.Get(HeaderContentType)) //nolint:testifylint + assert.Equal(t, JSONMime, req.Header.Get(HeaderAccept)) //nolint:testifylint req, err = JSONRequest(http.MethodGet, "%2", nil) require.Error(t, err) diff --git a/security/apikey_auth_test.go b/security/apikey_auth_test.go index e57a06be..3899f737 100644 --- a/security/apikey_auth_test.go +++ b/security/apikey_auth_test.go @@ -27,7 +27,7 @@ import ( const ( apiKeyParam = "api_key" - apiKeyHeader = "X-API-KEY" + apiKeyHeader = "X-Api-Key" //nolint:gosec ) func TestApiKeyAuth(t *testing.T) { diff --git a/security/authenticator.go b/security/authenticator.go index bb30472b..14735ce5 100644 --- a/security/authenticator.go +++ b/security/authenticator.go @@ -159,7 +159,7 @@ func APIKeyAuth(name, in string, authenticate TokenAuthentication) runtime.Authe inl := strings.ToLower(in) if inl != query && inl != header { // panic because this is most likely a typo - panic(errors.New(500, "api key auth: in value needs to be either \"query\" or \"header\"")) + panic(errors.New(http.StatusInternalServerError, "api key auth: in value needs to be either \"query\" or \"header\"")) } var getToken func(*http.Request) string @@ -187,7 +187,7 @@ func APIKeyAuthCtx(name, in string, authenticate TokenAuthenticationCtx) runtime inl := strings.ToLower(in) if inl != query && inl != header { // panic because this is most likely a typo - panic(errors.New(500, "api key auth: in value needs to be either \"query\" or \"header\"")) + panic(errors.New(http.StatusInternalServerError, "api key auth: in value needs to be either \"query\" or \"header\"")) } var getToken func(*http.Request) string diff --git a/security/bearer_auth_test.go b/security/bearer_auth_test.go index 42ff57b5..eea0c59f 100644 --- a/security/bearer_auth_test.go +++ b/security/bearer_auth_test.go @@ -167,7 +167,7 @@ func TestBearerAuthCtx(t *testing.T) { } func testIsAuthorized(_ context.Context, req *http.Request, authorizer runtime.Authenticator, expectAuthorized authExpectation, extraAsserters ...func(context.Context, *testing.T)) func(*testing.T) { - return func(t *testing.T) { + return func(t *testing.T) { //nolint:contextcheck hasToken, usr, err := authorizer.Authenticate(&ScopedAuthRequest{Request: req}) switch expectAuthorized { diff --git a/yamlpc/yaml_test.go b/yamlpc/yaml_test.go index a51ff558..befbba71 100644 --- a/yamlpc/yaml_test.go +++ b/yamlpc/yaml_test.go @@ -48,7 +48,7 @@ func TestYAMLProducer(t *testing.T) { rw := httptest.NewRecorder() err := prod.Produce(rw, data) require.NoError(t, err) - assert.Equal(t, consProdYAML, rw.Body.String()) + assert.YAMLEq(t, consProdYAML, rw.Body.String()) } type failReaderWriter struct {