From 167a8b5e9421e0ab51fbf44c5621632f4a1a90c5 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Fri, 27 Jan 2023 09:01:37 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20Feature:=20Add=20and=20apply=20m?= =?UTF-8?q?ore=20stricter=20golangci-lint=20linting=20rules=20(#2286)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * golangci-lint: add and apply more stricter linting rules * github: drop security workflow now that we use gosec linter inside golangci-lint * github: use official golangci-lint CI linter * Add editorconfig and gitattributes file --- .editorconfig | 8 + .gitattributes | 12 + .github/workflows/linter.yml | 35 +- .github/workflows/security.yml | 17 - .golangci.yml | 258 ++++++++++++++ app.go | 58 ++-- app_test.go | 65 ++-- client.go | 83 +++-- client_test.go | 42 ++- color.go | 2 + ctx.go | 188 ++++++----- ctx_test.go | 314 ++++++++++-------- error_test.go | 15 +- group.go | 1 - helpers.go | 78 +++-- helpers_test.go | 1 + hooks.go | 30 +- hooks_test.go | 3 +- internal/dictpool/pool.go | 2 +- internal/gopsutil/common/common.go | 2 +- internal/gopsutil/mem/mem.go | 3 +- internal/gopsutil/mem/mem_freebsd.go | 1 - internal/gopsutil/mem/mem_linux.go | 10 +- internal/msgp/json.go | 2 +- internal/schema/cache.go | 2 +- internal/storage/memory/memory_test.go | 4 +- internal/template/html/html.go | 1 - internal/template/utils/utils.go | 1 - listen.go | 62 ++-- listen_test.go | 14 +- middleware/basicauth/basicauth_test.go | 13 +- middleware/basicauth/config.go | 2 + middleware/cache/cache.go | 13 +- middleware/cache/cache_test.go | 159 ++++----- middleware/cache/config.go | 12 +- middleware/cache/heap.go | 6 +- middleware/cache/manager.go | 39 ++- middleware/compress/compress.go | 1 + middleware/compress/compress_test.go | 16 +- middleware/compress/config.go | 2 + middleware/cors/cors.go | 5 +- middleware/cors/cors_test.go | 3 +- middleware/cors/utils.go | 12 +- middleware/csrf/config.go | 21 +- middleware/csrf/csrf.go | 8 +- middleware/csrf/csrf_test.go | 155 ++++----- middleware/csrf/manager.go | 63 +--- middleware/encryptcookie/config.go | 6 +- middleware/encryptcookie/encryptcookie.go | 1 + .../encryptcookie/encryptcookie_test.go | 27 +- middleware/encryptcookie/utils.go | 34 +- middleware/envvar/envvar.go | 7 +- middleware/envvar/envvar_test.go | 76 +++-- middleware/etag/config.go | 2 + middleware/etag/etag.go | 40 +-- middleware/etag/etag_test.go | 25 +- middleware/expvar/config.go | 5 +- middleware/expvar/expvar.go | 3 +- middleware/favicon/favicon.go | 2 + middleware/favicon/favicon_test.go | 31 +- middleware/filesystem/filesystem.go | 40 +-- middleware/filesystem/filesystem_test.go | 18 +- middleware/filesystem/utils.go | 20 +- middleware/idempotency/config.go | 10 +- middleware/idempotency/idempotency_test.go | 3 +- middleware/limiter/config.go | 18 +- middleware/limiter/limiter_test.go | 48 +-- middleware/limiter/manager.go | 50 +-- middleware/logger/README.md | 4 +- middleware/logger/config.go | 4 +- middleware/logger/data.go | 3 - middleware/logger/logger.go | 39 ++- middleware/logger/logger_test.go | 68 ++-- middleware/logger/tags.go | 4 +- middleware/logger/template_chain.go | 17 +- middleware/monitor/config.go | 38 ++- middleware/monitor/config_test.go | 46 +-- middleware/monitor/index.go | 13 +- middleware/monitor/monitor.go | 77 +++-- middleware/monitor/monitor_test.go | 10 +- middleware/pprof/config.go | 5 +- middleware/pprof/pprof.go | 31 +- middleware/proxy/config.go | 5 +- middleware/proxy/proxy.go | 19 +- middleware/proxy/proxy_test.go | 89 +++-- middleware/recover/config.go | 4 +- middleware/recover/recover.go | 6 +- middleware/recover/recover_test.go | 8 +- middleware/requestid/config.go | 2 + middleware/requestid/requestid_test.go | 16 +- middleware/session/README.md | 4 +- middleware/session/config.go | 12 +- middleware/session/data.go | 3 +- middleware/session/session.go | 7 +- middleware/session/session_test.go | 94 +++--- middleware/session/store.go | 9 +- middleware/session/store_test.go | 1 + middleware/skip/skip.go | 4 +- middleware/skip/skip_test.go | 6 +- middleware/timeout/timeout_test.go | 14 +- mount.go | 1 - mount_test.go | 2 +- path.go | 21 +- path_test.go | 2 - prefork.go | 60 ++-- prefork_test.go | 5 +- router.go | 43 ++- router_test.go | 14 +- utils/assertions.go | 17 +- utils/assertions_test.go | 4 +- utils/common.go | 12 +- utils/common_test.go | 7 +- utils/convert.go | 10 +- utils/convert_test.go | 4 +- utils/deprecated.go | 8 +- utils/http.go | 14 +- utils/ips.go | 4 + utils/time.go | 1 + utils/time_test.go | 7 +- utils/xml_test.go | 2 +- 120 files changed, 1889 insertions(+), 1321 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes delete mode 100644 .github/workflows/security.yml create mode 100644 .golangci.yml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..6a4ec76843 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org +; This style originates from https://github.com/fewagency/best-practices +root = true + +[*] +charset = utf-8 +end_of_line = lf diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..963a68ec2d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# Handle line endings automatically for files detected as text +# and leave all files detected as binary untouched. +* text=auto eol=lf + +# Force batch scripts to always use CRLF line endings so that if a repo is accessed +# in Windows via a file share from Linux, the scripts will work. +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Force bash scripts to always use LF line endings so that if a repo is accessed +# in Unix via a file share from Windows, the scripts will work. +*.sh text eol=lf diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 4397113a31..b259fccfd7 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -1,17 +1,28 @@ +# Adapted from https://github.com/golangci/golangci-lint-action/blob/b56f6f529003f1c81d4d759be6bd5f10bf9a0fa0/README.md#how-to-use + +name: golangci-lint on: - push: - branches: - - master - - main - pull_request: -name: Linter + push: + tags: + - v* + branches: + - master + - main + pull_request: +permissions: + contents: read jobs: - Golint: + golangci: + name: lint runs-on: ubuntu-latest steps: - - name: Fetch Repository - uses: actions/checkout@v3 - - name: Run Golint - uses: reviewdog/action-golangci-lint@v2 + - uses: actions/setup-go@v3 with: - golangci_lint_flags: "--tests=false" + # NOTE: Keep this in sync with the version from go.mod + go-version: 1.19 + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + # NOTE: Keep this in sync with the version from .golangci.yml + version: v1.50.1 diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml deleted file mode 100644 index e7d8bade55..0000000000 --- a/.github/workflows/security.yml +++ /dev/null @@ -1,17 +0,0 @@ -on: - push: - branches: - - master - - main - pull_request: -name: Security -jobs: - Gosec: - runs-on: ubuntu-latest - steps: - - name: Fetch Repository - uses: actions/checkout@v3 - - name: Run Gosec - uses: securego/gosec@master - with: - args: -exclude-dir=internal/*/ ./... diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000000..87e975e036 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,258 @@ +# Created based on v1.50.1 +# NOTE: Keep this in sync with the version in .github/workflows/linter.yml + +run: + modules-download-mode: readonly + skip-dirs-use-default: false + skip-dirs: + - internal # TODO: Also apply proper linting for internal dir + +output: + sort-results: true + +linters-settings: + # TODO: Eventually enable these checks + # depguard: + # include-go-root: true + # packages: + # - flag + # - io/ioutil + # - reflect + # - unsafe + # packages-with-error-message: + # - flag: '`flag` package is only allowed in main.go' + # - io/ioutil: '`io/ioutil` package is deprecated, use the `io` and `os` package instead' + # - reflect: '`reflect` package is dangerous to use' + # - unsafe: '`unsafe` package is dangerous to use' + + errcheck: + check-type-assertions: true + check-blank: true + disable-default-exclusions: true + + errchkjson: + report-no-exported: true + + exhaustive: + default-signifies-exhaustive: true + + forbidigo: + forbid: + - ^(fmt\.Print(|f|ln)|print|println)$ + - 'http\.Default(Client|Transport)' + # TODO: Eventually enable these patterns + # - 'time\.Sleep' + # - 'panic' + + gci: + sections: + - standard + - prefix(github.com/gofiber/fiber) + - default + - blank + - dot + custom-order: true + + gocritic: + disabled-checks: + - ifElseChain + + gofumpt: + module-path: github.com/gofiber/fiber + extra-rules: true + + gosec: + config: + global: + audit: true + + govet: + check-shadowing: true + enable-all: true + disable: + - shadow + - fieldalignment + + grouper: + import-require-single-import: true + import-require-grouping: true + + misspell: + locale: US + + nolintlint: + require-explanation: true + require-specific: true + + nonamedreturns: + report-error-in-defer: true + + predeclared: + q: true + + promlinter: + strict: true + + revive: + enable-all-rules: true + rules: + # Provided by gomnd linter + - name: add-constant + disabled: true + - name: argument-limit + disabled: true + # Provided by bidichk + - name: banned-characters + disabled: true + - name: cognitive-complexity + disabled: true + - name: cyclomatic + disabled: true + - name: exported + disabled: true + - name: file-header + disabled: true + - name: function-result-limit + disabled: true + - name: function-length + disabled: true + - name: line-length-limit + disabled: true + - name: max-public-structs + disabled: true + - name: modifies-parameter + disabled: true + - name: nested-structs + disabled: true + - name: package-comments + disabled: true + + stylecheck: + checks: + - all + - -ST1000 + - -ST1020 + - -ST1021 + - -ST1022 + + tagliatelle: + case: + rules: + json: snake + + #tenv: + # all: true + + #unparam: + # check-exported: true + + wrapcheck: + ignorePackageGlobs: + - github.com/gofiber/fiber/* + - github.com/valyala/fasthttp + +issues: + exclude-use-default: false + +linters: + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - containedctx + - contextcheck + # - cyclop + # - deadcode + # - decorder + - depguard + - dogsled + # - dupl + # - dupword + - durationcheck + - errcheck + - errchkjson + - errname + - errorlint + - execinquery + - exhaustive + # - exhaustivestruct + # - exhaustruct + - exportloopref + - forbidigo + - forcetypeassert + # - funlen + - gci + - gochecknoglobals + - gochecknoinits + # - gocognit + - goconst + - gocritic + # - gocyclo + # - godot + # - godox + # - goerr113 + - gofmt + - gofumpt + # - goheader + - goimports + # - golint + - gomnd + - gomoddirectives + # - gomodguard + - goprintffuncname + - gosec + - gosimple + - govet + - grouper + # - ifshort + # - importas + - ineffassign + # - interfacebloat + # - interfacer + # - ireturn + # - lll + - loggercheck + # - maintidx + # - makezero + # - maligned + - misspell + - nakedret + # - nestif + - nilerr + - nilnil + # - nlreturn + - noctx + - nolintlint + - nonamedreturns + # - nosnakecase + - nosprintfhostport + # - paralleltest # TODO: Enable once https://github.com/gofiber/fiber/issues/2254 is implemented + # - prealloc + - predeclared + - promlinter + - reassign + - revive + - rowserrcheck + # - scopelint + - sqlclosecheck + - staticcheck + # - structcheck + - stylecheck + - tagliatelle + # - tenv # TODO: Enable once we drop support for Go 1.16 + # - testableexamples + # - testpackage # TODO: Enable once https://github.com/gofiber/fiber/issues/2252 is implemented + - thelper + # - tparallel # TODO: Enable once https://github.com/gofiber/fiber/issues/2254 is implemented + - typecheck + - unconvert + - unparam + - unused + - usestdlibvars + # - varcheck + # - varnamelen + - wastedassign + - whitespace + - wrapcheck + # - wsl diff --git a/app.go b/app.go index e299075b37..002da62c4b 100644 --- a/app.go +++ b/app.go @@ -14,6 +14,7 @@ import ( "encoding/xml" "errors" "fmt" + "log" "net" "net/http" "net/http/httputil" @@ -24,6 +25,7 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -306,7 +308,7 @@ type Config struct { // FEATURE: v2.3.x // The router executes the same handler by default if StrictRouting or CaseSensitive is disabled. - // Enabling RedirectFixedPath will change this behaviour into a client redirect to the original route path. + // Enabling RedirectFixedPath will change this behavior into a client redirect to the original route path. // Using the status code 301 for GET requests and 308 for all other request methods. // // Default: false @@ -454,6 +456,8 @@ const ( ) // HTTP methods enabled by default +// +//nolint:gochecknoglobals // Using a global var is fine here var DefaultMethods = []string{ MethodGet, MethodHead, @@ -467,7 +471,7 @@ var DefaultMethods = []string{ } // DefaultErrorHandler that process return errors from handlers -var DefaultErrorHandler = func(c *Ctx, err error) error { +func DefaultErrorHandler(c *Ctx, err error) error { code := StatusInternalServerError var e *Error if errors.As(err, &e) { @@ -519,7 +523,7 @@ func New(config ...Config) *App { if app.config.ETag { if !IsChild() { - fmt.Println("[Warning] Config.ETag is deprecated since v2.0.6, please use 'middleware/etag'.") + log.Printf("[Warning] Config.ETag is deprecated since v2.0.6, please use 'middleware/etag'.\n") } } @@ -587,7 +591,7 @@ func (app *App) handleTrustedProxy(ipAddress string) { if strings.Contains(ipAddress, "/") { _, ipNet, err := net.ParseCIDR(ipAddress) if err != nil { - fmt.Printf("[Warning] IP range %q could not be parsed: %v\n", ipAddress, err) + log.Printf("[Warning] IP range %q could not be parsed: %v\n", ipAddress, err) } else { app.config.trustedProxyRanges = append(app.config.trustedProxyRanges, ipNet) } @@ -822,7 +826,7 @@ func (app *App) Config() Config { } // Handler returns the server handler. -func (app *App) Handler() fasthttp.RequestHandler { +func (app *App) Handler() fasthttp.RequestHandler { //revive:disable-line:confusing-naming // Having both a Handler() (uppercase) and a handler() (lowercase) is fine. TODO: Use nolint:revive directive instead. See https://github.com/golangci/golangci-lint/issues/3476 // prepare the server for the start app.startupProcess() return app.handler @@ -887,7 +891,7 @@ func (app *App) Hooks() *Hooks { // Test is used for internal debugging by passing a *http.Request. // Timeout is optional and defaults to 1s, -1 will disable it completely. -func (app *App) Test(req *http.Request, msTimeout ...int) (resp *http.Response, err error) { +func (app *App) Test(req *http.Request, msTimeout ...int) (*http.Response, error) { // Set timeout timeout := 1000 if len(msTimeout) > 0 { @@ -902,15 +906,15 @@ func (app *App) Test(req *http.Request, msTimeout ...int) (resp *http.Response, // Dump raw http request dump, err := httputil.DumpRequest(req, true) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to dump request: %w", err) } // Create test connection conn := new(testConn) // Write raw http request - if _, err = conn.r.Write(dump); err != nil { - return nil, err + if _, err := conn.r.Write(dump); err != nil { + return nil, fmt.Errorf("failed to write: %w", err) } // prepare the server for the start app.startupProcess() @@ -943,7 +947,7 @@ func (app *App) Test(req *http.Request, msTimeout ...int) (resp *http.Response, } // Check for errors - if err != nil && err != fasthttp.ErrGetOnly { + if err != nil && !errors.Is(err, fasthttp.ErrGetOnly) { return nil, err } @@ -951,12 +955,17 @@ func (app *App) Test(req *http.Request, msTimeout ...int) (resp *http.Response, buffer := bufio.NewReader(&conn.w) // Convert raw http response to *http.Response - return http.ReadResponse(buffer, req) + res, err := http.ReadResponse(buffer, req) + if err != nil { + return nil, fmt.Errorf("failed to read response: %w", err) + } + + return res, nil } type disableLogger struct{} -func (dl *disableLogger) Printf(_ string, _ ...interface{}) { +func (*disableLogger) Printf(_ string, _ ...interface{}) { // fmt.Println(fmt.Sprintf(format, args...)) } @@ -967,7 +976,7 @@ func (app *App) init() *App { // Only load templates if a view engine is specified if app.config.Views != nil { if err := app.config.Views.Load(); err != nil { - fmt.Printf("views: %v\n", err) + log.Printf("[Warning]: failed to load views: %v\n", err) } } @@ -1039,25 +1048,30 @@ func (app *App) ErrorHandler(ctx *Ctx, err error) error { // errors before calling the application's error handler method. func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) { c := app.AcquireCtx(fctx) - if _, ok := err.(*fasthttp.ErrSmallBuffer); ok { + defer app.ReleaseCtx(c) + + var errNetOP *net.OpError + + switch { + case errors.As(err, new(*fasthttp.ErrSmallBuffer)): err = ErrRequestHeaderFieldsTooLarge - } else if netErr, ok := err.(*net.OpError); ok && netErr.Timeout() { + case errors.As(err, &errNetOP) && errNetOP.Timeout(): err = ErrRequestTimeout - } else if err == fasthttp.ErrBodyTooLarge { + case errors.Is(err, fasthttp.ErrBodyTooLarge): err = ErrRequestEntityTooLarge - } else if err == fasthttp.ErrGetOnly { + case errors.Is(err, fasthttp.ErrGetOnly): err = ErrMethodNotAllowed - } else if strings.Contains(err.Error(), "timeout") { + case strings.Contains(err.Error(), "timeout"): err = ErrRequestTimeout - } else { + default: err = NewError(StatusBadRequest, err.Error()) } if catch := app.ErrorHandler(c, err); catch != nil { - _ = c.SendStatus(StatusInternalServerError) + log.Printf("serverErrorHandler: failed to call ErrorHandler: %v\n", catch) + _ = c.SendStatus(StatusInternalServerError) //nolint:errcheck // It is fine to ignore the error here + return } - - app.ReleaseCtx(c) } // startupProcess Is the method which executes all the necessary processes just before the start of the server. diff --git a/app_test.go b/app_test.go index 7182ee6c8a..5f4fbc8767 100644 --- a/app_test.go +++ b/app_test.go @@ -2,6 +2,7 @@ // 🤖 Github Repository: https://github.com/gofiber/fiber // 📌 API Documentation: https://docs.gofiber.io +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package fiber import ( @@ -23,15 +24,16 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" "github.com/valyala/fasthttp/fasthttputil" ) -var testEmptyHandler = func(c *Ctx) error { +func testEmptyHandler(_ *Ctx) error { return nil } -func testStatus200(t *testing.T, app *App, url string, method string) { +func testStatus200(t *testing.T, app *App, url, method string) { t.Helper() req := httptest.NewRequest(method, url, nil) @@ -42,6 +44,8 @@ func testStatus200(t *testing.T, app *App, url string, method string) { } func testErrorResponse(t *testing.T, err error, resp *http.Response, expectedBodyError string) { + t.Helper() + utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 500, resp.StatusCode, "Status code") @@ -140,7 +144,6 @@ func Test_App_ServerErrorHandler_SmallReadBuffer(t *testing.T) { logHeaderSlice := make([]string, 5000) request.Header.Set("Very-Long-Header", strings.Join(logHeaderSlice, "-")) _, err := app.Test(request) - if err == nil { t.Error("Expect an error at app.Test(request)") } @@ -470,7 +473,6 @@ func Test_App_Use_MultiplePrefix(t *testing.T) { body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "/test/doe", string(body)) - } func Test_App_Use_StrictRouting(t *testing.T) { @@ -515,7 +517,7 @@ func Test_App_Add_Method_Test(t *testing.T) { } }() - methods := append(DefaultMethods, "JOHN") + methods := append(DefaultMethods, "JOHN") //nolint:gocritic // We want a new slice here app := New(Config{ RequestMethods: methods, }) @@ -780,7 +782,7 @@ func Test_App_ShutdownWithTimeout(t *testing.T) { case <-timer.C: t.Fatal("idle connections not closed on shutdown") case err := <-shutdownErr: - if err == nil || err != context.DeadlineExceeded { + if err == nil || !errors.Is(err, context.DeadlineExceeded) { t.Fatalf("unexpected err %v. Expecting %v", err, context.DeadlineExceeded) } } @@ -852,7 +854,7 @@ func Test_App_Static_MaxAge(t *testing.T) { app.Static("/", "./.github", Static{MaxAge: 100}) - resp, err := app.Test(httptest.NewRequest("GET", "/index.html", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/index.html", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") @@ -866,19 +868,19 @@ func Test_App_Static_Custom_CacheControl(t *testing.T) { app := New() app.Static("/", "./.github", Static{ModifyResponse: func(c *Ctx) error { - if strings.Contains(string(c.GetRespHeader("Content-Type")), "text/html") { + if strings.Contains(c.GetRespHeader("Content-Type"), "text/html") { c.Response().Header.Set("Cache-Control", "no-cache, no-store, must-revalidate") } return nil }}) - resp, err := app.Test(httptest.NewRequest("GET", "/index.html", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/index.html", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "no-cache, no-store, must-revalidate", resp.Header.Get(HeaderCacheControl), "CacheControl Control") - normal_resp, normal_err := app.Test(httptest.NewRequest("GET", "/config.yml", nil)) - utils.AssertEqual(t, nil, normal_err, "app.Test(req)") - utils.AssertEqual(t, "", normal_resp.Header.Get(HeaderCacheControl), "CacheControl Control") + respNormal, errNormal := app.Test(httptest.NewRequest(MethodGet, "/config.yml", nil)) + utils.AssertEqual(t, nil, errNormal, "app.Test(req)") + utils.AssertEqual(t, "", respNormal.Header.Get(HeaderCacheControl), "CacheControl Control") } // go test -run Test_App_Static_Download @@ -890,7 +892,7 @@ func Test_App_Static_Download(t *testing.T) { app.Static("/fiber.png", "./.github/testdata/fs/img/fiber.png", Static{Download: true}) - resp, err := app.Test(httptest.NewRequest("GET", "/fiber.png", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/fiber.png", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") @@ -1067,7 +1069,7 @@ func Test_App_Static_Next(t *testing.T) { t.Run("app.Static is skipped: invoking Get handler", func(t *testing.T) { t.Parallel() - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(MethodGet, "/", nil) req.Header.Set("X-Custom-Header", "skip") resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -1082,7 +1084,7 @@ func Test_App_Static_Next(t *testing.T) { t.Run("app.Static is not skipped: serving index.html", func(t *testing.T) { t.Parallel() - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(MethodGet, "/", nil) req.Header.Set("X-Custom-Header", "don't skip") resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -1379,7 +1381,7 @@ func Test_Test_DumpError(t *testing.T) { resp, err := app.Test(httptest.NewRequest(MethodGet, "/", errorReader(0))) utils.AssertEqual(t, true, resp == nil) - utils.AssertEqual(t, "errorReader", err.Error()) + utils.AssertEqual(t, "failed to dump request: errorReader", err.Error()) } // go test -run Test_App_Handler @@ -1393,7 +1395,7 @@ type invalidView struct{} func (invalidView) Load() error { return errors.New("invalid view") } -func (i invalidView) Render(io.Writer, string, interface{}, ...string) error { panic("implement me") } +func (invalidView) Render(io.Writer, string, interface{}, ...string) error { panic("implement me") } // go test -run Test_App_Init_Error_View func Test_App_Init_Error_View(t *testing.T) { @@ -1405,7 +1407,9 @@ func Test_App_Init_Error_View(t *testing.T) { utils.AssertEqual(t, "implement me", fmt.Sprintf("%v", err)) } }() - _ = app.config.Views.Render(nil, "", nil) + + err := app.config.Views.Render(nil, "", nil) + utils.AssertEqual(t, nil, err) } // go test -run Test_App_Stack @@ -1535,11 +1539,12 @@ func Test_App_SmallReadBuffer(t *testing.T) { go func() { time.Sleep(500 * time.Millisecond) - resp, err := http.Get("http://127.0.0.1:4006/small-read-buffer") - if resp != nil { - utils.AssertEqual(t, 431, resp.StatusCode) - } + req, err := http.NewRequestWithContext(context.Background(), MethodGet, "http://127.0.0.1:4006/small-read-buffer", http.NoBody) utils.AssertEqual(t, nil, err) + var client http.Client + resp, err := client.Do(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 431, resp.StatusCode) utils.AssertEqual(t, nil, app.Shutdown()) }() @@ -1572,13 +1577,13 @@ func Test_App_New_Test_Parallel(t *testing.T) { t.Run("Test_App_New_Test_Parallel_1", func(t *testing.T) { t.Parallel() app := New(Config{Immutable: true}) - _, err := app.Test(httptest.NewRequest("GET", "/", nil)) + _, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) }) t.Run("Test_App_New_Test_Parallel_2", func(t *testing.T) { t.Parallel() app := New(Config{Immutable: true}) - _, err := app.Test(httptest.NewRequest("GET", "/", nil)) + _, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) }) } @@ -1591,7 +1596,7 @@ func Test_App_ReadBodyStream(t *testing.T) { return c.SendString(fmt.Sprintf("%v %s", c.Request().IsBodyStream(), c.Body())) }) testString := "this is a test" - resp, err := app.Test(httptest.NewRequest("POST", "/", bytes.NewBufferString(testString))) + resp, err := app.Test(httptest.NewRequest(MethodPost, "/", bytes.NewBufferString(testString))) utils.AssertEqual(t, nil, err, "app.Test(req)") body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "io.ReadAll(resp.Body)") @@ -1615,12 +1620,12 @@ func Test_App_DisablePreParseMultipartForm(t *testing.T) { } file, err := mpf.File["test"][0].Open() if err != nil { - return err + return fmt.Errorf("failed to open: %w", err) } buffer := make([]byte, len(testString)) n, err := file.Read(buffer) if err != nil { - return err + return fmt.Errorf("failed to read: %w", err) } if n != len(testString) { return fmt.Errorf("bad read length") @@ -1636,7 +1641,7 @@ func Test_App_DisablePreParseMultipartForm(t *testing.T) { utils.AssertEqual(t, len(testString), n, "writer n") utils.AssertEqual(t, nil, w.Close(), "w.Close()") - req := httptest.NewRequest("POST", "/", b) + req := httptest.NewRequest(MethodPost, "/", b) req.Header.Set("Content-Type", w.FormDataContentType()) resp, err := app.Test(req) utils.AssertEqual(t, nil, err, "app.Test(req)") @@ -1659,7 +1664,7 @@ func Test_App_Test_no_timeout_infinitely(t *testing.T) { return nil }) - req := httptest.NewRequest(http.MethodGet, "/", http.NoBody) + req := httptest.NewRequest(MethodGet, "/", http.NoBody) _, err = app.Test(req, -1) }() @@ -1696,7 +1701,7 @@ func Test_App_SetTLSHandler(t *testing.T) { func Test_App_AddCustomRequestMethod(t *testing.T) { t.Parallel() - methods := append(DefaultMethods, "TEST") + methods := append(DefaultMethods, "TEST") //nolint:gocritic // We want a new slice here app := New(Config{ RequestMethods: methods, }) diff --git a/client.go b/client.go index f2fbec41a0..334b99a890 100644 --- a/client.go +++ b/client.go @@ -15,6 +15,7 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -50,6 +51,7 @@ type Args = fasthttp.Args // Copy from fasthttp type RetryIfFunc = fasthttp.RetryIfFunc +//nolint:gochecknoglobals // TODO: Do not use a global var here var defaultClient Client // Client implements http client. @@ -186,11 +188,11 @@ func (a *Agent) Parse() error { uri := a.req.URI() - isTLS := false + var isTLS bool scheme := uri.Scheme() - if bytes.Equal(scheme, strHTTPS) { + if bytes.Equal(scheme, []byte(schemeHTTPS)) { isTLS = true - } else if !bytes.Equal(scheme, strHTTP) { + } else if !bytes.Equal(scheme, []byte(schemeHTTP)) { return fmt.Errorf("unsupported protocol %q. http and https are supported", scheme) } @@ -241,7 +243,7 @@ func (a *Agent) SetBytesV(k string, v []byte) *Agent { // SetBytesKV sets the given 'key: value' header. // // Use AddBytesKV for setting multiple header values under the same key. -func (a *Agent) SetBytesKV(k []byte, v []byte) *Agent { +func (a *Agent) SetBytesKV(k, v []byte) *Agent { a.req.Header.SetBytesKV(k, v) return a @@ -281,7 +283,7 @@ func (a *Agent) AddBytesV(k string, v []byte) *Agent { // // Multiple headers with the same key may be added with this function. // Use SetBytesKV for setting a single header for the given key. -func (a *Agent) AddBytesKV(k []byte, v []byte) *Agent { +func (a *Agent) AddBytesKV(k, v []byte) *Agent { a.req.Header.AddBytesKV(k, v) return a @@ -652,10 +654,8 @@ func (a *Agent) Reuse() *Agent { // certificate chain and host name. func (a *Agent) InsecureSkipVerify() *Agent { if a.HostClient.TLSConfig == nil { - /* #nosec G402 */ - a.HostClient.TLSConfig = &tls.Config{InsecureSkipVerify: true} // #nosec G402 + a.HostClient.TLSConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec // We explicitly let the user set insecure mode here } else { - /* #nosec G402 */ a.HostClient.TLSConfig.InsecureSkipVerify = true } @@ -728,14 +728,14 @@ func (a *Agent) RetryIf(retryIf RetryIfFunc) *Agent { // Bytes returns the status code, bytes body and errors of url. // // it's not safe to use Agent after calling [Agent.Bytes] -func (a *Agent) Bytes() (code int, body []byte, errs []error) { +func (a *Agent) Bytes() (int, []byte, []error) { defer a.release() return a.bytes() } -func (a *Agent) bytes() (code int, body []byte, errs []error) { +func (a *Agent) bytes() (code int, body []byte, errs []error) { //nolint:nonamedreturns,revive // We want to overwrite the body in a deferred func. TODO: Check if we really need to do this. We eventually want to get rid of all named returns. if errs = append(errs, a.errs...); len(errs) > 0 { - return + return code, body, errs } var ( @@ -760,7 +760,7 @@ func (a *Agent) bytes() (code int, body []byte, errs []error) { code = resp.StatusCode() } - body = append(a.dest, resp.Body()...) + body = append(a.dest, resp.Body()...) //nolint:gocritic // We want to append to the returned slice here if nilResp { ReleaseResponse(resp) @@ -770,25 +770,25 @@ func (a *Agent) bytes() (code int, body []byte, errs []error) { if a.timeout > 0 { if err := a.HostClient.DoTimeout(req, resp, a.timeout); err != nil { errs = append(errs, err) - return + return code, body, errs } } else if a.maxRedirectsCount > 0 && (string(req.Header.Method()) == MethodGet || string(req.Header.Method()) == MethodHead) { if err := a.HostClient.DoRedirects(req, resp, a.maxRedirectsCount); err != nil { errs = append(errs, err) - return + return code, body, errs } } else if err := a.HostClient.Do(req, resp); err != nil { errs = append(errs, err) } - return + return code, body, errs } func printDebugInfo(req *Request, resp *Response, w io.Writer) { msg := fmt.Sprintf("Connected to %s(%s)\r\n\r\n", req.URI().Host(), resp.RemoteAddr()) - _, _ = w.Write(utils.UnsafeBytes(msg)) - _, _ = req.WriteTo(w) - _, _ = resp.WriteTo(w) + _, _ = w.Write(utils.UnsafeBytes(msg)) //nolint:errcheck // This will never fail + _, _ = req.WriteTo(w) //nolint:errcheck // This will never fail + _, _ = resp.WriteTo(w) //nolint:errcheck // This will never fail } // String returns the status code, string body and errors of url. @@ -797,6 +797,7 @@ func printDebugInfo(req *Request, resp *Response, w io.Writer) { func (a *Agent) String() (int, string, []error) { defer a.release() code, body, errs := a.bytes() + // TODO: There might be a data race here on body. Maybe use utils.CopyBytes on it? return code, utils.UnsafeString(body), errs } @@ -805,12 +806,15 @@ func (a *Agent) String() (int, string, []error) { // And bytes body will be unmarshalled to given v. // // it's not safe to use Agent after calling [Agent.Struct] -func (a *Agent) Struct(v interface{}) (code int, body []byte, errs []error) { +func (a *Agent) Struct(v interface{}) (int, []byte, []error) { defer a.release() - if code, body, errs = a.bytes(); len(errs) > 0 { - return + + code, body, errs := a.bytes() + if len(errs) > 0 { + return code, body, errs } + // TODO: This should only be done once if a.jsonDecoder == nil { a.jsonDecoder = json.Unmarshal } @@ -819,7 +823,7 @@ func (a *Agent) Struct(v interface{}) (code int, body []byte, errs []error) { errs = append(errs, err) } - return + return code, body, errs } func (a *Agent) release() { @@ -855,6 +859,7 @@ func (a *Agent) reset() { a.formFiles = a.formFiles[:0] } +//nolint:gochecknoglobals // TODO: Do not use global vars here var ( clientPool sync.Pool agentPool = sync.Pool{ @@ -877,7 +882,11 @@ func AcquireClient() *Client { if v == nil { return &Client{} } - return v.(*Client) + c, ok := v.(*Client) + if !ok { + panic(fmt.Errorf("failed to type-assert to *Client")) + } + return c } // ReleaseClient returns c acquired via AcquireClient to client pool. @@ -899,7 +908,11 @@ func ReleaseClient(c *Client) { // no longer needed. This allows Agent recycling, reduces GC pressure // and usually improves performance. func AcquireAgent() *Agent { - return agentPool.Get().(*Agent) + a, ok := agentPool.Get().(*Agent) + if !ok { + panic(fmt.Errorf("failed to type-assert to *Agent")) + } + return a } // ReleaseAgent returns a acquired via AcquireAgent to Agent pool. @@ -922,7 +935,11 @@ func AcquireResponse() *Response { if v == nil { return &Response{} } - return v.(*Response) + r, ok := v.(*Response) + if !ok { + panic(fmt.Errorf("failed to type-assert to *Response")) + } + return r } // ReleaseResponse return resp acquired via AcquireResponse to response pool. @@ -945,7 +962,11 @@ func AcquireArgs() *Args { if v == nil { return &Args{} } - return v.(*Args) + a, ok := v.(*Args) + if !ok { + panic(fmt.Errorf("failed to type-assert to *Args")) + } + return a } // ReleaseArgs returns the object acquired via AcquireArgs to the pool. @@ -966,7 +987,11 @@ func AcquireFormFile() *FormFile { if v == nil { return &FormFile{} } - return v.(*FormFile) + ff, ok := v.(*FormFile) + if !ok { + panic(fmt.Errorf("failed to type-assert to *FormFile")) + } + return ff } // ReleaseFormFile returns the object acquired via AcquireFormFile to the pool. @@ -981,9 +1006,7 @@ func ReleaseFormFile(ff *FormFile) { formFilePool.Put(ff) } -var ( - strHTTP = []byte("http") - strHTTPS = []byte("https") +const ( defaultUserAgent = "fiber" ) diff --git a/client_test.go b/client_test.go index d9fd61a893..8df0562d61 100644 --- a/client_test.go +++ b/client_test.go @@ -1,3 +1,4 @@ +//nolint:wrapcheck // We must not wrap errors in tests package fiber import ( @@ -19,6 +20,7 @@ import ( "github.com/gofiber/fiber/v2/internal/tlstest" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp/fasthttputil" ) @@ -295,8 +297,10 @@ func Test_Client_Agent_Set_Or_Add_Headers(t *testing.T) { handler := func(c *Ctx) error { c.Request().Header.VisitAll(func(key, value []byte) { if k := string(key); k == "K1" || k == "K2" { - _, _ = c.Write(key) - _, _ = c.Write(value) + _, err := c.Write(key) + utils.AssertEqual(t, nil, err) + _, err = c.Write(value) + utils.AssertEqual(t, nil, err) } }) return nil @@ -581,25 +585,26 @@ type readErrorConn struct { net.Conn } -func (r *readErrorConn) Read(p []byte) (int, error) { +func (*readErrorConn) Read(_ []byte) (int, error) { return 0, fmt.Errorf("error") } -func (r *readErrorConn) Write(p []byte) (int, error) { +func (*readErrorConn) Write(p []byte) (int, error) { return len(p), nil } -func (r *readErrorConn) Close() error { +func (*readErrorConn) Close() error { return nil } -func (r *readErrorConn) LocalAddr() net.Addr { +func (*readErrorConn) LocalAddr() net.Addr { return nil } -func (r *readErrorConn) RemoteAddr() net.Addr { +func (*readErrorConn) RemoteAddr() net.Addr { return nil } + func Test_Client_Agent_RetryIf(t *testing.T) { t.Parallel() @@ -783,7 +788,10 @@ func Test_Client_Agent_MultipartForm_SendFiles(t *testing.T) { buf := make([]byte, fh1.Size) f, err := fh1.Open() utils.AssertEqual(t, nil, err) - defer func() { _ = f.Close() }() + defer func() { + err := f.Close() + utils.AssertEqual(t, nil, err) + }() _, err = f.Read(buf) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "form file", string(buf)) @@ -831,13 +839,16 @@ func checkFormFile(t *testing.T, fh *multipart.FileHeader, filename string) { basename := filepath.Base(filename) utils.AssertEqual(t, fh.Filename, basename) - b1, err := os.ReadFile(filename) + b1, err := os.ReadFile(filename) //nolint:gosec // We're in a test so reading user-provided files by name is fine utils.AssertEqual(t, nil, err) b2 := make([]byte, fh.Size) f, err := fh.Open() utils.AssertEqual(t, nil, err) - defer func() { _ = f.Close() }() + defer func() { + err := f.Close() + utils.AssertEqual(t, nil, err) + }() _, err = f.Read(b2) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, b1, b2) @@ -962,6 +973,7 @@ func Test_Client_Agent_InsecureSkipVerify(t *testing.T) { cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") utils.AssertEqual(t, nil, err) + //nolint:gosec // We're in a test so using old ciphers is fine serverTLSConf := &tls.Config{ Certificates: []tls.Certificate{cer}, } @@ -1137,7 +1149,7 @@ func Test_Client_Agent_Struct(t *testing.T) { defer ReleaseAgent(a) defer a.ConnectionClose() request := a.Request() - request.Header.SetMethod("GET") + request.Header.SetMethod(MethodGet) request.SetRequestURI("http://example.com") err := a.Parse() utils.AssertEqual(t, nil, err) @@ -1198,8 +1210,8 @@ type errorMultipartWriter struct { count int } -func (e *errorMultipartWriter) Boundary() string { return "myBoundary" } -func (e *errorMultipartWriter) SetBoundary(_ string) error { return nil } +func (*errorMultipartWriter) Boundary() string { return "myBoundary" } +func (*errorMultipartWriter) SetBoundary(_ string) error { return nil } func (e *errorMultipartWriter) CreateFormFile(_, _ string) (io.Writer, error) { if e.count == 0 { e.count++ @@ -1207,8 +1219,8 @@ func (e *errorMultipartWriter) CreateFormFile(_, _ string) (io.Writer, error) { } return errorWriter{}, nil } -func (e *errorMultipartWriter) WriteField(_, _ string) error { return errors.New("WriteField error") } -func (e *errorMultipartWriter) Close() error { return errors.New("Close error") } +func (*errorMultipartWriter) WriteField(_, _ string) error { return errors.New("WriteField error") } +func (*errorMultipartWriter) Close() error { return errors.New("Close error") } type errorWriter struct{} diff --git a/color.go b/color.go index cbccd2ebee..944a9b3695 100644 --- a/color.go +++ b/color.go @@ -53,6 +53,8 @@ type Colors struct { } // DefaultColors Default color codes +// +//nolint:gochecknoglobals // Using a global var is fine here var DefaultColors = Colors{ Black: "\u001b[90m", Red: "\u001b[91m", diff --git a/ctx.go b/ctx.go index 4d91d311b0..2b648fe8d7 100644 --- a/ctx.go +++ b/ctx.go @@ -27,10 +27,16 @@ import ( "github.com/gofiber/fiber/v2/internal/dictpool" "github.com/gofiber/fiber/v2/internal/schema" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) +const ( + schemeHTTP = "http" + schemeHTTPS = "https" +) + // maxParams defines the maximum number of parameters per route. const maxParams = 30 @@ -45,6 +51,7 @@ const ( // userContextKey define the key name for storing context.Context in *fasthttp.RequestCtx const userContextKey = "__local_user_context__" +//nolint:gochecknoglobals // TODO: Do not use global vars here var ( // decoderPoolMap helps to improve BodyParser's, QueryParser's and ReqHeaderParser's performance decoderPoolMap = map[string]*sync.Pool{} @@ -52,6 +59,7 @@ var ( tags = []string{queryTag, bodyTag, reqHeaderTag, paramsTag} ) +//nolint:gochecknoinits // init() is used to initialize a global map variable func init() { for _, tag := range tags { decoderPoolMap[tag] = &sync.Pool{New: func() interface{} { @@ -100,9 +108,10 @@ type TLSHandler struct { } // GetClientInfo Callback function to set CHI +// TODO: Why is this a getter which sets stuff? func (t *TLSHandler) GetClientInfo(info *tls.ClientHelloInfo) (*tls.Certificate, error) { t.clientHelloInfo = info - return nil, nil + return nil, nil //nolint:nilnil // Not returning anything useful here is probably fine } // Range data for c.Range @@ -151,7 +160,10 @@ type ParserConfig struct { // AcquireCtx retrieves a new Ctx from the pool. func (app *App) AcquireCtx(fctx *fasthttp.RequestCtx) *Ctx { - c := app.pool.Get().(*Ctx) + c, ok := app.pool.Get().(*Ctx) + if !ok { + panic(fmt.Errorf("failed to type-assert to *Ctx")) + } // Set app reference c.app = app // Reset route and handler index @@ -388,7 +400,6 @@ func (c *Ctx) BodyParser(out interface{}) error { } else { data[k] = append(data[k], v) } - }) return c.parseToStruct(bodyTag, out, data) @@ -401,7 +412,10 @@ func (c *Ctx) BodyParser(out interface{}) error { return c.parseToStruct(bodyTag, out, data.Value) } if strings.HasPrefix(ctype, MIMETextXML) || strings.HasPrefix(ctype, MIMEApplicationXML) { - return xml.Unmarshal(c.Body(), out) + if err := xml.Unmarshal(c.Body(), out); err != nil { + return fmt.Errorf("failed to unmarshal: %w", err) + } + return nil } // No suitable content type found return ErrUnprocessableEntity @@ -673,8 +687,11 @@ func (c *Ctx) Hostname() string { // Port returns the remote port of the request. func (c *Ctx) Port() string { - port := c.fasthttp.RemoteAddr().(*net.TCPAddr).Port - return strconv.Itoa(port) + tcpaddr, ok := c.fasthttp.RemoteAddr().(*net.TCPAddr) + if !ok { + panic(fmt.Errorf("failed to type-assert to *net.TCPAddr")) + } + return strconv.Itoa(tcpaddr.Port) } // IP returns the remote IP address of the request. @@ -691,13 +708,16 @@ func (c *Ctx) IP() string { // extractIPsFromHeader will return a slice of IPs it found given a header name in the order they appear. // When IP validation is enabled, any invalid IPs will be omitted. func (c *Ctx) extractIPsFromHeader(header string) []string { + // TODO: Reuse the c.extractIPFromHeader func somehow in here + headerValue := c.Get(header) // We can't know how many IPs we will return, but we will try to guess with this constant division. // Counting ',' makes function slower for about 50ns in general case. - estimatedCount := len(headerValue) / 8 - if estimatedCount > 8 { - estimatedCount = 8 // Avoid big allocation on big header + const maxEstimatedCount = 8 + estimatedCount := len(headerValue) / maxEstimatedCount + if estimatedCount > maxEstimatedCount { + estimatedCount = maxEstimatedCount // Avoid big allocation on big header } ipsFound := make([]string, 0, estimatedCount) @@ -707,11 +727,10 @@ func (c *Ctx) extractIPsFromHeader(header string) []string { iploop: for { - v4 := false - v6 := false + var v4, v6 bool // Manually splitting string without allocating slice, working with parts directly - i, j = j+1, j+2 + i, j = j+1, j+2 //nolint:gomnd // Using these values is fine if j > len(headerValue) { break @@ -758,9 +777,10 @@ func (c *Ctx) extractIPFromHeader(header string) string { iploop: for { - v4 := false - v6 := false - i, j = j+1, j+2 + var v4, v6 bool + + // Manually splitting string without allocating slice, working with parts directly + i, j = j+1, j+2 //nolint:gomnd // Using these values is fine if j > len(headerValue) { break @@ -793,14 +813,14 @@ func (c *Ctx) extractIPFromHeader(header string) string { return c.fasthttp.RemoteIP().String() } - // default behaviour if IP validation is not enabled is just to return whatever value is + // default behavior if IP validation is not enabled is just to return whatever value is // in the proxy header. Even if it is empty or invalid return c.Get(c.app.config.ProxyHeader) } // IPs returns a string slice of IP addresses specified in the X-Forwarded-For request header. // When IP validation is enabled, only valid IPs are returned. -func (c *Ctx) IPs() (ips []string) { +func (c *Ctx) IPs() []string { return c.extractIPsFromHeader(HeaderXForwardedFor) } @@ -839,7 +859,7 @@ func (c *Ctx) JSON(data interface{}) error { func (c *Ctx) JSONP(data interface{}, callback ...string) error { raw, err := json.Marshal(data) if err != nil { - return err + return fmt.Errorf("failed to marshal: %w", err) } var result, cb string @@ -877,11 +897,11 @@ func (c *Ctx) Links(link ...string) { bb := bytebufferpool.Get() for i := range link { if i%2 == 0 { - _ = bb.WriteByte('<') - _, _ = bb.WriteString(link[i]) - _ = bb.WriteByte('>') + _ = bb.WriteByte('<') //nolint:errcheck // This will never fail + _, _ = bb.WriteString(link[i]) //nolint:errcheck // This will never fail + _ = bb.WriteByte('>') //nolint:errcheck // This will never fail } else { - _, _ = bb.WriteString(`; rel="` + link[i] + `",`) + _, _ = bb.WriteString(`; rel="` + link[i] + `",`) //nolint:errcheck // This will never fail } } c.setCanonical(HeaderLink, utils.TrimRight(c.app.getString(bb.Bytes()), ',')) @@ -890,7 +910,7 @@ func (c *Ctx) Links(link ...string) { // Locals makes it possible to pass interface{} values under keys scoped to the request // and therefore available to all following routes that match the request. -func (c *Ctx) Locals(key interface{}, value ...interface{}) (val interface{}) { +func (c *Ctx) Locals(key interface{}, value ...interface{}) interface{} { if len(value) == 0 { return c.fasthttp.UserValue(key) } @@ -933,9 +953,10 @@ func (c *Ctx) ClientHelloInfo() *tls.ClientHelloInfo { } // Next executes the next method in the stack that matches the current route. -func (c *Ctx) Next() (err error) { +func (c *Ctx) Next() error { // Increment handler index c.indexHandler++ + var err error // Did we executed all route handlers? if c.indexHandler < len(c.route.Handlers) { // Continue route stack @@ -947,7 +968,7 @@ func (c *Ctx) Next() (err error) { return err } -// RestartRouting instead of going to the next handler. This may be usefull after +// RestartRouting instead of going to the next handler. This may be useful after // changing the request path. Note that handlers might be executed again. func (c *Ctx) RestartRouting() error { c.indexRoute = -1 @@ -1017,9 +1038,8 @@ func (c *Ctx) ParamsInt(key string, defaultValue ...int) (int, error) { if err != nil { if len(defaultValue) > 0 { return defaultValue[0], nil - } else { - return 0, err } + return 0, fmt.Errorf("failed to convert: %w", err) } return value, nil @@ -1044,15 +1064,16 @@ func (c *Ctx) Path(override ...string) string { // Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. func (c *Ctx) Protocol() string { if c.fasthttp.IsTLS() { - return "https" + return schemeHTTPS } if !c.IsProxyTrusted() { - return "http" + return schemeHTTP } - scheme := "http" + scheme := schemeHTTP + const lenXHeaderName = 12 c.fasthttp.Request.Header.VisitAll(func(key, val []byte) { - if len(key) < 12 { + if len(key) < lenXHeaderName { return // Neither "X-Forwarded-" nor "X-Url-Scheme" } switch { @@ -1067,7 +1088,7 @@ func (c *Ctx) Protocol() string { scheme = v } } else if bytes.Equal(key, []byte(HeaderXForwardedSsl)) && bytes.Equal(val, []byte("on")) { - scheme = "https" + scheme = schemeHTTPS } case bytes.Equal(key, []byte(HeaderXUrlScheme)): @@ -1100,9 +1121,8 @@ func (c *Ctx) QueryInt(key string, defaultValue ...int) int { if err != nil { if len(defaultValue) > 0 { return defaultValue[0] - } else { - return 0 } + return 0 } return value @@ -1133,7 +1153,6 @@ func (c *Ctx) QueryParser(out interface{}) error { } else { data[k] = append(data[k], v) } - }) if err != nil { @@ -1150,10 +1169,9 @@ func parseParamSquareBrackets(k string) (string, error) { kbytes := []byte(k) for i, b := range kbytes { - if b == '[' && kbytes[i+1] != ']' { if err := bb.WriteByte('.'); err != nil { - return "", err + return "", fmt.Errorf("failed to write: %w", err) } } @@ -1162,7 +1180,7 @@ func parseParamSquareBrackets(k string) (string, error) { } if err := bb.WriteByte(b); err != nil { - return "", err + return "", fmt.Errorf("failed to write: %w", err) } } @@ -1184,21 +1202,27 @@ func (c *Ctx) ReqHeaderParser(out interface{}) error { } else { data[k] = append(data[k], v) } - }) return c.parseToStruct(reqHeaderTag, out, data) } -func (c *Ctx) parseToStruct(aliasTag string, out interface{}, data map[string][]string) error { +func (*Ctx) parseToStruct(aliasTag string, out interface{}, data map[string][]string) error { // Get decoder from pool - schemaDecoder := decoderPoolMap[aliasTag].Get().(*schema.Decoder) + schemaDecoder, ok := decoderPoolMap[aliasTag].Get().(*schema.Decoder) + if !ok { + panic(fmt.Errorf("failed to type-assert to *schema.Decoder")) + } defer decoderPoolMap[aliasTag].Put(schemaDecoder) // Set alias tag schemaDecoder.SetAliasTag(aliasTag) - return schemaDecoder.Decode(out, data) + if err := schemaDecoder.Decode(out, data); err != nil { + return fmt.Errorf("failed to decode: %w", err) + } + + return nil } func equalFieldType(out interface{}, kind reflect.Kind, key string) bool { @@ -1248,24 +1272,23 @@ var ( ) // Range returns a struct containing the type and a slice of ranges. -func (c *Ctx) Range(size int) (rangeData Range, err error) { +func (c *Ctx) Range(size int) (Range, error) { + var rangeData Range rangeStr := c.Get(HeaderRange) if rangeStr == "" || !strings.Contains(rangeStr, "=") { - err = ErrRangeMalformed - return + return rangeData, ErrRangeMalformed } data := strings.Split(rangeStr, "=") - if len(data) != 2 { - err = ErrRangeMalformed - return + const expectedDataParts = 2 + if len(data) != expectedDataParts { + return rangeData, ErrRangeMalformed } rangeData.Type = data[0] arr := strings.Split(data[1], ",") for i := 0; i < len(arr); i++ { item := strings.Split(arr[i], "-") if len(item) == 1 { - err = ErrRangeMalformed - return + return rangeData, ErrRangeMalformed } start, startErr := strconv.Atoi(item[0]) end, endErr := strconv.Atoi(item[1]) @@ -1290,11 +1313,10 @@ func (c *Ctx) Range(size int) (rangeData Range, err error) { }) } if len(rangeData.Ranges) < 1 { - err = ErrRangeUnsatisfiable - return + return rangeData, ErrRangeUnsatisfiable } - return + return rangeData, nil } // Redirect to the URL derived from the specified path, with specified status. @@ -1330,7 +1352,7 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { if !segment.IsParam { _, err := buf.WriteString(segment.Const) if err != nil { - return "", err + return "", fmt.Errorf("failed to write string: %w", err) } continue } @@ -1341,7 +1363,7 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { if isSame || isGreedy { _, err := buf.WriteString(utils.ToString(val)) if err != nil { - return "", err + return "", fmt.Errorf("failed to write string: %w", err) } } } @@ -1373,10 +1395,10 @@ func (c *Ctx) RedirectToRoute(routeName string, params Map, status ...int) error i := 1 for k, v := range queries { - _, _ = queryText.WriteString(k + "=" + v) + _, _ = queryText.WriteString(k + "=" + v) //nolint:errcheck // This will never fail if i != len(queries) { - _, _ = queryText.WriteString("&") + _, _ = queryText.WriteString("&") //nolint:errcheck // This will never fail } i++ } @@ -1399,7 +1421,6 @@ func (c *Ctx) RedirectBack(fallback string, status ...int) error { // Render a template with data and sends a text/html response. // We support the following engines: html, amber, handlebars, mustache, pug func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error { - var err error // Get new buffer from pool buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) @@ -1421,7 +1442,7 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error { // Render template from Views if app.config.Views != nil { if err := app.config.Views.Render(buf, name, bind, layouts...); err != nil { - return err + return fmt.Errorf("failed to render: %w", err) } rendered = true @@ -1433,17 +1454,18 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error { if !rendered { // Render raw template using 'name' as filepath if no engine is set var tmpl *template.Template - if _, err = readContent(buf, name); err != nil { + if _, err := readContent(buf, name); err != nil { return err } // Parse template - if tmpl, err = template.New("").Parse(c.app.getString(buf.Bytes())); err != nil { - return err + tmpl, err := template.New("").Parse(c.app.getString(buf.Bytes())) + if err != nil { + return fmt.Errorf("failed to parse: %w", err) } buf.Reset() // Render template - if err = tmpl.Execute(buf, bind); err != nil { - return err + if err := tmpl.Execute(buf, bind); err != nil { + return fmt.Errorf("failed to execute: %w", err) } } @@ -1451,8 +1473,8 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error { c.fasthttp.Response.Header.SetContentType(MIMETextHTMLCharsetUTF8) // Set rendered template to body c.fasthttp.Response.SetBody(buf.Bytes()) - // Return err if exist - return err + + return nil } func (c *Ctx) renderExtensions(bind interface{}) { @@ -1501,28 +1523,32 @@ func (c *Ctx) Route() *Route { } // SaveFile saves any multipart file to disk. -func (c *Ctx) SaveFile(fileheader *multipart.FileHeader, path string) error { +func (*Ctx) SaveFile(fileheader *multipart.FileHeader, path string) error { return fasthttp.SaveMultipartFile(fileheader, path) } // SaveFileToStorage saves any multipart file to an external storage system. -func (c *Ctx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, storage Storage) error { +func (*Ctx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, storage Storage) error { file, err := fileheader.Open() if err != nil { - return err + return fmt.Errorf("failed to open: %w", err) } content, err := io.ReadAll(file) if err != nil { - return err + return fmt.Errorf("failed to read: %w", err) + } + + if err := storage.Set(path, content, 0); err != nil { + return fmt.Errorf("failed to store: %w", err) } - return storage.Set(path, content, 0) + return nil } // Secure returns whether a secure connection was established. func (c *Ctx) Secure() bool { - return c.Protocol() == "https" + return c.Protocol() == schemeHTTPS } // Send sets the HTTP response body without copying it. @@ -1533,6 +1559,7 @@ func (c *Ctx) Send(body []byte) error { return nil } +//nolint:gochecknoglobals // TODO: Do not use global vars here var ( sendFileOnce sync.Once sendFileFS *fasthttp.FS @@ -1548,6 +1575,7 @@ func (c *Ctx) SendFile(file string, compress ...bool) error { // https://github.com/valyala/fasthttp/blob/c7576cc10cabfc9c993317a2d3f8355497bea156/fs.go#L129-L134 sendFileOnce.Do(func() { + const cacheDuration = 10 * time.Second sendFileFS = &fasthttp.FS{ Root: "", AllowEmptyRoot: true, @@ -1555,7 +1583,7 @@ func (c *Ctx) SendFile(file string, compress ...bool) error { AcceptByteRange: true, Compress: true, CompressedFileSuffix: c.app.config.CompressedFileSuffix, - CacheDuration: 10 * time.Second, + CacheDuration: cacheDuration, IndexNames: []string{"index.html"}, PathNotFound: func(ctx *fasthttp.RequestCtx) { ctx.Response.SetStatusCode(StatusNotFound) @@ -1579,7 +1607,7 @@ func (c *Ctx) SendFile(file string, compress ...bool) error { var err error file = filepath.FromSlash(file) if file, err = filepath.Abs(file); err != nil { - return err + return fmt.Errorf("failed to determine abs file path: %w", err) } if hasTrailingSlash { file += "/" @@ -1644,11 +1672,11 @@ func (c *Ctx) SendStream(stream io.Reader, size ...int) error { } // Set sets the response's HTTP header field to the specified key, value. -func (c *Ctx) Set(key string, val string) { +func (c *Ctx) Set(key, val string) { c.fasthttp.Response.Header.Set(key, val) } -func (c *Ctx) setCanonical(key string, val string) { +func (c *Ctx) setCanonical(key, val string) { c.fasthttp.Response.Header.SetCanonical(c.app.getBytes(key), c.app.getBytes(val)) } @@ -1719,6 +1747,7 @@ func (c *Ctx) Write(p []byte) (int, error) { // Writef appends f & a into response body writer. func (c *Ctx) Writef(f string, a ...interface{}) (int, error) { + //nolint:wrapcheck // This must not be wrapped return fmt.Fprintf(c.fasthttp.Response.BodyWriter(), f, a...) } @@ -1760,8 +1789,9 @@ func (c *Ctx) configDependentPaths() { // Define the path for dividing routes into areas for fast tree detection, so that fewer routes need to be traversed, // since the first three characters area select a list of routes c.treePath = c.treePath[0:0] - if len(c.detectionPath) >= 3 { - c.treePath = c.detectionPath[:3] + const maxDetectionPaths = 3 + if len(c.detectionPath) >= maxDetectionPaths { + c.treePath = c.detectionPath[:maxDetectionPaths] } } @@ -1786,7 +1816,7 @@ func (c *Ctx) IsProxyTrusted() bool { } // IsLocalHost will return true if address is a localhost address. -func (c *Ctx) isLocalHost(address string) bool { +func (*Ctx) isLocalHost(address string) bool { localHosts := []string{"127.0.0.1", "0.0.0.0", "::1"} for _, h := range localHosts { if strings.Contains(address, h) { diff --git a/ctx_test.go b/ctx_test.go index b8ae027bd5..7fc59dc89c 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2,6 +2,7 @@ // 🤖 Github Repository: https://github.com/gofiber/fiber // 📌 API Documentation: https://docs.gofiber.io +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package fiber import ( @@ -28,6 +29,7 @@ import ( "github.com/gofiber/fiber/v2/internal/storage/memory" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) @@ -361,8 +363,10 @@ func Test_Ctx_BodyParser(t *testing.T) { { var gzipJSON bytes.Buffer w := gzip.NewWriter(&gzipJSON) - _, _ = w.Write([]byte(`{"name":"john"}`)) - _ = w.Close() + _, err := w.Write([]byte(`{"name":"john"}`)) + utils.AssertEqual(t, nil, err) + err = w.Close() + utils.AssertEqual(t, nil, err) c.Request().Header.SetContentType(MIMEApplicationJSON) c.Request().Header.Set(HeaderContentEncoding, "gzip") @@ -431,9 +435,7 @@ func Test_Ctx_ParamParser(t *testing.T) { UserID uint `params:"userId"` RoleID uint `params:"roleId"` } - var ( - d = new(Demo) - ) + d := new(Demo) if err := ctx.ParamsParser(d); err != nil { t.Fatal(err) } @@ -519,7 +521,7 @@ func Benchmark_Ctx_BodyParser_JSON(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.BodyParser(d) + _ = c.BodyParser(d) //nolint:errcheck // It is fine to ignore the error here as we check it once further below } utils.AssertEqual(b, nil, c.BodyParser(d)) utils.AssertEqual(b, "john", d.Name) @@ -543,7 +545,7 @@ func Benchmark_Ctx_BodyParser_XML(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.BodyParser(d) + _ = c.BodyParser(d) //nolint:errcheck // It is fine to ignore the error here as we check it once further below } utils.AssertEqual(b, nil, c.BodyParser(d)) utils.AssertEqual(b, "john", d.Name) @@ -567,7 +569,7 @@ func Benchmark_Ctx_BodyParser_Form(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.BodyParser(d) + _ = c.BodyParser(d) //nolint:errcheck // It is fine to ignore the error here as we check it once further below } utils.AssertEqual(b, nil, c.BodyParser(d)) utils.AssertEqual(b, "john", d.Name) @@ -592,7 +594,7 @@ func Benchmark_Ctx_BodyParser_MultipartForm(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.BodyParser(d) + _ = c.BodyParser(d) //nolint:errcheck // It is fine to ignore the error here as we check it once further below } utils.AssertEqual(b, nil, c.BodyParser(d)) utils.AssertEqual(b, "john", d.Name) @@ -879,12 +881,13 @@ func Test_Ctx_FormFile(t *testing.T) { f, err := fh.Open() utils.AssertEqual(t, nil, err) + defer func() { + utils.AssertEqual(t, nil, f.Close()) + }() b := new(bytes.Buffer) _, err = io.Copy(b, f) utils.AssertEqual(t, nil, err) - - f.Close() utils.AssertEqual(t, "hello world", b.String()) return nil }) @@ -897,8 +900,7 @@ func Test_Ctx_FormFile(t *testing.T) { _, err = ioWriter.Write([]byte("hello world")) utils.AssertEqual(t, nil, err) - - writer.Close() + utils.AssertEqual(t, nil, writer.Close()) req := httptest.NewRequest(MethodPost, "/test", body) req.Header.Set(HeaderContentType, writer.FormDataContentType()) @@ -921,10 +923,9 @@ func Test_Ctx_FormValue(t *testing.T) { body := &bytes.Buffer{} writer := multipart.NewWriter(body) - utils.AssertEqual(t, nil, writer.WriteField("name", "john")) + utils.AssertEqual(t, nil, writer.Close()) - writer.Close() req := httptest.NewRequest(MethodPost, "/test", body) req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%s", writer.Boundary())) req.Header.Set("Content-Length", strconv.Itoa(len(body.Bytes()))) @@ -1240,7 +1241,7 @@ func Test_Ctx_IP(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - // default behaviour will return the remote IP from the stack + // default behavior will return the remote IP from the stack utils.AssertEqual(t, "0.0.0.0", c.IP()) // X-Forwarded-For is set, but it is ignored because proxyHeader is not set @@ -1252,7 +1253,7 @@ func Test_Ctx_IP(t *testing.T) { func Test_Ctx_IP_ProxyHeader(t *testing.T) { t.Parallel() - // make sure that the same behaviour exists for different proxy header names + // make sure that the same behavior exists for different proxy header names proxyHeaderNames := []string{"Real-Ip", HeaderXForwardedFor} for _, proxyHeaderName := range proxyHeaderNames { @@ -1286,7 +1287,7 @@ func Test_Ctx_IP_ProxyHeader(t *testing.T) { func Test_Ctx_IP_ProxyHeader_With_IP_Validation(t *testing.T) { t.Parallel() - // make sure that the same behaviour exists for different proxy header names + // make sure that the same behavior exists for different proxy header names proxyHeaderNames := []string{"Real-Ip", HeaderXForwardedFor} for _, proxyHeaderName := range proxyHeaderNames { @@ -1625,35 +1626,43 @@ func Test_Ctx_ClientHelloInfo(t *testing.T) { }) // Test without TLS handler - resp, _ := app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) - body, _ := io.ReadAll(resp.Body) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) + utils.AssertEqual(t, nil, err) + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, []byte("ClientHelloInfo is nil"), body) // Test with TLS Handler const ( - PSSWithSHA256 = 0x0804 - VersionTLS13 = 0x0304 + pssWithSHA256 = 0x0804 + versionTLS13 = 0x0304 ) app.tlsHandler = &TLSHandler{clientHelloInfo: &tls.ClientHelloInfo{ ServerName: "example.golang", - SignatureSchemes: []tls.SignatureScheme{PSSWithSHA256}, - SupportedVersions: []uint16{VersionTLS13}, + SignatureSchemes: []tls.SignatureScheme{pssWithSHA256}, + SupportedVersions: []uint16{versionTLS13}, }} // Test ServerName - resp, _ = app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) - body, _ = io.ReadAll(resp.Body) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) + utils.AssertEqual(t, nil, err) + body, err = io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, []byte("example.golang"), body) // Test SignatureSchemes - resp, _ = app.Test(httptest.NewRequest(MethodGet, "/SignatureSchemes", nil)) - body, _ = io.ReadAll(resp.Body) - utils.AssertEqual(t, "["+strconv.Itoa(PSSWithSHA256)+"]", string(body)) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/SignatureSchemes", nil)) + utils.AssertEqual(t, nil, err) + body, err = io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "["+strconv.Itoa(pssWithSHA256)+"]", string(body)) // Test SupportedVersions - resp, _ = app.Test(httptest.NewRequest(MethodGet, "/SupportedVersions", nil)) - body, _ = io.ReadAll(resp.Body) - utils.AssertEqual(t, "["+strconv.Itoa(VersionTLS13)+"]", string(body)) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/SupportedVersions", nil)) + utils.AssertEqual(t, nil, err) + body, err = io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "["+strconv.Itoa(versionTLS13)+"]", string(body)) } // go test -run Test_Ctx_InvalidMethod @@ -1688,10 +1697,9 @@ func Test_Ctx_MultipartForm(t *testing.T) { body := &bytes.Buffer{} writer := multipart.NewWriter(body) - utils.AssertEqual(t, nil, writer.WriteField("name", "john")) + utils.AssertEqual(t, nil, writer.Close()) - writer.Close() req := httptest.NewRequest(MethodPost, "/test", body) req.Header.Set(HeaderContentType, fmt.Sprintf("multipart/form-data; boundary=%s", writer.Boundary())) req.Header.Set(HeaderContentLength, strconv.Itoa(len(body.Bytes()))) @@ -1706,8 +1714,8 @@ func Benchmark_Ctx_MultipartForm(b *testing.B) { app := New() app.Post("/", func(c *Ctx) error { - _, _ = c.MultipartForm() - return nil + _, err := c.MultipartForm() + return err }) c := &fasthttp.RequestCtx{} @@ -1889,11 +1897,16 @@ func Benchmark_Ctx_AllParams(b *testing.B) { for n := 0; n < b.N; n++ { res = c.AllParams() } - utils.AssertEqual(b, map[string]string{"param1": "john", - "param2": "doe", - "param3": "is", - "param4": "awesome"}, - res) + utils.AssertEqual( + b, + map[string]string{ + "param1": "john", + "param2": "doe", + "param3": "is", + "param4": "awesome", + }, + res, + ) } // go test -v -run=^$ -bench=Benchmark_Ctx_ParamsParse -benchmem -count=4 @@ -1964,31 +1977,31 @@ func Test_Ctx_Protocol(t *testing.T) { c := app.AcquireCtx(freq) defer app.ReleaseCtx(c) - c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedProto, "https, http") - utils.AssertEqual(t, "https", c.Protocol()) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedProtocol, "https, http") - utils.AssertEqual(t, "https", c.Protocol()) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "https", c.Protocol()) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) } // go test -v -run=^$ -bench=Benchmark_Ctx_Protocol -benchmem -count=4 @@ -2002,7 +2015,7 @@ func Benchmark_Ctx_Protocol(b *testing.B) { for n := 0; n < b.N; n++ { res = c.Protocol() } - utils.AssertEqual(b, "http", res) + utils.AssertEqual(b, schemeHTTP, res) } // go test -run Test_Ctx_Protocol_TrustedProxy @@ -2012,23 +2025,23 @@ func Test_Ctx_Protocol_TrustedProxy(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "https", c.Protocol()) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) } // go test -run Test_Ctx_Protocol_TrustedProxyRange @@ -2038,23 +2051,23 @@ func Test_Ctx_Protocol_TrustedProxyRange(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "https", c.Protocol()) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) } // go test -run Test_Ctx_Protocol_UntrustedProxyRange @@ -2064,23 +2077,23 @@ func Test_Ctx_Protocol_UntrustedProxyRange(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "http", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "http", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "http", c.Protocol()) + c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) } // go test -run Test_Ctx_Protocol_UnTrustedProxy @@ -2090,23 +2103,23 @@ func Test_Ctx_Protocol_UnTrustedProxy(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "http", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "http", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "http", c.Protocol()) + c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) } // go test -run Test_Ctx_Query @@ -2224,7 +2237,12 @@ func Test_Ctx_SaveFile(t *testing.T) { tempFile, err := os.CreateTemp(os.TempDir(), "test-") utils.AssertEqual(t, nil, err) - defer os.Remove(tempFile.Name()) + defer func(file *os.File) { + err := file.Close() + utils.AssertEqual(t, nil, err) + err = os.Remove(file.Name()) + utils.AssertEqual(t, nil, err) + }(tempFile) err = c.SaveFile(fh, tempFile.Name()) utils.AssertEqual(t, nil, err) @@ -2242,7 +2260,7 @@ func Test_Ctx_SaveFile(t *testing.T) { _, err = ioWriter.Write([]byte("hello world")) utils.AssertEqual(t, nil, err) - writer.Close() + utils.AssertEqual(t, nil, writer.Close()) req := httptest.NewRequest(MethodPost, "/test", body) req.Header.Set("Content-Type", writer.FormDataContentType()) @@ -2284,7 +2302,7 @@ func Test_Ctx_SaveFileToStorage(t *testing.T) { _, err = ioWriter.Write([]byte("hello world")) utils.AssertEqual(t, nil, err) - writer.Close() + utils.AssertEqual(t, nil, writer.Close()) req := httptest.NewRequest(MethodPost, "/test", body) req.Header.Set("Content-Type", writer.FormDataContentType()) @@ -2370,7 +2388,9 @@ func Test_Ctx_Download(t *testing.T) { f, err := os.Open("./ctx.go") utils.AssertEqual(t, nil, err) - defer f.Close() + defer func() { + utils.AssertEqual(t, nil, f.Close()) + }() expect, err := io.ReadAll(f) utils.AssertEqual(t, nil, err) @@ -2389,7 +2409,9 @@ func Test_Ctx_SendFile(t *testing.T) { // fetch file content f, err := os.Open("./ctx.go") utils.AssertEqual(t, nil, err) - defer f.Close() + defer func() { + utils.AssertEqual(t, nil, f.Close()) + }() expectFileContent, err := io.ReadAll(f) utils.AssertEqual(t, nil, err) // fetch file info for the not modified test case @@ -2435,7 +2457,7 @@ func Test_Ctx_SendFile_404(t *testing.T) { return err }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, StatusNotFound, resp.StatusCode) } @@ -2473,11 +2495,11 @@ func Test_Ctx_SendFile_Immutable(t *testing.T) { for _, endpoint := range endpointsForTest { t.Run(endpoint, func(t *testing.T) { // 1st try - resp, err := app.Test(httptest.NewRequest("GET", endpoint, nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, endpoint, nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, StatusOK, resp.StatusCode) // 2nd try - resp, err = app.Test(httptest.NewRequest("GET", endpoint, nil)) + resp, err = app.Test(httptest.NewRequest(MethodGet, endpoint, nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, StatusOK, resp.StatusCode) }) @@ -2495,9 +2517,9 @@ func Test_Ctx_SendFile_RestoreOriginalURL(t *testing.T) { return err }) - _, err1 := app.Test(httptest.NewRequest("GET", "/?test=true", nil)) + _, err1 := app.Test(httptest.NewRequest(MethodGet, "/?test=true", nil)) // second request required to confirm with zero allocation - _, err2 := app.Test(httptest.NewRequest("GET", "/?test=true", nil)) + _, err2 := app.Test(httptest.NewRequest(MethodGet, "/?test=true", nil)) utils.AssertEqual(t, nil, err1) utils.AssertEqual(t, nil, err2) @@ -2893,12 +2915,12 @@ func Test_Ctx_Render(t *testing.T) { err := c.Render("./.github/testdata/index.tmpl", Map{ "Title": "Hello, World!", }) + utils.AssertEqual(t, nil, err) buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") + _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

Hello, World!

", string(c.Response().Body())) err = c.Render("./.github/testdata/template-non-exists.html", nil) @@ -2918,12 +2940,12 @@ func Test_Ctx_RenderWithoutLocals(t *testing.T) { c.Locals("Title", "Hello, World!") defer app.ReleaseCtx(c) err := c.Render("./.github/testdata/index.tmpl", Map{}) + utils.AssertEqual(t, nil, err) buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") + _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

