diff --git a/.github/README.md b/.github/README.md index 3396838761..68fbd9c9bf 100644 --- a/.github/README.md +++ b/.github/README.md @@ -39,7 +39,7 @@ Fiber v3 is currently in beta and under active development. While it offers exci ## ⚙️ Installation -Fiber requires **Go version `1.21` or higher** to run. If you need to install or upgrade Go, visit the [official Go download page](https://go.dev/dl/). To start setting up your project. Create a new directory for your project and navigate into it. Then, initialize your project with Go modules by executing the following command in your terminal: +Fiber requires **Go version `1.22` or higher** to run. If you need to install or upgrade Go, visit the [official Go download page](https://go.dev/dl/). To start setting up your project. Create a new directory for your project and navigate into it. Then, initialize your project with Go modules by executing the following command in your terminal: ```bash go mod init github.com/your/repo @@ -124,7 +124,7 @@ We **listen** to our users in [issues](https://github.com/gofiber/fiber/issues), ## ⚠️ Limitations -- Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber v3 has been tested with Go versions 1.21 and 1.22. +- Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber v3 has been tested with Go versions 1.22 and 1.23. - Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Examples @@ -615,7 +615,7 @@ List of externally hosted middleware modules and maintained by the [Fiber team]( | :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | | [contrib](https://github.com/gofiber/contrib) | Third party middlewares | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | -| [template](https://github.com/gofiber/template) | This package contains 9 template engines that can be used with Fiber `v3` Go version 1.21 or higher is required. | +| [template](https://github.com/gofiber/template) | This package contains 9 template engines that can be used with Fiber `v3` Go version 1.22 or higher is required. | ## 🕶️ Awesome List diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 8973f90a89..e8531e1cb8 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -31,7 +31,7 @@ jobs: uses: actions/setup-go@v5 with: # NOTE: Keep this in sync with the version from go.mod - go-version: "1.21.x" + go-version: "1.22.x" - name: Run Benchmark run: set -o pipefail; go test ./... -benchmem -run=^$ -bench . | tee output.txt diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 87ca5a1e1c..aa9c94597f 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -30,11 +30,11 @@ jobs: - uses: actions/setup-go@v5 with: # NOTE: Keep this in sync with the version from go.mod - go-version: "1.21.x" + go-version: "1.22.x" cache: false - name: golangci-lint uses: golangci/golangci-lint-action@v6 with: # NOTE: Keep this in sync with the version from .golangci.yml - version: v1.59.1 + version: v1.60.1 diff --git a/.github/workflows/manual-dependabot.yml b/.github/workflows/manual-dependabot.yml index ccbeda87b1..41e0630354 100644 --- a/.github/workflows/manual-dependabot.yml +++ b/.github/workflows/manual-dependabot.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Checkout dependabot run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d7dc643b71..f7770993a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,8 +15,8 @@ jobs: unit: strategy: matrix: - go-version: [1.21.x, 1.22.x] - platform: [ubuntu-latest, windows-latest, macos-latest, macos-14] + go-version: [1.22.x, 1.23.x] + platform: [ubuntu-latest, windows-latest, macos-latest, macos-13] runs-on: ${{ matrix.platform }} steps: - name: Fetch Repository @@ -31,7 +31,7 @@ jobs: run: go run gotest.tools/gotestsum@latest -f testname -- ./... -race -count=1 -coverprofile=coverage.txt -covermode=atomic -shuffle=on - name: Upload coverage reports to Codecov - if: ${{ matrix.platform == 'ubuntu-latest' && matrix.go-version == '1.22.x' }} + if: ${{ matrix.platform == 'ubuntu-latest' && matrix.go-version == '1.23.x' }} uses: codecov/codecov-action@v4.5.0 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/Makefile b/Makefile index 08bf38a2d3..905f4f3672 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ markdown: ## lint: 🚨 Run lint checks .PHONY: lint lint: - go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.59.1 run ./... + go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.60.1 run ./... ## test: 🚦 Execute all tests .PHONY: test @@ -55,4 +55,13 @@ tidy: ## betteralign: 📐 Optimize alignment of fields in structs .PHONY: betteralign betteralign: - go run github.com/dkorunic/betteralign/cmd/betteralign@latest -test_files -generated_files -apply ./... \ No newline at end of file + go run github.com/dkorunic/betteralign/cmd/betteralign@latest -test_files -generated_files -apply ./... + +## tidy: ⚡️ Generate msgp +.PHONY: msgp +msgp: + go run github.com/tinylib/msgp@latest -file="middleware/cache/manager.go" -o="middleware/cache/manager_msgp.go" -tests=true -unexported + go run github.com/tinylib/msgp@latest -file="middleware/session/data.go" -o="middleware/session/data_msgp.go" -tests=true -unexported + go run github.com/tinylib/msgp@latest -file="middleware/csrf/storage_manager.go" -o="middleware/csrf/storage_manager_msgp.go" -tests=true -unexported + go run github.com/tinylib/msgp@latest -file="middleware/limiter/manager.go" -o="middleware/limiter/manager_msgp.go" -tests=true -unexported + go run github.com/tinylib/msgp@latest -file="middleware/idempotency/response.go" -o="middleware/idempotency/response_msgp.go" -tests=true -unexported diff --git a/binder/form.go b/binder/form.go index 0b469086c7..f45407fe93 100644 --- a/binder/form.go +++ b/binder/form.go @@ -40,6 +40,10 @@ func (b *formBinding) Bind(reqCtx *fasthttp.RequestCtx, out any) error { } }) + if err != nil { + return err + } + return parse(b.Name(), out, data) } diff --git a/client/client.go b/client/client.go index 0e77109034..12601275e4 100644 --- a/client/client.go +++ b/client/client.go @@ -674,7 +674,7 @@ func setConfigToRequest(req *Request, config ...Config) { return } - if cfg.File != nil && len(cfg.File) != 0 { + if len(cfg.File) != 0 { req.AddFiles(cfg.File...) return } diff --git a/client/client_test.go b/client/client_test.go index bdbb7facac..7a3ec10ff1 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -1422,7 +1422,7 @@ func Test_Set_Config_To_Request(t *testing.T) { key := struct{}{} ctx := context.Background() - ctx = context.WithValue(ctx, key, "v1") + ctx = context.WithValue(ctx, key, "v1") //nolint: staticcheck // not needed for tests req := AcquireRequest() diff --git a/client/request_test.go b/client/request_test.go index 00f19654a1..3724e165ce 100644 --- a/client/request_test.go +++ b/client/request_test.go @@ -84,7 +84,7 @@ func Test_Request_Context(t *testing.T) { require.Nil(t, ctx.Value(key)) - ctx = context.WithValue(ctx, key, "string") + ctx = context.WithValue(ctx, key, "string") //nolint: staticcheck // not needed for tests req.SetContext(ctx) ctx = req.Context() @@ -1603,8 +1603,8 @@ func Benchmark_SetValWithStruct(b *testing.B) { require.Empty(b, string(p.Peek("TInt"))) require.Empty(b, string(p.Peek("TString"))) require.Empty(b, string(p.Peek("TFloat"))) - require.Empty(b, len(p.PeekMulti("TSlice"))) - require.Empty(b, len(p.PeekMulti("int_slice"))) + require.Empty(b, p.PeekMulti("TSlice")) + require.Empty(b, p.PeekMulti("int_slice")) }) b.Run("error type should ignore", func(b *testing.B) { diff --git a/client/response.go b/client/response.go index 847107681f..a8a032b6c5 100644 --- a/client/response.go +++ b/client/response.go @@ -8,7 +8,6 @@ import ( "io/fs" "os" "path/filepath" - "strings" "sync" "github.com/gofiber/utils/v2" @@ -68,7 +67,7 @@ func (r *Response) Body() []byte { // String method returns the body of the server response as String. func (r *Response) String() string { - return strings.TrimSpace(string(r.Body())) + return utils.Trim(string(r.Body()), ' ') } // JSON method will unmarshal body to json. diff --git a/ctx.go b/ctx.go index eabfa7e1d4..84378d7690 100644 --- a/ctx.go +++ b/ctx.go @@ -273,10 +273,7 @@ func (c *DefaultCtx) BaseURL() string { // Returned value is only valid within the handler. Do not store any references. // Make copies or use the Immutable setting instead. func (c *DefaultCtx) BodyRaw() []byte { - if c.app.config.Immutable { - return utils.CopyBytes(c.fasthttp.Request.Body()) - } - return c.fasthttp.Request.Body() + return c.getBody() } func (c *DefaultCtx) tryDecodeBodyInOrder( @@ -339,21 +336,19 @@ func (c *DefaultCtx) Body() []byte { encodingOrder = []string{"", "", ""} ) - // faster than peek - c.Request().Header.VisitAll(func(key, value []byte) { - if c.app.getString(key) == HeaderContentEncoding { - headerEncoding = c.app.getString(value) - } - }) + // Get Content-Encoding header + headerEncoding = utils.UnsafeString(c.Request().Header.ContentEncoding()) + + // If no encoding is provided, return the original body + if len(headerEncoding) == 0 { + return c.getBody() + } // Split and get the encodings list, in order to attend the // rule defined at: https://www.rfc-editor.org/rfc/rfc9110#section-8.4-5 encodingOrder = getSplicedStrList(headerEncoding, encodingOrder) if len(encodingOrder) == 0 { - if c.app.config.Immutable { - return utils.CopyBytes(c.fasthttp.Request.Body()) - } - return c.fasthttp.Request.Body() + return c.getBody() } var decodesRealized uint8 @@ -778,7 +773,7 @@ iploop: i++ } - s := strings.TrimRight(headerValue[i:j], " ") + s := utils.TrimRight(headerValue[i:j], ' ') if c.app.config.EnableIPValidation { // Skip validation if IP is clearly not IPv4/IPv6, otherwise validate without allocations @@ -828,7 +823,7 @@ func (c *DefaultCtx) extractIPFromHeader(header string) string { i++ } - s := strings.TrimRight(headerValue[i:j], " ") + s := utils.TrimRight(headerValue[i:j], ' ') if c.app.config.EnableIPValidation { if (!v6 && !v4) || (v6 && !utils.IsIPv6(s)) || (v4 && !utils.IsIPv4(s)) { @@ -862,7 +857,7 @@ func (c *DefaultCtx) Is(extension string) bool { } return strings.HasPrefix( - strings.TrimLeft(utils.UnsafeString(c.fasthttp.Request.Header.ContentType()), " "), + utils.TrimLeft(utils.UnsafeString(c.fasthttp.Request.Header.ContentType()), ' '), extensionHeader, ) } @@ -939,7 +934,7 @@ func (c *DefaultCtx) Links(link ...string) { bb.WriteString(`; rel="` + link[i] + `",`) } } - c.setCanonical(HeaderLink, strings.TrimRight(c.app.getString(bb.Bytes()), ",")) + c.setCanonical(HeaderLink, utils.TrimRight(c.app.getString(bb.Bytes()), ',')) bytebufferpool.Put(bb) } @@ -1810,7 +1805,7 @@ func (c *DefaultCtx) configDependentPaths() { } // If StrictRouting is disabled, we strip all trailing slashes if !c.app.config.StrictRouting && len(c.detectionPathBuffer) > 1 && c.detectionPathBuffer[len(c.detectionPathBuffer)-1] == '/' { - c.detectionPathBuffer = bytes.TrimRight(c.detectionPathBuffer, "/") + c.detectionPathBuffer = utils.TrimRight(c.detectionPathBuffer, '/') } c.detectionPath = c.app.getString(c.detectionPathBuffer) @@ -1909,6 +1904,14 @@ func (c *DefaultCtx) release() { } } +func (c *DefaultCtx) getBody() []byte { + if c.app.config.Immutable { + return utils.CopyBytes(c.fasthttp.Request.Body()) + } + + return c.fasthttp.Request.Body() +} + // Methods to use with next stack. func (c *DefaultCtx) getMethodINT() int { return c.methodINT diff --git a/ctx_test.go b/ctx_test.go index c7a7ae9ee6..a1685898b0 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -356,6 +356,26 @@ func Test_Ctx_Body(t *testing.T) { require.Equal(t, []byte("john=doe"), c.Body()) } +// go test -run Test_Ctx_BodyRaw +func Test_Ctx_BodyRaw(t *testing.T) { + t.Parallel() + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + + c.Request().SetBodyRaw([]byte("john=doe")) + require.Equal(t, []byte("john=doe"), c.BodyRaw()) +} + +// go test -run Test_Ctx_BodyRaw_Immutable +func Test_Ctx_BodyRaw_Immutable(t *testing.T) { + t.Parallel() + app := New(Config{Immutable: true}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + + c.Request().SetBodyRaw([]byte("john=doe")) + require.Equal(t, []byte("john=doe"), c.BodyRaw()) +} + // go test -v -run=^$ -bench=Benchmark_Ctx_Body -benchmem -count=4 func Benchmark_Ctx_Body(b *testing.B) { const input = "john=doe" @@ -373,6 +393,40 @@ func Benchmark_Ctx_Body(b *testing.B) { require.Equal(b, []byte(input), c.Body()) } +// go test -v -run=^$ -bench=Benchmark_Ctx_BodyRaw -benchmem -count=4 +func Benchmark_Ctx_BodyRaw(b *testing.B) { + const input = "john=doe" + + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + + c.Request().SetBodyRaw([]byte(input)) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = c.BodyRaw() + } + + require.Equal(b, []byte(input), c.BodyRaw()) +} + +// go test -v -run=^$ -bench=Benchmark_Ctx_BodyRaw_Immutable -benchmem -count=4 +func Benchmark_Ctx_BodyRaw_Immutable(b *testing.B) { + const input = "john=doe" + + app := New(Config{Immutable: true}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + + c.Request().SetBodyRaw([]byte(input)) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = c.BodyRaw() + } + + require.Equal(b, []byte(input), c.BodyRaw()) +} + // go test -run Test_Ctx_Body_Immutable func Test_Ctx_Body_Immutable(t *testing.T) { t.Parallel() @@ -813,7 +867,7 @@ func Test_Ctx_UserContext(t *testing.T) { t.Parallel() testKey := struct{}{} testValue := "Test Value" - ctx := context.WithValue(context.Background(), testKey, testValue) + ctx := context.WithValue(context.Background(), testKey, testValue) //nolint: staticcheck // not needed for tests require.Equal(t, testValue, ctx.Value(testKey)) }) } @@ -826,7 +880,7 @@ func Test_Ctx_SetUserContext(t *testing.T) { testKey := struct{}{} testValue := "Test Value" - ctx := context.WithValue(context.Background(), testKey, testValue) + ctx := context.WithValue(context.Background(), testKey, testValue) //nolint: staticcheck // not needed for tests c.SetUserContext(ctx) require.Equal(t, testValue, c.UserContext().Value(testKey)) } @@ -846,7 +900,7 @@ func Test_Ctx_UserContext_Multiple_Requests(t *testing.T) { } input := utils.CopyString(Query(c, "input", "NO_VALUE")) - ctx = context.WithValue(ctx, testKey, fmt.Sprintf("%s_%s", testValue, input)) + ctx = context.WithValue(ctx, testKey, fmt.Sprintf("%s_%s", testValue, input)) //nolint: staticcheck // not needed for tests c.SetUserContext(ctx) return c.Status(StatusOK).SendString(fmt.Sprintf("resp_%s_returned", input)) diff --git a/docs/extra/faq.md b/docs/extra/faq.md index 6731e841ee..3e48c89e70 100644 --- a/docs/extra/faq.md +++ b/docs/extra/faq.md @@ -32,7 +32,7 @@ app.Use(func(c fiber.Ctx) error { ## How can i use live reload ? -[Air](https://github.com/cosmtrek/air) is a handy tool that automatically restarts your Go applications whenever the source code changes, making your development process faster and more efficient. +[Air](https://github.com/air-verse/air) is a handy tool that automatically restarts your Go applications whenever the source code changes, making your development process faster and more efficient. To use Air in a Fiber project, follow these steps: diff --git a/docs/intro.md b/docs/intro.md index 10e94b3dd6..7e0e1798c9 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -12,7 +12,7 @@ These docs are for **Fiber v3**, which was released on **March XX, 2024**. ### Installation -First of all, [download](https://go.dev/dl/) and install Go. `1.21` or higher is required. +First of all, [download](https://go.dev/dl/) and install Go. `1.22` or higher is required. Installation is done using the [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) command: diff --git a/docs/whats_new.md b/docs/whats_new.md index b088b77b2a..963d1daece 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -38,7 +38,7 @@ Here's a quick overview of the changes in Fiber `v3`: ## Drop for old Go versions -Fiber `v3` drops support for Go versions below `1.21`. We recommend upgrading to Go `1.21` or higher to use Fiber `v3`. +Fiber `v3` drops support for Go versions below `1.22`. We recommend upgrading to Go `1.22` or higher to use Fiber `v3`. ## 🚀 App diff --git a/go.mod b/go.mod index 7d01cbecd5..6b7d619f44 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ module github.com/gofiber/fiber/v3 -go 1.21 +go 1.22 require ( - github.com/gofiber/utils/v2 v2.0.0-beta.5 + github.com/gofiber/utils/v2 v2.0.0-beta.6 github.com/google/uuid v1.6.0 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.20 diff --git a/go.sum b/go.sum index f64b064c3c..4841052223 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gofiber/utils/v2 v2.0.0-beta.5 h1:zbDIU8gVAlZ2Ak9Fk8APlis4S7wUiQFbcvv6UASkm6A= -github.com/gofiber/utils/v2 v2.0.0-beta.5/go.mod h1:3Kz8Px3jInKFvqxDzDeoSygwEOO+3uyubTmUa6PqY+0= +github.com/gofiber/utils/v2 v2.0.0-beta.6 h1:ED62bOmpRXdgviPlfTmf0Q+AXzhaTUAFtdWjgx+XkYI= +github.com/gofiber/utils/v2 v2.0.0-beta.6/go.mod h1:3Kz8Px3jInKFvqxDzDeoSygwEOO+3uyubTmUa6PqY+0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= diff --git a/helpers.go b/helpers.go index 804ab78f58..2a2a2cbad2 100644 --- a/helpers.go +++ b/helpers.go @@ -222,7 +222,7 @@ func getGroupPath(prefix, path string) string { path = "/" + path } - return strings.TrimRight(prefix, "/") + path + return utils.TrimRight(prefix, '/') + path } // acceptsOffer This function determines if an offer matches a given specification. @@ -336,7 +336,7 @@ func getSplicedStrList(headerValue string, dst []string) []string { dst = make([]string, len(dst)+(len(dst)>>1)+2) copy(dst, oldSlice) } - dst[insertIndex] = strings.TrimLeft(headerValue[lastElementEndsAt:index], " ") + dst[insertIndex] = utils.TrimLeft(headerValue[lastElementEndsAt:index], ' ') lastElementEndsAt = uint8(index + 1) insertIndex++ } @@ -356,7 +356,7 @@ func forEachMediaRange(header []byte, functor func([]byte)) { for len(header) > 0 { n := 0 - header = bytes.TrimLeft(header, " ") + header = utils.TrimLeft(header, ' ') quotes := 0 escaping := false @@ -459,7 +459,7 @@ func getOffer(header []byte, isAccepted func(spec, offer string, specParams head } } - spec = bytes.TrimSpace(spec) + spec = utils.Trim(spec, ' ') // Determine specificity var specificity int diff --git a/helpers_test.go b/helpers_test.go index ac07a77af9..ddee434098 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -5,7 +5,6 @@ package fiber import ( - "fmt" "strings" "testing" "time" @@ -533,8 +532,7 @@ func Test_Utils_IsNoCache(t *testing.T) { for _, c := range testCases { ok := isNoCache(c.string) - require.Equal(t, c.bool, ok, - fmt.Sprintf("want %t, got isNoCache(%s)=%t", c.bool, c.string, ok)) + require.Equal(t, c.bool, ok, "want %t, got isNoCache(%s)=%t", c.bool, c.string, ok) } } diff --git a/listen.go b/listen.go index 543aec0269..0df4e1a060 100644 --- a/listen.go +++ b/listen.go @@ -351,7 +351,7 @@ func (app *App) startupMessage(addr string, isTLS bool, pids string, cfg ListenC } fmt.Fprintf(out, "%s\n", fmt.Sprintf(figletFiberText, colors.Red+"v"+Version+colors.Reset)) //nolint:errcheck,revive // ignore error - fmt.Fprintf(out, strings.Repeat("-", 50)+"\n") //nolint:errcheck,revive // ignore error + fmt.Fprintf(out, strings.Repeat("-", 50)+"\n") //nolint:errcheck,revive,govet // ignore error if host == "0.0.0.0" { //nolint:errcheck,revive // ignore error diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go index 1cc3b7375b..8f00f1f156 100644 --- a/middleware/cache/cache_test.go +++ b/middleware/cache/cache_test.go @@ -880,7 +880,7 @@ func Test_Cache_MaxBytesOrder(t *testing.T) { for idx, tcase := range cases { rsp, err := app.Test(httptest.NewRequest(fiber.MethodGet, tcase[0], nil)) require.NoError(t, err) - require.Equal(t, tcase[1], rsp.Header.Get("X-Cache"), fmt.Sprintf("Case %v", idx)) + require.Equal(t, tcase[1], rsp.Header.Get("X-Cache"), "Case %v", idx) } } @@ -914,7 +914,7 @@ func Test_Cache_MaxBytesSizes(t *testing.T) { for idx, tcase := range cases { rsp, err := app.Test(httptest.NewRequest(fiber.MethodGet, tcase[0], nil)) require.NoError(t, err) - require.Equal(t, tcase[1], rsp.Header.Get("X-Cache"), fmt.Sprintf("Case %v", idx)) + require.Equal(t, tcase[1], rsp.Header.Get("X-Cache"), "Case %v", idx) } } diff --git a/middleware/cache/manager.go b/middleware/cache/manager.go index 7e86dd1483..dd83b7a758 100644 --- a/middleware/cache/manager.go +++ b/middleware/cache/manager.go @@ -8,8 +8,9 @@ import ( "github.com/gofiber/fiber/v3/internal/memory" ) -// go:generate msgp -// msgp -file="manager.go" -o="manager_msgp.go" -tests=false -unexported +// msgp -file="manager.go" -o="manager_msgp.go" -tests=true -unexported +// +//go:generate msgp type item struct { headers map[string][]byte body []byte diff --git a/middleware/cache/manager_msgp.go b/middleware/cache/manager_msgp.go index e053416c1f..bf5d615200 100644 --- a/middleware/cache/manager_msgp.go +++ b/middleware/cache/manager_msgp.go @@ -6,12 +6,202 @@ import ( "github.com/tinylib/msgp/msgp" ) +// DecodeMsg implements msgp.Decodable +func (z *item) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "headers": + var zb0002 uint32 + zb0002, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err, "headers") + return + } + if z.headers == nil { + z.headers = make(map[string][]byte, zb0002) + } else if len(z.headers) > 0 { + for key := range z.headers { + delete(z.headers, key) + } + } + for zb0002 > 0 { + zb0002-- + var za0001 string + var za0002 []byte + za0001, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "headers") + return + } + za0002, err = dc.ReadBytes(za0002) + if err != nil { + err = msgp.WrapError(err, "headers", za0001) + return + } + z.headers[za0001] = za0002 + } + case "body": + z.body, err = dc.ReadBytes(z.body) + if err != nil { + err = msgp.WrapError(err, "body") + return + } + case "ctype": + z.ctype, err = dc.ReadBytes(z.ctype) + if err != nil { + err = msgp.WrapError(err, "ctype") + return + } + case "cencoding": + z.cencoding, err = dc.ReadBytes(z.cencoding) + if err != nil { + err = msgp.WrapError(err, "cencoding") + return + } + case "status": + z.status, err = dc.ReadInt() + if err != nil { + err = msgp.WrapError(err, "status") + return + } + case "exp": + z.exp, err = dc.ReadUint64() + if err != nil { + err = msgp.WrapError(err, "exp") + return + } + case "heapidx": + z.heapidx, err = dc.ReadInt() + if err != nil { + err = msgp.WrapError(err, "heapidx") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z *item) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 7 + // write "headers" + err = en.Append(0x87, 0xa7, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73) + if err != nil { + return + } + err = en.WriteMapHeader(uint32(len(z.headers))) + if err != nil { + err = msgp.WrapError(err, "headers") + return + } + for za0001, za0002 := range z.headers { + err = en.WriteString(za0001) + if err != nil { + err = msgp.WrapError(err, "headers") + return + } + err = en.WriteBytes(za0002) + if err != nil { + err = msgp.WrapError(err, "headers", za0001) + return + } + } + // write "body" + err = en.Append(0xa4, 0x62, 0x6f, 0x64, 0x79) + if err != nil { + return + } + err = en.WriteBytes(z.body) + if err != nil { + err = msgp.WrapError(err, "body") + return + } + // write "ctype" + err = en.Append(0xa5, 0x63, 0x74, 0x79, 0x70, 0x65) + if err != nil { + return + } + err = en.WriteBytes(z.ctype) + if err != nil { + err = msgp.WrapError(err, "ctype") + return + } + // write "cencoding" + err = en.Append(0xa9, 0x63, 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67) + if err != nil { + return + } + err = en.WriteBytes(z.cencoding) + if err != nil { + err = msgp.WrapError(err, "cencoding") + return + } + // write "status" + err = en.Append(0xa6, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73) + if err != nil { + return + } + err = en.WriteInt(z.status) + if err != nil { + err = msgp.WrapError(err, "status") + return + } + // write "exp" + err = en.Append(0xa3, 0x65, 0x78, 0x70) + if err != nil { + return + } + err = en.WriteUint64(z.exp) + if err != nil { + err = msgp.WrapError(err, "exp") + return + } + // write "heapidx" + err = en.Append(0xa7, 0x68, 0x65, 0x61, 0x70, 0x69, 0x64, 0x78) + if err != nil { + return + } + err = en.WriteInt(z.heapidx) + if err != nil { + err = msgp.WrapError(err, "heapidx") + return + } + return +} + // MarshalMsg implements msgp.Marshaler func (z *item) MarshalMsg(b []byte) (o []byte, err error) { o = msgp.Require(b, z.Msgsize()) // map header, size 7 + // string "headers" + o = append(o, 0x87, 0xa7, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73) + o = msgp.AppendMapHeader(o, uint32(len(z.headers))) + for za0001, za0002 := range z.headers { + o = msgp.AppendString(o, za0001) + o = msgp.AppendBytes(o, za0002) + } // string "body" - o = append(o, 0x87, 0xa4, 0x62, 0x6f, 0x64, 0x79) + o = append(o, 0xa4, 0x62, 0x6f, 0x64, 0x79) o = msgp.AppendBytes(o, z.body) // string "ctype" o = append(o, 0xa5, 0x63, 0x74, 0x79, 0x70, 0x65) @@ -25,13 +215,6 @@ func (z *item) MarshalMsg(b []byte) (o []byte, err error) { // string "exp" o = append(o, 0xa3, 0x65, 0x78, 0x70) o = msgp.AppendUint64(o, z.exp) - // string "headers" - o = append(o, 0xa7, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73) - o = msgp.AppendMapHeader(o, uint32(len(z.headers))) - for za0001, za0002 := range z.headers { - o = msgp.AppendString(o, za0001) - o = msgp.AppendBytes(o, za0002) - } // string "heapidx" o = append(o, 0xa7, 0x68, 0x65, 0x61, 0x70, 0x69, 0x64, 0x78) o = msgp.AppendInt(o, z.heapidx) @@ -56,36 +239,6 @@ func (z *item) UnmarshalMsg(bts []byte) (o []byte, err error) { return } switch msgp.UnsafeString(field) { - case "body": - z.body, bts, err = msgp.ReadBytesBytes(bts, z.body) - if err != nil { - err = msgp.WrapError(err, "body") - return - } - case "ctype": - z.ctype, bts, err = msgp.ReadBytesBytes(bts, z.ctype) - if err != nil { - err = msgp.WrapError(err, "ctype") - return - } - case "cencoding": - z.cencoding, bts, err = msgp.ReadBytesBytes(bts, z.cencoding) - if err != nil { - err = msgp.WrapError(err, "cencoding") - return - } - case "status": - z.status, bts, err = msgp.ReadIntBytes(bts) - if err != nil { - err = msgp.WrapError(err, "status") - return - } - case "exp": - z.exp, bts, err = msgp.ReadUint64Bytes(bts) - if err != nil { - err = msgp.WrapError(err, "exp") - return - } case "headers": var zb0002 uint32 zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) @@ -116,6 +269,36 @@ func (z *item) UnmarshalMsg(bts []byte) (o []byte, err error) { } z.headers[za0001] = za0002 } + case "body": + z.body, bts, err = msgp.ReadBytesBytes(bts, z.body) + if err != nil { + err = msgp.WrapError(err, "body") + return + } + case "ctype": + z.ctype, bts, err = msgp.ReadBytesBytes(bts, z.ctype) + if err != nil { + err = msgp.WrapError(err, "ctype") + return + } + case "cencoding": + z.cencoding, bts, err = msgp.ReadBytesBytes(bts, z.cencoding) + if err != nil { + err = msgp.WrapError(err, "cencoding") + return + } + case "status": + z.status, bts, err = msgp.ReadIntBytes(bts) + if err != nil { + err = msgp.WrapError(err, "status") + return + } + case "exp": + z.exp, bts, err = msgp.ReadUint64Bytes(bts) + if err != nil { + err = msgp.WrapError(err, "exp") + return + } case "heapidx": z.heapidx, bts, err = msgp.ReadIntBytes(bts) if err != nil { @@ -136,13 +319,13 @@ func (z *item) UnmarshalMsg(bts []byte) (o []byte, err error) { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *item) Msgsize() (s int) { - s = 1 + 5 + msgp.BytesPrefixSize + len(z.body) + 6 + msgp.BytesPrefixSize + len(z.ctype) + 10 + msgp.BytesPrefixSize + len(z.cencoding) + 7 + msgp.IntSize + 4 + msgp.Uint64Size + 8 + msgp.MapHeaderSize + s = 1 + 8 + msgp.MapHeaderSize if z.headers != nil { for za0001, za0002 := range z.headers { _ = za0002 s += msgp.StringPrefixSize + len(za0001) + msgp.BytesPrefixSize + len(za0002) } } - s += 8 + msgp.IntSize + s += 5 + msgp.BytesPrefixSize + len(z.body) + 6 + msgp.BytesPrefixSize + len(z.ctype) + 10 + msgp.BytesPrefixSize + len(z.cencoding) + 7 + msgp.IntSize + 4 + msgp.Uint64Size + 8 + msgp.IntSize return } diff --git a/middleware/cache/manager_msgp_test.go b/middleware/cache/manager_msgp_test.go index ab4d912be8..68693796d3 100644 --- a/middleware/cache/manager_msgp_test.go +++ b/middleware/cache/manager_msgp_test.go @@ -3,12 +3,13 @@ package cache // Code generated by github.com/tinylib/msgp DO NOT EDIT. import ( + "bytes" "testing" "github.com/tinylib/msgp/msgp" ) -func Test_MarshalUnmarshalitem(t *testing.T) { +func TestMarshalUnmarshalitem(t *testing.T) { v := item{} bts, err := v.MarshalMsg(nil) if err != nil { @@ -31,7 +32,7 @@ func Test_MarshalUnmarshalitem(t *testing.T) { } } -func Benchmark_MarshalMsgitem(b *testing.B) { +func BenchmarkMarshalMsgitem(b *testing.B) { v := item{} b.ReportAllocs() b.ResetTimer() @@ -40,7 +41,7 @@ func Benchmark_MarshalMsgitem(b *testing.B) { } } -func Benchmark_AppendMsgitem(b *testing.B) { +func BenchmarkAppendMsgitem(b *testing.B) { v := item{} bts := make([]byte, 0, v.Msgsize()) bts, _ = v.MarshalMsg(bts[0:0]) @@ -52,7 +53,7 @@ func Benchmark_AppendMsgitem(b *testing.B) { } } -func Benchmark_Unmarshalitem(b *testing.B) { +func BenchmarkUnmarshalitem(b *testing.B) { v := item{} bts, _ := v.MarshalMsg(nil) b.ReportAllocs() @@ -65,3 +66,58 @@ func Benchmark_Unmarshalitem(b *testing.B) { } } } + +func TestEncodeDecodeitem(t *testing.T) { + v := item{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + + m := v.Msgsize() + if buf.Len() > m { + t.Log("WARNING: TestEncodeDecodeitem Msgsize() is inaccurate") + } + + vn := item{} + err := msgp.Decode(&buf, &vn) + if err != nil { + t.Error(err) + } + + buf.Reset() + msgp.Encode(&buf, &v) + err = msgp.NewReader(&buf).Skip() + if err != nil { + t.Error(err) + } +} + +func BenchmarkEncodeitem(b *testing.B) { + v := item{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + en := msgp.NewWriter(msgp.Nowhere) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.EncodeMsg(en) + } + en.Flush() +} + +func BenchmarkDecodeitem(b *testing.B) { + v := item{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + rd := msgp.NewEndlessReader(buf.Bytes(), b) + dc := msgp.NewReader(rd) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := v.DecodeMsg(dc) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index e0fac91739..7b17e14367 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -6,6 +6,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/log" + "github.com/gofiber/utils/v2" ) // New creates a new middleware handler @@ -44,7 +45,7 @@ func New(config ...Config) fiber.Handler { break } if i := strings.Index(origin, "://*."); i != -1 { - trimmedOrigin := strings.TrimSpace(origin[:i+3] + origin[i+4:]) + trimmedOrigin := utils.Trim(origin[:i+3]+origin[i+4:], ' ') isValid, normalizedOrigin := normalizeOrigin(trimmedOrigin) if !isValid { panic("[CORS] Invalid origin format in configuration: " + trimmedOrigin) @@ -52,7 +53,7 @@ func New(config ...Config) fiber.Handler { sd := subdomain{prefix: normalizedOrigin[:i+3], suffix: normalizedOrigin[i+3:]} allowSOrigins = append(allowSOrigins, sd) } else { - trimmedOrigin := strings.TrimSpace(origin) + trimmedOrigin := utils.Trim(origin, ' ') isValid, normalizedOrigin := normalizeOrigin(trimmedOrigin) if !isValid { panic("[CORS] Invalid origin format in configuration: " + trimmedOrigin) diff --git a/middleware/csrf/csrf.go b/middleware/csrf/csrf.go index 182cbaea70..d417730416 100644 --- a/middleware/csrf/csrf.go +++ b/middleware/csrf/csrf.go @@ -8,6 +8,7 @@ import ( "time" "github.com/gofiber/fiber/v3" + "github.com/gofiber/utils/v2" ) var ( @@ -62,7 +63,7 @@ func New(config ...Config) fiber.Handler { for _, origin := range cfg.TrustedOrigins { if i := strings.Index(origin, "://*."); i != -1 { - trimmedOrigin := strings.TrimSpace(origin[:i+3] + origin[i+4:]) + trimmedOrigin := utils.Trim(origin[:i+3]+origin[i+4:], ' ') isValid, normalizedOrigin := normalizeOrigin(trimmedOrigin) if !isValid { panic("[CSRF] Invalid origin format in configuration:" + origin) @@ -70,7 +71,7 @@ func New(config ...Config) fiber.Handler { sd := subdomain{prefix: normalizedOrigin[:i+3], suffix: normalizedOrigin[i+3:]} trustedSubOrigins = append(trustedSubOrigins, sd) } else { - trimmedOrigin := strings.TrimSpace(origin) + trimmedOrigin := utils.Trim(origin, ' ') isValid, normalizedOrigin := normalizeOrigin(trimmedOrigin) if !isValid { panic("[CSRF] Invalid origin format in configuration:" + origin) diff --git a/middleware/csrf/csrf_test.go b/middleware/csrf/csrf_test.go index e98902be19..82252549bd 100644 --- a/middleware/csrf/csrf_test.go +++ b/middleware/csrf/csrf_test.go @@ -139,7 +139,7 @@ func Test_CSRF_WithSession(t *testing.T) { h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) for _, header := range strings.Split(token, ";") { - if strings.Split(strings.TrimSpace(header), "=")[0] == ConfigDefault.CookieName { + if strings.Split(utils.Trim(header, ' '), "=")[0] == ConfigDefault.CookieName { token = strings.Split(header, "=")[1] break } @@ -248,7 +248,7 @@ func Test_CSRF_ExpiredToken_WithSession(t *testing.T) { h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) for _, header := range strings.Split(token, ";") { - if strings.Split(strings.TrimSpace(header), "=")[0] == ConfigDefault.CookieName { + if strings.Split(utils.Trim(header, ' '), "=")[0] == ConfigDefault.CookieName { token = strings.Split(header, "=")[1] break } diff --git a/middleware/csrf/manager_msgp_test.go b/middleware/csrf/manager_msgp_test.go deleted file mode 100644 index 4d2bd2371b..0000000000 --- a/middleware/csrf/manager_msgp_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package csrf - -// Code generated by github.com/tinylib/msgp DO NOT EDIT. - -import ( - "testing" - - "github.com/tinylib/msgp/msgp" -) - -func Test_MarshalUnmarshalitem(t *testing.T) { - v := item{} - bts, err := v.MarshalMsg(nil) - if err != nil { - t.Fatal(err) - } - left, err := v.UnmarshalMsg(bts) - if err != nil { - t.Fatal(err) - } - if len(left) > 0 { - t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) - } - - left, err = msgp.Skip(bts) - if err != nil { - t.Fatal(err) - } - if len(left) > 0 { - t.Errorf("%d bytes left over after Skip(): %q", len(left), left) - } -} - -func Benchmark_MarshalMsgitem(b *testing.B) { - v := item{} - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - v.MarshalMsg(nil) - } -} - -func Benchmark_AppendMsgitem(b *testing.B) { - v := item{} - bts := make([]byte, 0, v.Msgsize()) - bts, _ = v.MarshalMsg(bts[0:0]) - b.SetBytes(int64(len(bts))) - b.ReportAllocs() - b.ResetTimer() - for i := 0; i < b.N; i++ { - bts, _ = v.MarshalMsg(bts[0:0]) - } -} - -func Benchmark_Unmarshalitem(b *testing.B) { - v := item{} - bts, _ := v.MarshalMsg(nil) - b.ReportAllocs() - b.SetBytes(int64(len(bts))) - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, err := v.UnmarshalMsg(bts) - if err != nil { - b.Fatal(err) - } - } -} diff --git a/middleware/csrf/storage_manager.go b/middleware/csrf/storage_manager.go index b6d7f0160d..e572a9c766 100644 --- a/middleware/csrf/storage_manager.go +++ b/middleware/csrf/storage_manager.go @@ -9,15 +9,16 @@ import ( "github.com/gofiber/utils/v2" ) -// go:generate msgp -// msgp -file="storage_manager.go" -o="storage_manager_msgp.go" -tests=false -unexported +// msgp -file="storage_manager.go" -o="storage_manager_msgp.go" -tests=true -unexported +// +//go:generate msgp type item struct{} //msgp:ignore manager type storageManager struct { - pool sync.Pool - memory *memory.Storage - storage fiber.Storage + pool sync.Pool `msg:"-"` //nolint:revive // Ignore unexported type + memory *memory.Storage `msg:"-"` //nolint:revive // Ignore unexported type + storage fiber.Storage `msg:"-"` //nolint:revive // Ignore unexported type } func newStorageManager(storage fiber.Storage) *storageManager { diff --git a/middleware/csrf/storage_manager_msgp.go b/middleware/csrf/storage_manager_msgp.go index c382641ada..f0ee8bd721 100644 --- a/middleware/csrf/storage_manager_msgp.go +++ b/middleware/csrf/storage_manager_msgp.go @@ -6,10 +6,51 @@ import ( "github.com/tinylib/msgp/msgp" ) +// DecodeMsg implements msgp.Decodable +func (z *item) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z item) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 0 + _ = z + err = en.Append(0x80) + if err != nil { + return + } + return +} + // MarshalMsg implements msgp.Marshaler func (z item) MarshalMsg(b []byte) (o []byte, err error) { o = msgp.Require(b, z.Msgsize()) // map header, size 0 + _ = z o = append(o, 0x80) return } @@ -49,3 +90,88 @@ func (z item) Msgsize() (s int) { s = 1 return } + +// DecodeMsg implements msgp.Decodable +func (z *storageManager) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z storageManager) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 0 + _ = z + err = en.Append(0x80) + if err != nil { + return + } + return +} + +// MarshalMsg implements msgp.Marshaler +func (z storageManager) MarshalMsg(b []byte) (o []byte, err error) { + o = msgp.Require(b, z.Msgsize()) + // map header, size 0 + _ = z + o = append(o, 0x80) + return +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *storageManager) UnmarshalMsg(bts []byte) (o []byte, err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + default: + bts, err = msgp.Skip(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + o = bts + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z storageManager) Msgsize() (s int) { + s = 1 + return +} diff --git a/middleware/csrf/storage_manager_msgp_test.go b/middleware/csrf/storage_manager_msgp_test.go new file mode 100644 index 0000000000..0aad0c2ba9 --- /dev/null +++ b/middleware/csrf/storage_manager_msgp_test.go @@ -0,0 +1,236 @@ +package csrf + +// Code generated by github.com/tinylib/msgp DO NOT EDIT. + +import ( + "bytes" + "testing" + + "github.com/tinylib/msgp/msgp" +) + +func TestMarshalUnmarshalitem(t *testing.T) { + v := item{} + bts, err := v.MarshalMsg(nil) + if err != nil { + t.Fatal(err) + } + left, err := v.UnmarshalMsg(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) + } + + left, err = msgp.Skip(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after Skip(): %q", len(left), left) + } +} + +func BenchmarkMarshalMsgitem(b *testing.B) { + v := item{} + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.MarshalMsg(nil) + } +} + +func BenchmarkAppendMsgitem(b *testing.B) { + v := item{} + bts := make([]byte, 0, v.Msgsize()) + bts, _ = v.MarshalMsg(bts[0:0]) + b.SetBytes(int64(len(bts))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + bts, _ = v.MarshalMsg(bts[0:0]) + } +} + +func BenchmarkUnmarshalitem(b *testing.B) { + v := item{} + bts, _ := v.MarshalMsg(nil) + b.ReportAllocs() + b.SetBytes(int64(len(bts))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := v.UnmarshalMsg(bts) + if err != nil { + b.Fatal(err) + } + } +} + +func TestEncodeDecodeitem(t *testing.T) { + v := item{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + + m := v.Msgsize() + if buf.Len() > m { + t.Log("WARNING: TestEncodeDecodeitem Msgsize() is inaccurate") + } + + vn := item{} + err := msgp.Decode(&buf, &vn) + if err != nil { + t.Error(err) + } + + buf.Reset() + msgp.Encode(&buf, &v) + err = msgp.NewReader(&buf).Skip() + if err != nil { + t.Error(err) + } +} + +func BenchmarkEncodeitem(b *testing.B) { + v := item{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + en := msgp.NewWriter(msgp.Nowhere) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.EncodeMsg(en) + } + en.Flush() +} + +func BenchmarkDecodeitem(b *testing.B) { + v := item{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + rd := msgp.NewEndlessReader(buf.Bytes(), b) + dc := msgp.NewReader(rd) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := v.DecodeMsg(dc) + if err != nil { + b.Fatal(err) + } + } +} + +func TestMarshalUnmarshalstorageManager(t *testing.T) { + v := storageManager{} + bts, err := v.MarshalMsg(nil) + if err != nil { + t.Fatal(err) + } + left, err := v.UnmarshalMsg(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) + } + + left, err = msgp.Skip(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after Skip(): %q", len(left), left) + } +} + +func BenchmarkMarshalMsgstorageManager(b *testing.B) { + v := storageManager{} + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.MarshalMsg(nil) + } +} + +func BenchmarkAppendMsgstorageManager(b *testing.B) { + v := storageManager{} + bts := make([]byte, 0, v.Msgsize()) + bts, _ = v.MarshalMsg(bts[0:0]) + b.SetBytes(int64(len(bts))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + bts, _ = v.MarshalMsg(bts[0:0]) + } +} + +func BenchmarkUnmarshalstorageManager(b *testing.B) { + v := storageManager{} + bts, _ := v.MarshalMsg(nil) + b.ReportAllocs() + b.SetBytes(int64(len(bts))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := v.UnmarshalMsg(bts) + if err != nil { + b.Fatal(err) + } + } +} + +func TestEncodeDecodestorageManager(t *testing.T) { + v := storageManager{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + + m := v.Msgsize() + if buf.Len() > m { + t.Log("WARNING: TestEncodeDecodestorageManager Msgsize() is inaccurate") + } + + vn := storageManager{} + err := msgp.Decode(&buf, &vn) + if err != nil { + t.Error(err) + } + + buf.Reset() + msgp.Encode(&buf, &v) + err = msgp.NewReader(&buf).Skip() + if err != nil { + t.Error(err) + } +} + +func BenchmarkEncodestorageManager(b *testing.B) { + v := storageManager{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + en := msgp.NewWriter(msgp.Nowhere) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.EncodeMsg(en) + } + en.Flush() +} + +func BenchmarkDecodestorageManager(b *testing.B) { + v := storageManager{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + rd := msgp.NewEndlessReader(buf.Bytes(), b) + dc := msgp.NewReader(rd) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := v.DecodeMsg(dc) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/middleware/idempotency/response.go b/middleware/idempotency/response.go index e101b05341..0e47ac86cf 100644 --- a/middleware/idempotency/response.go +++ b/middleware/idempotency/response.go @@ -3,7 +3,7 @@ package idempotency // response is a struct that represents the response of a request. // generation tool `go install github.com/tinylib/msgp@latest` // -//go:generate msgp -o=response_msgp.go -io=false -unexported +//go:generate msgp -o=response_msgp.go -io=false -tests=true -unexported type response struct { Headers map[string][]string `msg:"hs"` diff --git a/middleware/idempotency/response_msgp.go b/middleware/idempotency/response_msgp.go index 410d118ca0..c7873f4dae 100644 --- a/middleware/idempotency/response_msgp.go +++ b/middleware/idempotency/response_msgp.go @@ -6,15 +6,151 @@ import ( "github.com/tinylib/msgp/msgp" ) +// DecodeMsg implements msgp.Decodable +func (z *response) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "hs": + var zb0002 uint32 + zb0002, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err, "Headers") + return + } + if z.Headers == nil { + z.Headers = make(map[string][]string, zb0002) + } else if len(z.Headers) > 0 { + for key := range z.Headers { + delete(z.Headers, key) + } + } + for zb0002 > 0 { + zb0002-- + var za0001 string + var za0002 []string + za0001, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Headers") + return + } + var zb0003 uint32 + zb0003, err = dc.ReadArrayHeader() + if err != nil { + err = msgp.WrapError(err, "Headers", za0001) + return + } + if cap(za0002) >= int(zb0003) { + za0002 = (za0002)[:zb0003] + } else { + za0002 = make([]string, zb0003) + } + for za0003 := range za0002 { + za0002[za0003], err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Headers", za0001, za0003) + return + } + } + z.Headers[za0001] = za0002 + } + case "b": + z.Body, err = dc.ReadBytes(z.Body) + if err != nil { + err = msgp.WrapError(err, "Body") + return + } + case "sc": + z.StatusCode, err = dc.ReadInt() + if err != nil { + err = msgp.WrapError(err, "StatusCode") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z *response) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 3 + // write "hs" + err = en.Append(0x83, 0xa2, 0x68, 0x73) + if err != nil { + return + } + err = en.WriteMapHeader(uint32(len(z.Headers))) + if err != nil { + err = msgp.WrapError(err, "Headers") + return + } + for za0001, za0002 := range z.Headers { + err = en.WriteString(za0001) + if err != nil { + err = msgp.WrapError(err, "Headers") + return + } + err = en.WriteArrayHeader(uint32(len(za0002))) + if err != nil { + err = msgp.WrapError(err, "Headers", za0001) + return + } + for za0003 := range za0002 { + err = en.WriteString(za0002[za0003]) + if err != nil { + err = msgp.WrapError(err, "Headers", za0001, za0003) + return + } + } + } + // write "b" + err = en.Append(0xa1, 0x62) + if err != nil { + return + } + err = en.WriteBytes(z.Body) + if err != nil { + err = msgp.WrapError(err, "Body") + return + } + // write "sc" + err = en.Append(0xa2, 0x73, 0x63) + if err != nil { + return + } + err = en.WriteInt(z.StatusCode) + if err != nil { + err = msgp.WrapError(err, "StatusCode") + return + } + return +} + // MarshalMsg implements msgp.Marshaler func (z *response) MarshalMsg(b []byte) (o []byte, err error) { o = msgp.Require(b, z.Msgsize()) // map header, size 3 - // string "sc" - o = append(o, 0x83, 0xa2, 0x73, 0x63) - o = msgp.AppendInt(o, z.StatusCode) // string "hs" - o = append(o, 0xa2, 0x68, 0x73) + o = append(o, 0x83, 0xa2, 0x68, 0x73) o = msgp.AppendMapHeader(o, uint32(len(z.Headers))) for za0001, za0002 := range z.Headers { o = msgp.AppendString(o, za0001) @@ -26,6 +162,9 @@ func (z *response) MarshalMsg(b []byte) (o []byte, err error) { // string "b" o = append(o, 0xa1, 0x62) o = msgp.AppendBytes(o, z.Body) + // string "sc" + o = append(o, 0xa2, 0x73, 0x63) + o = msgp.AppendInt(o, z.StatusCode) return } @@ -47,12 +186,6 @@ func (z *response) UnmarshalMsg(bts []byte) (o []byte, err error) { return } switch msgp.UnsafeString(field) { - case "sc": - z.StatusCode, bts, err = msgp.ReadIntBytes(bts) - if err != nil { - err = msgp.WrapError(err, "StatusCode") - return - } case "hs": var zb0002 uint32 zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) @@ -102,6 +235,12 @@ func (z *response) UnmarshalMsg(bts []byte) (o []byte, err error) { err = msgp.WrapError(err, "Body") return } + case "sc": + z.StatusCode, bts, err = msgp.ReadIntBytes(bts) + if err != nil { + err = msgp.WrapError(err, "StatusCode") + return + } default: bts, err = msgp.Skip(bts) if err != nil { @@ -116,7 +255,7 @@ func (z *response) UnmarshalMsg(bts []byte) (o []byte, err error) { // Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message func (z *response) Msgsize() (s int) { - s = 1 + 3 + msgp.IntSize + 3 + msgp.MapHeaderSize + s = 1 + 3 + msgp.MapHeaderSize if z.Headers != nil { for za0001, za0002 := range z.Headers { _ = za0002 @@ -126,6 +265,6 @@ func (z *response) Msgsize() (s int) { } } } - s += 2 + msgp.BytesPrefixSize + len(z.Body) + s += 2 + msgp.BytesPrefixSize + len(z.Body) + 3 + msgp.IntSize return } diff --git a/middleware/idempotency/response_msgp_test.go b/middleware/idempotency/response_msgp_test.go index a1241f9014..173c75adf4 100644 --- a/middleware/idempotency/response_msgp_test.go +++ b/middleware/idempotency/response_msgp_test.go @@ -3,6 +3,7 @@ package idempotency // Code generated by github.com/tinylib/msgp DO NOT EDIT. import ( + "bytes" "testing" "github.com/tinylib/msgp/msgp" @@ -31,7 +32,7 @@ func TestMarshalUnmarshalresponse(t *testing.T) { } } -func Benchmark_MarshalMsgresponse(b *testing.B) { +func BenchmarkMarshalMsgresponse(b *testing.B) { v := response{} b.ReportAllocs() b.ResetTimer() @@ -40,7 +41,7 @@ func Benchmark_MarshalMsgresponse(b *testing.B) { } } -func Benchmark_AppendMsgresponse(b *testing.B) { +func BenchmarkAppendMsgresponse(b *testing.B) { v := response{} bts := make([]byte, 0, v.Msgsize()) bts, _ = v.MarshalMsg(bts[0:0]) @@ -52,7 +53,7 @@ func Benchmark_AppendMsgresponse(b *testing.B) { } } -func Benchmark_Unmarshalresponse(b *testing.B) { +func BenchmarkUnmarshalresponse(b *testing.B) { v := response{} bts, _ := v.MarshalMsg(nil) b.ReportAllocs() @@ -65,3 +66,58 @@ func Benchmark_Unmarshalresponse(b *testing.B) { } } } + +func TestEncodeDecoderesponse(t *testing.T) { + v := response{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + + m := v.Msgsize() + if buf.Len() > m { + t.Log("WARNING: TestEncodeDecoderesponse Msgsize() is inaccurate") + } + + vn := response{} + err := msgp.Decode(&buf, &vn) + if err != nil { + t.Error(err) + } + + buf.Reset() + msgp.Encode(&buf, &v) + err = msgp.NewReader(&buf).Skip() + if err != nil { + t.Error(err) + } +} + +func BenchmarkEncoderesponse(b *testing.B) { + v := response{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + en := msgp.NewWriter(msgp.Nowhere) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.EncodeMsg(en) + } + en.Flush() +} + +func BenchmarkDecoderesponse(b *testing.B) { + v := response{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + rd := msgp.NewEndlessReader(buf.Bytes(), b) + dc := msgp.NewReader(rd) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := v.DecodeMsg(dc) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/middleware/limiter/limiter_fixed.go b/middleware/limiter/limiter_fixed.go index 42b08afaf6..010083f91e 100644 --- a/middleware/limiter/limiter_fixed.go +++ b/middleware/limiter/limiter_fixed.go @@ -26,11 +26,11 @@ func (FixedWindow) New(cfg Config) fiber.Handler { // Return new handler return func(c fiber.Ctx) error { - // Generate max from generator, if no generator was provided the default value returned is 5 - max := cfg.MaxFunc(c) + // Generate maxRequests from generator, if no generator was provided the default value returned is 5 + maxRequests := cfg.MaxFunc(c) // Don't execute middleware if Next returns true or if the max is 0 - if (cfg.Next != nil && cfg.Next(c)) || max == 0 { + if (cfg.Next != nil && cfg.Next(c)) || maxRequests == 0 { return c.Next() } @@ -62,7 +62,7 @@ func (FixedWindow) New(cfg Config) fiber.Handler { resetInSec := e.exp - ts // Set how many hits we have left - remaining := max - e.currHits + remaining := maxRequests - e.currHits // Update storage manager.set(key, e, cfg.Expiration) @@ -98,7 +98,7 @@ func (FixedWindow) New(cfg Config) fiber.Handler { } // We can continue, update RateLimit headers - c.Set(xRateLimitLimit, strconv.Itoa(max)) + c.Set(xRateLimitLimit, strconv.Itoa(maxRequests)) c.Set(xRateLimitRemaining, strconv.Itoa(remaining)) c.Set(xRateLimitReset, strconv.FormatUint(resetInSec, 10)) diff --git a/middleware/limiter/limiter_sliding.go b/middleware/limiter/limiter_sliding.go index 1fc3138b9b..8eeb3f8222 100644 --- a/middleware/limiter/limiter_sliding.go +++ b/middleware/limiter/limiter_sliding.go @@ -27,11 +27,11 @@ func (SlidingWindow) New(cfg Config) fiber.Handler { // Return new handler return func(c fiber.Ctx) error { - // Generate max from generator, if no generator was provided the default value returned is 5 - max := cfg.MaxFunc(c) + // Generate maxRequests from generator, if no generator was provided the default value returned is 5 + maxRequests := cfg.MaxFunc(c) // Don't execute middleware if Next returns true or if the max is 0 - if (cfg.Next != nil && cfg.Next(c)) || max == 0 { + if (cfg.Next != nil && cfg.Next(c)) || maxRequests == 0 { return c.Next() } @@ -129,7 +129,7 @@ func (SlidingWindow) New(cfg Config) fiber.Handler { } // We can continue, update RateLimit headers - c.Set(xRateLimitLimit, strconv.Itoa(max)) + c.Set(xRateLimitLimit, strconv.Itoa(maxRequests)) c.Set(xRateLimitRemaining, strconv.Itoa(remaining)) c.Set(xRateLimitReset, strconv.FormatUint(resetInSec, 10)) diff --git a/middleware/limiter/limiter_test.go b/middleware/limiter/limiter_test.go index 41bd2e8ddf..bd277ff1db 100644 --- a/middleware/limiter/limiter_test.go +++ b/middleware/limiter/limiter_test.go @@ -97,11 +97,11 @@ func Test_Limiter_With_Max_Func_With_Zero(t *testing.T) { func Test_Limiter_With_Max_Func(t *testing.T) { t.Parallel() app := fiber.New() - max := 10 + maxRequests := 10 app.Use(New(Config{ MaxFunc: func(_ fiber.Ctx) int { - return max + return maxRequests }, Expiration: 2 * time.Second, Storage: memory.New(), @@ -113,7 +113,7 @@ func Test_Limiter_With_Max_Func(t *testing.T) { var wg sync.WaitGroup - for i := 0; i <= max-1; i++ { + for i := 0; i <= maxRequests-1; i++ { wg.Add(1) go func(wg *sync.WaitGroup) { defer wg.Done() diff --git a/middleware/limiter/manager.go b/middleware/limiter/manager.go index 4225cdd3ac..e91c401304 100644 --- a/middleware/limiter/manager.go +++ b/middleware/limiter/manager.go @@ -8,8 +8,9 @@ import ( "github.com/gofiber/fiber/v3/internal/memory" ) -// go:generate msgp // msgp -file="manager.go" -o="manager_msgp.go" -tests=false -unexported +// +//go:generate msgp type item struct { currHits int prevHits int diff --git a/middleware/limiter/manager_msgp.go b/middleware/limiter/manager_msgp.go index 8437eb81ae..a0d81ec91d 100644 --- a/middleware/limiter/manager_msgp.go +++ b/middleware/limiter/manager_msgp.go @@ -6,6 +6,89 @@ import ( "github.com/tinylib/msgp/msgp" ) +// DecodeMsg implements msgp.Decodable +func (z *item) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "currHits": + z.currHits, err = dc.ReadInt() + if err != nil { + err = msgp.WrapError(err, "currHits") + return + } + case "prevHits": + z.prevHits, err = dc.ReadInt() + if err != nil { + err = msgp.WrapError(err, "prevHits") + return + } + case "exp": + z.exp, err = dc.ReadUint64() + if err != nil { + err = msgp.WrapError(err, "exp") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z item) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 3 + // write "currHits" + err = en.Append(0x83, 0xa8, 0x63, 0x75, 0x72, 0x72, 0x48, 0x69, 0x74, 0x73) + if err != nil { + return + } + err = en.WriteInt(z.currHits) + if err != nil { + err = msgp.WrapError(err, "currHits") + return + } + // write "prevHits" + err = en.Append(0xa8, 0x70, 0x72, 0x65, 0x76, 0x48, 0x69, 0x74, 0x73) + if err != nil { + return + } + err = en.WriteInt(z.prevHits) + if err != nil { + err = msgp.WrapError(err, "prevHits") + return + } + // write "exp" + err = en.Append(0xa3, 0x65, 0x78, 0x70) + if err != nil { + return + } + err = en.WriteUint64(z.exp) + if err != nil { + err = msgp.WrapError(err, "exp") + return + } + return +} + // MarshalMsg implements msgp.Marshaler func (z item) MarshalMsg(b []byte) (o []byte, err error) { o = msgp.Require(b, z.Msgsize()) diff --git a/middleware/limiter/manager_msgp_test.go b/middleware/limiter/manager_msgp_test.go index 39562df395..122b8a6034 100644 --- a/middleware/limiter/manager_msgp_test.go +++ b/middleware/limiter/manager_msgp_test.go @@ -3,12 +3,13 @@ package limiter // Code generated by github.com/tinylib/msgp DO NOT EDIT. import ( + "bytes" "testing" "github.com/tinylib/msgp/msgp" ) -func Test_MarshalUnmarshalitem(t *testing.T) { +func TestMarshalUnmarshalitem(t *testing.T) { v := item{} bts, err := v.MarshalMsg(nil) if err != nil { @@ -31,7 +32,7 @@ func Test_MarshalUnmarshalitem(t *testing.T) { } } -func Benchmark_MarshalMsgitem(b *testing.B) { +func BenchmarkMarshalMsgitem(b *testing.B) { v := item{} b.ReportAllocs() b.ResetTimer() @@ -40,7 +41,7 @@ func Benchmark_MarshalMsgitem(b *testing.B) { } } -func Benchmark_AppendMsgitem(b *testing.B) { +func BenchmarkAppendMsgitem(b *testing.B) { v := item{} bts := make([]byte, 0, v.Msgsize()) bts, _ = v.MarshalMsg(bts[0:0]) @@ -52,7 +53,7 @@ func Benchmark_AppendMsgitem(b *testing.B) { } } -func Benchmark_Unmarshalitem(b *testing.B) { +func BenchmarkUnmarshalitem(b *testing.B) { v := item{} bts, _ := v.MarshalMsg(nil) b.ReportAllocs() @@ -65,3 +66,58 @@ func Benchmark_Unmarshalitem(b *testing.B) { } } } + +func TestEncodeDecodeitem(t *testing.T) { + v := item{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + + m := v.Msgsize() + if buf.Len() > m { + t.Log("WARNING: TestEncodeDecodeitem Msgsize() is inaccurate") + } + + vn := item{} + err := msgp.Decode(&buf, &vn) + if err != nil { + t.Error(err) + } + + buf.Reset() + msgp.Encode(&buf, &v) + err = msgp.NewReader(&buf).Skip() + if err != nil { + t.Error(err) + } +} + +func BenchmarkEncodeitem(b *testing.B) { + v := item{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + en := msgp.NewWriter(msgp.Nowhere) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.EncodeMsg(en) + } + en.Flush() +} + +func BenchmarkDecodeitem(b *testing.B) { + v := item{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + rd := msgp.NewEndlessReader(buf.Bytes(), b) + dc := msgp.NewReader(rd) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := v.DecodeMsg(dc) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/middleware/pprof/pprof.go b/middleware/pprof/pprof.go index bd6a58e72d..13b01b533c 100644 --- a/middleware/pprof/pprof.go +++ b/middleware/pprof/pprof.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/gofiber/fiber/v3" + "github.com/gofiber/utils/v2" "github.com/valyala/fasthttp/fasthttpadaptor" ) @@ -71,7 +72,7 @@ func New(config ...Config) fiber.Handler { default: // pprof index only works with trailing slash if strings.HasSuffix(path, "/") { - path = strings.TrimRight(path, "/") + path = utils.TrimRight(path, '/') } else { path = prefix + "/" } diff --git a/middleware/rewrite/rewrite_test.go b/middleware/rewrite/rewrite_test.go index 4cd1c4d00b..314a82149d 100644 --- a/middleware/rewrite/rewrite_test.go +++ b/middleware/rewrite/rewrite_test.go @@ -9,6 +9,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/stretchr/testify/require" + "github.com/valyala/fasthttp" ) func Test_New(t *testing.T) { @@ -170,3 +171,209 @@ func Test_Rewrite(t *testing.T) { require.NoError(t, err) require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } + +func Benchmark_Rewrite(b *testing.B) { + // Helper function to create a new Fiber app with rewrite middleware + createApp := func(config Config) *fiber.App { + app := fiber.New() + app.Use(New(config)) + return app + } + + // Benchmark: Rewrite with Next function always returns true + b.Run("Next always true", func(b *testing.B) { + app := createApp(Config{ + Next: func(fiber.Ctx) bool { + return true + }, + Rules: map[string]string{ + "/old": "/new", + }, + }) + + reqCtx := &fasthttp.RequestCtx{} + reqCtx.Request.SetRequestURI("/old") + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + app.Handler()(reqCtx) + } + }) + + // Benchmark: Rewrite with Next function always returns false + b.Run("Next always false", func(b *testing.B) { + app := createApp(Config{ + Next: func(fiber.Ctx) bool { + return false + }, + Rules: map[string]string{ + "/old": "/new", + }, + }) + + reqCtx := &fasthttp.RequestCtx{} + reqCtx.Request.SetRequestURI("/old") + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + app.Handler()(reqCtx) + } + }) + + // Benchmark: Rewrite with tokens + b.Run("Rewrite with tokens", func(b *testing.B) { + app := createApp(Config{ + Rules: map[string]string{ + "/users/*/orders/*": "/user/$1/order/$2", + }, + }) + + reqCtx := &fasthttp.RequestCtx{} + reqCtx.Request.SetRequestURI("/users/123/orders/456") + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + app.Handler()(reqCtx) + } + }) + + // Benchmark: Non-matching request, handled by default route + b.Run("NonMatch with default", func(b *testing.B) { + app := createApp(Config{ + Rules: map[string]string{ + "/users/*/orders/*": "/user/$1/order/$2", + }, + }) + app.Use(func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + reqCtx := &fasthttp.RequestCtx{} + reqCtx.Request.SetRequestURI("/not-matching-any-rule") + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + app.Handler()(reqCtx) + } + }) + + // Benchmark: Non-matching request, with no default route + b.Run("NonMatch without default", func(b *testing.B) { + app := createApp(Config{ + Rules: map[string]string{ + "/users/*/orders/*": "/user/$1/order/$2", + }, + }) + + reqCtx := &fasthttp.RequestCtx{} + reqCtx.Request.SetRequestURI("/not-matching-any-rule") + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + app.Handler()(reqCtx) + } + }) +} + +func Benchmark_Rewrite_Parallel(b *testing.B) { + // Helper function to create a new Fiber app with rewrite middleware + createApp := func(config Config) *fiber.App { + app := fiber.New() + app.Use(New(config)) + return app + } + + // Parallel Benchmark: Rewrite with Next function always returns true + b.Run("Next always true", func(b *testing.B) { + app := createApp(Config{ + Next: func(fiber.Ctx) bool { + return true + }, + Rules: map[string]string{ + "/old": "/new", + }, + }) + + b.RunParallel(func(pb *testing.PB) { + reqCtx := &fasthttp.RequestCtx{} + reqCtx.Request.SetRequestURI("/old") + for pb.Next() { + app.Handler()(reqCtx) + } + }) + }) + + // Parallel Benchmark: Rewrite with Next function always returns false + b.Run("Next always false", func(b *testing.B) { + app := createApp(Config{ + Next: func(fiber.Ctx) bool { + return false + }, + Rules: map[string]string{ + "/old": "/new", + }, + }) + + b.RunParallel(func(pb *testing.PB) { + reqCtx := &fasthttp.RequestCtx{} + reqCtx.Request.SetRequestURI("/old") + for pb.Next() { + app.Handler()(reqCtx) + } + }) + }) + + // Parallel Benchmark: Rewrite with tokens + b.Run("Rewrite with tokens", func(b *testing.B) { + app := createApp(Config{ + Rules: map[string]string{ + "/users/*/orders/*": "/user/$1/order/$2", + }, + }) + + b.RunParallel(func(pb *testing.PB) { + reqCtx := &fasthttp.RequestCtx{} + reqCtx.Request.SetRequestURI("/users/123/orders/456") + for pb.Next() { + app.Handler()(reqCtx) + } + }) + }) + + // Parallel Benchmark: Non-matching request, handled by default route + b.Run("NonMatch with default", func(b *testing.B) { + app := createApp(Config{ + Rules: map[string]string{ + "/users/*/orders/*": "/user/$1/order/$2", + }, + }) + app.Use(func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + b.RunParallel(func(pb *testing.PB) { + reqCtx := &fasthttp.RequestCtx{} + reqCtx.Request.SetRequestURI("/not-matching-any-rule") + for pb.Next() { + app.Handler()(reqCtx) + } + }) + }) + + // Parallel Benchmark: Non-matching request, with no default route + b.Run("NonMatch without default", func(b *testing.B) { + app := createApp(Config{ + Rules: map[string]string{ + "/users/*/orders/*": "/user/$1/order/$2", + }, + }) + + b.RunParallel(func(pb *testing.PB) { + reqCtx := &fasthttp.RequestCtx{} + reqCtx.Request.SetRequestURI("/not-matching-any-rule") + for pb.Next() { + app.Handler()(reqCtx) + } + }) + }) +} diff --git a/middleware/session/data.go b/middleware/session/data.go index 02dfc945c2..8d7287236f 100644 --- a/middleware/session/data.go +++ b/middleware/session/data.go @@ -4,11 +4,12 @@ import ( "sync" ) -// go:generate msgp -// msgp -file="data.go" -o="data_msgp.go" -tests=false -unexported +// msgp -file="data.go" -o="data_msgp.go" -tests=true -unexported +// +//go:generate msgp type data struct { - Data map[string]any - sync.RWMutex + Data map[string]any + sync.RWMutex `msg:"-"` } var dataPool = sync.Pool{ diff --git a/middleware/session/data_msgp.go b/middleware/session/data_msgp.go new file mode 100644 index 0000000000..a93ffcfb27 --- /dev/null +++ b/middleware/session/data_msgp.go @@ -0,0 +1,184 @@ +package session + +// Code generated by github.com/tinylib/msgp DO NOT EDIT. + +import ( + "github.com/tinylib/msgp/msgp" +) + +// DecodeMsg implements msgp.Decodable +func (z *data) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "Data": + var zb0002 uint32 + zb0002, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err, "Data") + return + } + if z.Data == nil { + z.Data = make(map[string]interface{}, zb0002) + } else if len(z.Data) > 0 { + for key := range z.Data { + delete(z.Data, key) + } + } + for zb0002 > 0 { + zb0002-- + var za0001 string + var za0002 interface{} + za0001, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Data") + return + } + za0002, err = dc.ReadIntf() + if err != nil { + err = msgp.WrapError(err, "Data", za0001) + return + } + z.Data[za0001] = za0002 + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z *data) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 1 + // write "Data" + err = en.Append(0x81, 0xa4, 0x44, 0x61, 0x74, 0x61) + if err != nil { + return + } + err = en.WriteMapHeader(uint32(len(z.Data))) + if err != nil { + err = msgp.WrapError(err, "Data") + return + } + for za0001, za0002 := range z.Data { + err = en.WriteString(za0001) + if err != nil { + err = msgp.WrapError(err, "Data") + return + } + err = en.WriteIntf(za0002) + if err != nil { + err = msgp.WrapError(err, "Data", za0001) + return + } + } + return +} + +// MarshalMsg implements msgp.Marshaler +func (z *data) MarshalMsg(b []byte) (o []byte, err error) { + o = msgp.Require(b, z.Msgsize()) + // map header, size 1 + // string "Data" + o = append(o, 0x81, 0xa4, 0x44, 0x61, 0x74, 0x61) + o = msgp.AppendMapHeader(o, uint32(len(z.Data))) + for za0001, za0002 := range z.Data { + o = msgp.AppendString(o, za0001) + o, err = msgp.AppendIntf(o, za0002) + if err != nil { + err = msgp.WrapError(err, "Data", za0001) + return + } + } + return +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *data) UnmarshalMsg(bts []byte) (o []byte, err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "Data": + var zb0002 uint32 + zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Data") + return + } + if z.Data == nil { + z.Data = make(map[string]interface{}, zb0002) + } else if len(z.Data) > 0 { + for key := range z.Data { + delete(z.Data, key) + } + } + for zb0002 > 0 { + var za0001 string + var za0002 interface{} + zb0002-- + za0001, bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Data") + return + } + za0002, bts, err = msgp.ReadIntfBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Data", za0001) + return + } + z.Data[za0001] = za0002 + } + default: + bts, err = msgp.Skip(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + o = bts + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z *data) Msgsize() (s int) { + s = 1 + 5 + msgp.MapHeaderSize + if z.Data != nil { + for za0001, za0002 := range z.Data { + _ = za0002 + s += msgp.StringPrefixSize + len(za0001) + msgp.GuessSize(za0002) + } + } + return +} diff --git a/middleware/session/data_msgp_test.go b/middleware/session/data_msgp_test.go new file mode 100644 index 0000000000..7532b6af68 --- /dev/null +++ b/middleware/session/data_msgp_test.go @@ -0,0 +1,123 @@ +package session + +// Code generated by github.com/tinylib/msgp DO NOT EDIT. + +import ( + "bytes" + "testing" + + "github.com/tinylib/msgp/msgp" +) + +func TestMarshalUnmarshaldata(t *testing.T) { + v := data{} + bts, err := v.MarshalMsg(nil) + if err != nil { + t.Fatal(err) + } + left, err := v.UnmarshalMsg(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) + } + + left, err = msgp.Skip(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after Skip(): %q", len(left), left) + } +} + +func BenchmarkMarshalMsgdata(b *testing.B) { + v := data{} + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.MarshalMsg(nil) + } +} + +func BenchmarkAppendMsgdata(b *testing.B) { + v := data{} + bts := make([]byte, 0, v.Msgsize()) + bts, _ = v.MarshalMsg(bts[0:0]) + b.SetBytes(int64(len(bts))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + bts, _ = v.MarshalMsg(bts[0:0]) + } +} + +func BenchmarkUnmarshaldata(b *testing.B) { + v := data{} + bts, _ := v.MarshalMsg(nil) + b.ReportAllocs() + b.SetBytes(int64(len(bts))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := v.UnmarshalMsg(bts) + if err != nil { + b.Fatal(err) + } + } +} + +func TestEncodeDecodedata(t *testing.T) { + v := data{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + + m := v.Msgsize() + if buf.Len() > m { + t.Log("WARNING: TestEncodeDecodedata Msgsize() is inaccurate") + } + + vn := data{} + err := msgp.Decode(&buf, &vn) + if err != nil { + t.Error(err) + } + + buf.Reset() + msgp.Encode(&buf, &v) + err = msgp.NewReader(&buf).Skip() + if err != nil { + t.Error(err) + } +} + +func BenchmarkEncodedata(b *testing.B) { + v := data{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + en := msgp.NewWriter(msgp.Nowhere) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.EncodeMsg(en) + } + en.Flush() +} + +func BenchmarkDecodedata(b *testing.B) { + v := data{} + var buf bytes.Buffer + msgp.Encode(&buf, &v) + b.SetBytes(int64(buf.Len())) + rd := msgp.NewEndlessReader(buf.Bytes(), b) + dc := msgp.NewReader(rd) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + err := v.DecodeMsg(dc) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/middleware/static/config.go b/middleware/static/config.go index dab313d35f..7a6489ed4e 100644 --- a/middleware/static/config.go +++ b/middleware/static/config.go @@ -86,7 +86,7 @@ func configDefault(config ...Config) Config { cfg := config[0] // Set default values - if cfg.IndexNames == nil || len(cfg.IndexNames) == 0 { + if len(cfg.IndexNames) == 0 { cfg.IndexNames = ConfigDefault.IndexNames } diff --git a/mount.go b/mount.go index 07d7764888..61614a2f25 100644 --- a/mount.go +++ b/mount.go @@ -6,9 +6,10 @@ package fiber import ( "sort" - "strings" "sync" "sync/atomic" + + "github.com/gofiber/utils/v2" ) // Put fields related to mounting. @@ -39,7 +40,7 @@ func newMountFields(app *App) *mountFields { // any of the fiber's sub apps are added to the application's error handlers // to be invoked on errors that happen within the prefix route. func (app *App) mount(prefix string, subApp *App) Router { - prefix = strings.TrimRight(prefix, "/") + prefix = utils.TrimRight(prefix, '/') if prefix == "" { prefix = "/" } @@ -69,7 +70,7 @@ func (app *App) mount(prefix string, subApp *App) Router { // compose them as a single service using Mount. func (grp *Group) mount(prefix string, subApp *App) Router { groupPath := getGroupPath(grp.Prefix, prefix) - groupPath = strings.TrimRight(groupPath, "/") + groupPath = utils.TrimRight(groupPath, '/') if groupPath == "" { groupPath = "/" } diff --git a/path.go b/path.go index 53b80b9f33..e4d32c9b58 100644 --- a/path.go +++ b/path.go @@ -51,7 +51,7 @@ const ( optionalParam byte = '?' // concludes a parameter by name and makes it optional paramStarterChar byte = ':' // start character for a parameter with name slashDelimiter byte = '/' // separator for the route, unlike the other delimiters this character at the end can be optional - slashDelimiterStr = "/" // separator for the route, unlike the other delimiters this character at the end can be optional + slashDelimiterStr byte = '/' // separator for the route, unlike the other delimiters this character at the end can be optional escapeChar byte = '\\' // escape character paramConstraintStart byte = '<' // start of type constraint for a parameter paramConstraintEnd byte = '>' // end of type constraint for a parameter @@ -161,7 +161,7 @@ func RoutePatternMatch(path, pattern string, cfg ...Config) bool { } // Strict routing, remove trailing slashes if !config.StrictRouting && len(patternPretty) > 1 { - patternPretty = strings.TrimRight(patternPretty, "/") + patternPretty = utils.TrimRight(patternPretty, '/') } parser := parseRoute(patternPretty) @@ -233,7 +233,7 @@ func addParameterMetaInfo(segs []*routeSegment) []*routeSegment { } else { comparePart = segs[i].Const if len(comparePart) > 1 { - comparePart = strings.TrimRight(comparePart, slashDelimiterStr) + comparePart = utils.TrimRight(comparePart, slashDelimiterStr) } } } diff --git a/path_test.go b/path_test.go index 071f7e9e0e..14eda46c64 100644 --- a/path_test.go +++ b/path_test.go @@ -5,7 +5,6 @@ package fiber import ( - "fmt" "testing" "github.com/stretchr/testify/require" @@ -142,9 +141,9 @@ func Test_Path_matchParams(t *testing.T) { parser := parseRoute(testCollection.pattern) for _, c := range testCollection.testCases { match := parser.getMatch(c.url, c.url, &ctxParams, c.partialCheck) - require.Equal(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", testCollection.pattern, c.url)) + require.Equal(t, c.match, match, "route: '%s', url: '%s'", testCollection.pattern, c.url) if match && len(c.params) > 0 { - require.Equal(t, c.params[0:len(c.params)], ctxParams[0:len(c.params)], fmt.Sprintf("route: '%s', url: '%s'", testCollection.pattern, c.url)) + require.Equal(t, c.params[0:len(c.params)], ctxParams[0:len(c.params)], "route: '%s', url: '%s'", testCollection.pattern, c.url) } } } @@ -163,7 +162,7 @@ func Test_RoutePatternMatch(t *testing.T) { continue } match := RoutePatternMatch(c.url, pattern) - require.Equal(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", pattern, c.url)) + require.Equal(t, c.match, match, "route: '%s', url: '%s'", pattern, c.url) } } for _, testCase := range routeTestCases { @@ -224,9 +223,9 @@ func Benchmark_Path_matchParams(t *testing.B) { matchRes = true } } - require.Equal(t, c.match, matchRes, fmt.Sprintf("route: '%s', url: '%s'", testCollection.pattern, c.url)) + require.Equal(t, c.match, matchRes, "route: '%s', url: '%s'", testCollection.pattern, c.url) if matchRes && len(c.params) > 0 { - require.Equal(t, c.params[0:len(c.params)-1], ctxParams[0:len(c.params)-1], fmt.Sprintf("route: '%s', url: '%s'", testCollection.pattern, c.url)) + require.Equal(t, c.params[0:len(c.params)-1], ctxParams[0:len(c.params)-1], "route: '%s', url: '%s'", testCollection.pattern, c.url) } }) } @@ -257,7 +256,7 @@ func Benchmark_RoutePatternMatch(t *testing.B) { matchRes = true } } - require.Equal(t, c.match, matchRes, fmt.Sprintf("route: '%s', url: '%s'", testCollection.pattern, c.url)) + require.Equal(t, c.match, matchRes, "route: '%s', url: '%s'", testCollection.pattern, c.url) }) } } diff --git a/prefork.go b/prefork.go index b572d6a121..745ed30627 100644 --- a/prefork.go +++ b/prefork.go @@ -76,9 +76,9 @@ func (app *App) prefork(addr string, tlsConfig *tls.Config, cfg ListenConfig) er pid int } // create variables - max := runtime.GOMAXPROCS(0) + maxProcs := runtime.GOMAXPROCS(0) childs := make(map[int]*exec.Cmd) - channel := make(chan child, max) + channel := make(chan child, maxProcs) // kill child procs when master exits defer func() { @@ -95,7 +95,7 @@ func (app *App) prefork(addr string, tlsConfig *tls.Config, cfg ListenConfig) er var pids []string // launch child procs - for i := 0; i < max; i++ { + for i := 0; i < maxProcs; i++ { 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, diff --git a/prefork_test.go b/prefork_test.go index 63cd635acd..1cfdcdb04c 100644 --- a/prefork_test.go +++ b/prefork_test.go @@ -95,11 +95,11 @@ func Test_App_Prefork_Child_Process_Never_Show_Startup_Message(t *testing.T) { func setupIsChild(t *testing.T) { t.Helper() - require.NoError(t, os.Setenv(envPreforkChildKey, envPreforkChildVal)) + require.NoError(t, os.Setenv(envPreforkChildKey, envPreforkChildVal)) //nolint:tenv // Ignore error } func teardownIsChild(t *testing.T) { t.Helper() - require.NoError(t, os.Setenv(envPreforkChildKey, "")) + require.NoError(t, os.Setenv(envPreforkChildKey, "")) //nolint:tenv // Ignore error } diff --git a/redirect.go b/redirect.go index 02311859cb..c59a00a35e 100644 --- a/redirect.go +++ b/redirect.go @@ -249,10 +249,10 @@ func (r *Redirect) parseAndClearFlashMessages() { for { commaPos = findNextNonEscapedCharsetPosition(cookieValue, []byte(CookieDataSeparator)) if commaPos == -1 { - r.c.redirectionMessages = append(r.c.redirectionMessages, strings.Trim(cookieValue, " ")) + r.c.redirectionMessages = append(r.c.redirectionMessages, utils.Trim(cookieValue, ' ')) break } - r.c.redirectionMessages = append(r.c.redirectionMessages, strings.Trim(cookieValue[:commaPos], " ")) + r.c.redirectionMessages = append(r.c.redirectionMessages, utils.Trim(cookieValue[:commaPos], ' ')) cookieValue = cookieValue[commaPos+1:] } diff --git a/router.go b/router.go index e0c9a5d60f..5b06ae3e84 100644 --- a/router.go +++ b/router.go @@ -253,7 +253,7 @@ func (app *App) addPrefixToRoute(prefix string, route *Route) *Route { } // Strict routing, remove trailing slashes if !app.config.StrictRouting && len(prettyPath) > 1 { - prettyPath = strings.TrimRight(prettyPath, "/") + prettyPath = utils.TrimRight(prettyPath, '/') } route.Path = prefixedPath @@ -324,7 +324,7 @@ func (app *App) register(methods []string, pathRaw string, group *Group, handler } // Strict routing, remove trailing slashes if !app.config.StrictRouting && len(pathPretty) > 1 { - pathPretty = strings.TrimRight(pathPretty, "/") + pathPretty = utils.TrimRight(pathPretty, '/') } // Is layer a middleware? isUse := method == methodUse