", string(c.Response().Body())) } @@ -2937,14 +2959,13 @@ func Test_Ctx_RenderWithLocals(t *testing.T) { c.Locals("Title", "Hello, World!") defer app.ReleaseCtx(c) err := c.Render("./.github/testdata/index.tmpl", Map{}) + utils.AssertEqual(t, nil, err) buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") + _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

Hello, World!

", string(c.Response().Body())) - } func Test_Ctx_RenderWithBind(t *testing.T) { @@ -2959,14 +2980,13 @@ func Test_Ctx_RenderWithBind(t *testing.T) { utils.AssertEqual(t, nil, err) defer app.ReleaseCtx(c) err = c.Render("./.github/testdata/index.tmpl", Map{}) + utils.AssertEqual(t, nil, err) buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") + _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

Hello, World!

", string(c.Response().Body())) - } func Test_Ctx_RenderWithOverwrittenBind(t *testing.T) { @@ -2982,12 +3002,12 @@ func Test_Ctx_RenderWithOverwrittenBind(t *testing.T) { err = c.Render("./.github/testdata/index.tmpl", Map{ "Title": "Hello from Fiber!", }) + utils.AssertEqual(t, nil, err) buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") + _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

Hello from Fiber!

", string(c.Response().Body())) } @@ -3005,13 +3025,12 @@ func Test_Ctx_RenderWithBindLocals(t *testing.T) { utils.AssertEqual(t, nil, err) c.Locals("Summary", "Test") - defer app.ReleaseCtx(c) - err = c.Render("./.github/testdata/template.tmpl", Map{}) + err = c.Render("./.github/testdata/template.tmpl", Map{}) utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

Hello, World! Test

", string(c.Response().Body())) + utils.AssertEqual(t, "

Hello, World! Test

", string(c.Response().Body())) } func Test_Ctx_RenderWithLocalsAndBinding(t *testing.T) { @@ -3027,11 +3046,12 @@ func Test_Ctx_RenderWithLocalsAndBinding(t *testing.T) { c.Locals("Title", "This is a test.") defer app.ReleaseCtx(c) + err = c.Render("index.tmpl", Map{ "Title": "Hello, World!", }) - utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "

Hello, World!

", string(c.Response().Body())) } @@ -3060,8 +3080,8 @@ func Benchmark_Ctx_RenderWithLocalsAndBinding(b *testing.B) { for n := 0; n < b.N; n++ { err = c.Render("template.tmpl", Map{}) } - utils.AssertEqual(b, nil, err) + utils.AssertEqual(b, "

Hello, World! Test

", string(c.Response().Body())) } @@ -3083,8 +3103,8 @@ func Benchmark_Ctx_RedirectToRoute(b *testing.B) { "name": "fiber", }) } - utils.AssertEqual(b, nil, err) + utils.AssertEqual(b, 302, c.Response().StatusCode()) utils.AssertEqual(b, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } @@ -3108,8 +3128,8 @@ func Benchmark_Ctx_RedirectToRouteWithQueries(b *testing.B) { "queries": map[string]string{"a": "a", "b": "b"}, }) } - utils.AssertEqual(b, nil, err) + utils.AssertEqual(b, 302, c.Response().StatusCode()) // analysis of query parameters with url parsing, since a map pass is always randomly ordered location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) @@ -3138,8 +3158,8 @@ func Benchmark_Ctx_RenderLocals(b *testing.B) { for n := 0; n < b.N; n++ { err = c.Render("index.tmpl", Map{}) } - utils.AssertEqual(b, nil, err) + utils.AssertEqual(b, "

Hello, World!

", string(c.Response().Body())) } @@ -3164,8 +3184,8 @@ func Benchmark_Ctx_RenderBind(b *testing.B) { for n := 0; n < b.N; n++ { err = c.Render("index.tmpl", Map{}) } - utils.AssertEqual(b, nil, err) + utils.AssertEqual(b, "

Hello, World!

", string(c.Response().Body())) } @@ -3191,8 +3211,7 @@ func Test_Ctx_RestartRouting(t *testing.T) { func Test_Ctx_RestartRoutingWithChangedPath(t *testing.T) { t.Parallel() app := New() - executedOldHandler := false - executedNewHandler := false + var executedOldHandler, executedNewHandler bool app.Get("/old", func(c *Ctx) error { c.Path("/new") @@ -3242,10 +3261,18 @@ type testTemplateEngine struct { func (t *testTemplateEngine) Render(w io.Writer, name string, bind interface{}, layout ...string) error { if len(layout) == 0 { - return t.templates.ExecuteTemplate(w, name, bind) + if err := t.templates.ExecuteTemplate(w, name, bind); err != nil { + return fmt.Errorf("failed to execute template without layout: %w", err) + } + return nil + } + if err := t.templates.ExecuteTemplate(w, name, bind); err != nil { + return fmt.Errorf("failed to execute template: %w", err) } - _ = t.templates.ExecuteTemplate(w, name, bind) - return t.templates.ExecuteTemplate(w, layout[0], bind) + if err := t.templates.ExecuteTemplate(w, layout[0], bind); err != nil { + return fmt.Errorf("failed to execute template with layout: %w", err) + } + return nil } func (t *testTemplateEngine) Load() error { @@ -3324,7 +3351,6 @@ func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) { } utils.AssertEqual(b, "/user/fiber", location) utils.AssertEqual(b, nil, err) - } // go test -run Test_Ctx_Get_Location_From_Route_name @@ -3407,11 +3433,11 @@ func Test_Ctx_Get_Location_From_Route_name_Optional_greedy_one_param(t *testing. type errorTemplateEngine struct{} -func (t errorTemplateEngine) Render(w io.Writer, name string, bind interface{}, layout ...string) error { +func (errorTemplateEngine) Render(_ io.Writer, _ string, _ interface{}, _ ...string) error { return errors.New("errorTemplateEngine") } -func (t errorTemplateEngine) Load() error { return nil } +func (errorTemplateEngine) Load() error { return nil } // go test -run Test_Ctx_Render_Engine_Error func Test_Ctx_Render_Engine_Error(t *testing.T) { @@ -3429,7 +3455,10 @@ func Test_Ctx_Render_Go_Template(t *testing.T) { t.Parallel() file, err := os.CreateTemp(os.TempDir(), "fiber") utils.AssertEqual(t, nil, err) - defer os.Remove(file.Name()) + defer func() { + err := os.Remove(file.Name()) + utils.AssertEqual(t, nil, err) + }() _, err = file.Write([]byte("template")) utils.AssertEqual(t, nil, err) @@ -3821,7 +3850,7 @@ func Test_Ctx_QueryParser(t *testing.T) { } rq := new(RequiredQuery) c.Request().URI().SetQueryString("") - utils.AssertEqual(t, "name is empty", c.QueryParser(rq).Error()) + utils.AssertEqual(t, "failed to decode: name is empty", c.QueryParser(rq).Error()) type ArrayQuery struct { Data []string @@ -3837,7 +3866,7 @@ func Test_Ctx_QueryParser_WithSetParserDecoder(t *testing.T) { t.Parallel() type NonRFCTime time.Time - NonRFCConverter := func(value string) reflect.Value { + nonRFCConverter := func(value string) reflect.Value { if v, err := time.Parse("2006-01-02", value); err == nil { return reflect.ValueOf(v) } @@ -3846,7 +3875,7 @@ func Test_Ctx_QueryParser_WithSetParserDecoder(t *testing.T) { nonRFCTime := ParserType{ Customtype: NonRFCTime{}, - Converter: NonRFCConverter, + Converter: nonRFCConverter, } SetParserDecoder(ParserConfig{ @@ -3872,7 +3901,6 @@ func Test_Ctx_QueryParser_WithSetParserDecoder(t *testing.T) { c.Request().URI().SetQueryString("date=2021-04-10&title=CustomDateTest&Body=October") utils.AssertEqual(t, nil, c.QueryParser(q)) - fmt.Println(q.Date, "q.Date") utils.AssertEqual(t, "CustomDateTest", q.Title) date := fmt.Sprintf("%v", q.Date) utils.AssertEqual(t, "{0 63753609600 }", date) @@ -3907,7 +3935,7 @@ func Test_Ctx_QueryParser_Schema(t *testing.T) { c.Request().URI().SetQueryString("namex=tom&nested.age=10") q = new(Query1) - utils.AssertEqual(t, "name is empty", c.QueryParser(q).Error()) + utils.AssertEqual(t, "failed to decode: name is empty", c.QueryParser(q).Error()) c.Request().URI().SetQueryString("name=tom&nested.agex=10") q = new(Query1) @@ -3915,7 +3943,7 @@ func Test_Ctx_QueryParser_Schema(t *testing.T) { c.Request().URI().SetQueryString("name=tom&test.age=10") q = new(Query1) - utils.AssertEqual(t, "nested is empty", c.QueryParser(q).Error()) + utils.AssertEqual(t, "failed to decode: nested is empty", c.QueryParser(q).Error()) type Query2 struct { Name string `query:"name"` @@ -3933,11 +3961,11 @@ func Test_Ctx_QueryParser_Schema(t *testing.T) { c.Request().URI().SetQueryString("nested.agex=10") q2 = new(Query2) - utils.AssertEqual(t, "nested.age is empty", c.QueryParser(q2).Error()) + utils.AssertEqual(t, "failed to decode: nested.age is empty", c.QueryParser(q2).Error()) c.Request().URI().SetQueryString("nested.agex=10") q2 = new(Query2) - utils.AssertEqual(t, "nested.age is empty", c.QueryParser(q2).Error()) + utils.AssertEqual(t, "failed to decode: nested.age is empty", c.QueryParser(q2).Error()) type Node struct { Value int `query:"val,required"` @@ -3951,7 +3979,7 @@ func Test_Ctx_QueryParser_Schema(t *testing.T) { c.Request().URI().SetQueryString("next.val=2") n = new(Node) - utils.AssertEqual(t, "val is empty", c.QueryParser(n).Error()) + utils.AssertEqual(t, "failed to decode: val is empty", c.QueryParser(n).Error()) c.Request().URI().SetQueryString("val=3&next.value=2") n = new(Node) @@ -4057,7 +4085,7 @@ func Test_Ctx_ReqHeaderParser(t *testing.T) { } rh := new(RequiredHeader) c.Request().Header.Del("name") - utils.AssertEqual(t, "name is empty", c.ReqHeaderParser(rh).Error()) + utils.AssertEqual(t, "failed to decode: name is empty", c.ReqHeaderParser(rh).Error()) } // go test -run Test_Ctx_ReqHeaderParser_WithSetParserDecoder -v @@ -4065,7 +4093,7 @@ func Test_Ctx_ReqHeaderParser_WithSetParserDecoder(t *testing.T) { t.Parallel() type NonRFCTime time.Time - NonRFCConverter := func(value string) reflect.Value { + nonRFCConverter := func(value string) reflect.Value { if v, err := time.Parse("2006-01-02", value); err == nil { return reflect.ValueOf(v) } @@ -4074,7 +4102,7 @@ func Test_Ctx_ReqHeaderParser_WithSetParserDecoder(t *testing.T) { nonRFCTime := ParserType{ Customtype: NonRFCTime{}, - Converter: NonRFCConverter, + Converter: nonRFCConverter, } SetParserDecoder(ParserConfig{ @@ -4103,7 +4131,6 @@ func Test_Ctx_ReqHeaderParser_WithSetParserDecoder(t *testing.T) { c.Request().Header.Add("Body", "October") utils.AssertEqual(t, nil, c.ReqHeaderParser(r)) - fmt.Println(r.Date, "q.Date") utils.AssertEqual(t, "CustomDateTest", r.Title) date := fmt.Sprintf("%v", r.Date) utils.AssertEqual(t, "{0 63753609600 }", date) @@ -4140,7 +4167,7 @@ func Test_Ctx_ReqHeaderParser_Schema(t *testing.T) { c.Request().Header.Del("Name") q = new(Header1) - utils.AssertEqual(t, "Name is empty", c.ReqHeaderParser(q).Error()) + utils.AssertEqual(t, "failed to decode: Name is empty", c.ReqHeaderParser(q).Error()) c.Request().Header.Add("Name", "tom") c.Request().Header.Del("Nested.Age") @@ -4150,7 +4177,7 @@ func Test_Ctx_ReqHeaderParser_Schema(t *testing.T) { c.Request().Header.Del("Nested.Agex") q = new(Header1) - utils.AssertEqual(t, "Nested is empty", c.ReqHeaderParser(q).Error()) + utils.AssertEqual(t, "failed to decode: Nested is empty", c.ReqHeaderParser(q).Error()) c.Request().Header.Del("Nested.Agex") c.Request().Header.Del("Name") @@ -4176,7 +4203,7 @@ func Test_Ctx_ReqHeaderParser_Schema(t *testing.T) { c.Request().Header.Del("Nested.Age") c.Request().Header.Add("Nested.Agex", "10") h2 = new(Header2) - utils.AssertEqual(t, "Nested.age is empty", c.ReqHeaderParser(h2).Error()) + utils.AssertEqual(t, "failed to decode: Nested.age is empty", c.ReqHeaderParser(h2).Error()) type Node struct { Value int `reqHeader:"Val,required"` @@ -4191,7 +4218,7 @@ func Test_Ctx_ReqHeaderParser_Schema(t *testing.T) { c.Request().Header.Del("Val") n = new(Node) - utils.AssertEqual(t, "Val is empty", c.ReqHeaderParser(n).Error()) + utils.AssertEqual(t, "failed to decode: Val is empty", c.ReqHeaderParser(n).Error()) c.Request().Header.Add("Val", "3") c.Request().Header.Del("Next.Val") @@ -4628,8 +4655,9 @@ func Test_Ctx_RepeatParserWithSameStruct(t *testing.T) { var gzipJSON bytes.Buffer w := gzip.NewWriter(&gzipJSON) - _, _ = w.Write([]byte(`{"body_param":"body_param"}`)) - _ = w.Close() + _, _ = w.Write([]byte(`{"body_param":"body_param"}`)) //nolint:errcheck // This will never fail + err := w.Close() + utils.AssertEqual(t, nil, err) c.Request().Header.SetContentType(MIMEApplicationJSON) c.Request().Header.Set(HeaderContentEncoding, "gzip") c.Request().SetBody(gzipJSON.Bytes()) diff --git a/error_test.go b/error_test.go index fe0ec36605..5cd47e33fe 100644 --- a/error_test.go +++ b/error_test.go @@ -1,11 +1,10 @@ package fiber import ( + "encoding/json" "errors" "testing" - jerrors "encoding/json" - "github.com/gofiber/fiber/v2/internal/schema" "github.com/gofiber/fiber/v2/utils" ) @@ -36,42 +35,42 @@ func TestMultiError(t *testing.T) { func TestInvalidUnmarshalError(t *testing.T) { t.Parallel() - var e *jerrors.InvalidUnmarshalError + var e *json.InvalidUnmarshalError ok := errors.As(&InvalidUnmarshalError{}, &e) utils.AssertEqual(t, true, ok) } func TestMarshalerError(t *testing.T) { t.Parallel() - var e *jerrors.MarshalerError + var e *json.MarshalerError ok := errors.As(&MarshalerError{}, &e) utils.AssertEqual(t, true, ok) } func TestSyntaxError(t *testing.T) { t.Parallel() - var e *jerrors.SyntaxError + var e *json.SyntaxError ok := errors.As(&SyntaxError{}, &e) utils.AssertEqual(t, true, ok) } func TestUnmarshalTypeError(t *testing.T) { t.Parallel() - var e *jerrors.UnmarshalTypeError + var e *json.UnmarshalTypeError ok := errors.As(&UnmarshalTypeError{}, &e) utils.AssertEqual(t, true, ok) } func TestUnsupportedTypeError(t *testing.T) { t.Parallel() - var e *jerrors.UnsupportedTypeError + var e *json.UnsupportedTypeError ok := errors.As(&UnsupportedTypeError{}, &e) utils.AssertEqual(t, true, ok) } func TestUnsupportedValeError(t *testing.T) { t.Parallel() - var e *jerrors.UnsupportedValueError + var e *json.UnsupportedValueError ok := errors.As(&UnsupportedValueError{}, &e) utils.AssertEqual(t, true, ok) } diff --git a/group.go b/group.go index ce1e1b2db5..91c2806244 100644 --- a/group.go +++ b/group.go @@ -168,7 +168,6 @@ func (grp *Group) Group(prefix string, handlers ...Handler) Router { } return newGrp - } // Route is used to define routes with a common prefix inside the common function. diff --git a/helpers.go b/helpers.go index b074c6ca8e..e0e523d8ad 100644 --- a/helpers.go +++ b/helpers.go @@ -20,13 +20,13 @@ import ( "unsafe" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) -/* #nosec */ -// getTlsConfig returns a net listener's tls config -func getTlsConfig(ln net.Listener) *tls.Config { +// getTLSConfig returns a net listener's tls config +func getTLSConfig(ln net.Listener) *tls.Config { // Get listener type pointer := reflect.ValueOf(ln) @@ -37,12 +37,16 @@ func getTlsConfig(ln net.Listener) *tls.Config { // Get private field from value if field := val.FieldByName("config"); field.Type() != nil { // Copy value from pointer field (unsafe) - newval := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())) // #nosec G103 + newval := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())) //nolint:gosec // Probably the only way to extract the *tls.Config from a net.Listener. TODO: Verify there really is no easier way without using unsafe. if newval.Type() != nil { // Get element from pointer if elem := newval.Elem(); elem.Type() != nil { // Cast value to *tls.Config - return elem.Interface().(*tls.Config) + c, ok := elem.Interface().(*tls.Config) + if !ok { + panic(fmt.Errorf("failed to type-assert to *tls.Config")) + } + return c } } } @@ -53,19 +57,21 @@ func getTlsConfig(ln net.Listener) *tls.Config { } // readContent opens a named file and read content from it -func readContent(rf io.ReaderFrom, name string) (n int64, err error) { +func readContent(rf io.ReaderFrom, name string) (int64, error) { // Read file f, err := os.Open(filepath.Clean(name)) if err != nil { - return 0, err + return 0, fmt.Errorf("failed to open: %w", err) } - // #nosec G307 defer func() { if err = f.Close(); err != nil { log.Printf("Error closing file: %s\n", err) } }() - return rf.ReadFrom(f) + if n, err := rf.ReadFrom(f); err != nil { + return n, fmt.Errorf("failed to read: %w", err) + } + return 0, nil } // quoteString escape special characters in a given string @@ -78,7 +84,8 @@ func (app *App) quoteString(raw string) string { } // Scan stack if other methods match the request -func (app *App) methodExist(ctx *Ctx) (exist bool) { +func (app *App) methodExist(ctx *Ctx) bool { + var exists bool methods := app.config.RequestMethods for i := 0; i < len(methods); i++ { // Skip original method @@ -108,7 +115,7 @@ func (app *App) methodExist(ctx *Ctx) (exist bool) { // No match, next route if match { // We matched - exist = true + exists = true // Add method to Allow header ctx.Append(HeaderAllow, methods[i]) // Break stack loop @@ -116,7 +123,7 @@ func (app *App) methodExist(ctx *Ctx) (exist bool) { } } } - return + return exists } // uniqueRouteStack drop all not unique routes from the slice @@ -146,7 +153,7 @@ func defaultString(value string, defaultValue []string) string { const normalizedHeaderETag = "Etag" // Generate and set ETag header to response -func setETag(c *Ctx, weak bool) { +func setETag(c *Ctx, weak bool) { //nolint: revive // Accepting a bool param is fine here // Don't generate ETags for invalid responses if c.fasthttp.Response.StatusCode() != StatusOK { return @@ -160,7 +167,8 @@ func setETag(c *Ctx, weak bool) { clientEtag := c.Get(HeaderIfNoneMatch) // Generate ETag for response - crc32q := crc32.MakeTable(0xD5828281) + const pol = 0xD5828281 + crc32q := crc32.MakeTable(pol) etag := fmt.Sprintf("\"%d-%v\"", len(body), crc32.Checksum(body, crc32q)) // Enable weak tag @@ -173,7 +181,9 @@ func setETag(c *Ctx, weak bool) { // Check if server's ETag is weak if clientEtag[2:] == etag || clientEtag[2:] == etag[2:] { // W/1 == 1 || W/1 == W/1 - _ = c.SendStatus(StatusNotModified) + if err := c.SendStatus(StatusNotModified); err != nil { + log.Printf("setETag: failed to SendStatus: %v\n", err) + } c.fasthttp.ResetBody() return } @@ -183,7 +193,9 @@ func setETag(c *Ctx, weak bool) { } if strings.Contains(clientEtag, etag) { // 1 == 1 - _ = c.SendStatus(StatusNotModified) + if err := c.SendStatus(StatusNotModified); err != nil { + log.Printf("setETag: failed to SendStatus: %v\n", err) + } c.fasthttp.ResetBody() return } @@ -239,7 +251,7 @@ func getOffer(header string, offers ...string) string { return "" } -func matchEtag(s string, etag string) bool { +func matchEtag(s, etag string) bool { if s == etag || s == "W/"+etag || "W/"+s == etag { return true } @@ -254,12 +266,12 @@ func (app *App) isEtagStale(etag string, noneMatchBytes []byte) bool { // https://github.com/jshttp/fresh/blob/10e0471669dbbfbfd8de65bc6efac2ddd0bfa057/index.js#L110 for i := range noneMatchBytes { switch noneMatchBytes[i] { - case 0x20: + case 0x20: //nolint:gomnd // This is a space (" ") if start == end { start = i + 1 end = i + 1 } - case 0x2c: + case 0x2c: //nolint:gomnd // This is a comma (",") if matchEtag(app.getString(noneMatchBytes[start:end]), etag) { return false } @@ -273,7 +285,7 @@ func (app *App) isEtagStale(etag string, noneMatchBytes []byte) bool { return !matchEtag(app.getString(noneMatchBytes[start:end]), etag) } -func parseAddr(raw string) (host, port string) { +func parseAddr(raw string) (string, string) { //nolint:revive // Returns (host, port) if i := strings.LastIndex(raw, ":"); i != -1 { return raw[:i], raw[i+1:] } @@ -313,21 +325,21 @@ type testConn struct { w bytes.Buffer } -func (c *testConn) Read(b []byte) (int, error) { return c.r.Read(b) } -func (c *testConn) Write(b []byte) (int, error) { return c.w.Write(b) } -func (c *testConn) Close() error { return nil } +func (c *testConn) Read(b []byte) (int, error) { return c.r.Read(b) } //nolint:wrapcheck // This must not be wrapped +func (c *testConn) Write(b []byte) (int, error) { return c.w.Write(b) } //nolint:wrapcheck // This must not be wrapped +func (*testConn) Close() error { return nil } -func (c *testConn) LocalAddr() net.Addr { return &net.TCPAddr{Port: 0, Zone: "", IP: net.IPv4zero} } -func (c *testConn) RemoteAddr() net.Addr { return &net.TCPAddr{Port: 0, Zone: "", IP: net.IPv4zero} } -func (c *testConn) SetDeadline(_ time.Time) error { return nil } -func (c *testConn) SetReadDeadline(_ time.Time) error { return nil } -func (c *testConn) SetWriteDeadline(_ time.Time) error { return nil } +func (*testConn) LocalAddr() net.Addr { return &net.TCPAddr{Port: 0, Zone: "", IP: net.IPv4zero} } +func (*testConn) RemoteAddr() net.Addr { return &net.TCPAddr{Port: 0, Zone: "", IP: net.IPv4zero} } +func (*testConn) SetDeadline(_ time.Time) error { return nil } +func (*testConn) SetReadDeadline(_ time.Time) error { return nil } +func (*testConn) SetWriteDeadline(_ time.Time) error { return nil } -var getStringImmutable = func(b []byte) string { +func getStringImmutable(b []byte) string { return string(b) } -var getBytesImmutable = func(s string) (b []byte) { +func getBytesImmutable(s string) []byte { return []byte(s) } @@ -335,6 +347,7 @@ var getBytesImmutable = func(s string) (b []byte) { func (app *App) methodInt(s string) int { // For better performance if len(app.configured.RequestMethods) == 0 { + //nolint:gomnd // TODO: Use iota instead switch s { case MethodGet: return 0 @@ -391,8 +404,7 @@ func IsMethodIdempotent(m string) bool { } switch m { - case MethodPut, - MethodDelete: + case MethodPut, MethodDelete: return true default: return false @@ -714,7 +726,7 @@ const ( ConstraintBool = "bool" ConstraintFloat = "float" ConstraintAlpha = "alpha" - ConstraintGuid = "guid" + ConstraintGuid = "guid" //nolint:revive,stylecheck // TODO: Rename to "ConstraintGUID" in v3 ConstraintMinLen = "minLen" ConstraintMaxLen = "maxLen" ConstraintLen = "len" diff --git a/helpers_test.go b/helpers_test.go index fefb8f2527..7bb394e520 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) diff --git a/hooks.go b/hooks.go index b2766a8cab..c66e49fde8 100644 --- a/hooks.go +++ b/hooks.go @@ -1,14 +1,20 @@ package fiber +import ( + "log" +) + // OnRouteHandler Handlers define a function to create hooks for Fiber. -type OnRouteHandler = func(Route) error -type OnNameHandler = OnRouteHandler -type OnGroupHandler = func(Group) error -type OnGroupNameHandler = OnGroupHandler -type OnListenHandler = func() error -type OnShutdownHandler = OnListenHandler -type OnForkHandler = func(int) error -type OnMountHandler = func(*App) error +type ( + OnRouteHandler = func(Route) error + OnNameHandler = OnRouteHandler + OnGroupHandler = func(Group) error + OnGroupNameHandler = OnGroupHandler + OnListenHandler = func() error + OnShutdownHandler = OnListenHandler + OnForkHandler = func(int) error + OnMountHandler = func(*App) error +) // Hooks is a struct to use it with App. type Hooks struct { @@ -180,13 +186,17 @@ func (h *Hooks) executeOnListenHooks() error { func (h *Hooks) executeOnShutdownHooks() { for _, v := range h.onShutdown { - _ = v() + if err := v(); err != nil { + log.Printf("failed to call shutdown hook: %v\n", err) + } } } func (h *Hooks) executeOnForkHooks(pid int) { for _, v := range h.onFork { - _ = v(pid) + if err := v(pid); err != nil { + log.Printf("failed to call fork hook: %v\n", err) + } } } diff --git a/hooks_test.go b/hooks_test.go index df3c2aed96..6a2fc3bfbb 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -7,10 +7,11 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" ) -var testSimpleHandler = func(c *Ctx) error { +func testSimpleHandler(c *Ctx) error { return c.SendString("simple") } diff --git a/internal/dictpool/pool.go b/internal/dictpool/pool.go index 6160a4ff65..baebe7e236 100644 --- a/internal/dictpool/pool.go +++ b/internal/dictpool/pool.go @@ -10,7 +10,7 @@ var defaultPool = sync.Pool{ // AcquireDict acquire new dict. func AcquireDict() *Dict { - return defaultPool.Get().(*Dict) // nolint:forcetypeassert + return defaultPool.Get().(*Dict) } // ReleaseDict release dict. diff --git a/internal/gopsutil/common/common.go b/internal/gopsutil/common/common.go index a02fce4b1a..ed4a3bfcb3 100644 --- a/internal/gopsutil/common/common.go +++ b/internal/gopsutil/common/common.go @@ -366,7 +366,7 @@ func HostDev(combineWith ...string) string { // getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running // sysctl commands (see DoSysctrl). func getSysctrlEnv(env []string) []string { - foundLC := false + var foundLC bool for i, line := range env { if strings.HasPrefix(line, "LC_ALL") { env[i] = "LC_ALL=C" diff --git a/internal/gopsutil/mem/mem.go b/internal/gopsutil/mem/mem.go index b039c4ce4e..e2ee01e5c6 100644 --- a/internal/gopsutil/mem/mem.go +++ b/internal/gopsutil/mem/mem.go @@ -6,8 +6,7 @@ import ( "github.com/gofiber/fiber/v2/internal/gopsutil/common" ) -//lint:ignore U1000 we need this elsewhere -var invoke common.Invoker = common.Invoke{} //nolint:all +var invoke common.Invoker = common.Invoke{} //nolint:unused // We use this only for some OS'es // Memory usage statistics. Total, Available and Used contain numbers of bytes // for human consumption. diff --git a/internal/gopsutil/mem/mem_freebsd.go b/internal/gopsutil/mem/mem_freebsd.go index 0682edb7cf..d30e7bd315 100644 --- a/internal/gopsutil/mem/mem_freebsd.go +++ b/internal/gopsutil/mem/mem_freebsd.go @@ -86,7 +86,6 @@ func SwapMemory() (*SwapMemoryStat, error) { } // Constants from vm/vm_param.h -// nolint: golint const ( XSWDEV_VERSION11 = 1 XSWDEV_VERSION = 2 diff --git a/internal/gopsutil/mem/mem_linux.go b/internal/gopsutil/mem/mem_linux.go index a0fc7fd44c..3e7e93b5a8 100644 --- a/internal/gopsutil/mem/mem_linux.go +++ b/internal/gopsutil/mem/mem_linux.go @@ -57,10 +57,12 @@ func fillFromMeminfoWithContext(ctx context.Context) (*VirtualMemoryStat, *Virtu lines, _ := common.ReadLines(filename) // flag if MemAvailable is in /proc/meminfo (kernel 3.14+) - memavail := false - activeFile := false // "Active(file)" not available: 2.6.28 / Dec 2008 - inactiveFile := false // "Inactive(file)" not available: 2.6.28 / Dec 2008 - sReclaimable := false // "SReclaimable:" not available: 2.6.19 / Nov 2006 + var ( + memavail bool + activeFile bool // "Active(file)" not available: 2.6.28 / Dec 2008 + inactiveFile bool // "Inactive(file)" not available: 2.6.28 / Dec 2008 + sReclaimable bool // "SReclaimable:" not available: 2.6.19 / Nov 2006 + ) ret := &VirtualMemoryStat{} retEx := &VirtualMemoryExStat{} diff --git a/internal/msgp/json.go b/internal/msgp/json.go index 0e11e603c0..80d39635ca 100644 --- a/internal/msgp/json.go +++ b/internal/msgp/json.go @@ -168,7 +168,7 @@ func rwArray(dst jsWriter, src *Reader) (n int, err error) { if err != nil { return } - comma := false + var comma bool for i := uint32(0); i < sz; i++ { if comma { err = dst.WriteByte(',') diff --git a/internal/schema/cache.go b/internal/schema/cache.go index bf21697cf1..cb227a6969 100644 --- a/internal/schema/cache.go +++ b/internal/schema/cache.go @@ -163,7 +163,7 @@ func (c *cache) createField(field reflect.StructField, parentAlias string) *fiel } // Check if the type is supported and don't cache it if not. // First let's get the basic type. - isSlice, isStruct := false, false + var isSlice, isStruct bool ft := field.Type m := isTextUnmarshaler(reflect.Zero(ft)) if ft.Kind() == reflect.Ptr { diff --git a/internal/storage/memory/memory_test.go b/internal/storage/memory/memory_test.go index fb2b88a0e5..7fc6b955d8 100644 --- a/internal/storage/memory/memory_test.go +++ b/internal/storage/memory/memory_test.go @@ -149,13 +149,13 @@ func Benchmark_Storage_Memory(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { for _, key := range keys { - d.Set(key, value, ttl) + _ = d.Set(key, value, ttl) } for _, key := range keys { _, _ = d.Get(key) } for _, key := range keys { - d.Delete(key) + _ = d.Delete(key) } } }) diff --git a/internal/template/html/html.go b/internal/template/html/html.go index 71f6f02cb5..446431f7b2 100644 --- a/internal/template/html/html.go +++ b/internal/template/html/html.go @@ -156,7 +156,6 @@ func (e *Engine) Load() error { name = strings.TrimSuffix(name, e.extension) // name = strings.Replace(name, e.extension, "", -1) // Read the file - // #gosec G304 buf, err := utils.ReadFile(path, e.fileSystem) if err != nil { return err diff --git a/internal/template/utils/utils.go b/internal/template/utils/utils.go index 0ae8f22fd8..ee681b4f99 100644 --- a/internal/template/utils/utils.go +++ b/internal/template/utils/utils.go @@ -21,7 +21,6 @@ func Walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error { return walk(fs, root, info, walkFn) } -// #nosec G304 // ReadFile returns the raw content of a file func ReadFile(path string, fs http.FileSystem) ([]byte, error) { if fs != nil { diff --git a/listen.go b/listen.go index ddbf8c81fc..19dbae3621 100644 --- a/listen.go +++ b/listen.go @@ -9,6 +9,7 @@ import ( "crypto/x509" "errors" "fmt" + "log" "net" "os" "path/filepath" @@ -31,7 +32,7 @@ func (app *App) Listener(ln net.Listener) error { // Print startup message if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), getTlsConfig(ln) != nil, "") + app.startupMessage(ln.Addr().String(), getTLSConfig(ln) != nil, "") } // Print routes @@ -41,7 +42,7 @@ func (app *App) Listener(ln net.Listener) error { // Prefork is not supported for custom listeners if app.config.Prefork { - fmt.Println("[Warning] Prefork isn't supported for custom listeners.") + log.Printf("[Warning] Prefork isn't supported for custom listeners.\n") } // Start listening @@ -61,7 +62,7 @@ func (app *App) Listen(addr string) error { // Setup listener ln, err := net.Listen(app.config.Network, addr) if err != nil { - return err + return fmt.Errorf("failed to listen: %w", err) } // prepare the server for the start @@ -94,7 +95,7 @@ func (app *App) ListenTLS(addr, certFile, keyFile string) error { // Set TLS config with handler cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { - return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) + return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %w", certFile, keyFile, err) } tlsHandler := &TLSHandler{} @@ -115,7 +116,7 @@ func (app *App) ListenTLS(addr, certFile, keyFile string) error { ln, err := net.Listen(app.config.Network, addr) ln = tls.NewListener(ln, config) if err != nil { - return err + return fmt.Errorf("failed to listen: %w", err) } // prepare the server for the start @@ -150,12 +151,12 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { - return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) + return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %w", certFile, keyFile, err) } clientCACert, err := os.ReadFile(filepath.Clean(clientCertFile)) if err != nil { - return err + return fmt.Errorf("failed to read file: %w", err) } clientCertPool := x509.NewCertPool() clientCertPool.AppendCertsFromPEM(clientCACert) @@ -179,7 +180,7 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) // Setup listener ln, err := tls.Listen(app.config.Network, addr, config) if err != nil { - return err + return fmt.Errorf("failed to listen: %w", err) } // prepare the server for the start @@ -203,7 +204,7 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) } // startupMessage prepares the startup message with the handler number, port, address and other information -func (app *App) startupMessage(addr string, tls bool, pids string) { +func (app *App) startupMessage(addr string, tls bool, pids string) { //nolint: revive // Accepting a bool param is fine here // ignore child processes if IsChild() { return @@ -227,7 +228,8 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { } center := func(s string, width int) string { - pad := strconv.Itoa((width - len(s)) / 2) + const padDiv = 2 + pad := strconv.Itoa((width - len(s)) / padDiv) str := fmt.Sprintf("%"+pad+"s", " ") str += s str += fmt.Sprintf("%"+pad+"s", " ") @@ -238,7 +240,8 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { } centerValue := func(s string, width int) string { - pad := strconv.Itoa((width - runewidth.StringWidth(s)) / 2) + const padDiv = 2 + pad := strconv.Itoa((width - runewidth.StringWidth(s)) / padDiv) str := fmt.Sprintf("%"+pad+"s", " ") str += fmt.Sprintf("%s%s%s", colors.Cyan, s, colors.Black) str += fmt.Sprintf("%"+pad+"s", " ") @@ -249,13 +252,13 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { return str } - pad := func(s string, width int) (str string) { + pad := func(s string, width int) string { toAdd := width - len(s) - str += s + str := s for i := 0; i < toAdd; i++ { str += " " } - return + return str } host, port := parseAddr(addr) @@ -267,9 +270,9 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { } } - scheme := "http" + scheme := schemeHTTP if tls { - scheme = "https" + scheme = schemeHTTPS } isPrefork := "Disabled" @@ -282,19 +285,18 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { procs = "1" } + const lineLen = 49 mainLogo := colors.Black + " ┌───────────────────────────────────────────────────┐\n" if app.config.AppName != "" { - mainLogo += " │ " + centerValue(app.config.AppName, 49) + " │\n" + mainLogo += " │ " + centerValue(app.config.AppName, lineLen) + " │\n" } - mainLogo += " │ " + centerValue("Fiber v"+Version, 49) + " │\n" + mainLogo += " │ " + centerValue("Fiber v"+Version, lineLen) + " │\n" if host == "0.0.0.0" { - mainLogo += - " │ " + center(fmt.Sprintf("%s://127.0.0.1:%s", scheme, port), 49) + " │\n" + - " │ " + center(fmt.Sprintf("(bound on host 0.0.0.0 and port %s)", port), 49) + " │\n" + mainLogo += " │ " + center(fmt.Sprintf("%s://127.0.0.1:%s", scheme, port), lineLen) + " │\n" + + " │ " + center(fmt.Sprintf("(bound on host 0.0.0.0 and port %s)", port), lineLen) + " │\n" } else { - mainLogo += - " │ " + center(fmt.Sprintf("%s://%s:%s", scheme, host, port), 49) + " │\n" + mainLogo += " │ " + center(fmt.Sprintf("%s://%s:%s", scheme, host, port), lineLen) + " │\n" } mainLogo += fmt.Sprintf( @@ -303,8 +305,8 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { " │ Prefork .%s PID ....%s │\n"+ " └───────────────────────────────────────────────────┘"+ colors.Reset, - value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12), - value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14), + value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12), //nolint:gomnd // Using random padding lengths is fine here + value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14), //nolint:gomnd // Using random padding lengths is fine here ) var childPidsLogo string @@ -329,19 +331,21 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { thisLine := "Child PIDs ... " var itemsOnThisLine []string + const maxLineLen = 49 + addLine := func() { lines = append(lines, fmt.Sprintf( newLine, colors.Black, - thisLine+colors.Cyan+pad(strings.Join(itemsOnThisLine, ", "), 49-len(thisLine)), + thisLine+colors.Cyan+pad(strings.Join(itemsOnThisLine, ", "), maxLineLen-len(thisLine)), colors.Black, ), ) } for _, pid := range pidSlice { - if len(thisLine+strings.Join(append(itemsOnThisLine, pid), ", ")) > 49 { + if len(thisLine+strings.Join(append(itemsOnThisLine, pid), ", ")) > maxLineLen { addLine() thisLine = "" itemsOnThisLine = []string{pid} @@ -415,7 +419,7 @@ func (app *App) printRoutesMessage() { var routes []RouteMessage for _, routeStack := range app.stack { for _, route := range routeStack { - var newRoute = RouteMessage{} + var newRoute RouteMessage newRoute.name = route.Name newRoute.method = route.Method newRoute.path = route.Path @@ -443,5 +447,5 @@ func (app *App) printRoutesMessage() { _, _ = fmt.Fprintf(w, "%s%s\t%s| %s%s\t%s| %s%s\t%s| %s%s\n", colors.Blue, route.method, colors.White, colors.Green, route.path, colors.White, colors.Cyan, route.name, colors.White, colors.Yellow, route.handlers) } - _ = w.Flush() + _ = w.Flush() //nolint:errcheck // It is fine to ignore the error here } diff --git a/listen_test.go b/listen_test.go index 0dd2cb2937..63283d3485 100644 --- a/listen_test.go +++ b/listen_test.go @@ -7,7 +7,6 @@ package fiber import ( "bytes" "crypto/tls" - "fmt" "io" "log" "os" @@ -17,6 +16,7 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp/fasthttputil" ) @@ -125,8 +125,10 @@ func Test_App_Listener_TLS_Listener(t *testing.T) { if err != nil { utils.AssertEqual(t, nil, err) } + //nolint:gosec // We're in a test so using old ciphers is fine config := &tls.Config{Certificates: []tls.Certificate{cer}} + //nolint:gosec // We're in a test so listening on all interfaces is fine ln, err := tls.Listen(NetworkTCP4, ":0", config) utils.AssertEqual(t, nil, err) @@ -182,7 +184,6 @@ func Test_App_Master_Process_Show_Startup_Message(t *testing.T) { New(Config{Prefork: true}). startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) }) - fmt.Println(startupMessage) utils.AssertEqual(t, true, strings.Contains(startupMessage, "https://127.0.0.1:3000")) utils.AssertEqual(t, true, strings.Contains(startupMessage, "(bound on host 0.0.0.0 and port 3000)")) utils.AssertEqual(t, true, strings.Contains(startupMessage, "Child PIDs")) @@ -196,7 +197,6 @@ func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { startupMessage := captureOutput(func() { app.startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) }) - fmt.Println(startupMessage) utils.AssertEqual(t, "Test App v1.0.1", app.Config().AppName) utils.AssertEqual(t, true, strings.Contains(startupMessage, app.Config().AppName)) } @@ -208,7 +208,6 @@ func Test_App_Master_Process_Show_Startup_MessageWithAppNameNonAscii(t *testing. startupMessage := captureOutput(func() { app.startupMessage(":3000", false, "") }) - fmt.Println(startupMessage) utils.AssertEqual(t, true, strings.Contains(startupMessage, "│ Serveur de vérification des données │")) } @@ -219,8 +218,7 @@ func Test_App_print_Route(t *testing.T) { printRoutesMessage := captureOutput(func() { app.printRoutesMessage() }) - fmt.Println(printRoutesMessage) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, MethodGet)) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "routeName")) @@ -240,11 +238,11 @@ func Test_App_print_Route_with_group(t *testing.T) { app.printRoutesMessage() }) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, MethodGet)) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "POST")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, MethodPost)) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber")) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "PUT")) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber/*")) diff --git a/middleware/basicauth/basicauth_test.go b/middleware/basicauth/basicauth_test.go index 20f103e6b6..1284163203 100644 --- a/middleware/basicauth/basicauth_test.go +++ b/middleware/basicauth/basicauth_test.go @@ -1,15 +1,15 @@ package basicauth import ( + "encoding/base64" "fmt" "io" "net/http/httptest" "testing" - b64 "encoding/base64" - "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -23,7 +23,7 @@ func Test_BasicAuth_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -39,6 +39,7 @@ func Test_Middleware_BasicAuth(t *testing.T) { }, })) + //nolint:forcetypeassert,errcheck // TODO: Do not force-type assert app.Get("/testauth", func(c *fiber.Ctx) error { username := c.Locals("username").(string) password := c.Locals("password").(string) @@ -74,9 +75,9 @@ func Test_Middleware_BasicAuth(t *testing.T) { for _, tt := range tests { // Base64 encode credentials for http auth header - creds := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", tt.username, tt.password))) + creds := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", tt.username, tt.password))) - req := httptest.NewRequest("GET", "/testauth", nil) + req := httptest.NewRequest(fiber.MethodGet, "/testauth", nil) req.Header.Add("Authorization", "Basic "+creds) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -108,7 +109,7 @@ func Benchmark_Middleware_BasicAuth(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") fctx.Request.Header.Set(fiber.HeaderAuthorization, "basic am9objpkb2U=") // john:doe diff --git a/middleware/basicauth/config.go b/middleware/basicauth/config.go index 3845e91538..b19d5087ea 100644 --- a/middleware/basicauth/config.go +++ b/middleware/basicauth/config.go @@ -53,6 +53,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Users: map[string]string{}, diff --git a/middleware/cache/cache.go b/middleware/cache/cache.go index f6db49e2cf..8a733b7571 100644 --- a/middleware/cache/cache.go +++ b/middleware/cache/cache.go @@ -34,6 +34,7 @@ const ( noStore = "no-store" ) +//nolint:gochecknoglobals // TODO: Do not use a global var here var ignoreHeaders = map[string]interface{}{ "Connection": nil, "Keep-Alive": nil, @@ -43,8 +44,8 @@ var ignoreHeaders = map[string]interface{}{ "Trailers": nil, "Transfer-Encoding": nil, "Upgrade": nil, - "Content-Type": nil, // already stored explicitely by the cache manager - "Content-Encoding": nil, // already stored explicitely by the cache manager + "Content-Type": nil, // already stored explicitly by the cache manager + "Content-Encoding": nil, // already stored explicitly by the cache manager } // New creates a new middleware handler @@ -69,7 +70,7 @@ func New(config ...Config) fiber.Handler { // Create indexed heap for tracking expirations ( see heap.go ) heap := &indexedHeap{} // count stored bytes (sizes of response bodies) - var storedBytes uint = 0 + var storedBytes uint // Update timestamp in the configured interval go func() { @@ -81,10 +82,10 @@ func New(config ...Config) fiber.Handler { // Delete key from both manager and storage deleteKey := func(dkey string) { - manager.delete(dkey) + manager.del(dkey) // External storage saves body data with different key if cfg.Storage != nil { - manager.delete(dkey + "_body") + manager.del(dkey + "_body") } } @@ -205,7 +206,7 @@ func New(config ...Config) fiber.Handler { if cfg.StoreResponseHeaders { e.headers = make(map[string][]byte) c.Response().Header.VisitAll( - func(key []byte, value []byte) { + func(key, value []byte) { // create real copy keyS := string(key) if _, ok := ignoreHeaders[keyS]; !ok { diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go index e261023820..404a1edf2d 100644 --- a/middleware/cache/cache_test.go +++ b/middleware/cache/cache_test.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "math" - "net/http" "net/http/httptest" "os" "strconv" @@ -18,6 +17,7 @@ import ( "github.com/gofiber/fiber/v2/internal/storage/memory" "github.com/gofiber/fiber/v2/middleware/etag" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -35,10 +35,10 @@ func Test_Cache_CacheControl(t *testing.T) { return c.SendString("Hello, World!") }) - _, err := app.Test(httptest.NewRequest("GET", "/", nil)) + _, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "public, max-age=10", resp.Header.Get(fiber.HeaderCacheControl)) } @@ -53,7 +53,7 @@ func Test_Cache_Expired(t *testing.T) { return c.SendString(fmt.Sprintf("%d", time.Now().UnixNano())) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) @@ -61,7 +61,7 @@ func Test_Cache_Expired(t *testing.T) { // Sleep until the cache is expired time.Sleep(3 * time.Second) - respCached, err := app.Test(httptest.NewRequest("GET", "/", nil)) + respCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) bodyCached, err := io.ReadAll(respCached.Body) utils.AssertEqual(t, nil, err) @@ -71,7 +71,7 @@ func Test_Cache_Expired(t *testing.T) { } // Next response should be also cached - respCachedNextRound, err := app.Test(httptest.NewRequest("GET", "/", nil)) + respCachedNextRound, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) bodyCachedNextRound, err := io.ReadAll(respCachedNextRound.Body) utils.AssertEqual(t, nil, err) @@ -92,11 +92,11 @@ func Test_Cache(t *testing.T) { return c.SendString(now) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) - cachedReq := httptest.NewRequest("GET", "/", nil) + cachedReq := httptest.NewRequest(fiber.MethodGet, "/", nil) cachedResp, err := app.Test(cachedReq) utils.AssertEqual(t, nil, err) @@ -120,31 +120,31 @@ func Test_Cache_WithNoCacheRequestDirective(t *testing.T) { }) // Request id = 1 - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err := app.Test(req) - defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("1"), body) // Response cached, entry id = 1 // Request id = 2 without Cache-Control: no-cache - cachedReq := httptest.NewRequest("GET", "/?id=2", nil) + cachedReq := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) cachedResp, err := app.Test(cachedReq) - defer cachedResp.Body.Close() - cachedBody, _ := io.ReadAll(cachedResp.Body) + utils.AssertEqual(t, nil, err) + cachedBody, err := io.ReadAll(cachedResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, cachedResp.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("1"), cachedBody) // Response not cached, returns cached response, entry id = 1 // Request id = 2 with Cache-Control: no-cache - noCacheReq := httptest.NewRequest("GET", "/?id=2", nil) + noCacheReq := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) noCacheReq.Header.Set(fiber.HeaderCacheControl, noCache) noCacheResp, err := app.Test(noCacheReq) - defer noCacheResp.Body.Close() - noCacheBody, _ := io.ReadAll(noCacheResp.Body) + utils.AssertEqual(t, nil, err) + noCacheBody, err := io.ReadAll(noCacheResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, noCacheResp.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("2"), noCacheBody) @@ -152,21 +152,21 @@ func Test_Cache_WithNoCacheRequestDirective(t *testing.T) { /* Check Test_Cache_WithETagAndNoCacheRequestDirective */ // Request id = 2 with Cache-Control: no-cache again - noCacheReq1 := httptest.NewRequest("GET", "/?id=2", nil) + noCacheReq1 := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) noCacheReq1.Header.Set(fiber.HeaderCacheControl, noCache) noCacheResp1, err := app.Test(noCacheReq1) - defer noCacheResp1.Body.Close() - noCacheBody1, _ := io.ReadAll(noCacheResp1.Body) + utils.AssertEqual(t, nil, err) + noCacheBody1, err := io.ReadAll(noCacheResp1.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, noCacheResp1.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("2"), noCacheBody1) // Response cached, returns updated response, entry = 2 // Request id = 1 without Cache-Control: no-cache - cachedReq1 := httptest.NewRequest("GET", "/", nil) + cachedReq1 := httptest.NewRequest(fiber.MethodGet, "/", nil) cachedResp1, err := app.Test(cachedReq1) - defer cachedResp1.Body.Close() - cachedBody1, _ := io.ReadAll(cachedResp1.Body) + utils.AssertEqual(t, nil, err) + cachedBody1, err := io.ReadAll(cachedResp1.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, cachedResp1.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("2"), cachedBody1) @@ -188,7 +188,7 @@ func Test_Cache_WithETagAndNoCacheRequestDirective(t *testing.T) { }) // Request id = 1 - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) @@ -199,7 +199,7 @@ func Test_Cache_WithETagAndNoCacheRequestDirective(t *testing.T) { etagToken := resp.Header.Get("Etag") // Request id = 2 with ETag but without Cache-Control: no-cache - cachedReq := httptest.NewRequest("GET", "/?id=2", nil) + cachedReq := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) cachedReq.Header.Set(fiber.HeaderIfNoneMatch, etagToken) cachedResp, err := app.Test(cachedReq) utils.AssertEqual(t, nil, err) @@ -208,7 +208,7 @@ func Test_Cache_WithETagAndNoCacheRequestDirective(t *testing.T) { // Response not cached, returns cached response, entry id = 1, status not modified // Request id = 2 with ETag and Cache-Control: no-cache - noCacheReq := httptest.NewRequest("GET", "/?id=2", nil) + noCacheReq := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) noCacheReq.Header.Set(fiber.HeaderCacheControl, noCache) noCacheReq.Header.Set(fiber.HeaderIfNoneMatch, etagToken) noCacheResp, err := app.Test(noCacheReq) @@ -221,7 +221,7 @@ func Test_Cache_WithETagAndNoCacheRequestDirective(t *testing.T) { etagToken = noCacheResp.Header.Get("Etag") // Request id = 2 with ETag and Cache-Control: no-cache again - noCacheReq1 := httptest.NewRequest("GET", "/?id=2", nil) + noCacheReq1 := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) noCacheReq1.Header.Set(fiber.HeaderCacheControl, noCache) noCacheReq1.Header.Set(fiber.HeaderIfNoneMatch, etagToken) noCacheResp1, err := app.Test(noCacheReq1) @@ -231,7 +231,7 @@ func Test_Cache_WithETagAndNoCacheRequestDirective(t *testing.T) { // Response cached, returns updated response, entry id = 2, status not modified // Request id = 1 without ETag and Cache-Control: no-cache - cachedReq1 := httptest.NewRequest("GET", "/", nil) + cachedReq1 := httptest.NewRequest(fiber.MethodGet, "/", nil) cachedResp1, err := app.Test(cachedReq1) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, cachedResp1.Header.Get("X-Cache")) @@ -251,11 +251,11 @@ func Test_Cache_WithNoStoreRequestDirective(t *testing.T) { }) // Request id = 2 - noStoreReq := httptest.NewRequest("GET", "/?id=2", nil) + noStoreReq := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) noStoreReq.Header.Set(fiber.HeaderCacheControl, noStore) noStoreResp, err := app.Test(noStoreReq) - defer noStoreResp.Body.Close() - noStoreBody, _ := io.ReadAll(noStoreResp.Body) + utils.AssertEqual(t, nil, err) + noStoreBody, err := io.ReadAll(noStoreResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, []byte("2"), noStoreBody) // Response not cached, returns updated response @@ -278,11 +278,11 @@ func Test_Cache_WithSeveralRequests(t *testing.T) { for runs := 0; runs < 10; runs++ { for i := 0; i < 10; i++ { func(id int) { - rsp, err := app.Test(httptest.NewRequest(http.MethodGet, fmt.Sprintf("/%d", id), nil)) + rsp, err := app.Test(httptest.NewRequest(fiber.MethodGet, fmt.Sprintf("/%d", id), nil)) utils.AssertEqual(t, nil, err) - defer func(Body io.ReadCloser) { - err := Body.Close() + defer func(body io.ReadCloser) { + err := body.Close() utils.AssertEqual(t, nil, err) }(rsp.Body) @@ -311,11 +311,11 @@ func Test_Cache_Invalid_Expiration(t *testing.T) { return c.SendString(now) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) - cachedReq := httptest.NewRequest("GET", "/", nil) + cachedReq := httptest.NewRequest(fiber.MethodGet, "/", nil) cachedResp, err := app.Test(cachedReq) utils.AssertEqual(t, nil, err) @@ -342,25 +342,25 @@ func Test_Cache_Get(t *testing.T) { return c.SendString(c.Query("cache")) }) - resp, err := app.Test(httptest.NewRequest("POST", "/?cache=123", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodPost, "/?cache=123", nil)) utils.AssertEqual(t, nil, err) body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) - resp, err = app.Test(httptest.NewRequest("POST", "/?cache=12345", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodPost, "/?cache=12345", nil)) utils.AssertEqual(t, nil, err) body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "12345", string(body)) - resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=123", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/get?cache=123", nil)) utils.AssertEqual(t, nil, err) body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) - resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=12345", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/get?cache=12345", nil)) utils.AssertEqual(t, nil, err) body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) @@ -384,25 +384,25 @@ func Test_Cache_Post(t *testing.T) { return c.SendString(c.Query("cache")) }) - resp, err := app.Test(httptest.NewRequest("POST", "/?cache=123", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodPost, "/?cache=123", nil)) utils.AssertEqual(t, nil, err) body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) - resp, err = app.Test(httptest.NewRequest("POST", "/?cache=12345", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodPost, "/?cache=12345", nil)) utils.AssertEqual(t, nil, err) body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) - resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=123", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/get?cache=123", nil)) utils.AssertEqual(t, nil, err) body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) - resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=12345", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/get?cache=12345", nil)) utils.AssertEqual(t, nil, err) body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) @@ -420,14 +420,14 @@ func Test_Cache_NothingToCache(t *testing.T) { return c.SendString(time.Now().String()) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) time.Sleep(500 * time.Millisecond) - respCached, err := app.Test(httptest.NewRequest("GET", "/", nil)) + respCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) bodyCached, err := io.ReadAll(respCached.Body) utils.AssertEqual(t, nil, err) @@ -457,22 +457,22 @@ func Test_Cache_CustomNext(t *testing.T) { return c.Status(fiber.StatusInternalServerError).SendString(time.Now().String()) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) - respCached, err := app.Test(httptest.NewRequest("GET", "/", nil)) + respCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) bodyCached, err := io.ReadAll(respCached.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, bytes.Equal(body, bodyCached)) utils.AssertEqual(t, true, respCached.Header.Get(fiber.HeaderCacheControl) != "") - _, err = app.Test(httptest.NewRequest("GET", "/error", nil)) + _, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/error", nil)) utils.AssertEqual(t, nil, err) - errRespCached, err := app.Test(httptest.NewRequest("GET", "/error", nil)) + errRespCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/error", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, errRespCached.Header.Get(fiber.HeaderCacheControl) == "") } @@ -491,7 +491,7 @@ func Test_CustomKey(t *testing.T) { return c.SendString("hi") }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) _, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, called) @@ -505,7 +505,9 @@ func Test_CustomExpiration(t *testing.T) { var newCacheTime int app.Use(New(Config{ExpirationGenerator: func(c *fiber.Ctx, cfg *Config) time.Duration { called = true - newCacheTime, _ = strconv.Atoi(c.GetRespHeader("Cache-Time", "600")) + var err error + newCacheTime, err = strconv.Atoi(c.GetRespHeader("Cache-Time", "600")) + utils.AssertEqual(t, nil, err) return time.Second * time.Duration(newCacheTime) }})) @@ -515,7 +517,7 @@ func Test_CustomExpiration(t *testing.T) { return c.SendString(now) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, called) utils.AssertEqual(t, 1, newCacheTime) @@ -523,7 +525,7 @@ func Test_CustomExpiration(t *testing.T) { // Sleep until the cache is expired time.Sleep(1 * time.Second) - cachedResp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + cachedResp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) body, err := io.ReadAll(resp.Body) @@ -536,7 +538,7 @@ func Test_CustomExpiration(t *testing.T) { } // Next response should be cached - cachedRespNextRound, err := app.Test(httptest.NewRequest("GET", "/", nil)) + cachedRespNextRound, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) cachedBodyNextRound, err := io.ReadAll(cachedRespNextRound.Body) utils.AssertEqual(t, nil, err) @@ -559,12 +561,12 @@ func Test_AdditionalE2EResponseHeaders(t *testing.T) { return c.SendString("hi") }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "foobar", resp.Header.Get("X-Foobar")) - req = httptest.NewRequest("GET", "/", nil) + req = httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err = app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "foobar", resp.Header.Get("X-Foobar")) @@ -594,19 +596,19 @@ func Test_CacheHeader(t *testing.T) { return c.Status(fiber.StatusInternalServerError).SendString(time.Now().String()) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) - resp, err = app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, resp.Header.Get("X-Cache")) - resp, err = app.Test(httptest.NewRequest("POST", "/?cache=12345", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodPost, "/?cache=12345", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheUnreachable, resp.Header.Get("X-Cache")) - errRespCached, err := app.Test(httptest.NewRequest("GET", "/error", nil)) + errRespCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/error", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheUnreachable, errRespCached.Header.Get("X-Cache")) } @@ -622,12 +624,12 @@ func Test_Cache_WithHead(t *testing.T) { return c.SendString(now) }) - req := httptest.NewRequest("HEAD", "/", nil) + req := httptest.NewRequest(fiber.MethodHead, "/", nil) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) - cachedReq := httptest.NewRequest("HEAD", "/", nil) + cachedReq := httptest.NewRequest(fiber.MethodHead, "/", nil) cachedResp, err := app.Test(cachedReq) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, cachedResp.Header.Get("X-Cache")) @@ -649,28 +651,28 @@ func Test_Cache_WithHeadThenGet(t *testing.T) { return c.SendString(c.Query("cache")) }) - headResp, err := app.Test(httptest.NewRequest("HEAD", "/?cache=123", nil)) + headResp, err := app.Test(httptest.NewRequest(fiber.MethodHead, "/?cache=123", nil)) utils.AssertEqual(t, nil, err) headBody, err := io.ReadAll(headResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "", string(headBody)) utils.AssertEqual(t, cacheMiss, headResp.Header.Get("X-Cache")) - headResp, err = app.Test(httptest.NewRequest("HEAD", "/?cache=123", nil)) + headResp, err = app.Test(httptest.NewRequest(fiber.MethodHead, "/?cache=123", nil)) utils.AssertEqual(t, nil, err) headBody, err = io.ReadAll(headResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "", string(headBody)) utils.AssertEqual(t, cacheHit, headResp.Header.Get("X-Cache")) - getResp, err := app.Test(httptest.NewRequest("GET", "/?cache=123", nil)) + getResp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/?cache=123", nil)) utils.AssertEqual(t, nil, err) getBody, err := io.ReadAll(getResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(getBody)) utils.AssertEqual(t, cacheMiss, getResp.Header.Get("X-Cache")) - getResp, err = app.Test(httptest.NewRequest("GET", "/?cache=123", nil)) + getResp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/?cache=123", nil)) utils.AssertEqual(t, nil, err) getBody, err = io.ReadAll(getResp.Body) utils.AssertEqual(t, nil, err) @@ -691,7 +693,7 @@ func Test_CustomCacheHeader(t *testing.T) { return c.SendString("Hello, World!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, resp.Header.Get("Cache-Status")) } @@ -702,7 +704,7 @@ func Test_CustomCacheHeader(t *testing.T) { func stableAscendingExpiration() func(c1 *fiber.Ctx, c2 *Config) time.Duration { i := 0 return func(c1 *fiber.Ctx, c2 *Config) time.Duration { - i += 1 + i++ return time.Hour * time.Duration(i) } } @@ -738,7 +740,7 @@ func Test_Cache_MaxBytesOrder(t *testing.T) { } for idx, tcase := range cases { - rsp, err := app.Test(httptest.NewRequest("GET", tcase[0], nil)) + rsp, err := app.Test(httptest.NewRequest(fiber.MethodGet, tcase[0], nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, tcase[1], rsp.Header.Get("X-Cache"), fmt.Sprintf("Case %v", idx)) } @@ -756,7 +758,8 @@ func Test_Cache_MaxBytesSizes(t *testing.T) { app.Get("/*", func(c *fiber.Ctx) error { path := c.Context().URI().LastPathSegment() - size, _ := strconv.Atoi(string(path)) + size, err := strconv.Atoi(string(path)) + utils.AssertEqual(t, nil, err) return c.Send(make([]byte, size)) }) @@ -772,7 +775,7 @@ func Test_Cache_MaxBytesSizes(t *testing.T) { } for idx, tcase := range cases { - rsp, err := app.Test(httptest.NewRequest("GET", tcase[0], nil)) + rsp, err := app.Test(httptest.NewRequest(fiber.MethodGet, tcase[0], nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, tcase[1], rsp.Header.Get("X-Cache"), fmt.Sprintf("Case %v", idx)) } @@ -785,14 +788,14 @@ func Benchmark_Cache(b *testing.B) { app.Use(New()) app.Get("/demo", func(c *fiber.Ctx) error { - data, _ := os.ReadFile("../../.github/README.md") + data, _ := os.ReadFile("../../.github/README.md") //nolint:errcheck // We're inside a benchmark return c.Status(fiber.StatusTeapot).Send(data) }) h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/demo") b.ReportAllocs() @@ -815,14 +818,14 @@ func Benchmark_Cache_Storage(b *testing.B) { })) app.Get("/demo", func(c *fiber.Ctx) error { - data, _ := os.ReadFile("../../.github/README.md") + data, _ := os.ReadFile("../../.github/README.md") //nolint:errcheck // We're inside a benchmark return c.Status(fiber.StatusTeapot).Send(data) }) h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/demo") b.ReportAllocs() @@ -850,7 +853,7 @@ func Benchmark_Cache_AdditionalHeaders(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/demo") b.ReportAllocs() @@ -882,7 +885,7 @@ func Benchmark_Cache_MaxSize(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) b.ReportAllocs() b.ResetTimer() diff --git a/middleware/cache/config.go b/middleware/cache/config.go index 12f81e2ae8..9c2d2e104d 100644 --- a/middleware/cache/config.go +++ b/middleware/cache/config.go @@ -1,7 +1,7 @@ package cache import ( - "fmt" + "log" "time" "github.com/gofiber/fiber/v2" @@ -49,10 +49,10 @@ type Config struct { // Default: an in memory store for this process only Storage fiber.Storage - // Deprecated, use Storage instead + // Deprecated: Use Storage instead Store fiber.Storage - // Deprecated, use KeyGenerator instead + // Deprecated: Use KeyGenerator instead Key func(*fiber.Ctx) string // allows you to store additional headers generated by next middlewares & handler @@ -75,6 +75,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Expiration: 1 * time.Minute, @@ -102,11 +104,11 @@ func configDefault(config ...Config) Config { // Set default values if cfg.Store != nil { - fmt.Println("[CACHE] Store is deprecated, please use Storage") + log.Printf("[CACHE] Store is deprecated, please use Storage\n") cfg.Storage = cfg.Store } if cfg.Key != nil { - fmt.Println("[CACHE] Key is deprecated, please use KeyGenerator") + log.Printf("[CACHE] Key is deprecated, please use KeyGenerator\n") cfg.KeyGenerator = cfg.Key } if cfg.Next == nil { diff --git a/middleware/cache/heap.go b/middleware/cache/heap.go index 70271b84b8..fcd8356749 100644 --- a/middleware/cache/heap.go +++ b/middleware/cache/heap.go @@ -41,7 +41,7 @@ func (h indexedHeap) Swap(i, j int) { } func (h *indexedHeap) Push(x interface{}) { - h.pushInternal(x.(heapEntry)) + h.pushInternal(x.(heapEntry)) //nolint:forcetypeassert // Forced type assertion required to implement the heap.Interface interface } func (h *indexedHeap) Pop() interface{} { @@ -65,7 +65,7 @@ func (h *indexedHeap) put(key string, exp uint64, bytes uint) int { idx = h.entries[:n+1][n].idx } else { idx = h.maxidx - h.maxidx += 1 + h.maxidx++ h.indices = append(h.indices, idx) } // Push manually to avoid allocation @@ -77,7 +77,7 @@ func (h *indexedHeap) put(key string, exp uint64, bytes uint) int { } func (h *indexedHeap) removeInternal(realIdx int) (string, uint) { - x := heap.Remove(h, realIdx).(heapEntry) + x := heap.Remove(h, realIdx).(heapEntry) //nolint:forcetypeassert,errcheck // Forced type assertion required to implement the heap.Interface interface return x.key, x.bytes } diff --git a/middleware/cache/manager.go b/middleware/cache/manager.go index 6b9256fd23..78660072e2 100644 --- a/middleware/cache/manager.go +++ b/middleware/cache/manager.go @@ -51,7 +51,7 @@ func newManager(storage fiber.Storage) *manager { // acquire returns an *entry from the sync.Pool func (m *manager) acquire() *item { - return m.pool.Get().(*item) + return m.pool.Get().(*item) //nolint:forcetypeassert // We store nothing else in the pool } // release and reset *entry to sync.Pool @@ -69,38 +69,47 @@ func (m *manager) release(e *item) { } // get data from storage or memory -func (m *manager) get(key string) (it *item) { +func (m *manager) get(key string) *item { + var it *item if m.storage != nil { it = m.acquire() - if raw, _ := m.storage.Get(key); raw != nil { + raw, err := m.storage.Get(key) + if err != nil { + return it + } + if raw != nil { if _, err := it.UnmarshalMsg(raw); err != nil { - return + return it } } - return + return it } - if it, _ = m.memory.Get(key).(*item); it == nil { + if it, _ = m.memory.Get(key).(*item); it == nil { //nolint:errcheck // We store nothing else in the pool it = m.acquire() + return it } - return + return it } // get raw data from storage or memory -func (m *manager) getRaw(key string) (raw []byte) { +func (m *manager) getRaw(key string) []byte { + var raw []byte if m.storage != nil { - raw, _ = m.storage.Get(key) + raw, _ = m.storage.Get(key) //nolint:errcheck // TODO: Handle error here } else { - raw, _ = m.memory.Get(key).([]byte) + raw, _ = m.memory.Get(key).([]byte) //nolint:errcheck // TODO: Handle error here } - return + return raw } // set data to storage or memory func (m *manager) set(key string, it *item, exp time.Duration) { if m.storage != nil { if raw, err := it.MarshalMsg(nil); err == nil { - _ = m.storage.Set(key, raw, exp) + _ = m.storage.Set(key, raw, exp) //nolint:errcheck // TODO: Handle error here } + // we can release data because it's serialized to database + m.release(it) } else { m.memory.Set(key, it, exp) } @@ -109,16 +118,16 @@ func (m *manager) set(key string, it *item, exp time.Duration) { // set data to storage or memory func (m *manager) setRaw(key string, raw []byte, exp time.Duration) { if m.storage != nil { - _ = m.storage.Set(key, raw, exp) + _ = m.storage.Set(key, raw, exp) //nolint:errcheck // TODO: Handle error here } else { m.memory.Set(key, raw, exp) } } // delete data from storage or memory -func (m *manager) delete(key string) { +func (m *manager) del(key string) { if m.storage != nil { - _ = m.storage.Delete(key) + _ = m.storage.Delete(key) //nolint:errcheck // TODO: Handle error here } else { m.memory.Delete(key) } diff --git a/middleware/compress/compress.go b/middleware/compress/compress.go index e65d78558b..626faf4348 100644 --- a/middleware/compress/compress.go +++ b/middleware/compress/compress.go @@ -2,6 +2,7 @@ package compress import ( "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp" ) diff --git a/middleware/compress/compress_test.go b/middleware/compress/compress_test.go index 371a755976..28dd3f2a2f 100644 --- a/middleware/compress/compress_test.go +++ b/middleware/compress/compress_test.go @@ -12,8 +12,10 @@ import ( "github.com/gofiber/fiber/v2/utils" ) +//nolint:gochecknoglobals // Using a global var is fine here var filedata []byte +//nolint:gochecknoinits // init() is used to populate a global var from a README file func init() { dat, err := os.ReadFile("../../.github/README.md") if err != nil { @@ -34,7 +36,7 @@ func Test_Compress_Gzip(t *testing.T) { return c.Send(filedata) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set("Accept-Encoding", "gzip") resp, err := app.Test(req) @@ -64,7 +66,7 @@ func Test_Compress_Different_Level(t *testing.T) { return c.Send(filedata) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set("Accept-Encoding", "gzip") resp, err := app.Test(req) @@ -90,7 +92,7 @@ func Test_Compress_Deflate(t *testing.T) { return c.Send(filedata) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set("Accept-Encoding", "deflate") resp, err := app.Test(req) @@ -114,7 +116,7 @@ func Test_Compress_Brotli(t *testing.T) { return c.Send(filedata) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set("Accept-Encoding", "br") resp, err := app.Test(req, 10000) @@ -138,7 +140,7 @@ func Test_Compress_Disabled(t *testing.T) { return c.Send(filedata) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set("Accept-Encoding", "br") resp, err := app.Test(req) @@ -162,7 +164,7 @@ func Test_Compress_Next_Error(t *testing.T) { return errors.New("next error") }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set("Accept-Encoding", "gzip") resp, err := app.Test(req) @@ -185,7 +187,7 @@ func Test_Compress_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } diff --git a/middleware/compress/config.go b/middleware/compress/config.go index 5495ad4c42..fb176c6083 100644 --- a/middleware/compress/config.go +++ b/middleware/compress/config.go @@ -33,6 +33,8 @@ const ( ) // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Level: LevelDefault, diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index 5640cc3f2d..78dcae2dd3 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -1,7 +1,6 @@ package cors import ( - "net/http" "strconv" "strings" @@ -54,6 +53,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, AllowOrigins: "*", @@ -128,7 +129,7 @@ func New(config ...Config) fiber.Handler { } // Simple request - if c.Method() != http.MethodOptions { + if c.Method() != fiber.MethodOptions { c.Vary(fiber.HeaderOrigin) c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin) diff --git a/middleware/cors/cors_test.go b/middleware/cors/cors_test.go index 7d42c3c318..3600282331 100644 --- a/middleware/cors/cors_test.go +++ b/middleware/cors/cors_test.go @@ -6,6 +6,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -237,7 +238,7 @@ func Test_CORS_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } diff --git a/middleware/cors/utils.go b/middleware/cors/utils.go index fee658ef93..8b6114bdab 100644 --- a/middleware/cors/utils.go +++ b/middleware/cors/utils.go @@ -1,6 +1,8 @@ package cors -import "strings" +import ( + "strings" +) func matchScheme(domain, pattern string) bool { didx := strings.Index(domain, ":") @@ -20,18 +22,20 @@ func matchSubdomain(domain, pattern string) bool { } domAuth := domain[didx+3:] // to avoid long loop by invalid long domain - if len(domAuth) > 253 { + const maxDomainLen = 253 + if len(domAuth) > maxDomainLen { return false } patAuth := pattern[pidx+3:] domComp := strings.Split(domAuth, ".") patComp := strings.Split(patAuth, ".") - for i := len(domComp)/2 - 1; i >= 0; i-- { + const divHalf = 2 + for i := len(domComp)/divHalf - 1; i >= 0; i-- { opp := len(domComp) - 1 - i domComp[i], domComp[opp] = domComp[opp], domComp[i] } - for i := len(patComp)/2 - 1; i >= 0; i-- { + for i := len(patComp)/divHalf - 1; i >= 0; i-- { opp := len(patComp) - 1 - i patComp[i], patComp[opp] = patComp[opp], patComp[i] } diff --git a/middleware/csrf/config.go b/middleware/csrf/config.go index 94c5287b65..7482b60a8f 100644 --- a/middleware/csrf/config.go +++ b/middleware/csrf/config.go @@ -1,7 +1,7 @@ package csrf import ( - "fmt" + "log" "net/textproto" "strings" "time" @@ -80,13 +80,13 @@ type Config struct { // Optional. Default: utils.UUID KeyGenerator func() string - // Deprecated, please use Expiration + // Deprecated: Please use Expiration CookieExpires time.Duration - // Deprecated, please use Cookie* related fields + // Deprecated: Please use Cookie* related fields Cookie *fiber.Cookie - // Deprecated, please use KeyLookup + // Deprecated: Please use KeyLookup TokenLookup string // ErrorHandler is executed when an error is returned from fiber.Handler. @@ -105,6 +105,8 @@ type Config struct { const HeaderName = "X-Csrf-Token" // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ KeyLookup: "header:" + HeaderName, CookieName: "csrf_", @@ -116,7 +118,7 @@ var ConfigDefault = Config{ } // default ErrorHandler that process return error from fiber.Handler -var defaultErrorHandler = func(c *fiber.Ctx, err error) error { +func defaultErrorHandler(_ *fiber.Ctx, _ error) error { return fiber.ErrForbidden } @@ -132,15 +134,15 @@ func configDefault(config ...Config) Config { // Set default values if cfg.TokenLookup != "" { - fmt.Println("[CSRF] TokenLookup is deprecated, please use KeyLookup") + log.Printf("[CSRF] TokenLookup is deprecated, please use KeyLookup\n") cfg.KeyLookup = cfg.TokenLookup } if int(cfg.CookieExpires.Seconds()) > 0 { - fmt.Println("[CSRF] CookieExpires is deprecated, please use Expiration") + log.Printf("[CSRF] CookieExpires is deprecated, please use Expiration\n") cfg.Expiration = cfg.CookieExpires } if cfg.Cookie != nil { - fmt.Println("[CSRF] Cookie is deprecated, please use Cookie* related fields") + log.Printf("[CSRF] Cookie is deprecated, please use Cookie* related fields\n") if cfg.Cookie.Name != "" { cfg.CookieName = cfg.Cookie.Name } @@ -178,7 +180,8 @@ func configDefault(config ...Config) Config { // Generate the correct extractor to get the token from the correct location selectors := strings.Split(cfg.KeyLookup, ":") - if len(selectors) != 2 { + const numParts = 2 + if len(selectors) != numParts { panic("[CSRF] KeyLookup must in the form of :") } diff --git a/middleware/csrf/csrf.go b/middleware/csrf/csrf.go index e7ad4f2a72..22123441d1 100644 --- a/middleware/csrf/csrf.go +++ b/middleware/csrf/csrf.go @@ -7,9 +7,7 @@ import ( "github.com/gofiber/fiber/v2" ) -var ( - errTokenNotFound = errors.New("csrf token not found") -) +var errTokenNotFound = errors.New("csrf token not found") // New creates a new middleware handler func New(config ...Config) fiber.Handler { @@ -22,7 +20,7 @@ func New(config ...Config) fiber.Handler { dummyValue := []byte{'+'} // Return new handler - return func(c *fiber.Ctx) (err error) { + return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() @@ -39,7 +37,7 @@ func New(config ...Config) fiber.Handler { // Assume that anything not defined as 'safe' by RFC7231 needs protection // Extract token from client request i.e. header, query, param, form or cookie - token, err = cfg.Extractor(c) + token, err := cfg.Extractor(c) if err != nil { return cfg.ErrorHandler(c, err) } diff --git a/middleware/csrf/csrf_test.go b/middleware/csrf/csrf_test.go index ffa6af3e9f..446ae2f72d 100644 --- a/middleware/csrf/csrf_test.go +++ b/middleware/csrf/csrf_test.go @@ -7,6 +7,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -23,7 +24,7 @@ func Test_CSRF(t *testing.T) { h := app.Handler() ctx := &fasthttp.RequestCtx{} - methods := [4]string{"GET", "HEAD", "OPTIONS", "TRACE"} + methods := [4]string{fiber.MethodGet, fiber.MethodHead, fiber.MethodOptions, fiber.MethodTrace} for _, method := range methods { // Generate CSRF token @@ -33,14 +34,14 @@ func Test_CSRF(t *testing.T) { // Without CSRF cookie ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) // Empty/invalid CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(HeaderName, "johndoe") h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) @@ -55,7 +56,7 @@ func Test_CSRF(t *testing.T) { ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(HeaderName, token) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) @@ -72,7 +73,7 @@ func Test_CSRF_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -92,7 +93,7 @@ func Test_CSRF_Invalid_KeyLookup(t *testing.T) { h := app.Handler() ctx := &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) } @@ -110,7 +111,7 @@ func Test_CSRF_From_Form(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Invalid CSRF token - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationForm) h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) @@ -118,12 +119,12 @@ func Test_CSRF_From_Form(t *testing.T) { // Generate CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) token = strings.Split(strings.Split(token, ";")[0], "=")[1] - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationForm) ctx.Request.SetBodyString("_csrf=" + token) h(ctx) @@ -144,7 +145,7 @@ func Test_CSRF_From_Query(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Invalid CSRF token - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.SetRequestURI("/?_csrf=" + utils.UUID()) h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) @@ -152,7 +153,7 @@ func Test_CSRF_From_Query(t *testing.T) { // Generate CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) ctx.Request.SetRequestURI("/") h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) @@ -161,7 +162,7 @@ func Test_CSRF_From_Query(t *testing.T) { ctx.Request.Reset() ctx.Response.Reset() ctx.Request.SetRequestURI("/?_csrf=" + token) - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) utils.AssertEqual(t, "OK", string(ctx.Response.Body())) @@ -181,7 +182,7 @@ func Test_CSRF_From_Param(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Invalid CSRF token - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.SetRequestURI("/" + utils.UUID()) h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) @@ -189,7 +190,7 @@ func Test_CSRF_From_Param(t *testing.T) { // Generate CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) ctx.Request.SetRequestURI("/" + utils.UUID()) h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) @@ -198,7 +199,7 @@ func Test_CSRF_From_Param(t *testing.T) { ctx.Request.Reset() ctx.Response.Reset() ctx.Request.SetRequestURI("/" + token) - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) utils.AssertEqual(t, "OK", string(ctx.Response.Body())) @@ -218,7 +219,7 @@ func Test_CSRF_From_Cookie(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Invalid CSRF token - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.SetRequestURI("/") ctx.Request.Header.Set(fiber.HeaderCookie, "csrf="+utils.UUID()+";") h(ctx) @@ -227,7 +228,7 @@ func Test_CSRF_From_Cookie(t *testing.T) { // Generate CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) ctx.Request.SetRequestURI("/") h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) @@ -235,7 +236,7 @@ func Test_CSRF_From_Cookie(t *testing.T) { ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(fiber.HeaderCookie, "csrf="+token+";") ctx.Request.SetRequestURI("/") h(ctx) @@ -268,7 +269,7 @@ func Test_CSRF_From_Custom(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Invalid CSRF token - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMETextPlain) h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) @@ -276,12 +277,12 @@ func Test_CSRF_From_Custom(t *testing.T) { // Generate CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) token = strings.Split(strings.Split(token, ";")[0], "=")[1] - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMETextPlain) ctx.Request.SetBodyString("_csrf=" + token) h(ctx) @@ -307,13 +308,13 @@ func Test_CSRF_ErrorHandler_InvalidToken(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Generate CSRF token - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) // invalid CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(HeaderName, "johndoe") h(ctx) utils.AssertEqual(t, 419, ctx.Response.StatusCode()) @@ -339,69 +340,69 @@ func Test_CSRF_ErrorHandler_EmptyToken(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Generate CSRF token - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) // empty CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) h(ctx) utils.AssertEqual(t, 419, ctx.Response.StatusCode()) utils.AssertEqual(t, "empty CSRF token", string(ctx.Response.Body())) } // TODO: use this test case and make the unsafe header value bug from https://github.com/gofiber/fiber/issues/2045 reproducible and permanently fixed/tested by this testcase -//func Test_CSRF_UnsafeHeaderValue(t *testing.T) { +// func Test_CSRF_UnsafeHeaderValue(t *testing.T) { // t.Parallel() -// app := fiber.New() -// -// app.Use(New()) -// app.Get("/", func(c *fiber.Ctx) error { -// return c.SendStatus(fiber.StatusOK) -// }) -// app.Get("/test", func(c *fiber.Ctx) error { -// return c.SendStatus(fiber.StatusOK) -// }) -// app.Post("/", func(c *fiber.Ctx) error { -// return c.SendStatus(fiber.StatusOK) -// }) -// -// resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) -// utils.AssertEqual(t, nil, err) -// utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) -// -// var token string -// for _, c := range resp.Cookies() { -// if c.Name != ConfigDefault.CookieName { -// continue -// } -// token = c.Value -// break -// } -// -// fmt.Println("token", token) -// -// getReq := httptest.NewRequest(http.MethodGet, "/", nil) -// getReq.Header.Set(HeaderName, token) -// resp, err = app.Test(getReq) -// -// getReq = httptest.NewRequest(http.MethodGet, "/test", nil) -// getReq.Header.Set("X-Requested-With", "XMLHttpRequest") -// getReq.Header.Set(fiber.HeaderCacheControl, "no") -// getReq.Header.Set(HeaderName, token) -// -// resp, err = app.Test(getReq) -// -// getReq.Header.Set(fiber.HeaderAccept, "*/*") -// getReq.Header.Del(HeaderName) -// resp, err = app.Test(getReq) -// -// postReq := httptest.NewRequest(http.MethodPost, "/", nil) -// postReq.Header.Set("X-Requested-With", "XMLHttpRequest") -// postReq.Header.Set(HeaderName, token) -// resp, err = app.Test(postReq) -//} +// app := fiber.New() + +// app.Use(New()) +// app.Get("/", func(c *fiber.Ctx) error { +// return c.SendStatus(fiber.StatusOK) +// }) +// app.Get("/test", func(c *fiber.Ctx) error { +// return c.SendStatus(fiber.StatusOK) +// }) +// app.Post("/", func(c *fiber.Ctx) error { +// return c.SendStatus(fiber.StatusOK) +// }) + +// resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) +// utils.AssertEqual(t, nil, err) +// utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + +// var token string +// for _, c := range resp.Cookies() { +// if c.Name != ConfigDefault.CookieName { +// continue +// } +// token = c.Value +// break +// } + +// fmt.Println("token", token) + +// getReq := httptest.NewRequest(fiber.MethodGet, "/", nil) +// getReq.Header.Set(HeaderName, token) +// resp, err = app.Test(getReq) + +// getReq = httptest.NewRequest(fiber.MethodGet, "/test", nil) +// getReq.Header.Set("X-Requested-With", "XMLHttpRequest") +// getReq.Header.Set(fiber.HeaderCacheControl, "no") +// getReq.Header.Set(HeaderName, token) + +// resp, err = app.Test(getReq) + +// getReq.Header.Set(fiber.HeaderAccept, "*/*") +// getReq.Header.Del(HeaderName) +// resp, err = app.Test(getReq) + +// postReq := httptest.NewRequest(fiber.MethodPost, "/", nil) +// postReq.Header.Set("X-Requested-With", "XMLHttpRequest") +// postReq.Header.Set(HeaderName, token) +// resp, err = app.Test(postReq) +// } // go test -v -run=^$ -bench=Benchmark_Middleware_CSRF_Check -benchmem -count=4 func Benchmark_Middleware_CSRF_Check(b *testing.B) { @@ -417,12 +418,12 @@ func Benchmark_Middleware_CSRF_Check(b *testing.B) { ctx := &fasthttp.RequestCtx{} // Generate CSRF token - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) token = strings.Split(strings.Split(token, ";")[0], "=")[1] - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(HeaderName, token) b.ReportAllocs() @@ -449,7 +450,7 @@ func Benchmark_Middleware_CSRF_GenerateToken(b *testing.B) { ctx := &fasthttp.RequestCtx{} // Generate CSRF token - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) b.ReportAllocs() b.ResetTimer() diff --git a/middleware/csrf/manager.go b/middleware/csrf/manager.go index 13f0ccb657..a16f3a213a 100644 --- a/middleware/csrf/manager.go +++ b/middleware/csrf/manager.go @@ -41,74 +41,23 @@ func newManager(storage fiber.Storage) *manager { return manager } -// acquire returns an *entry from the sync.Pool -func (m *manager) acquire() *item { - return m.pool.Get().(*item) -} - -// release and reset *entry to sync.Pool -func (m *manager) release(e *item) { - // don't release item if we using memory storage - if m.storage != nil { - return - } - m.pool.Put(e) -} - -// get data from storage or memory -func (m *manager) get(key string) (it *item) { - if m.storage != nil { - it = m.acquire() - if raw, _ := m.storage.Get(key); raw != nil { - if _, err := it.UnmarshalMsg(raw); err != nil { - return - } - } - return - } - if it, _ = m.memory.Get(key).(*item); it == nil { - it = m.acquire() - } - return -} - // get raw data from storage or memory -func (m *manager) getRaw(key string) (raw []byte) { +func (m *manager) getRaw(key string) []byte { + var raw []byte if m.storage != nil { - raw, _ = m.storage.Get(key) + raw, _ = m.storage.Get(key) //nolint:errcheck // TODO: Do not ignore error } else { - raw, _ = m.memory.Get(key).([]byte) - } - return -} - -// set data to storage or memory -func (m *manager) set(key string, it *item, exp time.Duration) { - if m.storage != nil { - if raw, err := it.MarshalMsg(nil); err == nil { - _ = m.storage.Set(key, raw, exp) - } - } else { - // the key is crucial in crsf and sometimes a reference to another value which can be reused later(pool/unsafe values concept), so a copy is made here - m.memory.Set(utils.CopyString(key), it, exp) + raw, _ = m.memory.Get(key).([]byte) //nolint:errcheck // TODO: Do not ignore error } + return raw } // set data to storage or memory func (m *manager) setRaw(key string, raw []byte, exp time.Duration) { if m.storage != nil { - _ = m.storage.Set(key, raw, exp) + _ = m.storage.Set(key, raw, exp) //nolint:errcheck // TODO: Do not ignore error } else { // the key is crucial in crsf and sometimes a reference to another value which can be reused later(pool/unsafe values concept), so a copy is made here m.memory.Set(utils.CopyString(key), raw, exp) } } - -// delete data from storage or memory -func (m *manager) delete(key string) { - if m.storage != nil { - _ = m.storage.Delete(key) - } else { - m.memory.Delete(key) - } -} diff --git a/middleware/encryptcookie/config.go b/middleware/encryptcookie/config.go index 735c2f2d28..d8e4ba21da 100644 --- a/middleware/encryptcookie/config.go +++ b/middleware/encryptcookie/config.go @@ -1,6 +1,8 @@ package encryptcookie -import "github.com/gofiber/fiber/v2" +import ( + "github.com/gofiber/fiber/v2" +) // Config defines the config for middleware. type Config struct { @@ -32,6 +34,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Except: []string{"csrf_"}, diff --git a/middleware/encryptcookie/encryptcookie.go b/middleware/encryptcookie/encryptcookie.go index 1ba4779f56..9e323ce0e1 100644 --- a/middleware/encryptcookie/encryptcookie.go +++ b/middleware/encryptcookie/encryptcookie.go @@ -2,6 +2,7 @@ package encryptcookie import ( "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp" ) diff --git a/middleware/encryptcookie/encryptcookie_test.go b/middleware/encryptcookie/encryptcookie_test.go index 8241d6133c..aed45d6903 100644 --- a/middleware/encryptcookie/encryptcookie_test.go +++ b/middleware/encryptcookie/encryptcookie_test.go @@ -7,9 +7,11 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) +//nolint:gochecknoglobals // Using a global var is fine here var testKey = GenerateKey() func Test_Middleware_Encrypt_Cookie(t *testing.T) { @@ -35,14 +37,14 @@ func Test_Middleware_Encrypt_Cookie(t *testing.T) { // Test empty cookie ctx := &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) utils.AssertEqual(t, "value=", string(ctx.Response.Body())) // Test invalid cookie ctx = &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) ctx.Request.Header.SetCookie("test", "Invalid") h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) @@ -54,18 +56,19 @@ func Test_Middleware_Encrypt_Cookie(t *testing.T) { // Test valid cookie ctx = &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) encryptedCookie := fasthttp.Cookie{} encryptedCookie.SetKey("test") utils.AssertEqual(t, true, ctx.Response.Header.Cookie(&encryptedCookie), "Get cookie value") - decryptedCookieValue, _ := DecryptCookie(string(encryptedCookie.Value()), testKey) + decryptedCookieValue, err := DecryptCookie(string(encryptedCookie.Value()), testKey) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "SomeThing", decryptedCookieValue) ctx = &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) ctx.Request.Header.SetCookie("test", string(encryptedCookie.Value())) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) @@ -91,7 +94,7 @@ func Test_Encrypt_Cookie_Next(t *testing.T) { return nil }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "SomeThing", resp.Cookies()[0].Value) } @@ -123,7 +126,7 @@ func Test_Encrypt_Cookie_Except(t *testing.T) { h := app.Handler() ctx := &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) @@ -135,7 +138,8 @@ func Test_Encrypt_Cookie_Except(t *testing.T) { encryptedCookie := fasthttp.Cookie{} encryptedCookie.SetKey("test2") utils.AssertEqual(t, true, ctx.Response.Header.Cookie(&encryptedCookie), "Get cookie value") - decryptedCookieValue, _ := DecryptCookie(string(encryptedCookie.Value()), testKey) + decryptedCookieValue, err := DecryptCookie(string(encryptedCookie.Value()), testKey) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "SomeThing", decryptedCookieValue) } @@ -169,18 +173,19 @@ func Test_Encrypt_Cookie_Custom_Encryptor(t *testing.T) { h := app.Handler() ctx := &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) encryptedCookie := fasthttp.Cookie{} encryptedCookie.SetKey("test") utils.AssertEqual(t, true, ctx.Response.Header.Cookie(&encryptedCookie), "Get cookie value") - decodedBytes, _ := base64.StdEncoding.DecodeString(string(encryptedCookie.Value())) + decodedBytes, err := base64.StdEncoding.DecodeString(string(encryptedCookie.Value())) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "SomeThing", string(decodedBytes)) ctx = &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) ctx.Request.Header.SetCookie("test", string(encryptedCookie.Value())) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) diff --git a/middleware/encryptcookie/utils.go b/middleware/encryptcookie/utils.go index 542d160c17..c35064d954 100644 --- a/middleware/encryptcookie/utils.go +++ b/middleware/encryptcookie/utils.go @@ -6,47 +6,56 @@ import ( "crypto/rand" "encoding/base64" "errors" + "fmt" "io" ) // EncryptCookie Encrypts a cookie value with specific encryption key func EncryptCookie(value, key string) (string, error) { - keyDecoded, _ := base64.StdEncoding.DecodeString(key) - plaintext := []byte(value) + keyDecoded, err := base64.StdEncoding.DecodeString(key) + if err != nil { + return "", fmt.Errorf("failed to base64-decode key: %w", err) + } block, err := aes.NewCipher(keyDecoded) if err != nil { - return "", err + return "", fmt.Errorf("failed to create AES cipher: %w", err) } gcm, err := cipher.NewGCM(block) if err != nil { - return "", err + return "", fmt.Errorf("failed to create GCM mode: %w", err) } nonce := make([]byte, gcm.NonceSize()) if _, err = io.ReadFull(rand.Reader, nonce); err != nil { - return "", err + return "", fmt.Errorf("failed to read: %w", err) } - ciphertext := gcm.Seal(nonce, nonce, plaintext, nil) + ciphertext := gcm.Seal(nonce, nonce, []byte(value), nil) return base64.StdEncoding.EncodeToString(ciphertext), nil } // DecryptCookie Decrypts a cookie value with specific encryption key func DecryptCookie(value, key string) (string, error) { - keyDecoded, _ := base64.StdEncoding.DecodeString(key) - enc, _ := base64.StdEncoding.DecodeString(value) + keyDecoded, err := base64.StdEncoding.DecodeString(key) + if err != nil { + return "", fmt.Errorf("failed to base64-decode key: %w", err) + } + enc, err := base64.StdEncoding.DecodeString(value) + if err != nil { + return "", fmt.Errorf("failed to base64-decode value: %w", err) + } block, err := aes.NewCipher(keyDecoded) if err != nil { - return "", err + return "", fmt.Errorf("failed to create AES cipher: %w", err) } gcm, err := cipher.NewGCM(block) if err != nil { - return "", err + return "", fmt.Errorf("failed to create GCM mode: %w", err) } nonceSize := gcm.NonceSize() @@ -59,7 +68,7 @@ func DecryptCookie(value, key string) (string, error) { plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) if err != nil { - return "", err + return "", fmt.Errorf("failed to decrypt ciphertext: %w", err) } return string(plaintext), nil @@ -67,7 +76,8 @@ func DecryptCookie(value, key string) (string, error) { // GenerateKey Generates an encryption key func GenerateKey() string { - ret := make([]byte, 32) + const keyLen = 32 + ret := make([]byte, keyLen) if _, err := rand.Read(ret); err != nil { panic(err) diff --git a/middleware/envvar/envvar.go b/middleware/envvar/envvar.go index c8ed80de3d..debfc1bbb5 100644 --- a/middleware/envvar/envvar.go +++ b/middleware/envvar/envvar.go @@ -23,10 +23,8 @@ func (envVar *EnvVar) set(key, val string) { envVar.Vars[key] = val } -var defaultConfig = Config{} - func New(config ...Config) fiber.Handler { - var cfg = defaultConfig + var cfg Config if len(config) > 0 { cfg = config[0] } @@ -57,8 +55,9 @@ func newEnvVar(cfg Config) *EnvVar { } } } else { + const numElems = 2 for _, envVal := range os.Environ() { - keyVal := strings.SplitN(envVal, "=", 2) + keyVal := strings.SplitN(envVal, "=", numElems) if _, exists := cfg.ExcludeVars[keyVal[0]]; !exists { vars.set(keyVal[0], keyVal[1]) } diff --git a/middleware/envvar/envvar_test.go b/middleware/envvar/envvar_test.go index 34d8a05074..83101a4761 100644 --- a/middleware/envvar/envvar_test.go +++ b/middleware/envvar/envvar_test.go @@ -1,6 +1,8 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package envvar import ( + "context" "encoding/json" "io" "net/http" @@ -12,16 +14,25 @@ import ( ) func TestEnvVarStructWithExportVarsExcludeVars(t *testing.T) { - os.Setenv("testKey", "testEnvValue") - os.Setenv("anotherEnvKey", "anotherEnvVal") - os.Setenv("excludeKey", "excludeEnvValue") - defer os.Unsetenv("testKey") - defer os.Unsetenv("anotherEnvKey") - defer os.Unsetenv("excludeKey") + err := os.Setenv("testKey", "testEnvValue") + utils.AssertEqual(t, nil, err) + err = os.Setenv("anotherEnvKey", "anotherEnvVal") + utils.AssertEqual(t, nil, err) + err = os.Setenv("excludeKey", "excludeEnvValue") + utils.AssertEqual(t, nil, err) + defer func() { + err := os.Unsetenv("testKey") + utils.AssertEqual(t, nil, err) + err = os.Unsetenv("anotherEnvKey") + utils.AssertEqual(t, nil, err) + err = os.Unsetenv("excludeKey") + utils.AssertEqual(t, nil, err) + }() vars := newEnvVar(Config{ ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, - ExcludeVars: map[string]string{"excludeKey": ""}}) + ExcludeVars: map[string]string{"excludeKey": ""}, + }) utils.AssertEqual(t, vars.Vars["testKey"], "testEnvValue") utils.AssertEqual(t, vars.Vars["testDefaultKey"], "testDefaultVal") @@ -30,21 +41,28 @@ func TestEnvVarStructWithExportVarsExcludeVars(t *testing.T) { } func TestEnvVarHandler(t *testing.T) { - os.Setenv("testKey", "testVal") - defer os.Unsetenv("testKey") + err := os.Setenv("testKey", "testVal") + utils.AssertEqual(t, nil, err) + defer func() { + err := os.Unsetenv("testKey") + utils.AssertEqual(t, nil, err) + }() - expectedEnvVarResponse, _ := json.Marshal( + expectedEnvVarResponse, err := json.Marshal( struct { Vars map[string]string `json:"vars"` }{ map[string]string{"testKey": "testVal"}, }) + utils.AssertEqual(t, nil, err) app := fiber.New() app.Use("/envvars", New(Config{ - ExportVars: map[string]string{"testKey": ""}})) + ExportVars: map[string]string{"testKey": ""}, + })) - req, _ := http.NewRequest("GET", "http://localhost/envvars", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "http://localhost/envvars", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -57,14 +75,16 @@ func TestEnvVarHandler(t *testing.T) { func TestEnvVarHandlerNotMatched(t *testing.T) { app := fiber.New() app.Use("/envvars", New(Config{ - ExportVars: map[string]string{"testKey": ""}})) + ExportVars: map[string]string{"testKey": ""}, + })) app.Get("/another-path", func(ctx *fiber.Ctx) error { utils.AssertEqual(t, nil, ctx.SendString("OK")) return nil }) - req, _ := http.NewRequest("GET", "http://localhost/another-path", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "http://localhost/another-path", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -75,13 +95,18 @@ func TestEnvVarHandlerNotMatched(t *testing.T) { } func TestEnvVarHandlerDefaultConfig(t *testing.T) { - os.Setenv("testEnvKey", "testEnvVal") - defer os.Unsetenv("testEnvKey") + err := os.Setenv("testEnvKey", "testEnvVal") + utils.AssertEqual(t, nil, err) + defer func() { + err := os.Unsetenv("testEnvKey") + utils.AssertEqual(t, nil, err) + }() app := fiber.New() app.Use("/envvars", New()) - req, _ := http.NewRequest("GET", "http://localhost/envvars", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "http://localhost/envvars", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -98,7 +123,8 @@ func TestEnvVarHandlerMethod(t *testing.T) { app := fiber.New() app.Use("/envvars", New()) - req, _ := http.NewRequest("POST", "http://localhost/envvars", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodPost, "http://localhost/envvars", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusMethodNotAllowed, resp.StatusCode) @@ -107,14 +133,19 @@ func TestEnvVarHandlerMethod(t *testing.T) { func TestEnvVarHandlerSpecialValue(t *testing.T) { testEnvKey := "testEnvKey" fakeBase64 := "testBase64:TQ==" - os.Setenv(testEnvKey, fakeBase64) - defer os.Unsetenv(testEnvKey) + err := os.Setenv(testEnvKey, fakeBase64) + utils.AssertEqual(t, nil, err) + defer func() { + err := os.Unsetenv(testEnvKey) + utils.AssertEqual(t, nil, err) + }() app := fiber.New() app.Use("/envvars", New()) app.Use("/envvars/export", New(Config{ExportVars: map[string]string{testEnvKey: ""}})) - req, _ := http.NewRequest("GET", "http://localhost/envvars", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "http://localhost/envvars", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -126,7 +157,8 @@ func TestEnvVarHandlerSpecialValue(t *testing.T) { val := envVars.Vars[testEnvKey] utils.AssertEqual(t, fakeBase64, val) - req, _ = http.NewRequest("GET", "http://localhost/envvars/export", nil) + req, err = http.NewRequestWithContext(context.Background(), fiber.MethodGet, "http://localhost/envvars/export", nil) + utils.AssertEqual(t, nil, err) resp, err = app.Test(req) utils.AssertEqual(t, nil, err) diff --git a/middleware/etag/config.go b/middleware/etag/config.go index 57a7c787ab..efc31d86c5 100644 --- a/middleware/etag/config.go +++ b/middleware/etag/config.go @@ -23,6 +23,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Weak: false, Next: nil, diff --git a/middleware/etag/etag.go b/middleware/etag/etag.go index 72ad71ba36..98955833f8 100644 --- a/middleware/etag/etag.go +++ b/middleware/etag/etag.go @@ -5,12 +5,8 @@ import ( "hash/crc32" "github.com/gofiber/fiber/v2" - "github.com/valyala/bytebufferpool" -) -var ( - normalizedHeaderETag = []byte("Etag") - weakPrefix = []byte("W/") + "github.com/valyala/bytebufferpool" ) // New creates a new middleware handler @@ -18,32 +14,38 @@ func New(config ...Config) fiber.Handler { // Set default config cfg := configDefault(config...) - crc32q := crc32.MakeTable(0xD5828281) + var ( + normalizedHeaderETag = []byte("Etag") + weakPrefix = []byte("W/") + ) + + const crcPol = 0xD5828281 + crc32q := crc32.MakeTable(crcPol) // Return new handler - return func(c *fiber.Ctx) (err error) { + return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() } // Return err if next handler returns one - if err = c.Next(); err != nil { - return + if err := c.Next(); err != nil { + return err } // Don't generate ETags for invalid responses if c.Response().StatusCode() != fiber.StatusOK { - return + return nil } body := c.Response().Body() // Skips ETag if no response body is present if len(body) == 0 { - return + return nil } // Skip ETag if header is already present if c.Response().Header.PeekBytes(normalizedHeaderETag) != nil { - return + return nil } // Generate ETag for response @@ -52,14 +54,14 @@ func New(config ...Config) fiber.Handler { // Enable weak tag if cfg.Weak { - _, _ = bb.Write(weakPrefix) + _, _ = bb.Write(weakPrefix) //nolint:errcheck // This will never fail } - _ = bb.WriteByte('"') + _ = bb.WriteByte('"') //nolint:errcheck // This will never fail bb.B = appendUint(bb.Bytes(), uint32(len(body))) - _ = bb.WriteByte('-') + _ = bb.WriteByte('-') //nolint:errcheck // This will never fail bb.B = appendUint(bb.Bytes(), crc32.Checksum(body, crc32q)) - _ = bb.WriteByte('"') + _ = bb.WriteByte('"') //nolint:errcheck // This will never fail etag := bb.Bytes() @@ -78,7 +80,7 @@ func New(config ...Config) fiber.Handler { // W/1 != W/2 || W/1 != 2 c.Response().Header.SetCanonical(normalizedHeaderETag, etag) - return + return nil } if bytes.Contains(clientEtag, etag) { @@ -90,7 +92,7 @@ func New(config ...Config) fiber.Handler { // 1 != 2 c.Response().Header.SetCanonical(normalizedHeaderETag, etag) - return + return nil } } @@ -102,7 +104,7 @@ func appendUint(dst []byte, n uint32) []byte { var q uint32 for n >= 10 { i-- - q = n / 10 + q = n / 10 //nolint:gomnd // TODO: Explain why we divide by 10 here buf[i] = '0' + byte(n-q*10) n = q } diff --git a/middleware/etag/etag_test.go b/middleware/etag/etag_test.go index 567e1e2e94..ff9833f812 100644 --- a/middleware/etag/etag_test.go +++ b/middleware/etag/etag_test.go @@ -8,6 +8,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -21,7 +22,7 @@ func Test_ETag_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -37,7 +38,7 @@ func Test_ETag_SkipError(t *testing.T) { return fiber.ErrForbidden }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusForbidden, resp.StatusCode) } @@ -53,7 +54,7 @@ func Test_ETag_NotStatusOK(t *testing.T) { return c.SendStatus(fiber.StatusCreated) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusCreated, resp.StatusCode) } @@ -69,7 +70,7 @@ func Test_ETag_NoBody(t *testing.T) { return nil }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) } @@ -91,7 +92,7 @@ func Test_ETag_NewEtag(t *testing.T) { }) } -func testETagNewEtag(t *testing.T, headerIfNoneMatch, matched bool) { +func testETagNewEtag(t *testing.T, headerIfNoneMatch, matched bool) { //nolint:revive // We're in a test, so using bools as a flow-control is fine t.Helper() app := fiber.New() @@ -102,7 +103,7 @@ func testETagNewEtag(t *testing.T, headerIfNoneMatch, matched bool) { return c.SendString("Hello, World!") }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) if headerIfNoneMatch { etag := `"non-match"` if matched { @@ -145,7 +146,7 @@ func Test_ETag_WeakEtag(t *testing.T) { }) } -func testETagWeakEtag(t *testing.T, headerIfNoneMatch, matched bool) { +func testETagWeakEtag(t *testing.T, headerIfNoneMatch, matched bool) { //nolint:revive // We're in a test, so using bools as a flow-control is fine t.Helper() app := fiber.New() @@ -156,7 +157,7 @@ func testETagWeakEtag(t *testing.T, headerIfNoneMatch, matched bool) { return c.SendString("Hello, World!") }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) if headerIfNoneMatch { etag := `W/"non-match"` if matched { @@ -199,7 +200,7 @@ func Test_ETag_CustomEtag(t *testing.T) { }) } -func testETagCustomEtag(t *testing.T, headerIfNoneMatch, matched bool) { +func testETagCustomEtag(t *testing.T, headerIfNoneMatch, matched bool) { //nolint:revive // We're in a test, so using bools as a flow-control is fine t.Helper() app := fiber.New() @@ -214,7 +215,7 @@ func testETagCustomEtag(t *testing.T, headerIfNoneMatch, matched bool) { return c.SendString("Hello, World!") }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) if headerIfNoneMatch { etag := `"non-match"` if matched { @@ -255,7 +256,7 @@ func Test_ETag_CustomEtagPut(t *testing.T) { return c.SendString("Hello, World!") }) - req := httptest.NewRequest("PUT", "/", nil) + req := httptest.NewRequest(fiber.MethodPut, "/", nil) req.Header.Set(fiber.HeaderIfMatch, `"non-match"`) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -275,7 +276,7 @@ func Benchmark_Etag(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") b.ReportAllocs() diff --git a/middleware/expvar/config.go b/middleware/expvar/config.go index 6691dc1a3a..c31cebcf3d 100644 --- a/middleware/expvar/config.go +++ b/middleware/expvar/config.go @@ -1,6 +1,8 @@ package expvar -import "github.com/gofiber/fiber/v2" +import ( + "github.com/gofiber/fiber/v2" +) // Config defines the config for middleware. type Config struct { @@ -10,6 +12,7 @@ type Config struct { Next func(c *fiber.Ctx) bool } +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, } diff --git a/middleware/expvar/expvar.go b/middleware/expvar/expvar.go index 7576401781..6436395914 100644 --- a/middleware/expvar/expvar.go +++ b/middleware/expvar/expvar.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp/expvarhandler" ) @@ -29,6 +30,6 @@ func New(config ...Config) fiber.Handler { return nil } - return c.Redirect("/debug/vars", 302) + return c.Redirect("/debug/vars", fiber.StatusFound) } } diff --git a/middleware/favicon/favicon.go b/middleware/favicon/favicon.go index 66b688093c..e7ef5d4dbb 100644 --- a/middleware/favicon/favicon.go +++ b/middleware/favicon/favicon.go @@ -34,6 +34,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, File: "", diff --git a/middleware/favicon/favicon_test.go b/middleware/favicon/favicon_test.go index f53311e3ae..8597b7ec47 100644 --- a/middleware/favicon/favicon_test.go +++ b/middleware/favicon/favicon_test.go @@ -1,16 +1,18 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package favicon import ( + "fmt" "net/http" "net/http/httptest" "os" "strings" "testing" - "github.com/valyala/fasthttp" - "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + + "github.com/valyala/fasthttp" ) // go test -run Test_Middleware_Favicon @@ -25,22 +27,22 @@ func Test_Middleware_Favicon(t *testing.T) { }) // Skip Favicon middleware - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") - resp, err = app.Test(httptest.NewRequest("GET", "/favicon.ico", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/favicon.ico", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusNoContent, resp.StatusCode, "Status code") - resp, err = app.Test(httptest.NewRequest("OPTIONS", "/favicon.ico", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodOptions, "/favicon.ico", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") - resp, err = app.Test(httptest.NewRequest("PUT", "/favicon.ico", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodPut, "/favicon.ico", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusMethodNotAllowed, resp.StatusCode, "Status code") - utils.AssertEqual(t, "GET, HEAD, OPTIONS", resp.Header.Get(fiber.HeaderAllow)) + utils.AssertEqual(t, strings.Join([]string{fiber.MethodGet, fiber.MethodHead, fiber.MethodOptions}, ", "), resp.Header.Get(fiber.HeaderAllow)) } // go test -run Test_Middleware_Favicon_Not_Found @@ -70,8 +72,7 @@ func Test_Middleware_Favicon_Found(t *testing.T) { return nil }) - resp, err := app.Test(httptest.NewRequest("GET", "/favicon.ico", nil)) - + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/favicon.ico", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) @@ -83,15 +84,15 @@ func Test_Middleware_Favicon_Found(t *testing.T) { // TODO use os.Dir if fiber upgrades to 1.16 type mockFS struct{} -func (m mockFS) Open(name string) (http.File, error) { +func (mockFS) Open(name string) (http.File, error) { if name == "/" { name = "." } else { name = strings.TrimPrefix(name, "/") } - file, err := os.Open(name) + file, err := os.Open(name) //nolint:gosec // We're in a test func, so this is fine if err != nil { - return nil, err + return nil, fmt.Errorf("failed to open: %w", err) } return file, nil } @@ -106,7 +107,7 @@ func Test_Middleware_Favicon_FileSystem(t *testing.T) { FileSystem: mockFS{}, })) - resp, err := app.Test(httptest.NewRequest("GET", "/favicon.ico", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/favicon.ico", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) @@ -123,7 +124,7 @@ func Test_Middleware_Favicon_CacheControl(t *testing.T) { File: "../../.github/testdata/favicon.ico", })) - resp, err := app.Test(httptest.NewRequest("GET", "/favicon.ico", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/favicon.ico", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) @@ -159,7 +160,7 @@ func Test_Favicon_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } diff --git a/middleware/filesystem/filesystem.go b/middleware/filesystem/filesystem.go index 890fdf9824..1913ef59e5 100644 --- a/middleware/filesystem/filesystem.go +++ b/middleware/filesystem/filesystem.go @@ -1,6 +1,7 @@ package filesystem import ( + "fmt" "net/http" "os" "strconv" @@ -55,6 +56,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Root: nil, @@ -102,7 +105,7 @@ func New(config ...Config) fiber.Handler { cacheControlStr := "public, max-age=" + strconv.Itoa(cfg.MaxAge) // Return new handler - return func(c *fiber.Ctx) (err error) { + return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() @@ -131,28 +134,23 @@ func New(config ...Config) fiber.Handler { path = cfg.PathPrefix + path } - var ( - file http.File - stat os.FileInfo - ) - if len(path) > 1 { path = utils.TrimRight(path, '/') } - file, err = cfg.Root.Open(path) + file, err := cfg.Root.Open(path) if err != nil && os.IsNotExist(err) && cfg.NotFoundFile != "" { file, err = cfg.Root.Open(cfg.NotFoundFile) } - if err != nil { if os.IsNotExist(err) { return c.Status(fiber.StatusNotFound).Next() } - return + return fmt.Errorf("failed to open: %w", err) } - if stat, err = file.Stat(); err != nil { - return + stat, err := file.Stat() + if err != nil { + return fmt.Errorf("failed to stat: %w", err) } // Serve index if path is directory @@ -200,7 +198,7 @@ func New(config ...Config) fiber.Handler { c.Response().SkipBody = true c.Response().Header.SetContentLength(contentLength) if err := file.Close(); err != nil { - return err + return fmt.Errorf("failed to close: %w", err) } return nil } @@ -210,22 +208,18 @@ func New(config ...Config) fiber.Handler { } // SendFile ... -func SendFile(c *fiber.Ctx, fs http.FileSystem, path string) (err error) { - var ( - file http.File - stat os.FileInfo - ) - - file, err = fs.Open(path) +func SendFile(c *fiber.Ctx, fs http.FileSystem, path string) error { + file, err := fs.Open(path) if err != nil { if os.IsNotExist(err) { return fiber.ErrNotFound } - return err + return fmt.Errorf("failed to open: %w", err) } - if stat, err = file.Stat(); err != nil { - return err + stat, err := file.Stat() + if err != nil { + return fmt.Errorf("failed to stat: %w", err) } // Serve index if path is directory @@ -268,7 +262,7 @@ func SendFile(c *fiber.Ctx, fs http.FileSystem, path string) (err error) { c.Response().SkipBody = true c.Response().Header.SetContentLength(contentLength) if err := file.Close(); err != nil { - return err + return fmt.Errorf("failed to close: %w", err) } return nil } diff --git a/middleware/filesystem/filesystem_test.go b/middleware/filesystem/filesystem_test.go index dcbfcace75..56c113e0c5 100644 --- a/middleware/filesystem/filesystem_test.go +++ b/middleware/filesystem/filesystem_test.go @@ -1,6 +1,8 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package filesystem import ( + "context" "net/http" "net/http/httptest" "testing" @@ -119,7 +121,7 @@ func Test_FileSystem(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - resp, err := app.Test(httptest.NewRequest("GET", tt.url, nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, tt.url, nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, tt.statusCode, resp.StatusCode) @@ -142,7 +144,7 @@ func Test_FileSystem_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -168,7 +170,8 @@ func Test_FileSystem_Head(t *testing.T) { Root: http.Dir("../../.github/testdata/fs"), })) - req, _ := http.NewRequest(fiber.MethodHead, "/test", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodHead, "/test", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) @@ -182,7 +185,8 @@ func Test_FileSystem_NoRoot(t *testing.T) { app := fiber.New() app.Use(New()) - _, _ = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + _, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) } func Test_FileSystem_UsingParam(t *testing.T) { @@ -193,7 +197,8 @@ func Test_FileSystem_UsingParam(t *testing.T) { return SendFile(c, http.Dir("../../.github/testdata/fs"), c.Params("path")+".html") }) - req, _ := http.NewRequest(fiber.MethodHead, "/index", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodHead, "/index", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) @@ -207,7 +212,8 @@ func Test_FileSystem_UsingParam_NonFile(t *testing.T) { return SendFile(c, http.Dir("../../.github/testdata/fs"), c.Params("path")+".html") }) - req, _ := http.NewRequest(fiber.MethodHead, "/template", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodHead, "/template", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 404, resp.StatusCode) diff --git a/middleware/filesystem/utils.go b/middleware/filesystem/utils.go index 386c13e389..4e96db62ac 100644 --- a/middleware/filesystem/utils.go +++ b/middleware/filesystem/utils.go @@ -13,18 +13,18 @@ import ( "github.com/gofiber/fiber/v2/utils" ) -func getFileExtension(path string) string { - n := strings.LastIndexByte(path, '.') +func getFileExtension(p string) string { + n := strings.LastIndexByte(p, '.') if n < 0 { return "" } - return path[n:] + return p[n:] } func dirList(c *fiber.Ctx, f http.File) error { fileinfos, err := f.Readdir(-1) if err != nil { - return err + return fmt.Errorf("failed to read dir: %w", err) } fm := make(map[string]os.FileInfo, len(fileinfos)) @@ -36,13 +36,13 @@ func dirList(c *fiber.Ctx, f http.File) error { } basePathEscaped := html.EscapeString(c.Path()) - fmt.Fprintf(c, "%s", basePathEscaped) - fmt.Fprintf(c, "

%s

", basePathEscaped) - fmt.Fprint(c, "
    ") + _, _ = fmt.Fprintf(c, "%s", basePathEscaped) + _, _ = fmt.Fprintf(c, "

    %s

    ", basePathEscaped) + _, _ = fmt.Fprint(c, "
      ") if len(basePathEscaped) > 1 { parentPathEscaped := html.EscapeString(utils.TrimRight(c.Path(), '/') + "/..") - fmt.Fprintf(c, `
    • ..
    • `, parentPathEscaped) + _, _ = fmt.Fprintf(c, `
    • ..
    • `, parentPathEscaped) } sort.Strings(filenames) @@ -55,10 +55,10 @@ func dirList(c *fiber.Ctx, f http.File) error { auxStr = fmt.Sprintf("file, %d bytes", fi.Size()) className = "file" } - fmt.Fprintf(c, `
    • %s, %s, last modified %s
    • `, + _, _ = fmt.Fprintf(c, `
    • %s, %s, last modified %s
    • `, pathEscaped, className, html.EscapeString(name), auxStr, fi.ModTime()) } - fmt.Fprint(c, "
    ") + _, _ = fmt.Fprint(c, "
") c.Type("html") diff --git a/middleware/idempotency/config.go b/middleware/idempotency/config.go index c8f249a074..21aeb6909c 100644 --- a/middleware/idempotency/config.go +++ b/middleware/idempotency/config.go @@ -9,9 +9,7 @@ import ( "github.com/gofiber/fiber/v2/internal/storage/memory" ) -var ( - ErrInvalidIdempotencyKey = errors.New("invalid idempotency key") -) +var ErrInvalidIdempotencyKey = errors.New("invalid idempotency key") // Config defines the config for middleware. type Config struct { @@ -51,13 +49,15 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: func(c *fiber.Ctx) bool { // Skip middleware if the request was done using a safe HTTP method return fiber.IsMethodSafe(c.Method()) }, - Lifetime: 30 * time.Minute, + Lifetime: 30 * time.Minute, //nolint:gomnd // No magic number, just the default config KeyHeader: "X-Idempotency-Key", KeyHeaderValidate: func(k string) error { @@ -112,7 +112,7 @@ func configDefault(config ...Config) Config { if cfg.Storage == nil { cfg.Storage = memory.New(memory.Config{ - GCInterval: cfg.Lifetime / 2, + GCInterval: cfg.Lifetime / 2, //nolint:gomnd // Half the lifetime interval }) } diff --git a/middleware/idempotency/idempotency_test.go b/middleware/idempotency/idempotency_test.go index e6fdbcd8a3..6612cc6417 100644 --- a/middleware/idempotency/idempotency_test.go +++ b/middleware/idempotency/idempotency_test.go @@ -1,3 +1,4 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package idempotency_test import ( @@ -14,6 +15,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/idempotency" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -172,5 +174,4 @@ func Benchmark_Idempotency(b *testing.B) { h(c) } }) - } diff --git a/middleware/limiter/config.go b/middleware/limiter/config.go index 6da6672b88..a123ea227b 100644 --- a/middleware/limiter/config.go +++ b/middleware/limiter/config.go @@ -1,7 +1,7 @@ package limiter import ( - "fmt" + "log" "time" "github.com/gofiber/fiber/v2" @@ -58,19 +58,21 @@ type Config struct { // Default: a new Fixed Window Rate Limiter LimiterMiddleware LimiterHandler - // DEPRECATED: Use Expiration instead + // Deprecated: Use Expiration instead Duration time.Duration - // DEPRECATED, use Storage instead + // Deprecated: Use Storage instead Store fiber.Storage - // DEPRECATED, use KeyGenerator instead + // Deprecated: Use KeyGenerator instead Key func(*fiber.Ctx) string } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ - Max: 5, + Max: 5, //nolint:gomnd // No magic number, just the default config Expiration: 1 * time.Minute, KeyGenerator: func(c *fiber.Ctx) string { return c.IP() @@ -95,15 +97,15 @@ func configDefault(config ...Config) Config { // Set default values if int(cfg.Duration.Seconds()) > 0 { - fmt.Println("[LIMITER] Duration is deprecated, please use Expiration") + log.Printf("[LIMITER] Duration is deprecated, please use Expiration\n") cfg.Expiration = cfg.Duration } if cfg.Key != nil { - fmt.Println("[LIMITER] Key is deprecated, please us KeyGenerator") + log.Printf("[LIMITER] Key is deprecated, please us KeyGenerator\n") cfg.KeyGenerator = cfg.Key } if cfg.Store != nil { - fmt.Println("[LIMITER] Store is deprecated, please use Storage") + log.Printf("[LIMITER] Store is deprecated, please use Storage\n") cfg.Storage = cfg.Store } if cfg.Next == nil { diff --git a/middleware/limiter/limiter_test.go b/middleware/limiter/limiter_test.go index 847aba354d..a57ca477f4 100644 --- a/middleware/limiter/limiter_test.go +++ b/middleware/limiter/limiter_test.go @@ -2,7 +2,6 @@ package limiter import ( "io" - "net/http" "net/http/httptest" "sync" "testing" @@ -11,6 +10,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/storage/memory" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -34,7 +34,7 @@ func Test_Limiter_Concurrency_Store(t *testing.T) { var wg sync.WaitGroup singleRequest := func(wg *sync.WaitGroup) { defer wg.Done() - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) @@ -50,13 +50,13 @@ func Test_Limiter_Concurrency_Store(t *testing.T) { wg.Wait() - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 429, resp.StatusCode) time.Sleep(3 * time.Second) - resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) } @@ -80,7 +80,7 @@ func Test_Limiter_Concurrency(t *testing.T) { var wg sync.WaitGroup singleRequest := func(wg *sync.WaitGroup) { defer wg.Done() - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) @@ -96,13 +96,13 @@ func Test_Limiter_Concurrency(t *testing.T) { wg.Wait() - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 429, resp.StatusCode) time.Sleep(3 * time.Second) - resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) } @@ -120,21 +120,21 @@ func Test_Limiter_No_Skip_Choices(t *testing.T) { })) app.Get("/:status", func(c *fiber.Ctx) error { - if c.Params("status") == "fail" { + if c.Params("status") == "fail" { //nolint:goconst // False positive return c.SendStatus(400) } return c.SendStatus(200) }) - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 400, resp.StatusCode) - resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) - resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 429, resp.StatusCode) } @@ -157,21 +157,21 @@ func Test_Limiter_Skip_Failed_Requests(t *testing.T) { return c.SendStatus(200) }) - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 400, resp.StatusCode) - resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) - resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 429, resp.StatusCode) time.Sleep(3 * time.Second) - resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) } @@ -196,21 +196,21 @@ func Test_Limiter_Skip_Successful_Requests(t *testing.T) { return c.SendStatus(200) }) - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/success", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) - resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 400, resp.StatusCode) - resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 429, resp.StatusCode) time.Sleep(3 * time.Second) - resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 400, resp.StatusCode) } @@ -232,7 +232,7 @@ func Benchmark_Limiter_Custom_Store(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") b.ResetTimer() @@ -252,7 +252,7 @@ func Test_Limiter_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -271,7 +271,7 @@ func Test_Limiter_Headers(t *testing.T) { }) fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") app.Handler()(fctx) @@ -301,7 +301,7 @@ func Benchmark_Limiter(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") b.ResetTimer() @@ -327,7 +327,7 @@ func Test_Sliding_Window(t *testing.T) { }) singleRequest := func(shouldFail bool) { - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) if shouldFail { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 429, resp.StatusCode) diff --git a/middleware/limiter/manager.go b/middleware/limiter/manager.go index 68a785a7c2..6b257f4593 100644 --- a/middleware/limiter/manager.go +++ b/middleware/limiter/manager.go @@ -46,7 +46,7 @@ func newManager(storage fiber.Storage) *manager { // acquire returns an *entry from the sync.Pool func (m *manager) acquire() *item { - return m.pool.Get().(*item) + return m.pool.Get().(*item) //nolint:forcetypeassert // We store nothing else in the pool } // release and reset *entry to sync.Pool @@ -58,37 +58,33 @@ func (m *manager) release(e *item) { } // get data from storage or memory -func (m *manager) get(key string) (it *item) { +func (m *manager) get(key string) *item { + var it *item if m.storage != nil { it = m.acquire() - if raw, _ := m.storage.Get(key); raw != nil { + raw, err := m.storage.Get(key) + if err != nil { + return it + } + if raw != nil { if _, err := it.UnmarshalMsg(raw); err != nil { - return + return it } } - return + return it } - if it, _ = m.memory.Get(key).(*item); it == nil { + if it, _ = m.memory.Get(key).(*item); it == nil { //nolint:errcheck // We store nothing else in the pool it = m.acquire() + return it } - return -} - -// get raw data from storage or memory -func (m *manager) getRaw(key string) (raw []byte) { - if m.storage != nil { - raw, _ = m.storage.Get(key) - } else { - raw, _ = m.memory.Get(key).([]byte) - } - return + return it } // set data to storage or memory func (m *manager) set(key string, it *item, exp time.Duration) { if m.storage != nil { if raw, err := it.MarshalMsg(nil); err == nil { - _ = m.storage.Set(key, raw, exp) + _ = m.storage.Set(key, raw, exp) //nolint:errcheck // TODO: Handle error here } // we can release data because it's serialized to database m.release(it) @@ -96,21 +92,3 @@ func (m *manager) set(key string, it *item, exp time.Duration) { m.memory.Set(key, it, exp) } } - -// set data to storage or memory -func (m *manager) setRaw(key string, raw []byte, exp time.Duration) { - if m.storage != nil { - _ = m.storage.Set(key, raw, exp) - } else { - m.memory.Set(key, raw, exp) - } -} - -// delete data from storage or memory -func (m *manager) delete(key string) { - if m.storage != nil { - _ = m.storage.Delete(key) - } else { - m.memory.Delete(key) - } -} diff --git a/middleware/logger/README.md b/middleware/logger/README.md index 60bb2eca68..652f375fe5 100644 --- a/middleware/logger/README.md +++ b/middleware/logger/README.md @@ -95,7 +95,7 @@ app.Use(logger.New(logger.Config{ TimeZone: "Asia/Shanghai", Done: func(c *fiber.Ctx, logString []byte) { if c.Response().StatusCode() != fiber.StatusOK { - reporter.SendToSlack(logString) + reporter.SendToSlack(logString) } }, })) @@ -189,7 +189,7 @@ const ( TagBytesReceived = "bytesReceived" TagRoute = "route" TagError = "error" - // DEPRECATED: Use TagReqHeader instead + // Deprecated: Use TagReqHeader instead TagHeader = "header:" // request header TagReqHeader = "reqHeader:" // request header TagRespHeader = "respHeader:" // response header diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 21f34aad7c..0d45468230 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -79,13 +79,15 @@ type Buffer interface { type LogFunc func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Done: nil, Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", TimeFormat: "15:04:05", TimeZone: "Local", - TimeInterval: 500 * time.Millisecond, + TimeInterval: 500 * time.Millisecond, //nolint:gomnd // No magic number, just the default config Output: os.Stdout, enableColors: true, } diff --git a/middleware/logger/data.go b/middleware/logger/data.go index 611a0aeee2..912d016a4b 100644 --- a/middleware/logger/data.go +++ b/middleware/logger/data.go @@ -1,13 +1,10 @@ package logger import ( - "sync" "sync/atomic" "time" ) -var DataPool = sync.Pool{New: func() interface{} { return new(Data) }} - // Data is a struct to define some variables to use in custom logger function. type Data struct { Pid string diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 1fa07439b4..daa97063f9 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -11,6 +11,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/valyala/bytebufferpool" @@ -55,6 +56,8 @@ func New(config ...Config) fiber.Handler { once sync.Once mu sync.Mutex errHandler fiber.ErrorHandler + + dataPool = sync.Pool{New: func() interface{} { return new(Data) }} ) // If colors are enabled, check terminal compatibility @@ -75,7 +78,7 @@ func New(config ...Config) fiber.Handler { } // Return new handler - return func(c *fiber.Ctx) (err error) { + return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() @@ -101,13 +104,13 @@ func New(config ...Config) fiber.Handler { }) // Logger data - data := DataPool.Get().(*Data) + data := dataPool.Get().(*Data) //nolint:forcetypeassert,errcheck // We store nothing else in the pool // no need for a reset, as long as we always override everything data.Pid = pid data.ErrPaddingStr = errPaddingStr data.Timestamp = timestamp // put data back in the pool - defer DataPool.Put(data) + defer dataPool.Put(data) // Set latency start time if cfg.enableLatency { @@ -121,7 +124,7 @@ func New(config ...Config) fiber.Handler { // Manually call error handler if chainErr != nil { if err := errHandler(c, chainErr); err != nil { - _ = c.SendStatus(fiber.StatusInternalServerError) + _ = c.SendStatus(fiber.StatusInternalServerError) //nolint:errcheck // TODO: Explain why we ignore the error here } } @@ -142,18 +145,20 @@ func New(config ...Config) fiber.Handler { } // Format log to buffer - _, _ = buf.WriteString(fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n", - timestamp.Load().(string), - statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset, - data.Stop.Sub(data.Start).Round(time.Millisecond), - c.IP(), - methodColor(c.Method(), colors), c.Method(), colors.Reset, - c.Path(), - formatErr, - )) + _, _ = buf.WriteString( //nolint:errcheck // This will never fail + fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n", + timestamp.Load().(string), + statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset, + data.Stop.Sub(data.Start).Round(time.Millisecond), + c.IP(), + methodColor(c.Method(), colors), c.Method(), colors.Reset, + c.Path(), + formatErr, + ), + ) // Write buffer to output - _, _ = cfg.Output.Write(buf.Bytes()) + _, _ = cfg.Output.Write(buf.Bytes()) //nolint:errcheck // This will never fail if cfg.Done != nil { cfg.Done(c, buf.Bytes()) @@ -169,7 +174,7 @@ func New(config ...Config) fiber.Handler { // Loop over template parts execute dynamic parts and add fixed parts to the buffer for i, logFunc := range logFunChain { if logFunc == nil { - _, _ = buf.Write(templateChain[i]) + _, _ = buf.Write(templateChain[i]) //nolint:errcheck // This will never fail } else if templateChain[i] == nil { _, err = logFunc(buf, c, data, "") } else { @@ -182,7 +187,7 @@ func New(config ...Config) fiber.Handler { // Also write errors to the buffer if err != nil { - _, _ = buf.WriteString(err.Error()) + _, _ = buf.WriteString(err.Error()) //nolint:errcheck // This will never fail } mu.Lock() // Write buffer to output @@ -190,7 +195,7 @@ func New(config ...Config) fiber.Handler { // Write error to output if _, err := cfg.Output.Write([]byte(err.Error())); err != nil { // There is something wrong with the given io.Writer - fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + _, _ = fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) } } mu.Unlock() diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 2295e8a4f5..fb81875223 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -1,3 +1,4 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package logger import ( @@ -16,6 +17,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/requestid" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) @@ -37,7 +39,7 @@ func Test_Logger(t *testing.T) { return errors.New("some random error") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) utils.AssertEqual(t, "some random error", buf.String()) @@ -70,21 +72,21 @@ func Test_Logger_locals(t *testing.T) { return c.SendStatus(fiber.StatusOK) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "johndoe", buf.String()) buf.Reset() - resp, err = app.Test(httptest.NewRequest("GET", "/int", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/int", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "55", buf.String()) buf.Reset() - resp, err = app.Test(httptest.NewRequest("GET", "/empty", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/empty", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "", buf.String()) @@ -100,7 +102,7 @@ func Test_Logger_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -113,15 +115,15 @@ func Test_Logger_Done(t *testing.T) { app.Use(New(Config{ Done: func(c *fiber.Ctx, logString []byte) { if c.Response().StatusCode() == fiber.StatusOK { - buf.Write(logString) + _, err := buf.Write(logString) + utils.AssertEqual(t, nil, err) } }, })).Get("/logging", func(ctx *fiber.Ctx) error { return ctx.SendStatus(fiber.StatusOK) }) - resp, err := app.Test(httptest.NewRequest("GET", "/logging", nil)) - + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/logging", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, true, buf.Len() > 0) @@ -135,7 +137,7 @@ func Test_Logger_ErrorTimeZone(t *testing.T) { TimeZone: "invalid", })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -156,7 +158,7 @@ func Test_Logger_ErrorOutput(t *testing.T) { Output: o, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) @@ -178,7 +180,7 @@ func Test_Logger_All(t *testing.T) { // Alias colors colors := app.Config().ColorScheme - resp, err := app.Test(httptest.NewRequest("GET", "/?foo=bar", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/?foo=bar", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) @@ -198,7 +200,7 @@ func Test_Query_Params(t *testing.T) { Output: buf, })) - resp, err := app.Test(httptest.NewRequest("GET", "/?foo=bar&baz=moz", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/?foo=bar&baz=moz", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) @@ -226,7 +228,7 @@ func Test_Response_Body(t *testing.T) { return c.Send([]byte("Post in test")) }) - _, err := app.Test(httptest.NewRequest("GET", "/", nil)) + _, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) expectedGetResponse := "Sample response body" @@ -234,7 +236,7 @@ func Test_Response_Body(t *testing.T) { buf.Reset() // Reset buffer to test POST - _, err = app.Test(httptest.NewRequest("POST", "/test", nil)) + _, err = app.Test(httptest.NewRequest(fiber.MethodPost, "/test", nil)) utils.AssertEqual(t, nil, err) expectedPostResponse := "Post in test" @@ -258,7 +260,7 @@ func Test_Logger_AppendUint(t *testing.T) { return c.SendString("hello") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "0 5 200", buf.String()) @@ -285,12 +287,11 @@ func Test_Logger_Data_Race(t *testing.T) { wg := &sync.WaitGroup{} wg.Add(1) go func() { - resp1, err1 = app.Test(httptest.NewRequest("GET", "/", nil)) + resp1, err1 = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) wg.Done() }() - resp2, err2 = app.Test(httptest.NewRequest("GET", "/", nil)) + resp2, err2 = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) wg.Wait() - utils.AssertEqual(t, nil, err1) utils.AssertEqual(t, fiber.StatusOK, resp1.StatusCode) utils.AssertEqual(t, nil, err2) @@ -299,21 +300,23 @@ func Test_Logger_Data_Race(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Logger -benchmem -count=4 func Benchmark_Logger(b *testing.B) { - benchSetup := func(bb *testing.B, app *fiber.App) { + benchSetup := func(b *testing.B, app *fiber.App) { + b.Helper() + h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") - bb.ReportAllocs() - bb.ResetTimer() + b.ReportAllocs() + b.ResetTimer() - for n := 0; n < bb.N; n++ { + for n := 0; n < b.N; n++ { h(fctx) } - utils.AssertEqual(bb, 200, fctx.Response.Header.StatusCode()) + utils.AssertEqual(b, 200, fctx.Response.Header.StatusCode()) } b.Run("Base", func(bb *testing.B) { @@ -375,8 +378,7 @@ func Test_Response_Header(t *testing.T) { return c.SendString("Hello fiber!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "Hello fiber!", buf.String()) @@ -396,10 +398,10 @@ func Test_Req_Header(t *testing.T) { app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello fiber!") }) - headerReq := httptest.NewRequest("GET", "/", nil) + headerReq := httptest.NewRequest(fiber.MethodGet, "/", nil) headerReq.Header.Add("test", "Hello fiber!") - resp, err := app.Test(headerReq) + resp, err := app.Test(headerReq) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "Hello fiber!", buf.String()) @@ -419,10 +421,10 @@ func Test_ReqHeader_Header(t *testing.T) { app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello fiber!") }) - reqHeaderReq := httptest.NewRequest("GET", "/", nil) + reqHeaderReq := httptest.NewRequest(fiber.MethodGet, "/", nil) reqHeaderReq.Header.Add("test", "Hello fiber!") - resp, err := app.Test(reqHeaderReq) + resp, err := app.Test(reqHeaderReq) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "Hello fiber!", buf.String()) @@ -449,10 +451,10 @@ func Test_CustomTags(t *testing.T) { app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello fiber!") }) - reqHeaderReq := httptest.NewRequest("GET", "/", nil) + reqHeaderReq := httptest.NewRequest(fiber.MethodGet, "/", nil) reqHeaderReq.Header.Add("test", "Hello fiber!") - resp, err := app.Test(reqHeaderReq) + resp, err := app.Test(reqHeaderReq) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, customTag, buf.String()) @@ -492,7 +494,7 @@ func Test_Logger_ByteSent_Streaming(t *testing.T) { return nil }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "0 0 200", buf.String()) diff --git a/middleware/logger/tags.go b/middleware/logger/tags.go index 7eb67582bc..2f746ddd4e 100644 --- a/middleware/logger/tags.go +++ b/middleware/logger/tags.go @@ -31,7 +31,7 @@ const ( TagBytesReceived = "bytesReceived" TagRoute = "route" TagError = "error" - // DEPRECATED: Use TagReqHeader instead + // Deprecated: Use TagReqHeader instead TagHeader = "header:" TagReqHeader = "reqHeader:" TagRespHeader = "respHeader:" @@ -195,7 +195,7 @@ func createTagMap(cfg *Config) map[string]LogFunc { return output.WriteString(fmt.Sprintf("%7v", latency)) }, TagTime: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return output.WriteString(data.Timestamp.Load().(string)) + return output.WriteString(data.Timestamp.Load().(string)) //nolint:forcetypeassert // We always store a string in here }, } // merge with custom tags from user diff --git a/middleware/logger/template_chain.go b/middleware/logger/template_chain.go index ceb31a3356..1a5dd448f6 100644 --- a/middleware/logger/template_chain.go +++ b/middleware/logger/template_chain.go @@ -14,13 +14,16 @@ import ( // funcChain contains for the parts which exist the functions for the dynamic parts // funcChain and fixParts always have the same length and contain nil for the parts where no data is required in the chain, // if a function exists for the part, a parameter for it can also exist in the fixParts slice -func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) (fixParts [][]byte, funcChain []LogFunc, err error) { +func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) ([][]byte, []LogFunc, error) { // process flow is copied from the fasttemplate flow https://github.com/valyala/fasttemplate/blob/2a2d1afadadf9715bfa19683cdaeac8347e5d9f9/template.go#L23-L62 templateB := utils.UnsafeBytes(cfg.Format) startTagB := utils.UnsafeBytes(startTag) endTagB := utils.UnsafeBytes(endTag) paramSeparatorB := utils.UnsafeBytes(paramSeparator) + var fixParts [][]byte + var funcChain []LogFunc + for { currentPos := bytes.Index(templateB, startTagB) if currentPos < 0 { @@ -42,13 +45,13 @@ func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) (fixParts [ // ## function block ## // first check for tags with parameters if index := bytes.Index(templateB[:currentPos], paramSeparatorB); index != -1 { - if logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:index+1])]; ok { - funcChain = append(funcChain, logFunc) - // add param to the fixParts - fixParts = append(fixParts, templateB[index+1:currentPos]) - } else { + logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:index+1])] + if !ok { return nil, nil, errors.New("No parameter found in \"" + utils.UnsafeString(templateB[:currentPos]) + "\"") } + funcChain = append(funcChain, logFunc) + // add param to the fixParts + fixParts = append(fixParts, templateB[index+1:currentPos]) } else if logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:currentPos])]; ok { // add functions without parameter funcChain = append(funcChain, logFunc) @@ -63,5 +66,5 @@ func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) (fixParts [ funcChain = append(funcChain, nil) fixParts = append(fixParts, templateB) - return + return fixParts, funcChain, nil } diff --git a/middleware/monitor/config.go b/middleware/monitor/config.go index 559dd405ef..10889707f9 100644 --- a/middleware/monitor/config.go +++ b/middleware/monitor/config.go @@ -41,36 +41,48 @@ type Config struct { // ChartJsURL for specify ChartJS library path or URL . also you can use relative path // // Optional. Default: https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js - ChartJsURL string + ChartJSURL string index string } +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Title: defaultTitle, Refresh: defaultRefresh, FontURL: defaultFontURL, - ChartJsURL: defaultChartJsURL, + ChartJSURL: defaultChartJSURL, CustomHead: defaultCustomHead, APIOnly: false, Next: nil, - index: newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, - defaultCustomHead}), + index: newIndex(viewBag{ + defaultTitle, + defaultRefresh, + defaultFontURL, + defaultChartJSURL, + defaultCustomHead, + }), } func configDefault(config ...Config) Config { // Users can change ConfigDefault.Title/Refresh which then // become incompatible with ConfigDefault.index - if ConfigDefault.Title != defaultTitle || ConfigDefault.Refresh != defaultRefresh || - ConfigDefault.FontURL != defaultFontURL || ConfigDefault.ChartJsURL != defaultChartJsURL || + if ConfigDefault.Title != defaultTitle || + ConfigDefault.Refresh != defaultRefresh || + ConfigDefault.FontURL != defaultFontURL || + ConfigDefault.ChartJSURL != defaultChartJSURL || ConfigDefault.CustomHead != defaultCustomHead { - if ConfigDefault.Refresh < minRefresh { ConfigDefault.Refresh = minRefresh } // update default index with new default title/refresh - ConfigDefault.index = newIndex(viewBag{ConfigDefault.Title, - ConfigDefault.Refresh, ConfigDefault.FontURL, ConfigDefault.ChartJsURL, ConfigDefault.CustomHead}) + ConfigDefault.index = newIndex(viewBag{ + ConfigDefault.Title, + ConfigDefault.Refresh, + ConfigDefault.FontURL, + ConfigDefault.ChartJSURL, + ConfigDefault.CustomHead, + }) } // Return default config if nothing provided @@ -93,8 +105,8 @@ func configDefault(config ...Config) Config { cfg.FontURL = defaultFontURL } - if cfg.ChartJsURL == "" { - cfg.ChartJsURL = defaultChartJsURL + if cfg.ChartJSURL == "" { + cfg.ChartJSURL = defaultChartJSURL } if cfg.Refresh < minRefresh { cfg.Refresh = minRefresh @@ -112,8 +124,8 @@ func configDefault(config ...Config) Config { cfg.index = newIndex(viewBag{ title: cfg.Title, refresh: cfg.Refresh, - fontUrl: cfg.FontURL, - chartJsUrl: cfg.ChartJsURL, + fontURL: cfg.FontURL, + chartJSURL: cfg.ChartJSURL, customHead: cfg.CustomHead, }) diff --git a/middleware/monitor/config_test.go b/middleware/monitor/config_test.go index 062d1e4070..60bf09ca70 100644 --- a/middleware/monitor/config_test.go +++ b/middleware/monitor/config_test.go @@ -18,11 +18,11 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) t.Run("set title", func(t *testing.T) { @@ -35,11 +35,11 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, title, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{title, defaultRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{title, defaultRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) t.Run("set refresh less than default", func(t *testing.T) { @@ -51,11 +51,11 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, minRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, minRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, minRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) t.Run("set refresh", func(t *testing.T) { @@ -68,45 +68,45 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, refresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, refresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, refresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) t.Run("set font url", func(t *testing.T) { t.Parallel() - fontUrl := "https://example.com" + fontURL := "https://example.com" cfg := configDefault(Config{ - FontURL: fontUrl, + FontURL: fontURL, }) utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) - utils.AssertEqual(t, fontUrl, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, fontURL, cfg.FontURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, fontUrl, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, fontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) t.Run("set chart js url", func(t *testing.T) { t.Parallel() - chartUrl := "http://example.com" + chartURL := "http://example.com" cfg := configDefault(Config{ - ChartJsURL: chartUrl, + ChartJSURL: chartURL, }) utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, chartUrl, cfg.ChartJsURL) + utils.AssertEqual(t, chartURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, chartUrl, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, chartURL, defaultCustomHead}), cfg.index) }) t.Run("set custom head", func(t *testing.T) { @@ -119,11 +119,11 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, head, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, head}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJSURL, head}), cfg.index) }) t.Run("set api only", func(t *testing.T) { @@ -135,11 +135,11 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, true, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) t.Run("set next", func(t *testing.T) { @@ -154,10 +154,10 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, f(nil), cfg.Next(nil)) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) } diff --git a/middleware/monitor/index.go b/middleware/monitor/index.go index b17c95f9e4..c873290c46 100644 --- a/middleware/monitor/index.go +++ b/middleware/monitor/index.go @@ -9,23 +9,22 @@ import ( type viewBag struct { title string refresh time.Duration - fontUrl string - chartJsUrl string + fontURL string + chartJSURL string customHead string } // returns index with new title/refresh func newIndex(dat viewBag) string { - timeout := dat.refresh.Milliseconds() - timeoutDiff if timeout < timeoutDiff { timeout = timeoutDiff } ts := strconv.FormatInt(timeout, 10) replacer := strings.NewReplacer("$TITLE", dat.title, "$TIMEOUT", ts, - "$FONT_URL", dat.fontUrl, "$CHART_JS_URL", dat.chartJsUrl, "$CUSTOM_HEAD", dat.customHead, + "$FONT_URL", dat.fontURL, "$CHART_JS_URL", dat.chartJSURL, "$CUSTOM_HEAD", dat.customHead, ) - return replacer.Replace(indexHtml) + return replacer.Replace(indexHTML) } const ( @@ -35,11 +34,11 @@ const ( timeoutDiff = 200 // timeout will be Refresh (in milliseconds) - timeoutDiff minRefresh = timeoutDiff * time.Millisecond defaultFontURL = `https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap` - defaultChartJsURL = `https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js` + defaultChartJSURL = `https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js` defaultCustomHead = `` // parametrized by $TITLE and $TIMEOUT - indexHtml = ` + indexHTML = ` diff --git a/middleware/monitor/monitor.go b/middleware/monitor/monitor.go index 729bc00988..e3e39f06fe 100644 --- a/middleware/monitor/monitor.go +++ b/middleware/monitor/monitor.go @@ -33,18 +33,20 @@ type statsOS struct { Conns int `json:"conns"` } +//nolint:gochecknoglobals // TODO: Do not use a global var here var ( - monitPidCpu atomic.Value - monitPidRam atomic.Value - monitPidConns atomic.Value - - monitOsCpu atomic.Value - monitOsRam atomic.Value - monitOsTotalRam atomic.Value - monitOsLoadAvg atomic.Value - monitOsConns atomic.Value + monitPIDCPU atomic.Value + monitPIDRAM atomic.Value + monitPIDConns atomic.Value + + monitOSCPU atomic.Value + monitOSRAM atomic.Value + monitOSTotalRAM atomic.Value + monitOSLoadAvg atomic.Value + monitOSConns atomic.Value ) +//nolint:gochecknoglobals // TODO: Do not use a global var here var ( mutex sync.RWMutex once sync.Once @@ -58,7 +60,7 @@ func New(config ...Config) fiber.Handler { // Start routine to update statistics once.Do(func() { - p, _ := process.NewProcess(int32(os.Getpid())) + p, _ := process.NewProcess(int32(os.Getpid())) //nolint:errcheck // TODO: Handle error updateStatistics(p) @@ -72,6 +74,7 @@ func New(config ...Config) fiber.Handler { }) // Return new handler + //nolint:errcheck // Ignore the type-assertion errors return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { @@ -83,15 +86,15 @@ func New(config ...Config) fiber.Handler { } if c.Get(fiber.HeaderAccept) == fiber.MIMEApplicationJSON || cfg.APIOnly { mutex.Lock() - data.PID.CPU = monitPidCpu.Load().(float64) - data.PID.RAM = monitPidRam.Load().(uint64) - data.PID.Conns = monitPidConns.Load().(int) - - data.OS.CPU = monitOsCpu.Load().(float64) - data.OS.RAM = monitOsRam.Load().(uint64) - data.OS.TotalRAM = monitOsTotalRam.Load().(uint64) - data.OS.LoadAvg = monitOsLoadAvg.Load().(float64) - data.OS.Conns = monitOsConns.Load().(int) + data.PID.CPU, _ = monitPIDCPU.Load().(float64) + data.PID.RAM, _ = monitPIDRAM.Load().(uint64) + data.PID.Conns, _ = monitPIDConns.Load().(int) + + data.OS.CPU, _ = monitOSCPU.Load().(float64) + data.OS.RAM, _ = monitOSRAM.Load().(uint64) + data.OS.TotalRAM, _ = monitOSTotalRAM.Load().(uint64) + data.OS.LoadAvg, _ = monitOSLoadAvg.Load().(float64) + data.OS.Conns, _ = monitOSConns.Load().(int) mutex.Unlock() return c.Status(fiber.StatusOK).JSON(data) } @@ -101,29 +104,35 @@ func New(config ...Config) fiber.Handler { } func updateStatistics(p *process.Process) { - pidCpu, _ := p.CPUPercent() - monitPidCpu.Store(pidCpu / 10) + pidCPU, err := p.CPUPercent() + if err != nil { + monitPIDCPU.Store(pidCPU / 10) //nolint:gomnd // TODO: Explain why we divide by 10 here + } - if osCpu, _ := cpu.Percent(0, false); len(osCpu) > 0 { - monitOsCpu.Store(osCpu[0]) + if osCPU, err := cpu.Percent(0, false); err != nil && len(osCPU) > 0 { + monitOSCPU.Store(osCPU[0]) } - if pidMem, _ := p.MemoryInfo(); pidMem != nil { - monitPidRam.Store(pidMem.RSS) + if pidRAM, err := p.MemoryInfo(); err != nil && pidRAM != nil { + monitPIDRAM.Store(pidRAM.RSS) } - if osMem, _ := mem.VirtualMemory(); osMem != nil { - monitOsRam.Store(osMem.Used) - monitOsTotalRam.Store(osMem.Total) + if osRAM, err := mem.VirtualMemory(); err != nil && osRAM != nil { + monitOSRAM.Store(osRAM.Used) + monitOSTotalRAM.Store(osRAM.Total) } - if loadAvg, _ := load.Avg(); loadAvg != nil { - monitOsLoadAvg.Store(loadAvg.Load1) + if loadAvg, err := load.Avg(); err != nil && loadAvg != nil { + monitOSLoadAvg.Store(loadAvg.Load1) } - pidConns, _ := net.ConnectionsPid("tcp", p.Pid) - monitPidConns.Store(len(pidConns)) + pidConns, err := net.ConnectionsPid("tcp", p.Pid) + if err != nil { + monitPIDConns.Store(len(pidConns)) + } - osConns, _ := net.Connections("tcp") - monitOsConns.Store(len(osConns)) + osConns, err := net.Connections("tcp") + if err != nil { + monitOSConns.Store(len(osConns)) + } } diff --git a/middleware/monitor/monitor_test.go b/middleware/monitor/monitor_test.go index d069bba08d..f6e63fd9d4 100644 --- a/middleware/monitor/monitor_test.go +++ b/middleware/monitor/monitor_test.go @@ -10,6 +10,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -61,6 +62,7 @@ func Test_Monitor_Html(t *testing.T) { conf.Refresh.Milliseconds()-timeoutDiff) utils.AssertEqual(t, true, bytes.Contains(buf, []byte(timeoutLine))) } + func Test_Monitor_Html_CustomCodes(t *testing.T) { t.Parallel() @@ -82,8 +84,10 @@ func Test_Monitor_Html_CustomCodes(t *testing.T) { utils.AssertEqual(t, true, bytes.Contains(buf, []byte(timeoutLine))) // custom config - conf := Config{Title: "New " + defaultTitle, Refresh: defaultRefresh + time.Second, - ChartJsURL: "https://cdnjs.com/libraries/Chart.js", + conf := Config{ + Title: "New " + defaultTitle, + Refresh: defaultRefresh + time.Second, + ChartJSURL: "https://cdnjs.com/libraries/Chart.js", FontURL: "/public/my-font.css", CustomHead: ``, } @@ -136,7 +140,7 @@ func Benchmark_Monitor(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") fctx.Request.Header.Set(fiber.HeaderAccept, fiber.MIMEApplicationJSON) diff --git a/middleware/pprof/config.go b/middleware/pprof/config.go index e69ffd7094..fb3e0978ae 100644 --- a/middleware/pprof/config.go +++ b/middleware/pprof/config.go @@ -1,6 +1,8 @@ package pprof -import "github.com/gofiber/fiber/v2" +import ( + "github.com/gofiber/fiber/v2" +) // Config defines the config for middleware. type Config struct { @@ -17,6 +19,7 @@ type Config struct { Prefix string } +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, } diff --git a/middleware/pprof/pprof.go b/middleware/pprof/pprof.go index 8ad85c9a23..6978d325e3 100644 --- a/middleware/pprof/pprof.go +++ b/middleware/pprof/pprof.go @@ -5,22 +5,8 @@ import ( "strings" "github.com/gofiber/fiber/v2" - "github.com/valyala/fasthttp/fasthttpadaptor" -) -// Set pprof adaptors -var ( - pprofIndex = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Index) - pprofCmdline = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Cmdline) - pprofProfile = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Profile) - pprofSymbol = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Symbol) - pprofTrace = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Trace) - pprofAllocs = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("allocs").ServeHTTP) - pprofBlock = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("block").ServeHTTP) - pprofGoroutine = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("goroutine").ServeHTTP) - pprofHeap = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("heap").ServeHTTP) - pprofMutex = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("mutex").ServeHTTP) - pprofThreadcreate = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("threadcreate").ServeHTTP) + "github.com/valyala/fasthttp/fasthttpadaptor" ) // New creates a new middleware handler @@ -28,6 +14,21 @@ func New(config ...Config) fiber.Handler { // Set default config cfg := configDefault(config...) + // Set pprof adaptors + var ( + pprofIndex = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Index) + pprofCmdline = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Cmdline) + pprofProfile = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Profile) + pprofSymbol = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Symbol) + pprofTrace = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Trace) + pprofAllocs = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("allocs").ServeHTTP) + pprofBlock = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("block").ServeHTTP) + pprofGoroutine = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("goroutine").ServeHTTP) + pprofHeap = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("heap").ServeHTTP) + pprofMutex = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("mutex").ServeHTTP) + pprofThreadcreate = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("threadcreate").ServeHTTP) + ) + // Return new handler return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true diff --git a/middleware/proxy/config.go b/middleware/proxy/config.go index be5e043e51..094b2c32a0 100644 --- a/middleware/proxy/config.go +++ b/middleware/proxy/config.go @@ -5,6 +5,7 @@ import ( "time" "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp" ) @@ -48,7 +49,7 @@ type Config struct { WriteBufferSize int // tls config for the http client. - TlsConfig *tls.Config + TlsConfig *tls.Config //nolint:stylecheck,revive // TODO: Rename to "TLSConfig" in v3 // Client is custom client when client config is complex. // Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig @@ -57,6 +58,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, ModifyRequest: nil, diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index a468f41b8b..356ebc10cb 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -3,19 +3,20 @@ package proxy import ( "bytes" "crypto/tls" - "fmt" + "log" "net/url" "strings" "sync" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) // New is deprecated func New(config Config) fiber.Handler { - fmt.Println("proxy.New is deprecated, please use proxy.Balancer instead") + log.Printf("proxy.New is deprecated, please use proxy.Balancer instead\n") return Balancer(config) } @@ -25,7 +26,7 @@ func Balancer(config Config) fiber.Handler { cfg := configDefault(config) // Load balanced client - var lbc = &fasthttp.LBClient{} + lbc := &fasthttp.LBClient{} // Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig // will not be used if the client are set. if config.Client == nil { @@ -61,7 +62,7 @@ func Balancer(config Config) fiber.Handler { } // Return new handler - return func(c *fiber.Ctx) (err error) { + return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() @@ -76,7 +77,7 @@ func Balancer(config Config) fiber.Handler { // Modify request if cfg.ModifyRequest != nil { - if err = cfg.ModifyRequest(c); err != nil { + if err := cfg.ModifyRequest(c); err != nil { return err } } @@ -84,7 +85,7 @@ func Balancer(config Config) fiber.Handler { req.SetRequestURI(utils.UnsafeString(req.RequestURI())) // Forward request - if err = lbc.Do(req, res); err != nil { + if err := lbc.Do(req, res); err != nil { return err } @@ -93,7 +94,7 @@ func Balancer(config Config) fiber.Handler { // Modify response if cfg.ModifyResponse != nil { - if err = cfg.ModifyResponse(c); err != nil { + if err := cfg.ModifyResponse(c); err != nil { return err } } @@ -103,16 +104,20 @@ func Balancer(config Config) fiber.Handler { } } +//nolint:gochecknoglobals // TODO: Do not use a global var here var client = &fasthttp.Client{ NoDefaultUserAgentHeader: true, DisablePathNormalizing: true, } +//nolint:gochecknoglobals // TODO: Do not use a global var here var lock sync.RWMutex // WithTlsConfig update http client with a user specified tls.config // This function should be called before Do and Forward. // Deprecated: use WithClient instead. +// +//nolint:stylecheck,revive // TODO: Rename to "WithTLSConfig" in v3 func WithTlsConfig(tlsConfig *tls.Config) { client.TLSConfig = tlsConfig } diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index d7626da34e..d87145514a 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -4,7 +4,6 @@ import ( "crypto/tls" "io" "net" - "net/http" "net/http/httptest" "strings" "testing" @@ -13,10 +12,11 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/tlstest" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) -func createProxyTestServer(handler fiber.Handler, t *testing.T) (*fiber.App, string) { +func createProxyTestServer(t *testing.T, handler fiber.Handler) (*fiber.App, string) { t.Helper() target := fiber.New(fiber.Config{DisableStartupMessage: true}) @@ -60,7 +60,7 @@ func Test_Proxy_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -69,11 +69,11 @@ func Test_Proxy_Next(t *testing.T) { func Test_Proxy(t *testing.T) { t.Parallel() - target, addr := createProxyTestServer( - func(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusTeapot) }, t, - ) + target, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusTeapot) + }) - resp, err := target.Test(httptest.NewRequest("GET", "/", nil), 2000) + resp, err := target.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 2000) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) @@ -81,7 +81,7 @@ func Test_Proxy(t *testing.T) { app.Use(Balancer(Config{Servers: []string{addr}})) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Host = addr resp, err = app.Test(req) utils.AssertEqual(t, nil, err) @@ -107,7 +107,7 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) { }) addr := ln.Addr().String() - clientTLSConf := &tls.Config{InsecureSkipVerify: true} + clientTLSConf := &tls.Config{InsecureSkipVerify: true} //nolint:gosec // We're in a test func, so this is fine // disable certificate verification in Balancer app.Use(Balancer(Config{ @@ -128,9 +128,9 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) { func Test_Proxy_Forward_WithTlsConfig_To_Http(t *testing.T) { t.Parallel() - _, targetAddr := createProxyTestServer(func(c *fiber.Ctx) error { + _, targetAddr := createProxyTestServer(t, func(c *fiber.Ctx) error { return c.SendString("hello from target") - }, t) + }) proxyServerTLSConf, _, err := tlstest.GetTLSConfigs() utils.AssertEqual(t, nil, err) @@ -164,13 +164,13 @@ func Test_Proxy_Forward(t *testing.T) { app := fiber.New() - _, addr := createProxyTestServer( - func(c *fiber.Ctx) error { return c.SendString("forwarded") }, t, - ) + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { + return c.SendString("forwarded") + }) app.Use(Forward("http://" + addr)) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) @@ -198,7 +198,7 @@ func Test_Proxy_Forward_WithTlsConfig(t *testing.T) { }) addr := ln.Addr().String() - clientTLSConf := &tls.Config{InsecureSkipVerify: true} + clientTLSConf := &tls.Config{InsecureSkipVerify: true} //nolint:gosec // We're in a test func, so this is fine // disable certificate verification WithTlsConfig(clientTLSConf) @@ -217,9 +217,9 @@ func Test_Proxy_Forward_WithTlsConfig(t *testing.T) { func Test_Proxy_Modify_Response(t *testing.T) { t.Parallel() - _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { return c.Status(500).SendString("not modified") - }, t) + }) app := fiber.New() app.Use(Balancer(Config{ @@ -230,7 +230,7 @@ func Test_Proxy_Modify_Response(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) @@ -243,10 +243,10 @@ func Test_Proxy_Modify_Response(t *testing.T) { func Test_Proxy_Modify_Request(t *testing.T) { t.Parallel() - _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { b := c.Request().Body() return c.SendString(string(b)) - }, t) + }) app := fiber.New() app.Use(Balancer(Config{ @@ -257,7 +257,7 @@ func Test_Proxy_Modify_Request(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) @@ -270,10 +270,10 @@ func Test_Proxy_Modify_Request(t *testing.T) { func Test_Proxy_Timeout_Slow_Server(t *testing.T) { t.Parallel() - _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { time.Sleep(2 * time.Second) return c.SendString("fiber is awesome") - }, t) + }) app := fiber.New() app.Use(Balancer(Config{ @@ -281,7 +281,7 @@ func Test_Proxy_Timeout_Slow_Server(t *testing.T) { Timeout: 3 * time.Second, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil), 5000) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 5000) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) @@ -294,10 +294,10 @@ func Test_Proxy_Timeout_Slow_Server(t *testing.T) { func Test_Proxy_With_Timeout(t *testing.T) { t.Parallel() - _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { time.Sleep(1 * time.Second) return c.SendString("fiber is awesome") - }, t) + }) app := fiber.New() app.Use(Balancer(Config{ @@ -305,7 +305,7 @@ func Test_Proxy_With_Timeout(t *testing.T) { Timeout: 100 * time.Millisecond, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil), 2000) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 2000) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) @@ -318,16 +318,16 @@ func Test_Proxy_With_Timeout(t *testing.T) { func Test_Proxy_Buffer_Size_Response(t *testing.T) { t.Parallel() - _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { long := strings.Join(make([]string, 5000), "-") c.Set("Very-Long-Header", long) return c.SendString("ok") - }, t) + }) app := fiber.New() app.Use(Balancer(Config{Servers: []string{addr}})) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) @@ -337,7 +337,7 @@ func Test_Proxy_Buffer_Size_Response(t *testing.T) { ReadBufferSize: 1024 * 8, })) - resp, err = app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) } @@ -357,9 +357,9 @@ func Test_Proxy_Do_RestoreOriginalURL(t *testing.T) { utils.AssertEqual(t, originalURL, c.OriginalURL()) return c.SendString("ok") }) - _, err1 := app.Test(httptest.NewRequest("GET", "/test", nil)) + _, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) // This test requires multiple requests due to zero allocation used in fiber - _, err2 := app.Test(httptest.NewRequest("GET", "/test", nil)) + _, err2 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) utils.AssertEqual(t, nil, err1) utils.AssertEqual(t, nil, err2) @@ -369,9 +369,9 @@ func Test_Proxy_Do_RestoreOriginalURL(t *testing.T) { func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) { t.Parallel() - _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { return c.SendString("hello world") - }, t) + }) app := fiber.New(fiber.Config{DisableStartupMessage: true}) app.Get("/*", func(c *fiber.Ctx) error { @@ -386,7 +386,7 @@ func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) { return nil }) - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/http://"+addr, nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/http://"+addr, nil)) utils.AssertEqual(t, nil, err) s, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) @@ -431,9 +431,8 @@ func Test_Proxy_Forward_Local_Client(t *testing.T) { app.Use(Forward("http://"+addr+"/test_local_client", &fasthttp.Client{ NoDefaultUserAgentHeader: true, DisablePathNormalizing: true, - Dial: func(addr string) (net.Conn, error) { - return fasthttp.Dial(addr) - }, + + Dial: fasthttp.Dial, })) go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() @@ -447,11 +446,11 @@ func Test_Proxy_Forward_Local_Client(t *testing.T) { func Test_ProxyBalancer_Custom_Client(t *testing.T) { t.Parallel() - target, addr := createProxyTestServer( - func(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusTeapot) }, t, - ) + target, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusTeapot) + }) - resp, err := target.Test(httptest.NewRequest("GET", "/", nil), 2000) + resp, err := target.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 2000) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) @@ -468,7 +467,7 @@ func Test_ProxyBalancer_Custom_Client(t *testing.T) { Timeout: time.Second, }})) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Host = addr resp, err = app.Test(req) utils.AssertEqual(t, nil, err) diff --git a/middleware/recover/config.go b/middleware/recover/config.go index 8e372a5aec..56b805d2e0 100644 --- a/middleware/recover/config.go +++ b/middleware/recover/config.go @@ -1,4 +1,4 @@ -package recover +package recover //nolint:predeclared // TODO: Rename to some non-builtin import ( "github.com/gofiber/fiber/v2" @@ -23,6 +23,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, EnableStackTrace: false, diff --git a/middleware/recover/recover.go b/middleware/recover/recover.go index 5d77d6d082..252d8be757 100644 --- a/middleware/recover/recover.go +++ b/middleware/recover/recover.go @@ -1,4 +1,4 @@ -package recover +package recover //nolint:predeclared // TODO: Rename to some non-builtin import ( "fmt" @@ -9,7 +9,7 @@ import ( ) func defaultStackTraceHandler(_ *fiber.Ctx, e interface{}) { - _, _ = os.Stderr.WriteString(fmt.Sprintf("panic: %v\n%s\n", e, debug.Stack())) + _, _ = os.Stderr.WriteString(fmt.Sprintf("panic: %v\n%s\n", e, debug.Stack())) //nolint:errcheck // This will never fail } // New creates a new middleware handler @@ -18,7 +18,7 @@ func New(config ...Config) fiber.Handler { cfg := configDefault(config...) // Return new handler - return func(c *fiber.Ctx) (err error) { + return func(c *fiber.Ctx) (err error) { //nolint:nonamedreturns // Uses recover() to overwrite the error // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() diff --git a/middleware/recover/recover_test.go b/middleware/recover/recover_test.go index 16bf164c12..82e34b63ef 100644 --- a/middleware/recover/recover_test.go +++ b/middleware/recover/recover_test.go @@ -1,4 +1,4 @@ -package recover +package recover //nolint:predeclared // TODO: Rename to some non-builtin import ( "net/http/httptest" @@ -24,7 +24,7 @@ func Test_Recover(t *testing.T) { panic("Hi, I'm an error!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/panic", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/panic", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) } @@ -39,7 +39,7 @@ func Test_Recover_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -55,7 +55,7 @@ func Test_Recover_EnableStackTrace(t *testing.T) { panic("Hi, I'm an error!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/panic", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/panic", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) } diff --git a/middleware/requestid/config.go b/middleware/requestid/config.go index b3b605e590..ca27b47f75 100644 --- a/middleware/requestid/config.go +++ b/middleware/requestid/config.go @@ -33,6 +33,8 @@ type Config struct { // It uses a fast UUID generator which will expose the number of // requests made to the server. To conceal this value for better // privacy, use the "utils.UUIDv4" generator. +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Header: fiber.HeaderXRequestID, diff --git a/middleware/requestid/requestid_test.go b/middleware/requestid/requestid_test.go index eddafff01b..451e96b4b2 100644 --- a/middleware/requestid/requestid_test.go +++ b/middleware/requestid/requestid_test.go @@ -19,14 +19,14 @@ func Test_RequestID(t *testing.T) { return c.SendString("Hello, World 👋!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) reqid := resp.Header.Get(fiber.HeaderXRequestID) utils.AssertEqual(t, 36, len(reqid)) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Add(fiber.HeaderXRequestID, reqid) resp, err = app.Test(req) @@ -45,7 +45,7 @@ func Test_RequestID_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, resp.Header.Get(fiber.HeaderXRequestID), "") utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) @@ -54,13 +54,13 @@ func Test_RequestID_Next(t *testing.T) { // go test -run Test_RequestID_Locals func Test_RequestID_Locals(t *testing.T) { t.Parallel() - reqId := "ThisIsARequestId" + reqID := "ThisIsARequestId" ctxKey := "ThisIsAContextKey" app := fiber.New() app.Use(New(Config{ Generator: func() string { - return reqId + return reqID }, ContextKey: ctxKey, })) @@ -68,11 +68,11 @@ func Test_RequestID_Locals(t *testing.T) { var ctxVal string app.Use(func(c *fiber.Ctx) error { - ctxVal = c.Locals(ctxKey).(string) + ctxVal = c.Locals(ctxKey).(string) //nolint:forcetypeassert,errcheck // We always store a string in here return c.Next() }) - _, err := app.Test(httptest.NewRequest("GET", "/", nil)) + _, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, reqId, ctxVal) + utils.AssertEqual(t, reqID, ctxVal) } diff --git a/middleware/session/README.md b/middleware/session/README.md index 32d8c1eda3..5e46cb1a6f 100644 --- a/middleware/session/README.md +++ b/middleware/session/README.md @@ -32,7 +32,7 @@ func (s *Session) Save() error func (s *Session) Fresh() bool func (s *Session) ID() string func (s *Session) Keys() []string -func (s *Session) SetExpiry(time.Duration) +func (s *Session) SetExpiry(time.Duration) ``` **⚠ _Storing `interface{}` values are limited to built-ins Go types_** @@ -148,7 +148,7 @@ type Config struct { // Optional. Default value utils.UUID KeyGenerator func() string - // Deprecated, please use KeyLookup + // Deprecated: Please use KeyLookup CookieName string // Source defines where to obtain the session id diff --git a/middleware/session/config.go b/middleware/session/config.go index ad08c8935e..c7c8662704 100644 --- a/middleware/session/config.go +++ b/middleware/session/config.go @@ -2,6 +2,7 @@ package session import ( "fmt" + "log" "strings" "time" @@ -49,7 +50,7 @@ type Config struct { // Optional. Default value utils.UUIDv4 KeyGenerator func() string - // Deprecated, please use KeyLookup + // Deprecated: Please use KeyLookup CookieName string // Source defines where to obtain the session id @@ -68,8 +69,10 @@ const ( ) // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ - Expiration: 24 * time.Hour, + Expiration: 24 * time.Hour, //nolint:gomnd // No magic number, just the default config KeyLookup: "cookie:session_id", KeyGenerator: utils.UUIDv4, source: "cookie", @@ -91,7 +94,7 @@ func configDefault(config ...Config) Config { cfg.Expiration = ConfigDefault.Expiration } if cfg.CookieName != "" { - fmt.Println("[session] CookieName is deprecated, please use KeyLookup") + log.Printf("[session] CookieName is deprecated, please use KeyLookup\n") cfg.KeyLookup = fmt.Sprintf("cookie:%s", cfg.CookieName) } if cfg.KeyLookup == "" { @@ -102,7 +105,8 @@ func configDefault(config ...Config) Config { } selectors := strings.Split(cfg.KeyLookup, ":") - if len(selectors) != 2 { + const numSelectors = 2 + if len(selectors) != numSelectors { panic("[session] KeyLookup must in the form of :") } switch Source(selectors[0]) { diff --git a/middleware/session/data.go b/middleware/session/data.go index 7c8f793103..f213b252f5 100644 --- a/middleware/session/data.go +++ b/middleware/session/data.go @@ -13,6 +13,7 @@ type data struct { Data map[string]interface{} } +//nolint:gochecknoglobals // TODO: Do not use a global var here var dataPool = sync.Pool{ New: func() interface{} { d := new(data) @@ -22,7 +23,7 @@ var dataPool = sync.Pool{ } func acquireData() *data { - return dataPool.Get().(*data) + return dataPool.Get().(*data) //nolint:forcetypeassert // We store nothing else in the pool } func (d *data) Reset() { diff --git a/middleware/session/session.go b/middleware/session/session.go index 33be2a1a7d..f18abbf8b2 100644 --- a/middleware/session/session.go +++ b/middleware/session/session.go @@ -3,11 +3,13 @@ package session import ( "bytes" "encoding/gob" + "fmt" "sync" "time" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -21,6 +23,7 @@ type Session struct { exp time.Duration // expiration of this session } +//nolint:gochecknoglobals // TODO: Do not use a global var here var sessionPool = sync.Pool{ New: func() interface{} { return new(Session) @@ -28,7 +31,7 @@ var sessionPool = sync.Pool{ } func acquireSession() *Session { - s := sessionPool.Get().(*Session) + s := sessionPool.Get().(*Session) //nolint:forcetypeassert,errcheck // We store nothing else in the pool if s.data == nil { s.data = acquireData() } @@ -153,7 +156,7 @@ func (s *Session) Save() error { encCache := gob.NewEncoder(s.byteBuffer) err := encCache.Encode(&s.data.Data) if err != nil { - return err + return fmt.Errorf("failed to encode data: %w", err) } // copy the data in buffer diff --git a/middleware/session/session_test.go b/middleware/session/session_test.go index 489ea46edf..20d683f458 100644 --- a/middleware/session/session_test.go +++ b/middleware/session/session_test.go @@ -7,6 +7,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/storage/memory" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -94,6 +95,8 @@ func Test_Session(t *testing.T) { } // go test -run Test_Session_Types +// +//nolint:forcetypeassert // TODO: Do not force-type assert func Test_Session_Types(t *testing.T) { t.Parallel() @@ -127,25 +130,27 @@ func Test_Session_Types(t *testing.T) { Name: "John", } // set value - var vbool = true - var vstring = "str" - var vint = 13 - var vint8 int8 = 13 - var vint16 int16 = 13 - var vint32 int32 = 13 - var vint64 int64 = 13 - var vuint uint = 13 - var vuint8 uint8 = 13 - var vuint16 uint16 = 13 - var vuint32 uint32 = 13 - var vuint64 uint64 = 13 - var vuintptr uintptr = 13 - var vbyte byte = 'k' - var vrune rune = 'k' - var vfloat32 float32 = 13 - var vfloat64 float64 = 13 - var vcomplex64 complex64 = 13 - var vcomplex128 complex128 = 13 + var ( + vbool = true + vstring = "str" + vint = 13 + vint8 int8 = 13 + vint16 int16 = 13 + vint32 int32 = 13 + vint64 int64 = 13 + vuint uint = 13 + vuint8 uint8 = 13 + vuint16 uint16 = 13 + vuint32 uint32 = 13 + vuint64 uint64 = 13 + vuintptr uintptr = 13 + vbyte byte = 'k' + vrune = 'k' + vfloat32 float32 = 13 + vfloat64 float64 = 13 + vcomplex64 complex64 = 13 + vcomplex128 complex128 = 13 + ) sess.Set("vuser", vuser) sess.Set("vbool", vbool) sess.Set("vstring", vstring) @@ -212,7 +217,8 @@ func Test_Session_Store_Reset(t *testing.T) { defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) // make sure its new utils.AssertEqual(t, true, sess.Fresh()) // set value & save @@ -224,7 +230,8 @@ func Test_Session_Store_Reset(t *testing.T) { utils.AssertEqual(t, nil, store.Reset()) // make sure the session is recreated - sess, _ = store.Get(ctx) + sess, err = store.Get(ctx) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, sess.Fresh()) utils.AssertEqual(t, nil, sess.Get("hello")) } @@ -242,12 +249,13 @@ func Test_Session_Save(t *testing.T) { ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) // set value sess.Set("name", "john") // save session - err := sess.Save() + err = sess.Save() utils.AssertEqual(t, nil, err) }) @@ -262,12 +270,13 @@ func Test_Session_Save(t *testing.T) { ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) // set value sess.Set("name", "john") // save session - err := sess.Save() + err = sess.Save() utils.AssertEqual(t, nil, err) utils.AssertEqual(t, store.getSessionID(ctx), string(ctx.Response().Header.Peek(store.sessionName))) utils.AssertEqual(t, store.getSessionID(ctx), string(ctx.Request().Header.Peek(store.sessionName))) @@ -287,7 +296,8 @@ func Test_Session_Save_Expiration(t *testing.T) { ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) // set value sess.Set("name", "john") @@ -295,18 +305,20 @@ func Test_Session_Save_Expiration(t *testing.T) { sess.SetExpiry(time.Second * 5) // save session - err := sess.Save() + err = sess.Save() utils.AssertEqual(t, nil, err) // here you need to get the old session yet - sess, _ = store.Get(ctx) + sess, err = store.Get(ctx) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "john", sess.Get("name")) // just to make sure the session has been expired time.Sleep(time.Second * 5) // here you should get a new session - sess, _ = store.Get(ctx) + sess, err = store.Get(ctx) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, sess.Get("name")) }) } @@ -325,7 +337,8 @@ func Test_Session_Reset(t *testing.T) { ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) sess.Set("name", "fenny") utils.AssertEqual(t, nil, sess.Destroy()) @@ -345,14 +358,16 @@ func Test_Session_Reset(t *testing.T) { ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) // set value & save sess.Set("name", "fenny") utils.AssertEqual(t, nil, sess.Save()) - sess, _ = store.Get(ctx) + sess, err = store.Get(ctx) + utils.AssertEqual(t, nil, err) - err := sess.Destroy() + err = sess.Destroy() utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "", string(ctx.Response().Header.Peek(store.sessionName))) utils.AssertEqual(t, "", string(ctx.Request().Header.Peek(store.sessionName))) @@ -383,7 +398,8 @@ func Test_Session_Cookie(t *testing.T) { defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, sess.Save()) // cookie should be set on Save ( even if empty data ) @@ -401,12 +417,14 @@ func Test_Session_Cookie_In_Response(t *testing.T) { defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) sess.Set("id", "1") utils.AssertEqual(t, true, sess.Fresh()) utils.AssertEqual(t, nil, sess.Save()) - sess, _ = store.Get(ctx) + sess, err = store.Get(ctx) + utils.AssertEqual(t, nil, err) sess.Set("name", "john") utils.AssertEqual(t, true, sess.Fresh()) @@ -497,7 +515,7 @@ func Benchmark_Session(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - sess, _ := store.Get(c) + sess, _ := store.Get(c) //nolint:errcheck // We're inside a benchmark sess.Set("john", "doe") err = sess.Save() } @@ -512,7 +530,7 @@ func Benchmark_Session(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - sess, _ := store.Get(c) + sess, _ := store.Get(c) //nolint:errcheck // We're inside a benchmark sess.Set("john", "doe") err = sess.Save() } diff --git a/middleware/session/store.go b/middleware/session/store.go index cc8a80a0cf..fd3b08d029 100644 --- a/middleware/session/store.go +++ b/middleware/session/store.go @@ -2,11 +2,13 @@ package session import ( "encoding/gob" + "fmt" "sync" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/storage/memory" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -14,6 +16,7 @@ type Store struct { Config } +//nolint:gochecknoglobals // TODO: Do not use a global var here var mux sync.Mutex func New(config ...Config) *Store { @@ -31,7 +34,7 @@ func New(config ...Config) *Store { // RegisterType will allow you to encode/decode custom types // into any Storage provider -func (s *Store) RegisterType(i interface{}) { +func (*Store) RegisterType(i interface{}) { gob.Register(i) } @@ -70,11 +73,11 @@ func (s *Store) Get(c *fiber.Ctx) (*Session, error) { if raw != nil && err == nil { mux.Lock() defer mux.Unlock() - _, _ = sess.byteBuffer.Write(raw) + _, _ = sess.byteBuffer.Write(raw) //nolint:errcheck // This will never fail encCache := gob.NewDecoder(sess.byteBuffer) err := encCache.Decode(&sess.data.Data) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to decode session data: %w", err) } } else if err != nil { return nil, err diff --git a/middleware/session/store_test.go b/middleware/session/store_test.go index 4c755c3290..c7e435f6ae 100644 --- a/middleware/session/store_test.go +++ b/middleware/session/store_test.go @@ -6,6 +6,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) diff --git a/middleware/skip/skip.go b/middleware/skip/skip.go index 616baf2dc3..de64601fda 100644 --- a/middleware/skip/skip.go +++ b/middleware/skip/skip.go @@ -1,6 +1,8 @@ package skip -import "github.com/gofiber/fiber/v2" +import ( + "github.com/gofiber/fiber/v2" +) // New creates a middleware handler which skips the wrapped handler // if the exclude predicate returns true. diff --git a/middleware/skip/skip_test.go b/middleware/skip/skip_test.go index cfbbec9d8d..6d0925591a 100644 --- a/middleware/skip/skip_test.go +++ b/middleware/skip/skip_test.go @@ -17,7 +17,7 @@ func Test_Skip(t *testing.T) { app.Use(skip.New(errTeapotHandler, func(*fiber.Ctx) bool { return true })) app.Get("/", helloWorldHandler) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) } @@ -30,7 +30,7 @@ func Test_SkipFalse(t *testing.T) { app.Use(skip.New(errTeapotHandler, func(*fiber.Ctx) bool { return false })) app.Get("/", helloWorldHandler) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) } @@ -43,7 +43,7 @@ func Test_SkipNilFunc(t *testing.T) { app.Use(skip.New(errTeapotHandler, nil)) app.Get("/", helloWorldHandler) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) } diff --git a/middleware/timeout/timeout_test.go b/middleware/timeout/timeout_test.go index aa60a3504c..8498cb816d 100644 --- a/middleware/timeout/timeout_test.go +++ b/middleware/timeout/timeout_test.go @@ -18,7 +18,8 @@ func Test_Timeout(t *testing.T) { // fiber instance app := fiber.New() h := New(func(c *fiber.Ctx) error { - sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") + sleepTime, err := time.ParseDuration(c.Params("sleepTime") + "ms") + utils.AssertEqual(t, nil, err) if err := sleepWithContext(c.UserContext(), sleepTime, context.DeadlineExceeded); err != nil { return fmt.Errorf("%w: l2 wrap", fmt.Errorf("%w: l1 wrap ", err)) } @@ -26,12 +27,12 @@ func Test_Timeout(t *testing.T) { }, 100*time.Millisecond) app.Get("/test/:sleepTime", h) testTimeout := func(timeoutStr string) { - resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test/"+timeoutStr, nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusRequestTimeout, resp.StatusCode, "Status code") } testSucces := func(timeoutStr string) { - resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test/"+timeoutStr, nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") } @@ -49,7 +50,8 @@ func Test_TimeoutWithCustomError(t *testing.T) { // fiber instance app := fiber.New() h := New(func(c *fiber.Ctx) error { - sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") + sleepTime, err := time.ParseDuration(c.Params("sleepTime") + "ms") + utils.AssertEqual(t, nil, err) if err := sleepWithContext(c.UserContext(), sleepTime, ErrFooTimeOut); err != nil { return fmt.Errorf("%w: execution error", err) } @@ -57,12 +59,12 @@ func Test_TimeoutWithCustomError(t *testing.T) { }, 100*time.Millisecond, ErrFooTimeOut) app.Get("/test/:sleepTime", h) testTimeout := func(timeoutStr string) { - resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test/"+timeoutStr, nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusRequestTimeout, resp.StatusCode, "Status code") } testSucces := func(timeoutStr string) { - resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test/"+timeoutStr, nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") } diff --git a/mount.go b/mount.go index abb4a73930..2becfef6bd 100644 --- a/mount.go +++ b/mount.go @@ -120,7 +120,6 @@ func (app *App) appendSubAppLists(appList map[string]*App, parent ...string) { if len(subApp.mountFields.appList) > 1 { app.appendSubAppLists(subApp.mountFields.appList, prefix) } - } } diff --git a/mount_test.go b/mount_test.go index 580366e69d..b70be3085c 100644 --- a/mount_test.go +++ b/mount_test.go @@ -2,6 +2,7 @@ // 🤖 Github Repository: https://github.com/gofiber/fiber // 📌 API Documentation: https://docs.gofiber.io +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package fiber import ( @@ -369,7 +370,6 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

I'm Bruh

", string(body)) - } func Test_Ctx_Render_MountGroup(t *testing.T) { diff --git a/path.go b/path.go index 2bc041d3d1..ba357328e8 100644 --- a/path.go +++ b/path.go @@ -88,12 +88,14 @@ const ( ) // list of possible parameter and segment delimiter +// +//nolint:gochecknoglobals // TODO: Do not use a global var here var ( // slash has a special role, unlike the other parameters it must not be interpreted as a parameter routeDelimiter = []byte{slashDelimiter, '-', '.'} // list of greedy parameters greedyParameters = []byte{wildcardParam, plusParam} - // list of chars for the parameter recognising + // list of chars for the parameter recognizing parameterStartChars = []byte{wildcardParam, plusParam, paramStarterChar} // list of chars of delimiters and the starting parameter name char parameterDelimiterChars = append([]byte{paramStarterChar, escapeChar}, routeDelimiter...) @@ -268,7 +270,7 @@ func findNextParamPosition(pattern string) int { } // analyseConstantPart find the end of the constant part and create the route segment -func (routeParser *routeParser) analyseConstantPart(pattern string, nextParamPosition int) (string, *routeSegment) { +func (*routeParser) analyseConstantPart(pattern string, nextParamPosition int) (string, *routeSegment) { // handle the constant part processedPart := pattern if nextParamPosition != -1 { @@ -297,11 +299,12 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r parameterConstraintStart := -1 parameterConstraintEnd := -1 // handle wildcard end - if isWildCard || isPlusParam { + switch { + case isWildCard, isPlusParam: parameterEndPosition = 0 - } else if parameterEndPosition == -1 { + case parameterEndPosition == -1: parameterEndPosition = len(pattern) - 1 - } else if !isInCharset(pattern[parameterEndPosition+1], parameterDelimiterChars) { + case !isInCharset(pattern[parameterEndPosition+1], parameterDelimiterChars): parameterEndPosition++ } @@ -338,7 +341,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r constraint.Data = splitNonEscaped(c[start+1:end], string(parameterConstraintDataSeparatorChars)) if len(constraint.Data) == 1 { constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) - } else if len(constraint.Data) == 2 { + } else if len(constraint.Data) == 2 { //nolint:gomnd // This is fine, we simply expect two parts constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) constraint.Data[1] = RemoveEscapeChar(constraint.Data[1]) } @@ -474,7 +477,7 @@ func splitNonEscaped(s, sep string) []string { } // getMatch parses the passed url and tries to match it against the route segments and determine the parameter positions -func (routeParser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { +func (routeParser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { //nolint: revive // Accepting a bool param is fine here var i, paramsIterator, partLen int for _, segment := range routeParser.segs { partLen = len(detectionPath) @@ -638,9 +641,9 @@ func getParamConstraintType(constraintPart string) TypeConstraint { default: return noConstraint } - } +//nolint:errcheck // TODO: Properly check _all_ errors in here, log them & immediately return func (c *Constraint) CheckConstraint(param string) bool { var err error var num int @@ -663,6 +666,8 @@ func (c *Constraint) CheckConstraint(param string) bool { // check constraints switch c.ID { + case noConstraint: + // Nothing to check case intConstraint: _, err = strconv.Atoi(param) case boolConstraint: diff --git a/path_test.go b/path_test.go index 656b82e12a..62a421ed3a 100644 --- a/path_test.go +++ b/path_test.go @@ -1119,7 +1119,6 @@ func Benchmark_Path_matchParams(t *testing.B) { utils.AssertEqual(t, c.params[0:len(c.params)-1], ctxParams[0:len(c.params)-1], fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) } }) - } } benchCase("/api/:param/fixedEnd", []testparams{ @@ -1348,7 +1347,6 @@ func Benchmark_RoutePatternMatch(t *testing.B) { } utils.AssertEqual(t, c.match, matchRes, fmt.Sprintf("route: '%s', url: '%s'", pattern, c.url)) }) - } } benchCase("/api/:param/fixedEnd", []testparams{ diff --git a/prefork.go b/prefork.go index b3049abc60..7eebd50636 100644 --- a/prefork.go +++ b/prefork.go @@ -2,13 +2,15 @@ package fiber import ( "crypto/tls" + "errors" "fmt" - "net" + "log" "os" "os/exec" "runtime" "strconv" "strings" + "sync/atomic" "time" "github.com/valyala/fasthttp/reuseport" @@ -19,8 +21,11 @@ const ( envPreforkChildVal = "1" ) -var testPreforkMaster = false -var testOnPrefork = false +//nolint:gochecknoglobals // TODO: Do not use global vars here +var ( + testPreforkMaster = false + testOnPrefork = false +) // IsChild determines if the current process is a child of Prefork func IsChild() bool { @@ -28,19 +33,20 @@ func IsChild() bool { } // prefork manages child processes to make use of the OS REUSEPORT or REUSEADDR feature -func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) { +func (app *App) prefork(network, addr string, tlsConfig *tls.Config) error { // 👶 child process 👶 if IsChild() { // use 1 cpu core per child process runtime.GOMAXPROCS(1) - var ln net.Listener // Linux will use SO_REUSEPORT and Windows falls back to SO_REUSEADDR // Only tcp4 or tcp6 is supported when preforking, both are not supported - if ln, err = reuseport.Listen(network, addr); err != nil { + ln, err := reuseport.Listen(network, addr) + if err != nil { if !app.config.DisableStartupMessage { - time.Sleep(100 * time.Millisecond) // avoid colliding with startup message + const sleepDuration = 100 * time.Millisecond + time.Sleep(sleepDuration) // avoid colliding with startup message } - return fmt.Errorf("prefork: %v", err) + return fmt.Errorf("prefork: %w", err) } // wrap a tls config around the listener if provided if tlsConfig != nil { @@ -70,7 +76,11 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) // kill child procs when master exits defer func() { for _, proc := range childs { - _ = proc.Process.Kill() + if err := proc.Process.Kill(); err != nil { + if !errors.Is(err, os.ErrProcessDone) { + log.Printf("prefork: failed to kill child: %v\n", err) + } + } } }() @@ -79,8 +89,7 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) // launch child procs for i := 0; i < max; i++ { - /* #nosec G204 */ - cmd := exec.Command(os.Args[0], os.Args[1:]...) // #nosec G204 + cmd := exec.Command(os.Args[0], os.Args[1:]...) //nolint:gosec // It's fine to launch the same process again if testPreforkMaster { // When test prefork master, // just start the child process with a dummy cmd, @@ -94,8 +103,8 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) cmd.Env = append(os.Environ(), fmt.Sprintf("%s=%s", envPreforkChildKey, envPreforkChildVal), ) - if err = cmd.Start(); err != nil { - return fmt.Errorf("failed to start a child prefork process, error: %v", err) + if err := cmd.Start(); err != nil { + return fmt.Errorf("failed to start a child prefork process, error: %w", err) } // store child process @@ -134,27 +143,34 @@ func watchMaster() { // and waits for it to exit p, err := os.FindProcess(os.Getppid()) if err == nil { - _, _ = p.Wait() + _, _ = p.Wait() //nolint:errcheck // It is fine to ignore the error here } - os.Exit(1) + os.Exit(1) //nolint:revive // Calling os.Exit is fine here in the prefork } // if it is equal to 1 (init process ID), // it indicates that the master process has exited - for range time.NewTicker(time.Millisecond * 500).C { + const watchInterval = 500 * time.Millisecond + for range time.NewTicker(watchInterval).C { if os.Getppid() == 1 { - os.Exit(1) + os.Exit(1) //nolint:revive // Calling os.Exit is fine here in the prefork } } } -var dummyChildCmd = "go" +//nolint:gochecknoglobals // TODO: Do not use global vars here +var ( + dummyPid = 1 + dummyChildCmd atomic.Value +) // dummyCmd is for internal prefork testing func dummyCmd() *exec.Cmd { + command := "go" + if storeCommand := dummyChildCmd.Load(); storeCommand != nil && storeCommand != "" { + command = storeCommand.(string) //nolint:forcetypeassert,errcheck // We always store a string in here + } if runtime.GOOS == "windows" { - return exec.Command("cmd", "/C", dummyChildCmd, "version") + return exec.Command("cmd", "/C", command, "version") } - return exec.Command(dummyChildCmd, "version") + return exec.Command(command, "version") } - -var dummyPid = 1 diff --git a/prefork_test.go b/prefork_test.go index 4842774f26..506244a263 100644 --- a/prefork_test.go +++ b/prefork_test.go @@ -38,6 +38,7 @@ func Test_App_Prefork_Child_Process(t *testing.T) { if err != nil { utils.AssertEqual(t, nil, err) } + //nolint:gosec // We're in a test so using old ciphers is fine config := &tls.Config{Certificates: []tls.Certificate{cer}} go func() { @@ -61,10 +62,12 @@ func Test_App_Prefork_Master_Process(t *testing.T) { utils.AssertEqual(t, nil, app.prefork(NetworkTCP4, ":3000", nil)) - dummyChildCmd = "invalid" + dummyChildCmd.Store("invalid") err := app.prefork(NetworkTCP4, "127.0.0.1:", nil) utils.AssertEqual(t, false, err == nil) + + dummyChildCmd.Store("") } func Test_App_Prefork_Child_Process_Never_Show_Startup_Message(t *testing.T) { diff --git a/router.go b/router.go index 84ff606443..ce27583955 100644 --- a/router.go +++ b/router.go @@ -13,6 +13,7 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -55,14 +56,15 @@ type Route struct { group *Group // Group instance. used for routes in groups // Public fields - Method string `json:"method"` // HTTP method - Name string `json:"name"` // Route's name + Method string `json:"method"` // HTTP method + Name string `json:"name"` // Route's name + //nolint:revive // Having both a Path (uppercase) and a path (lowercase) is fine Path string `json:"path"` // Original registered route path Params []string `json:"params"` // Case sensitive param keys Handlers []Handler `json:"-"` // Ctx handlers } -func (r *Route) match(detectionPath, path string, params *[maxParams]string) (match bool) { +func (r *Route) match(detectionPath, path string, params *[maxParams]string) bool { // root detectionPath check if r.root && detectionPath == "/" { return true @@ -97,7 +99,7 @@ func (r *Route) match(detectionPath, path string, params *[maxParams]string) (ma return false } -func (app *App) next(c *Ctx) (match bool, err error) { +func (app *App) next(c *Ctx) (bool, error) { // Get stack length tree, ok := app.treeStack[c.methodINT][c.treePath] if !ok { @@ -114,10 +116,9 @@ func (app *App) next(c *Ctx) (match bool, err error) { route := tree[c.indexRoute] // Check if it matches the request path - match = route.match(c.detectionPath, c.path, &c.values) - - // No match, next route + match := route.match(c.detectionPath, c.path, &c.values) if !match { + // No match, next route continue } // Pass route reference and param values @@ -130,29 +131,28 @@ func (app *App) next(c *Ctx) (match bool, err error) { // Execute first handler of route c.indexHandler = 0 - err = route.Handlers[0](c) + err := route.Handlers[0](c) return match, err // Stop scanning the stack } // If c.Next() does not match, return 404 - err = NewError(StatusNotFound, "Cannot "+c.method+" "+c.pathOriginal) - - // If no match, scan stack again if other methods match the request - // Moved from app.handler because middleware may break the route chain + err := NewError(StatusNotFound, "Cannot "+c.method+" "+c.pathOriginal) if !c.matched && app.methodExist(c) { + // If no match, scan stack again if other methods match the request + // Moved from app.handler because middleware may break the route chain err = ErrMethodNotAllowed } - return + return false, err } -func (app *App) handler(rctx *fasthttp.RequestCtx) { +func (app *App) handler(rctx *fasthttp.RequestCtx) { //revive:disable-line:confusing-naming // Having both a Handler() (uppercase) and a handler() (lowercase) is fine. TODO: Use nolint:revive directive instead. See https://github.com/golangci/golangci-lint/issues/3476 // Acquire Ctx with fasthttp request from pool c := app.AcquireCtx(rctx) + defer app.ReleaseCtx(c) // handle invalid http method directly if c.methodINT == -1 { - _ = c.Status(StatusBadRequest).SendString("Invalid http method") - app.ReleaseCtx(c) + _ = c.Status(StatusBadRequest).SendString("Invalid http method") //nolint:errcheck // It is fine to ignore the error here return } @@ -160,16 +160,14 @@ func (app *App) handler(rctx *fasthttp.RequestCtx) { match, err := app.next(c) if err != nil { if catch := c.app.ErrorHandler(c, err); catch != nil { - _ = c.SendStatus(StatusInternalServerError) + _ = c.SendStatus(StatusInternalServerError) //nolint:errcheck // It is fine to ignore the error here } + // TODO: Do we need to return here? } // Generate ETag if enabled if match && app.config.ETag { setETag(c, false) } - - // Release Ctx - app.ReleaseCtx(c) } func (app *App) addPrefixToRoute(prefix string, route *Route) *Route { @@ -193,7 +191,7 @@ func (app *App) addPrefixToRoute(prefix string, route *Route) *Route { return route } -func (app *App) copyRoute(route *Route) *Route { +func (*App) copyRoute(route *Route) *Route { return &Route{ // Router booleans use: route.use, @@ -327,6 +325,7 @@ func (app *App) registerStatic(prefix, root string, config ...Static) Router { prefixLen-- prefix = prefix[:prefixLen] } + const cacheDuration = 10 * time.Second // Fileserver settings fs := &fasthttp.FS{ Root: root, @@ -335,7 +334,7 @@ func (app *App) registerStatic(prefix, root string, config ...Static) Router { AcceptByteRange: false, Compress: false, CompressedFileSuffix: app.config.CompressedFileSuffix, - CacheDuration: 10 * time.Second, + CacheDuration: cacheDuration, IndexNames: []string{"index.html"}, PathRewrite: func(fctx *fasthttp.RequestCtx) []byte { path := fctx.Path() diff --git a/router_test.go b/router_test.go index 26407ce10e..0fbb7c8f46 100644 --- a/router_test.go +++ b/router_test.go @@ -2,6 +2,7 @@ // 📃 Github Repository: https://github.com/gofiber/fiber // 📌 API Documentation: https://docs.gofiber.io +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package fiber import ( @@ -15,11 +16,14 @@ import ( "testing" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) -var routesFixture = routeJSON{} +//nolint:gochecknoglobals // TODO: Do not use a global var here +var routesFixture routeJSON +//nolint:gochecknoinits // init() is used to populate a global struct from a JSON file func init() { dat, err := os.ReadFile("./.github/testdata/testRoutes.json") if err != nil { @@ -579,7 +583,7 @@ func Benchmark_Router_Chain(b *testing.B) { c := &fasthttp.RequestCtx{} - c.Request.Header.SetMethod("GET") + c.Request.Header.SetMethod(MethodGet) c.URI().SetPath("/") b.ResetTimer() for n := 0; n < b.N; n++ { @@ -603,7 +607,7 @@ func Benchmark_Router_WithCompression(b *testing.B) { appHandler := app.Handler() c := &fasthttp.RequestCtx{} - c.Request.Header.SetMethod("GET") + c.Request.Header.SetMethod(MethodGet) c.URI().SetPath("/") b.ResetTimer() for n := 0; n < b.N; n++ { @@ -829,6 +833,6 @@ type testRoute struct { } type routeJSON struct { - TestRoutes []testRoute `json:"testRoutes"` - GithubAPI []testRoute `json:"githubAPI"` + TestRoutes []testRoute `json:"test_routes"` + GithubAPI []testRoute `json:"github_api"` } diff --git a/utils/assertions.go b/utils/assertions.go index ec9a119c0f..2cc4cac21a 100644 --- a/utils/assertions.go +++ b/utils/assertions.go @@ -16,7 +16,7 @@ import ( ) // AssertEqual checks if values are equal -func AssertEqual(tb testing.TB, expected, actual interface{}, description ...string) { +func AssertEqual(tb testing.TB, expected, actual interface{}, description ...string) { //nolint:thelper // TODO: Verify if tb can be nil if tb != nil { tb.Helper() } @@ -43,14 +43,15 @@ func AssertEqual(tb testing.TB, expected, actual interface{}, description ...str _, file, line, _ := runtime.Caller(1) var buf bytes.Buffer - w := tabwriter.NewWriter(&buf, 0, 0, 5, ' ', 0) - fmt.Fprintf(w, "\nTest:\t%s", testName) - fmt.Fprintf(w, "\nTrace:\t%s:%d", filepath.Base(file), line) + const pad = 5 + w := tabwriter.NewWriter(&buf, 0, 0, pad, ' ', 0) + _, _ = fmt.Fprintf(w, "\nTest:\t%s", testName) + _, _ = fmt.Fprintf(w, "\nTrace:\t%s:%d", filepath.Base(file), line) if len(description) > 0 { - fmt.Fprintf(w, "\nDescription:\t%s", description[0]) + _, _ = fmt.Fprintf(w, "\nDescription:\t%s", description[0]) } - fmt.Fprintf(w, "\nExpect:\t%v\t(%s)", expected, aType) - fmt.Fprintf(w, "\nResult:\t%v\t(%s)", actual, bType) + _, _ = fmt.Fprintf(w, "\nExpect:\t%v\t(%s)", expected, aType) + _, _ = fmt.Fprintf(w, "\nResult:\t%v\t(%s)", actual, bType) result := "" if err := w.Flush(); err != nil { @@ -62,6 +63,6 @@ func AssertEqual(tb testing.TB, expected, actual interface{}, description ...str if tb != nil { tb.Fatal(result) } else { - log.Fatal(result) + log.Fatal(result) //nolint:revive // tb might be nil, so we need a fallback } } diff --git a/utils/assertions_test.go b/utils/assertions_test.go index d2d32dc0cf..d1e7f08732 100644 --- a/utils/assertions_test.go +++ b/utils/assertions_test.go @@ -4,7 +4,9 @@ package utils -import "testing" +import ( + "testing" +) func Test_AssertEqual(t *testing.T) { t.Parallel() diff --git a/utils/common.go b/utils/common.go index b0f4d76625..1789a239b0 100644 --- a/utils/common.go +++ b/utils/common.go @@ -31,6 +31,11 @@ const ( // github.com/rogpeppe/fastuuid // All rights reserved. +const ( + emptyUUID = "00000000-0000-0000-0000-000000000000" +) + +//nolint:gochecknoglobals // TODO: Do not use a global var here var ( uuidSeed [24]byte uuidCounter uint64 @@ -39,6 +44,8 @@ var ( ) // UUID generates an universally unique identifier (UUID) +// +//nolint:gomnd // Those are not random numbers, it's the fastuuid algorithm func UUID() string { // Setup seed & counter once uuidSetup.Do(func() { @@ -48,7 +55,7 @@ func UUID() string { uuidCounter = binary.LittleEndian.Uint64(uuidSeed[:8]) }) if atomic.LoadUint64(&uuidCounter) <= 0 { - return "00000000-0000-0000-0000-000000000000" + return emptyUUID } // first 8 bytes differ, taking a slice of the first 16 bytes x := atomic.AddUint64(&uuidCounter, 1) @@ -147,7 +154,8 @@ func ConvertToBytes(humanReadableString string) int { // convert multiplier char to lowercase and check if exists in units slice index := bytes.IndexByte(unitsSlice, toLowerTable[humanReadableString[unitPrefixPos]]) if index != -1 { - size *= math.Pow(1000, float64(index+1)) + const bytesPerKB = 1000 + size *= math.Pow(bytesPerKB, float64(index+1)) } } diff --git a/utils/common_test.go b/utils/common_test.go index b4b2c61cf9..1b0884fc86 100644 --- a/utils/common_test.go +++ b/utils/common_test.go @@ -24,7 +24,7 @@ func Test_UUID(t *testing.T) { t.Parallel() res := UUID() AssertEqual(t, 36, len(res)) - AssertEqual(t, true, res != "00000000-0000-0000-0000-000000000000") + AssertEqual(t, true, res != emptyUUID) } func Test_UUID_Concurrency(t *testing.T) { @@ -49,7 +49,7 @@ func Test_UUIDv4(t *testing.T) { t.Parallel() res := UUIDv4() AssertEqual(t, 36, len(res)) - AssertEqual(t, true, res != "00000000-0000-0000-0000-000000000000") + AssertEqual(t, true, res != emptyUUID) } func Test_UUIDv4_Concurrency(t *testing.T) { @@ -82,7 +82,8 @@ func Benchmark_UUID(b *testing.B) { }) b.Run("default", func(b *testing.B) { rnd := make([]byte, 16) - _, _ = rand.Read(rnd) + _, err := rand.Read(rnd) + AssertEqual(b, nil, err) for n := 0; n < b.N; n++ { res = fmt.Sprintf("%x-%x-%x-%x-%x", rnd[0:4], rnd[4:6], rnd[6:8], rnd[8:10], rnd[10:]) } diff --git a/utils/convert.go b/utils/convert.go index d1d3f52034..4a91ead995 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -15,15 +15,17 @@ import ( const MaxStringLen = 0x7fff0000 // Maximum string length for UnsafeBytes. (decimal: 2147418112) -// #nosec G103 // UnsafeString returns a string pointer without allocation +// +//nolint:gosec // unsafe is used for better performance here func UnsafeString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } -// #nosec G103 // UnsafeBytes returns a byte pointer without allocation. // String length shouldn't be more than 2147418112. +// +//nolint:gosec // unsafe is used for better performance here func UnsafeBytes(s string) []byte { if s == "" { return nil @@ -47,7 +49,7 @@ func CopyBytes(b []byte) []byte { } const ( - uByte = 1 << (10 * iota) + uByte = 1 << (10 * iota) //nolint:gomnd // 1 << 10 == 1024 uKilobyte uMegabyte uGigabyte @@ -92,7 +94,7 @@ func ByteSize(bytes uint64) string { // ToString Change arg to string func ToString(arg interface{}, timeFormat ...string) string { - var tmp = reflect.Indirect(reflect.ValueOf(arg)).Interface() + tmp := reflect.Indirect(reflect.ValueOf(arg)).Interface() switch v := tmp.(type) { case int: return strconv.Itoa(v) diff --git a/utils/convert_test.go b/utils/convert_test.go index 59ce625e53..4bfd6bf079 100644 --- a/utils/convert_test.go +++ b/utils/convert_test.go @@ -4,7 +4,9 @@ package utils -import "testing" +import ( + "testing" +) func Test_UnsafeString(t *testing.T) { t.Parallel() diff --git a/utils/deprecated.go b/utils/deprecated.go index ae6fd34e7b..a436e67a5b 100644 --- a/utils/deprecated.go +++ b/utils/deprecated.go @@ -1,18 +1,16 @@ package utils -// #nosec G103 -// DEPRECATED, Please use UnsafeString instead +// Deprecated: Please use UnsafeString instead func GetString(b []byte) string { return UnsafeString(b) } -// #nosec G103 -// DEPRECATED, Please use UnsafeBytes instead +// Deprecated: Please use UnsafeBytes instead func GetBytes(s string) []byte { return UnsafeBytes(s) } -// DEPRECATED, Please use CopyString instead +// Deprecated: Please use CopyString instead func ImmutableString(s string) string { return CopyString(s) } diff --git a/utils/http.go b/utils/http.go index bdcd834c98..69f25ef193 100644 --- a/utils/http.go +++ b/utils/http.go @@ -4,15 +4,18 @@ package utils -import "strings" +import ( + "strings" +) const MIMEOctetStream = "application/octet-stream" // GetMIME returns the content-type of a file extension -func GetMIME(extension string) (mime string) { +func GetMIME(extension string) string { if len(extension) == 0 { - return mime + return "" } + var mime string if extension[0] == '.' { mime = mimeExtensions[extension[1:]] } else { @@ -35,6 +38,7 @@ func ParseVendorSpecificContentType(cType string) string { } var parsableType string + //nolint:revive // Actually not simpler if semiColonIndex := strings.Index(cType, ";"); semiColonIndex == -1 { parsableType = cType[plusIndex+1:] } else if plusIndex < semiColonIndex { @@ -67,6 +71,8 @@ func StatusMessage(status int) string { } // NOTE: Keep this in sync with the status code list +// +//nolint:gochecknoglobals // Using a global var is fine here var statusMessage = []string{ 100: "Continue", // StatusContinue 101: "Switching Protocols", // StatusSwitchingProtocols @@ -140,6 +146,8 @@ var statusMessage = []string{ // MIME types were copied from https://github.com/nginx/nginx/blob/67d2a9541826ecd5db97d604f23460210fd3e517/conf/mime.types with the following updates: // - Use "application/xml" instead of "text/xml" as recommended per https://datatracker.ietf.org/doc/html/rfc7303#section-4.1 // - Use "text/javascript" instead of "application/javascript" as recommended per https://www.rfc-editor.org/rfc/rfc9239#name-text-javascript +// +//nolint:gochecknoglobals // Using a global var is fine here var mimeExtensions = map[string]string{ "html": "text/html", "htm": "text/html", diff --git a/utils/ips.go b/utils/ips.go index 4886c117f7..adac9d4255 100644 --- a/utils/ips.go +++ b/utils/ips.go @@ -6,6 +6,8 @@ import ( // IsIPv4 works the same way as net.ParseIP, // but without check for IPv6 case and without returning net.IP slice, whereby IsIPv4 makes no allocations. +// +//nolint:gomnd // Magic numbers used for a faster algorithm than net.ParseIP func IsIPv4(s string) bool { for i := 0; i < net.IPv4len; i++ { if len(s) == 0 { @@ -40,6 +42,8 @@ func IsIPv4(s string) bool { // IsIPv6 works the same way as net.ParseIP, // but without check for IPv4 case and without returning net.IP slice, whereby IsIPv6 makes no allocations. +// +//nolint:gomnd // Magic numbers used for a faster algorithm than net.ParseIP func IsIPv6(s string) bool { ellipsis := -1 // position of ellipsis in ip diff --git a/utils/time.go b/utils/time.go index 8ea13c2262..93fe88087e 100644 --- a/utils/time.go +++ b/utils/time.go @@ -6,6 +6,7 @@ import ( "time" ) +//nolint:gochecknoglobals // TODO: Do not use global vars here var ( timestampTimer sync.Once // Timestamp please start the timer function before you use this value diff --git a/utils/time_test.go b/utils/time_test.go index 75c13b1dd8..0f467f9f85 100644 --- a/utils/time_test.go +++ b/utils/time_test.go @@ -6,9 +6,12 @@ import ( "time" ) -func checkTimeStamp(t testing.TB, expectedCurrent, actualCurrent uint32) { +func checkTimeStamp(tb testing.TB, expectedCurrent, actualCurrent uint32) { //nolint:thelper // TODO: Verify if tb can be nil + if tb != nil { + tb.Helper() + } // test with some buffer in front and back of the expectedCurrent time -> because of the timing on the work machine - AssertEqual(t, true, actualCurrent >= expectedCurrent-1 || actualCurrent <= expectedCurrent+1) + AssertEqual(tb, true, actualCurrent >= expectedCurrent-1 || actualCurrent <= expectedCurrent+1) } func Test_TimeStampUpdater(t *testing.T) { diff --git a/utils/xml_test.go b/utils/xml_test.go index bbb11708d6..d0e53817f1 100644 --- a/utils/xml_test.go +++ b/utils/xml_test.go @@ -16,7 +16,7 @@ type serverXMLStructure struct { Name string `xml:"name"` } -var xmlString = `fiber onefiber two` +const xmlString = `fiber onefiber two` func Test_GolangXMLEncoder(t *testing.T) { t.Parallel()