From ec48a76f14914bbac38a992a25f2cbc91643999a Mon Sep 17 00:00:00 2001 From: RW Date: Thu, 29 Feb 2024 08:29:59 +0100 Subject: [PATCH] V2 to v3 merge (#2864) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update pull_request_template.md * Update v3-changes.md * Update CONTRIBUTING.md (#2752) Grammar correction. * chore(encryptcookie)!: update default config (#2753) * chore(encryptcookie)!: update default config docs(encryptcookie): enhance documentation and examples BREAKING CHANGE: removed the hardcoded "csrf_" from the Except. * docs(encryptcookie): reads or modifies cookies * chore(encryptcookie): csrf config example * docs(encryptcookie): md table spacing * build(deps): bump actions/setup-go from 4 to 5 (#2754) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * 🩹 middleware/logger/: log client IP address by default (#2755) * middleware/logger: Log client IP address by default. * Update doc. * fix: don't constrain middlewares' context-keys to strings :bug: (#2751) * Revert "Revert ":bug: requestid.Config.ContextKey is interface{} (#2369)" (#2742)" This reverts commit 28be17f929cfa7d3c27dd292fc3956f2f9882e22. * fix: request ContextKey default value condition Should check for `nil` since it is `any`. * fix: don't constrain middlewares' context-keys to strings `context` recommends using "unexported type" as context keys to avoid collisions https://pkg.go.dev/github.com/gofiber/fiber/v2#Ctx.Locals. The official go blog also recommends this https://go.dev/blog/context. `fiber.Ctx.Locals(key any, value any)` correctly allows consumers to use unexported types or e.g. strings. But some fiber middlewares constrain their context-keys to `string` in their "default config structs", making it impossible to use unexported types. This PR removes the `string` _constraint_ from all middlewares, allowing to now use unexported types as per the official guidelines. However the default value is still a string, so it's not a breaking change, and anyone still using strings as context keys is not affected. * πŸ“š Update app.md for indentation (#2761) Update app.md for indentation * build(deps): bump github.com/google/uuid from 1.4.0 to 1.5.0 (#2762) Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.4.0 to 1.5.0. - [Release notes](https://github.com/google/uuid/releases) - [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/uuid/compare/v1.4.0...v1.5.0) --- updated-dependencies: - dependency-name: github.com/google/uuid dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump github/codeql-action from 2 to 3 (#2763) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Changing default log output (#2730) changing default log output Closes #2729 * Update hooks.md fix wrong hooks signature * 🩹 Fix: CORS middleware should use the defined AllowedOriginsFunc config when AllowedOrigins is empty (#2771) * πŸ› [Bug]: Adaptator + otelfiber issue #2641 (#2772) * 🩹🚨 - fix for redirect with query params (#2748) * redirect with query params did not work, fix it and add test for it * redirect middleware - fix test typo * ♻️ logger/middleware colorize logger error message #2593 (#2773) * :sparkles: feat: add liveness and readiness checks (#2509) * :sparkles: feat: add liveness and readiness checkers * :memo: docs: add docs for liveness and readiness * :sparkles: feat: add options method for probe checkers * :white_check_mark: tests: add tests for liveness and readiness * :recycle: refactor: change default endpoint values * :recycle: refactor: change default value for liveness endpoint * :memo: docs: add return status for liveness and readiness probes * :recycle: refactor: change probechecker to middleware * :memo: docs: move docs to middleware session * :recycle: refactor: apply gofumpt formatting * :recycle: refactor: remove unused parameter * split config and apply a review * apply reviews and add testcases * add benchmark * cleanup * rename middleware * fix linter * Update docs and config values * Revert change to IsReady * Updates based on code review * Update docs to match other middlewares --------- Co-authored-by: Muhammed Efe Cetin Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Co-authored-by: Juan Calderon-Perez * prepare release v2.52.0 - add more Parser tests * fix healthcheck.md * configure workflows for V2 branch * configure workflows for V2 branch * Fix default value to false in docs of QueryBool (#2811) fix default value to false in docs of QueryBool * update queryParser config * Update ctx.md * Update routing.md * merge v2 in v3 * merge v2 in v3 * lint fixes * :books: Doc: Fix code snippet indentation in /docs/api/middleware/keyauth.md Removes an an extra level of indentation in line 51 of `keyauth.md` [here](https://github.com/gofiber/fiber/blob/v2/docs/api/middleware/keyauth.md?plain=1#L51) * fix: healthcheck middleware not working with route group (#2863) * fix: healthcheck middleware not working with route group * perf: change verification method to improve perf * Update healthcheck_test.go * test: add not matching route test for strict routing * add more test cases * correct tests * correct test helpers * correct tests * correct tests --------- Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Co-authored-by: RenΓ© Werner * merge v2 in v3 * Merge pull request from GHSA-fmg4-x8pw-hjhg * Enforce Wildcard Origins with AllowCredentials check * Expand unit-tests, fix issues with subdomains logic, update docs * Update cors.md * Added test using localhost, ipv4, and ipv6 address * improve documentation markdown --------- Co-authored-by: RenΓ© Werner * Update app.go prepare release v2.52.1 * fix cors domain normalize * fix sync-docs workflow * test: fix failing tests * fix sync-docs workflow * test: cors middleware use testify require * chore: fix lint warnings * chore: revert test isolation. * fixed the fasthttp ctx race condition problem * Update middleware/cors/utils.go Co-authored-by: Renan Bastos * fix sync_docs.sh * fix review comments/hints * fix review comments/hints * stabilize Test_Proxy_Timeout_Slow_Server test * stabilize Test_Proxy_.* tests * ignore bodyclose linter for tests use http.NoBody instead of nil * revert(tests): undo http.NoBody usage * fix(ctx pool): postpone the reset for some values shortly before the release in the pool * refactor(tests): use testify panic method instead of custom solution --------- Signed-off-by: dependabot[bot] Co-authored-by: tokelo-12 <113810058+tokelo-12@users.noreply.github.com> Co-authored-by: Jason McNeil Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: iRedMail <2048991+iredmail@users.noreply.github.com> Co-authored-by: Benjamin Grosse Co-authored-by: Mehmet Firat KOMURCU Co-authored-by: Bruno Co-authored-by: Muhammad Kholid B Co-authored-by: gilwo Co-authored-by: Lucas Lemos Co-authored-by: Muhammed Efe Cetin Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Co-authored-by: Juan Calderon-Perez Co-authored-by: Jongmin Kim Co-authored-by: Giovanni Rivera Co-authored-by: Renan Bastos --- .github/CONTRIBUTING.md | 2 +- .../ISSUE_TEMPLATE/feature-request-v3.yaml | 74 --- .../ISSUE_TEMPLATE/maintenance-task-v3.yaml | 54 -- .github/PULL_REQUEST_TEMPLATE/v3-changes.md | 45 -- .github/pull_request_template.md | 6 +- .golangci.yml | 6 + app.go | 6 +- app_test.go | 15 +- bind_test.go | 76 +-- ctx_interface.go | 32 +- ctx_test.go | 503 +++++++++++------- docs/api/app.md | 4 +- docs/api/ctx.md | 14 +- docs/api/middleware/cors.md | 31 +- docs/api/middleware/encryptcookie.md | 90 ++-- docs/api/middleware/healthcheck.md | 106 ++++ docs/api/middleware/logger.md | 30 +- docs/api/middleware/timeout.md | 18 +- docs/guide/hooks.md | 14 +- log/default.go | 9 + log/default_test.go | 48 +- middleware/adaptor/adaptor.go | 4 +- middleware/adaptor/adaptor_test.go | 21 +- middleware/cors/cors.go | 67 ++- middleware/cors/cors_test.go | 325 ++++++++++- middleware/cors/utils.go | 101 ++-- middleware/cors/utils_test.go | 145 +++++ middleware/csrf/csrf_test.go | 26 +- middleware/earlydata/earlydata_test.go | 10 +- middleware/encryptcookie/config.go | 2 +- middleware/envvar/envvar_test.go | 1 - middleware/favicon/favicon_test.go | 1 - middleware/filesystem/filesystem_test.go | 1 - middleware/healthcheck/config.go | 84 +++ middleware/healthcheck/healthcheck.go | 62 +++ middleware/healthcheck/healthcheck_test.go | 240 +++++++++ middleware/idempotency/idempotency_test.go | 4 +- middleware/keyauth/keyauth_test.go | 1 - middleware/logger/config.go | 4 +- middleware/logger/logger_test.go | 1 - middleware/logger/tags.go | 6 +- middleware/proxy/proxy_test.go | 10 +- middleware/redirect/redirect.go | 6 +- middleware/redirect/redirect_test.go | 13 +- middleware/rewrite/rewrite_test.go | 1 - middleware/session/session_test.go | 49 +- middleware/session/store_test.go | 10 +- middleware/timeout/timeout.go | 48 +- middleware/timeout/timeout_test.go | 4 +- mount_test.go | 42 +- redirect_test.go | 38 +- router.go | 8 +- router_test.go | 6 +- 53 files changed, 1713 insertions(+), 811 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/feature-request-v3.yaml delete mode 100644 .github/ISSUE_TEMPLATE/maintenance-task-v3.yaml delete mode 100644 .github/PULL_REQUEST_TEMPLATE/v3-changes.md create mode 100644 docs/api/middleware/healthcheck.md create mode 100644 middleware/cors/utils_test.go create mode 100644 middleware/healthcheck/config.go create mode 100644 middleware/healthcheck/healthcheck.go create mode 100644 middleware/healthcheck/healthcheck_test.go diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d23c99af24..e35ce19942 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -15,7 +15,7 @@ Titles always we must use prefix according to below: - πŸ“š Doc: Translate to Portuguese middleware redirect - 🎨 Style: Respected pattern Golint -All pull request that contains a feature or fix is mandatory to have unit tests. Your PR is only to be merged if you respect this flow. +All pull requests that contain a feature or fix are mandatory to have unit tests. Your PR is only to be merged if you respect this flow. # πŸ‘ Contribute diff --git a/.github/ISSUE_TEMPLATE/feature-request-v3.yaml b/.github/ISSUE_TEMPLATE/feature-request-v3.yaml deleted file mode 100644 index c7551b6d0e..0000000000 --- a/.github/ISSUE_TEMPLATE/feature-request-v3.yaml +++ /dev/null @@ -1,74 +0,0 @@ -name: "πŸ“ Feature Proposal for v3" -title: "πŸ“ [v3 Proposal]: " -description: Propose a feature or improvement for Fiber v3. -labels: ["πŸ“ Proposal", "v3"] - -body: - - type: markdown - id: notice - attributes: - value: | - ### Notice - - For questions, join our [Discord server](https://gofiber.io/discord). - - Please write in clear, understandable English. - - Ensure your proposal aligns with Express design principles and HTTP RFC standards. - - Describe features expected to remain stable and not require changes in the foreseeable future. - - - type: textarea - id: description - attributes: - label: "Feature Proposal Description" - description: "A clear and detailed description of the feature you are proposing for Fiber v3. How should it work, and what API endpoints and methods would it involve?" - placeholder: "Describe your feature proposal clearly and in detail, including API endpoints and methods." - validations: - required: true - - - type: textarea - id: express-alignment - attributes: - label: "Alignment with Express API" - description: "Explain how your proposal aligns with the design and API of Express.js. Provide comparative examples if possible." - placeholder: "Outline how the feature aligns with Express.js design principles and API standards." - validations: - required: true - - - type: textarea - id: standards-compliance - attributes: - label: "HTTP RFC Standards Compliance" - description: "Confirm that the feature complies with HTTP RFC standards, and describe any relevant aspects." - placeholder: "Detail how the feature adheres to HTTP RFC standards." - validations: - required: true - - - type: textarea - id: stability - attributes: - label: "API Stability" - description: "Discuss the expected stability of the feature and its API. How do you ensure that it will not require changes or deprecations in the near future?" - placeholder: "Describe measures taken to ensure the feature's API stability over time." - validations: - required: true - - - type: textarea - id: examples - attributes: - label: "Feature Examples" - description: "Provide concrete examples and code snippets to illustrate how the proposed feature should function." - placeholder: "Share code snippets that exemplify the proposed feature and its usage." - render: go - validations: - required: true - - - type: checkboxes - id: terms - attributes: - label: "Checklist:" - description: "By submitting this issue, you confirm that:" - options: - - label: "I agree to follow Fiber's [Code of Conduct](https://github.com/gofiber/fiber/blob/master/.github/CODE_OF_CONDUCT.md)." - required: true - - label: "I have searched for existing issues that describe my proposal before opening this one." - required: true - - label: "I understand that a proposal that does not meet these guidelines may be closed without explanation." - required: true diff --git a/.github/ISSUE_TEMPLATE/maintenance-task-v3.yaml b/.github/ISSUE_TEMPLATE/maintenance-task-v3.yaml deleted file mode 100644 index 2a58f78922..0000000000 --- a/.github/ISSUE_TEMPLATE/maintenance-task-v3.yaml +++ /dev/null @@ -1,54 +0,0 @@ -name: "🧹 v3 Maintenance Task" -title: "🧹 [v3 Maintenance]: " -description: Describe a maintenance task for the v3 of the Fiber project. -labels: ["🧹 Updates", "v3"] - -body: - - type: markdown - id: notice - attributes: - value: | - ### Notice - - Before submitting a maintenance task, please check if a similar task has already been filed. - - Clearly outline the purpose of the maintenance task and its impact on the project. - - Use clear and understandable English. - - - type: textarea - id: task-description - attributes: - label: "Maintenance Task Description" - description: "Provide a detailed description of the maintenance task. Include any specific areas of the codebase that require attention, and the desired outcomes of this task." - placeholder: "Detail the maintenance task, specifying what needs to be done and why it is necessary." - validations: - required: true - - - type: textarea - id: impact - attributes: - label: "Impact on the Project" - description: "Explain the impact this maintenance will have on the project. Include benefits and potential risks if applicable." - placeholder: "Describe how completing this task will benefit the project, or the risks of not addressing it." - validations: - required: false - - - type: textarea - id: additional-context - attributes: - label: "Additional Context (optional)" - description: "Any additional information or context regarding the maintenance task that might be helpful." - placeholder: "Provide any additional information that may be relevant to the task at hand." - validations: - required: false - - - type: checkboxes - id: terms - attributes: - label: "Checklist:" - description: "Please confirm the following:" - options: - - label: "I have confirmed that this maintenance task is currently not being addressed." - required: true - - label: "I understand that this task will be evaluated by the maintainers and prioritized accordingly." - required: true - - label: "I am available to provide further information if needed." - required: true diff --git a/.github/PULL_REQUEST_TEMPLATE/v3-changes.md b/.github/PULL_REQUEST_TEMPLATE/v3-changes.md deleted file mode 100644 index 3f4b095767..0000000000 --- a/.github/PULL_REQUEST_TEMPLATE/v3-changes.md +++ /dev/null @@ -1,45 +0,0 @@ -## Description - -Please provide a clear and concise description of the changes you've made and the problem they address. Include the purpose of the change, any relevant issues it solves, and the benefits it brings to the project. If this change introduces new features or adjustments, highlight them here. - -Related to # (issue) - -## Changes Introduced - -List the new features or adjustments introduced in this pull request. Provide details on benchmarks, documentation updates, changelog entries, and if applicable, the migration guide. - -- [ ] Benchmarks: Describe any performance benchmarks and improvements related to the changes. -- [ ] Documentation Update: Detail the updates made to the documentation and links to the changed files. -- [ ] Changelog/What's New: Include a summary of the additions for the upcoming release notes. -- [ ] Migration Guide: If necessary, provide a guide or steps for users to migrate their existing code to accommodate these changes. -- [ ] API Alignment with Express: Explain how the changes align with the Express API. -- [ ] API Longevity: Discuss the steps taken to ensure that the new or updated APIs are consistent and not prone to breaking changes. -- [ ] Examples: Provide examples demonstrating the new features or changes in action. - -## Type of Change - -Please delete options that are not relevant. - -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Enhancement (improvement to existing features and functionality) -- [ ] Documentation update (changes to documentation) -- [ ] Performance improvement (non-breaking change which improves efficiency) -- [ ] Code consistency (non-breaking change which improves code reliability and robustness) - -## Checklist - -Before you submit your pull request, please make sure you meet these requirements: - -- [ ] Followed the inspiration of the Express.js framework for new functionalities, making them similar in usage. -- [ ] Conducted a self-review of the code and provided comments for complex or critical parts. -- [ ] Updated the documentation in the `/docs/` directory for [Fiber's documentation](https://docs.gofiber.io/). -- [ ] Added or updated unit tests to validate the effectiveness of the changes or new features. -- [ ] Ensured that new and existing unit tests pass locally with the changes. -- [ ] Verified that any new dependencies are essential and have been agreed upon by the maintainers/community. -- [ ] Aimed for optimal performance with minimal allocations in the new code. -- [ ] Provided benchmarks for the new code to analyze and improve upon. - -## Commit Formatting - -Please use emojis in commit messages for an easy way to identify the purpose or intention of a commit. You can refer to the emoji cheatsheet here: https://gitmoji.carloscuesta.me/ - diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 250037be08..98778d50b5 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,7 +4,7 @@ Please provide a clear and concise description of the changes you've made and th Related to # (issue) -## Changes Introduced +## Changes introduced List the new features or adjustments introduced in this pull request. Provide details on benchmarks, documentation updates, changelog entries, and if applicable, the migration guide. @@ -16,7 +16,7 @@ List the new features or adjustments introduced in this pull request. Provide de - [ ] API Longevity: Discuss the steps taken to ensure that the new or updated APIs are consistent and not prone to breaking changes. - [ ] Examples: Provide examples demonstrating the new features or changes in action. -## Type of Change +## Type of change Please delete options that are not relevant. @@ -39,6 +39,6 @@ Before you submit your pull request, please make sure you meet these requirement - [ ] Aimed for optimal performance with minimal allocations in the new code. - [ ] Provided benchmarks for the new code to analyze and improve upon. -## Commit Formatting +## Commit formatting Please use emojis in commit messages for an easy way to identify the purpose or intention of a commit. Check out the emoji cheatsheet here: [CONTRIBUTING.md](https://github.com/gofiber/fiber/blob/master/.github/CONTRIBUTING.md#pull-requests-or-commits) diff --git a/.golangci.yml b/.golangci.yml index de70317d47..1f55c987fb 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -162,6 +162,12 @@ linters-settings: issues: exclude-use-default: false + exclude-rules: + # Exclude some linters from running on tests files. + - path: _test\.go + linters: + - bodyclose + linters: disable: - spancheck # opentelemetry, irrelevant diff --git a/app.go b/app.go index f681ae2ce2..f51186fe7e 100644 --- a/app.go +++ b/app.go @@ -496,7 +496,7 @@ func New(config ...Config) *App { // Create Ctx pool app.pool = sync.Pool{ New: func() any { - return app.NewCtx(&fasthttp.RequestCtx{}) + return app.newCtx() }, } @@ -1071,9 +1071,7 @@ 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) { // Acquire Ctx with fasthttp request from pool - c := app.AcquireCtx() - c.Reset(fctx) - + c := app.AcquireCtx(fctx) defer app.ReleaseCtx(c) var ( diff --git a/app_test.go b/app_test.go index 81d7650e44..58c05932b2 100644 --- a/app_test.go +++ b/app_test.go @@ -2,7 +2,7 @@ // πŸ€– Github Repository: https://github.com/gofiber/fiber // πŸ“Œ API Documentation: https://docs.gofiber.io -//nolint:bodyclose, goconst // Much easier to just ignore memory leaks in tests +//nolint:goconst // Much easier to just ignore memory leaks in tests package fiber import ( @@ -314,7 +314,7 @@ func Test_App_serverErrorHandler_Internal_Error(t *testing.T) { t.Parallel() app := New() msg := "test err" - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed app.serverErrorHandler(c.fasthttp, errors.New(msg)) require.Equal(t, string(c.fasthttp.Response.Body()), msg) @@ -324,7 +324,7 @@ func Test_App_serverErrorHandler_Internal_Error(t *testing.T) { func Test_App_serverErrorHandler_Network_Error(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed app.serverErrorHandler(c.fasthttp, &net.DNSError{ Err: "test error", @@ -1400,8 +1400,7 @@ func Test_App_Next_Method(t *testing.T) { func Benchmark_AcquireCtx(b *testing.B) { app := New() for n := 0; n < b.N; n++ { - c := app.AcquireCtx() - c.Reset(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) app.ReleaseCtx(c) } @@ -1612,7 +1611,7 @@ func Test_App_SmallReadBuffer(t *testing.T) { go func() { time.Sleep(500 * time.Millisecond) - req, err := http.NewRequestWithContext(context.Background(), MethodGet, "http://127.0.0.1:4006/small-read-buffer", http.NoBody) + req, err := http.NewRequestWithContext(context.Background(), MethodGet, "http://127.0.0.1:4006/small-read-buffer", nil) require.NoError(t, err) var client http.Client resp, err := client.Do(req) @@ -1736,7 +1735,7 @@ func Test_App_Test_no_timeout_infinitely(t *testing.T) { return nil }) - req := httptest.NewRequest(MethodGet, "/", http.NoBody) + req := httptest.NewRequest(MethodGet, "/", nil) _, err = app.Test(req, -1) }() @@ -1765,7 +1764,7 @@ func Test_App_SetTLSHandler(t *testing.T) { app := New() app.SetTLSHandler(tlsHandler) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) require.Equal(t, "example.golang", c.ClientHelloInfo().ServerName) diff --git a/bind_test.go b/bind_test.go index d8b3efe65a..436c2e4bc9 100644 --- a/bind_test.go +++ b/bind_test.go @@ -1,4 +1,4 @@ -//nolint:wrapcheck,tagliatelle,bodyclose // We must not wrap errors in tests +//nolint:wrapcheck,tagliatelle // We must not wrap errors in tests package fiber import ( @@ -23,7 +23,7 @@ const helloWorld = "hello world" func Test_Bind_Query(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Query struct { ID int @@ -98,7 +98,7 @@ func Test_Bind_Query_Map(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetBody([]byte(``)) c.Request().Header.SetContentType("") @@ -156,7 +156,7 @@ func Test_Bind_Query_WithSetParserDecoder(t *testing.T) { }) app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type NonRFCTimeInput struct { Date NonRFCTime `query:"date"` @@ -188,7 +188,7 @@ func Test_Bind_Query_WithSetParserDecoder(t *testing.T) { func Test_Bind_Query_Schema(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Query1 struct { Name string `query:"name,required"` @@ -289,7 +289,7 @@ func Test_Bind_Query_Schema(t *testing.T) { func Test_Bind_Header(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Header struct { ID int @@ -362,7 +362,7 @@ func Test_Bind_Header_Map(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetBody([]byte(``)) c.Request().Header.SetContentType("") @@ -410,7 +410,7 @@ func Test_Bind_Header_WithSetParserDecoder(t *testing.T) { }) app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type NonRFCTimeInput struct { Date NonRFCTime `req:"date"` @@ -445,7 +445,7 @@ func Test_Bind_Header_WithSetParserDecoder(t *testing.T) { func Test_Bind_Header_Schema(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Header1 struct { Name string `header:"Name,required"` @@ -530,7 +530,7 @@ func Test_Bind_Header_Schema(t *testing.T) { func Test_Bind_RespHeader(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Header struct { ID int @@ -603,7 +603,7 @@ func Test_Bind_RespHeader_Map(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetBody([]byte(``)) c.Request().Header.SetContentType("") @@ -632,7 +632,7 @@ func Benchmark_Bind_Query(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Query struct { ID int @@ -656,7 +656,7 @@ func Benchmark_Bind_Query_Map(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetBody([]byte(``)) c.Request().Header.SetContentType("") @@ -675,7 +675,7 @@ func Benchmark_Bind_Query_WithParseParam(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Person struct { Name string `query:"name"` @@ -705,7 +705,7 @@ func Benchmark_Bind_Query_Comma(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Query struct { ID int @@ -730,7 +730,7 @@ func Benchmark_Bind_Header(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type ReqHeader struct { ID int @@ -757,7 +757,7 @@ func Benchmark_Bind_Header(b *testing.B) { func Benchmark_Bind_Header_Map(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetBody([]byte(``)) c.Request().Header.SetContentType("") @@ -780,7 +780,7 @@ func Benchmark_Bind_RespHeader(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type ReqHeader struct { ID int @@ -807,7 +807,7 @@ func Benchmark_Bind_RespHeader(b *testing.B) { func Benchmark_Bind_RespHeader_Map(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetBody([]byte(``)) c.Request().Header.SetContentType("") @@ -829,7 +829,7 @@ func Benchmark_Bind_RespHeader_Map(b *testing.B) { func Test_Bind_Body(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Demo struct { Name string `json:"name" xml:"name" form:"name" query:"name"` @@ -926,7 +926,7 @@ func Test_Bind_Body_WithSetParserDecoder(t *testing.T) { }) app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Demo struct { Date CustomTime `form:"date"` @@ -958,7 +958,7 @@ func Benchmark_Bind_Body_JSON(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Demo struct { Name string `json:"name"` @@ -984,7 +984,7 @@ func Benchmark_Bind_Body_XML(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Demo struct { Name string `xml:"name"` @@ -1010,7 +1010,7 @@ func Benchmark_Bind_Body_Form(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Demo struct { Name string `form:"name"` @@ -1036,7 +1036,7 @@ func Benchmark_Bind_Body_MultipartForm(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Demo struct { Name string `form:"name"` @@ -1063,7 +1063,7 @@ func Benchmark_Bind_Body_Form_Map(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) body := []byte("name=john") c.Request().SetBody(body) @@ -1131,7 +1131,7 @@ func Benchmark_Bind_URI(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.route = &Route{ Params: []string{ @@ -1168,7 +1168,7 @@ func Benchmark_Bind_URI_Map(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.route = &Route{ Params: []string{ @@ -1200,7 +1200,7 @@ func Test_Bind_Cookie(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Cookie struct { ID int @@ -1273,7 +1273,7 @@ func Test_Bind_Cookie_Map(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetBody([]byte(``)) c.Request().Header.SetContentType("") @@ -1321,7 +1321,7 @@ func Test_Bind_Cookie_WithSetParserDecoder(t *testing.T) { }) app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type NonRFCTimeInput struct { Date NonRFCTime `cerez:"date"` @@ -1357,7 +1357,7 @@ func Test_Bind_Cookie_Schema(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Cookie1 struct { Name string `cookie:"Name,required"` @@ -1443,7 +1443,7 @@ func Benchmark_Bind_Cookie(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Cookie struct { ID int @@ -1472,7 +1472,7 @@ func Benchmark_Bind_Cookie_Map(b *testing.B) { var err error app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetBody([]byte(``)) c.Request().Header.SetContentType("") @@ -1509,7 +1509,7 @@ func (*customBinder) Parse(c Ctx, out any) error { // go test -run Test_Bind_CustomBinder func Test_Bind_CustomBinder(t *testing.T) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) // Register binder customBinder := &customBinder{} @@ -1533,7 +1533,7 @@ func Test_Bind_CustomBinder(t *testing.T) { // go test -run Test_Bind_Must func Test_Bind_Must(t *testing.T) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type RequiredQuery struct { Name string `query:"name,required"` @@ -1573,7 +1573,7 @@ type simpleQuery struct { // go test -run Test_Bind_StructValidator func Test_Bind_StructValidator(t *testing.T) { app := New(Config{StructValidator: &structValidator{}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) rq := new(simpleQuery) c.Request().URI().SetQueryString("name=efe") @@ -1588,7 +1588,7 @@ func Test_Bind_StructValidator(t *testing.T) { func Test_Bind_RepeatParserWithSameStruct(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) type Request struct { diff --git a/ctx_interface.go b/ctx_interface.go index 3eef6a6938..54cf6012fa 100644 --- a/ctx_interface.go +++ b/ctx_interface.go @@ -427,7 +427,7 @@ func NewDefaultCtx(app *App) *DefaultCtx { } } -func (app *App) NewCtx(fctx *fasthttp.RequestCtx) Ctx { +func (app *App) newCtx() Ctx { var c Ctx if app.newCtxFunc != nil { @@ -436,18 +436,18 @@ func (app *App) NewCtx(fctx *fasthttp.RequestCtx) Ctx { c = NewDefaultCtx(app) } - // Set request - c.setReq(fctx) - return c } // AcquireCtx retrieves a new Ctx from the pool. -func (app *App) AcquireCtx() Ctx { +func (app *App) AcquireCtx(fctx *fasthttp.RequestCtx) Ctx { ctx, ok := app.pool.Get().(Ctx) + if !ok { panic(errors.New("failed to type-assert to Ctx")) } + ctx.Reset(fctx) + return ctx } @@ -459,21 +459,11 @@ func (app *App) ReleaseCtx(c Ctx) { // Reset is a method to reset context fields by given request when to use server handlers. func (c *DefaultCtx) Reset(fctx *fasthttp.RequestCtx) { - // Reset route and handler index - c.indexRoute = -1 - c.indexHandler = 0 - - // Reset matched flag - c.matched = false - // Set paths c.pathOriginal = c.app.getString(fctx.URI().PathOriginal()) // Attach *fasthttp.RequestCtx to ctx - c.fasthttp = fctx - - // reset base uri - c.baseURI = "" + c.setReq(fctx) // Set method c.method = c.app.getString(fctx.Request.Header.Method()) @@ -485,6 +475,16 @@ func (c *DefaultCtx) Reset(fctx *fasthttp.RequestCtx) { // Release is a method to reset context fields when to use ReleaseCtx() func (c *DefaultCtx) release() { + // Reset route and handler index + c.indexRoute = -1 + c.indexHandler = 0 + + // Reset matched flag + c.matched = false + + // reset base uri + c.baseURI = "" + c.route = nil c.fasthttp = nil c.bind = nil diff --git a/ctx_test.go b/ctx_test.go index 2c99c0a553..fb601bf49b 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2,7 +2,6 @@ // πŸ€– 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 ( @@ -40,7 +39,7 @@ const epsilon = 0.001 func Test_Ctx_Accepts(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAccept, "text/html,application/xhtml+xml,application/xml;q=0.9") require.Equal(t, "", c.Accepts("")) @@ -70,7 +69,7 @@ func Test_Ctx_Accepts(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Accepts -benchmem -count=4 func Benchmark_Ctx_Accepts(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) acceptHeader := "text/html,application/xhtml+xml,application/xml;q=0.9" c.Request().Header.Set("Accept", acceptHeader) @@ -129,7 +128,7 @@ func Test_Ctx_CustomCtx(t *testing.T) { func Test_Ctx_Accepts_EmptyAccept(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.Equal(t, ".forwarded", c.Accepts(".forwarded")) } @@ -138,7 +137,7 @@ func Test_Ctx_Accepts_EmptyAccept(t *testing.T) { func Test_Ctx_Accepts_Wildcard(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAccept, "*/*;q=0.9") require.Equal(t, "html", c.Accepts("html")) @@ -152,7 +151,7 @@ func Test_Ctx_Accepts_Wildcard(t *testing.T) { func Test_Ctx_AcceptsCharsets(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAcceptCharset, "utf-8, iso-8859-1;q=0.5") require.Equal(t, "utf-8", c.AcceptsCharsets("utf-8")) @@ -161,7 +160,7 @@ func Test_Ctx_AcceptsCharsets(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsCharsets -benchmem -count=4 func Benchmark_Ctx_AcceptsCharsets(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set("Accept-Charset", "utf-8, iso-8859-1;q=0.5") var res string @@ -177,7 +176,7 @@ func Benchmark_Ctx_AcceptsCharsets(b *testing.B) { func Test_Ctx_AcceptsEncodings(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAcceptEncoding, "deflate, gzip;q=1.0, *;q=0.5") require.Equal(t, "gzip", c.AcceptsEncodings("gzip")) @@ -187,7 +186,7 @@ func Test_Ctx_AcceptsEncodings(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsEncodings -benchmem -count=4 func Benchmark_Ctx_AcceptsEncodings(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderAcceptEncoding, "deflate, gzip;q=1.0, *;q=0.5") var res string @@ -203,7 +202,7 @@ func Benchmark_Ctx_AcceptsEncodings(b *testing.B) { func Test_Ctx_AcceptsLanguages(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAcceptLanguage, "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5") require.Equal(t, "fr", c.AcceptsLanguages("fr")) @@ -212,7 +211,7 @@ func Test_Ctx_AcceptsLanguages(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsLanguages -benchmem -count=4 func Benchmark_Ctx_AcceptsLanguages(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderAcceptLanguage, "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5") var res string @@ -229,7 +228,7 @@ func Test_Ctx_App(t *testing.T) { t.Parallel() app := New() app.config.BodyLimit = 1000 - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.Equal(t, 1000, c.App().config.BodyLimit) } @@ -238,7 +237,7 @@ func Test_Ctx_App(t *testing.T) { func Test_Ctx_Append(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Append("X-Test", "Hello") c.Append("X-Test", "World") @@ -273,7 +272,7 @@ func Test_Ctx_Append(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Append -benchmem -count=4 func Benchmark_Ctx_Append(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -289,7 +288,7 @@ func Benchmark_Ctx_Append(b *testing.B) { func Test_Ctx_Attachment(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) // empty c.Attachment() @@ -306,7 +305,7 @@ func Test_Ctx_Attachment(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Attachment -benchmem -count=4 func Benchmark_Ctx_Attachment(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -321,7 +320,7 @@ func Benchmark_Ctx_Attachment(b *testing.B) { func Test_Ctx_BaseURL(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") require.Equal(t, "http://google.com", c.BaseURL()) @@ -332,7 +331,7 @@ func Test_Ctx_BaseURL(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_BaseURL -benchmem func Benchmark_Ctx_BaseURL(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().SetHost("google.com:1337") c.Request().URI().SetPath("/haha/oke/lol") @@ -349,7 +348,7 @@ func Benchmark_Ctx_BaseURL(b *testing.B) { func Test_Ctx_Body(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().SetBody([]byte("john=doe")) require.Equal(t, []byte("john=doe"), c.Body()) @@ -360,7 +359,7 @@ func Benchmark_Ctx_Body(b *testing.B) { const input = "john=doe" app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().SetBody([]byte(input)) b.ReportAllocs() @@ -377,7 +376,7 @@ func Test_Ctx_Body_Immutable(t *testing.T) { t.Parallel() app := New() app.config.Immutable = true - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().SetBody([]byte("john=doe")) require.Equal(t, []byte("john=doe"), c.Body()) @@ -389,7 +388,7 @@ func Benchmark_Ctx_Body_Immutable(b *testing.B) { app := New() app.config.Immutable = true - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().SetBody([]byte(input)) b.ReportAllocs() @@ -442,7 +441,7 @@ func Test_Ctx_Body_With_Compression(t *testing.T) { t.Run(tCase.name, func(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set("Content-Encoding", tCase.contentEncoding) if strings.Contains(tCase.contentEncoding, "gzip") { @@ -578,7 +577,7 @@ func Benchmark_Ctx_Body_With_Compression(b *testing.B) { b.Run(ct.contentEncoding, func(b *testing.B) { app := New() const input = "john=doe" - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("Content-Encoding", ct.contentEncoding) compressedBody, err := ct.compressWriter([]byte(input)) @@ -635,7 +634,7 @@ func Test_Ctx_Body_With_Compression_Immutable(t *testing.T) { t.Parallel() app := New() app.config.Immutable = true - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set("Content-Encoding", tCase.contentEncoding) if strings.Contains(tCase.contentEncoding, "gzip") { @@ -772,7 +771,7 @@ func Benchmark_Ctx_Body_With_Compression_Immutable(b *testing.B) { app := New() app.config.Immutable = true const input = "john=doe" - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("Content-Encoding", ct.contentEncoding) compressedBody, err := ct.compressWriter([]byte(input)) @@ -792,7 +791,7 @@ func Benchmark_Ctx_Body_With_Compression_Immutable(b *testing.B) { func Test_Ctx_Context(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.Equal(t, "*fasthttp.RequestCtx", fmt.Sprintf("%T", c.Context())) } @@ -801,7 +800,7 @@ func Test_Ctx_Context(t *testing.T) { func Test_Ctx_UserContext(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) t.Run("Nil_Context", func(t *testing.T) { t.Parallel() @@ -821,7 +820,7 @@ func Test_Ctx_UserContext(t *testing.T) { func Test_Ctx_SetUserContext(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) testKey := struct{}{} testValue := "Test Value" @@ -872,7 +871,7 @@ func Test_Ctx_UserContext_Multiple_Requests(t *testing.T) { func Test_Ctx_Cookie(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) expire := time.Now().Add(24 * time.Hour) var dst []byte @@ -924,7 +923,7 @@ func Test_Ctx_Cookie(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Cookie -benchmem -count=4 func Benchmark_Ctx_Cookie(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -941,7 +940,7 @@ func Benchmark_Ctx_Cookie(b *testing.B) { func Test_Ctx_Cookies(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("Cookie", "john=doe") require.Equal(t, "doe", c.Cookies("john")) @@ -952,7 +951,7 @@ func Test_Ctx_Cookies(t *testing.T) { func Test_Ctx_Format(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) // set `accepted` to whatever media type was chosen by Format var accepted string @@ -1001,7 +1000,7 @@ func Test_Ctx_Format(t *testing.T) { func Benchmark_Ctx_Format(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAccept, "application/json,text/plain; format=flowed; q=0.9") fail := func(_ Ctx) error { @@ -1068,7 +1067,7 @@ func Benchmark_Ctx_Format(b *testing.B) { func Test_Ctx_AutoFormat(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAccept, MIMETextPlain) err := c.AutoFormat([]byte("Hello, World!")) @@ -1114,7 +1113,7 @@ func Test_Ctx_AutoFormat(t *testing.T) { func Test_Ctx_AutoFormat_Struct(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type Message struct { Recipients []string @@ -1147,7 +1146,7 @@ func Test_Ctx_AutoFormat_Struct(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_AutoFormat -benchmem -count=4 func Benchmark_Ctx_AutoFormat(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("Accept", "text/plain") b.ReportAllocs() @@ -1164,7 +1163,7 @@ func Benchmark_Ctx_AutoFormat(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_AutoFormat_HTML -benchmem -count=4 func Benchmark_Ctx_AutoFormat_HTML(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("Accept", "text/html") b.ReportAllocs() @@ -1181,7 +1180,7 @@ func Benchmark_Ctx_AutoFormat_HTML(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_AutoFormat_JSON -benchmem -count=4 func Benchmark_Ctx_AutoFormat_JSON(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("Accept", "application/json") b.ReportAllocs() @@ -1198,7 +1197,7 @@ func Benchmark_Ctx_AutoFormat_JSON(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_AutoFormat_XML -benchmem -count=4 func Benchmark_Ctx_AutoFormat_XML(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("Accept", "application/xml") b.ReportAllocs() @@ -1282,7 +1281,7 @@ func Test_Ctx_FormValue(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Fresh_StaleEtag -benchmem -count=4 func Benchmark_Ctx_Fresh_StaleEtag(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) for n := 0; n < b.N; n++ { c.Request().Header.Set(HeaderIfNoneMatch, "a, b, c, d") @@ -1299,7 +1298,7 @@ func Benchmark_Ctx_Fresh_StaleEtag(b *testing.B) { func Test_Ctx_Fresh(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.False(t, c.Fresh()) @@ -1344,7 +1343,7 @@ func Test_Ctx_Fresh(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Fresh_WithNoCache -benchmem -count=4 func Benchmark_Ctx_Fresh_WithNoCache(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderIfNoneMatch, "*") c.Request().Header.Set(HeaderCacheControl, "no-cache") @@ -1353,11 +1352,107 @@ func Benchmark_Ctx_Fresh_WithNoCache(b *testing.B) { } } +// go test -run Test_Ctx_Parsers -v +func Test_Ctx_Parsers(t *testing.T) { + t.Parallel() + // setup + app := New() + + type TestStruct struct { + Name string + Class int + NameWithDefault string `json:"name2" xml:"Name2" form:"name2" cookie:"name2" query:"name2" params:"name2" header:"Name2"` + ClassWithDefault int `json:"class2" xml:"Class2" form:"class2" cookie:"class2" query:"class2" params:"class2" header:"Class2"` + } + + withValues := func(t *testing.T, actionFn func(c Ctx, testStruct *TestStruct) error) { + t.Helper() + + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + testStruct := new(TestStruct) + + require.NoError(t, actionFn(c, testStruct)) + require.Equal(t, "foo", testStruct.Name) + require.Equal(t, 111, testStruct.Class) + require.Equal(t, "bar", testStruct.NameWithDefault) + require.Equal(t, 222, testStruct.ClassWithDefault) + } + + t.Run("BodyParser:xml", func(t *testing.T) { + t.Parallel() + withValues(t, func(c Ctx, testStruct *TestStruct) error { + c.Request().Header.SetContentType(MIMEApplicationXML) + c.Request().SetBody([]byte(`foo111bar222`)) + return c.Bind().Body(testStruct) + }) + }) + t.Run("BodyParser:form", func(t *testing.T) { + t.Parallel() + withValues(t, func(c Ctx, testStruct *TestStruct) error { + c.Request().Header.SetContentType(MIMEApplicationForm) + c.Request().SetBody([]byte(`name=foo&class=111&name2=bar&class2=222`)) + return c.Bind().Body(testStruct) + }) + }) + t.Run("BodyParser:json", func(t *testing.T) { + t.Parallel() + withValues(t, func(c Ctx, testStruct *TestStruct) error { + c.Request().Header.SetContentType(MIMEApplicationJSON) + c.Request().SetBody([]byte(`{"name":"foo","class":111,"name2":"bar","class2":222}`)) + return c.Bind().Body(testStruct) + }) + }) + t.Run("BodyParser:multiform", func(t *testing.T) { + t.Parallel() + withValues(t, func(c Ctx, testStruct *TestStruct) error { + body := []byte("--b\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\nfoo\r\n--b\r\nContent-Disposition: form-data; name=\"class\"\r\n\r\n111\r\n--b\r\nContent-Disposition: form-data; name=\"name2\"\r\n\r\nbar\r\n--b\r\nContent-Disposition: form-data; name=\"class2\"\r\n\r\n222\r\n--b--") + c.Request().SetBody(body) + c.Request().Header.SetContentType(MIMEMultipartForm + `;boundary="b"`) + c.Request().Header.SetContentLength(len(body)) + return c.Bind().Body(testStruct) + }) + }) + t.Run("CookieParser", func(t *testing.T) { + t.Parallel() + withValues(t, func(c Ctx, testStruct *TestStruct) error { + c.Request().Header.Set("Cookie", "name=foo;name2=bar;class=111;class2=222") + return c.Bind().Cookie(testStruct) + }) + }) + t.Run("QueryParser", func(t *testing.T) { + t.Parallel() + withValues(t, func(c Ctx, testStruct *TestStruct) error { + c.Request().URI().SetQueryString("name=foo&name2=bar&class=111&class2=222") + return c.Bind().Query(testStruct) + }) + }) + t.Run("ParamsParser", func(t *testing.T) { + t.Skip("ParamsParser is not ready for v3") + // t.Parallel() + // withValues(t, func(c Ctx, testStruct *TestStruct) error { + // c.route = &Route{Params: []string{"name", "name2", "class", "class2"}} + // c.values = [30]string{"foo", "bar", "111", "222"} + // return c.ParamsParser(testStruct) + // }) + }) + t.Run("ReqHeaderParser", func(t *testing.T) { + t.Parallel() + withValues(t, func(c Ctx, testStruct *TestStruct) error { + c.Request().Header.Add("name", "foo") + c.Request().Header.Add("name2", "bar") + c.Request().Header.Add("class", "111") + c.Request().Header.Add("class2", "222") + return c.Bind().Header(testStruct) + }) + }) +} + // go test -run Test_Ctx_Get func Test_Ctx_Get(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAcceptCharset, "utf-8, iso-8859-1;q=0.5") c.Request().Header.Set(HeaderReferer, "Monster") @@ -1370,7 +1465,7 @@ func Test_Ctx_Get(t *testing.T) { func Test_Ctx_Host(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") require.Equal(t, "google.com", c.Host()) @@ -1382,7 +1477,7 @@ func Test_Ctx_Host_UntrustedProxy(t *testing.T) { // Don't trust any proxy { app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") require.Equal(t, "google.com", c.Host()) @@ -1391,7 +1486,7 @@ func Test_Ctx_Host_UntrustedProxy(t *testing.T) { // Trust to specific proxy list { app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.0", "0.8.0.1"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") require.Equal(t, "google.com", c.Host()) @@ -1404,7 +1499,7 @@ func Test_Ctx_Host_TrustedProxy(t *testing.T) { t.Parallel() { app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "0.8.0.1"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") require.Equal(t, "google1.com", c.Host()) @@ -1417,7 +1512,7 @@ func Test_Ctx_Host_TrustedProxyRange(t *testing.T) { t.Parallel() app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0/30"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") require.Equal(t, "google1.com", c.Host()) @@ -1429,7 +1524,7 @@ func Test_Ctx_Host_UntrustedProxyRange(t *testing.T) { t.Parallel() app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"1.0.0.0/30"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") require.Equal(t, "google.com", c.Host()) @@ -1439,7 +1534,7 @@ func Test_Ctx_Host_UntrustedProxyRange(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Host -benchmem -count=4 func Benchmark_Ctx_Host(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") var host string b.ReportAllocs() @@ -1456,7 +1551,7 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) { { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) require.True(t, c.IsProxyTrusted()) } @@ -1464,7 +1559,7 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) { app := New(Config{ EnableTrustedProxyCheck: false, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.True(t, c.IsProxyTrusted()) } @@ -1472,7 +1567,7 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) { app := New(Config{ EnableTrustedProxyCheck: true, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.False(t, c.IsProxyTrusted()) } { @@ -1481,7 +1576,7 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) { TrustedProxies: []string{}, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.False(t, c.IsProxyTrusted()) } { @@ -1492,7 +1587,7 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) { "127.0.0.1", }, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.False(t, c.IsProxyTrusted()) } { @@ -1503,7 +1598,7 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) { "127.0.0.1/8", }, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.False(t, c.IsProxyTrusted()) } { @@ -1514,7 +1609,7 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) { "0.0.0.0", }, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.True(t, c.IsProxyTrusted()) } { @@ -1525,7 +1620,7 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) { "0.0.0.1/31", }, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.True(t, c.IsProxyTrusted()) } { @@ -1536,7 +1631,7 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) { "0.0.0.1/31junk", }, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.False(t, c.IsProxyTrusted()) } } @@ -1545,7 +1640,7 @@ func Test_Ctx_IsProxyTrusted(t *testing.T) { func Test_Ctx_Hostname(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") require.Equal(t, "google.com", c.Hostname()) @@ -1557,7 +1652,7 @@ func Test_Ctx_Hostname(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Hostname -benchmem -count=4 func Benchmark_Ctx_Hostname(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com:8080/test") var hostname string b.ReportAllocs() @@ -1568,7 +1663,7 @@ func Benchmark_Ctx_Hostname(b *testing.B) { // Trust to specific proxy list { app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.0", "0.8.0.1"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") require.Equal(b, "google.com", hostname) @@ -1581,7 +1676,7 @@ func Test_Ctx_Hostname_TrustedProxy(t *testing.T) { t.Parallel() { app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "0.8.0.1"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") require.Equal(t, "google1.com", c.Hostname()) @@ -1594,7 +1689,7 @@ func Test_Ctx_Hostname_TrustedProxy_Multiple(t *testing.T) { t.Parallel() { app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "0.8.0.1"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com, google2.com") require.Equal(t, "google1.com", c.Hostname()) @@ -1607,7 +1702,7 @@ func Test_Ctx_Hostname_TrustedProxyRange(t *testing.T) { t.Parallel() app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0/30"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") require.Equal(t, "google1.com", c.Hostname()) @@ -1619,7 +1714,7 @@ func Test_Ctx_Hostname_UntrustedProxyRange(t *testing.T) { t.Parallel() app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"1.0.0.0/30"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") require.Equal(t, "google.com", c.Hostname()) @@ -1630,7 +1725,7 @@ func Test_Ctx_Hostname_UntrustedProxyRange(t *testing.T) { func Test_Ctx_Port(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.Equal(t, "0", c.Port()) } @@ -1658,7 +1753,7 @@ func Test_Ctx_IP(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) // default behavior will return the remote IP from the stack require.Equal(t, "0.0.0.0", c.IP()) @@ -1677,7 +1772,7 @@ func Test_Ctx_IP_ProxyHeader(t *testing.T) { for _, proxyHeaderName := range proxyHeaderNames { app := New(Config{ProxyHeader: proxyHeaderName}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(proxyHeaderName, "0.0.0.1") require.Equal(t, "0.0.0.1", c.IP()) @@ -1709,7 +1804,7 @@ func Test_Ctx_IP_ProxyHeader_With_IP_Validation(t *testing.T) { for _, proxyHeaderName := range proxyHeaderNames { app := New(Config{EnableIPValidation: true, ProxyHeader: proxyHeaderName}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) // when proxy header & validation is enabled and the value is a valid IP, we return it c.Request().Header.Set(proxyHeaderName, "0.0.0.1") @@ -1737,7 +1832,7 @@ func Test_Ctx_IP_ProxyHeader_With_IP_Validation(t *testing.T) { func Test_Ctx_IP_UntrustedProxy(t *testing.T) { t.Parallel() app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.1"}, ProxyHeader: HeaderXForwardedFor}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1") require.Equal(t, "0.0.0.0", c.IP()) } @@ -1746,7 +1841,7 @@ func Test_Ctx_IP_UntrustedProxy(t *testing.T) { func Test_Ctx_IP_TrustedProxy(t *testing.T) { t.Parallel() app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0"}, ProxyHeader: HeaderXForwardedFor}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1") require.Equal(t, "0.0.0.1", c.IP()) } @@ -1755,7 +1850,7 @@ func Test_Ctx_IP_TrustedProxy(t *testing.T) { func Test_Ctx_IPs(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) // normal happy path test case c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, 127.0.0.2, 127.0.0.3") @@ -1791,7 +1886,7 @@ func Test_Ctx_IPs(t *testing.T) { func Test_Ctx_IPs_With_IP_Validation(t *testing.T) { t.Parallel() app := New(Config{EnableIPValidation: true}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) // normal happy path test case c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, 127.0.0.2, 127.0.0.3") @@ -1827,7 +1922,7 @@ func Test_Ctx_IPs_With_IP_Validation(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_IPs -benchmem -count=4 func Benchmark_Ctx_IPs(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.1") var res []string b.ReportAllocs() @@ -1840,7 +1935,7 @@ func Benchmark_Ctx_IPs(b *testing.B) { func Benchmark_Ctx_IPs_v6(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) c.Request().Header.Set(HeaderXForwardedFor, "f037:825e:eadb:1b7b:1667:6f0a:5356:f604, invalid, 2345:0425:2CA1::0567:5673:23b5") var res []string @@ -1854,7 +1949,7 @@ func Benchmark_Ctx_IPs_v6(b *testing.B) { func Benchmark_Ctx_IPs_With_IP_Validation(b *testing.B) { app := New(Config{EnableIPValidation: true}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.1") var res []string b.ReportAllocs() @@ -1867,7 +1962,7 @@ func Benchmark_Ctx_IPs_With_IP_Validation(b *testing.B) { func Benchmark_Ctx_IPs_v6_With_IP_Validation(b *testing.B) { app := New(Config{EnableIPValidation: true}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) c.Request().Header.Set(HeaderXForwardedFor, "2345:0425:2CA1:0000:0000:0567:5673:23b5, invalid, 2345:0425:2CA1::0567:5673:23b5") var res []string @@ -1881,7 +1976,7 @@ func Benchmark_Ctx_IPs_v6_With_IP_Validation(b *testing.B) { func Benchmark_Ctx_IP_With_ProxyHeader(b *testing.B) { app := New(Config{ProxyHeader: HeaderXForwardedFor}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1") var res string b.ReportAllocs() @@ -1894,7 +1989,7 @@ func Benchmark_Ctx_IP_With_ProxyHeader(b *testing.B) { func Benchmark_Ctx_IP_With_ProxyHeader_and_IP_Validation(b *testing.B) { app := New(Config{ProxyHeader: HeaderXForwardedFor, EnableIPValidation: true}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1") var res string b.ReportAllocs() @@ -1907,7 +2002,7 @@ func Benchmark_Ctx_IP_With_ProxyHeader_and_IP_Validation(b *testing.B) { func Benchmark_Ctx_IP(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request() var res string b.ReportAllocs() @@ -1922,7 +2017,7 @@ func Benchmark_Ctx_IP(b *testing.B) { func Test_Ctx_Is(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderContentType, MIMETextHTML+"; boundary=something") require.True(t, c.Is(".html")) @@ -1956,7 +2051,7 @@ func Test_Ctx_Is(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Is -benchmem -count=4 func Benchmark_Ctx_Is(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderContentType, MIMEApplicationJSON) var res bool @@ -2037,7 +2132,7 @@ func Test_Ctx_Method(t *testing.T) { fctx := &fasthttp.RequestCtx{} fctx.Request.Header.SetMethod(MethodGet) app := New() - c := app.NewCtx(fctx) + c := app.AcquireCtx(fctx) require.Equal(t, MethodGet, c.Method()) c.Method(MethodPost) @@ -2193,7 +2288,7 @@ func Benchmark_Ctx_MultipartForm(b *testing.B) { func Test_Ctx_OriginalURL(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.SetRequestURI("http://google.com/test?search=demo") require.Equal(t, "http://google.com/test?search=demo", c.OriginalURL()) @@ -2272,7 +2367,7 @@ func Test_Ctx_Params_Case_Sensitive(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Params -benchmem -count=4 func Benchmark_Ctx_Params(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.route = &Route{ Params: []string{ @@ -2323,7 +2418,7 @@ func Test_Ctx_Protocol(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.Equal(t, "HTTP/1.1", c.Protocol()) @@ -2335,7 +2430,7 @@ func Test_Ctx_Protocol(t *testing.T) { func Benchmark_Ctx_Protocol(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) var res string b.ReportAllocs() @@ -2354,7 +2449,7 @@ func Test_Ctx_Scheme(t *testing.T) { freq := &fasthttp.RequestCtx{} freq.Request.Header.Set("X-Forwarded", "invalid") - c := app.NewCtx(freq) + c := app.AcquireCtx(freq) c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) require.Equal(t, schemeHTTPS, c.Scheme()) @@ -2386,7 +2481,7 @@ func Test_Ctx_Scheme(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Scheme -benchmem -count=4 func Benchmark_Ctx_Scheme(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) var res string b.ReportAllocs() @@ -2401,7 +2496,7 @@ func Benchmark_Ctx_Scheme(b *testing.B) { func Test_Ctx_Scheme_TrustedProxy(t *testing.T) { t.Parallel() app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) require.Equal(t, schemeHTTPS, c.Scheme()) @@ -2426,7 +2521,7 @@ func Test_Ctx_Scheme_TrustedProxy(t *testing.T) { func Test_Ctx_Scheme_TrustedProxyRange(t *testing.T) { t.Parallel() app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0/30"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) require.Equal(t, schemeHTTPS, c.Scheme()) @@ -2451,7 +2546,7 @@ func Test_Ctx_Scheme_TrustedProxyRange(t *testing.T) { func Test_Ctx_Scheme_UntrustedProxyRange(t *testing.T) { t.Parallel() app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"1.1.1.1/30"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) require.Equal(t, schemeHTTP, c.Scheme()) @@ -2476,7 +2571,7 @@ func Test_Ctx_Scheme_UntrustedProxyRange(t *testing.T) { func Test_Ctx_Scheme_UnTrustedProxy(t *testing.T) { t.Parallel() app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.1"}}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) require.Equal(t, schemeHTTP, c.Scheme()) @@ -2501,7 +2596,7 @@ func Test_Ctx_Scheme_UnTrustedProxy(t *testing.T) { func Test_Ctx_Query(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=20") require.Equal(t, "john", c.Query("search")) @@ -2517,7 +2612,7 @@ func Test_Ctx_Query(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Query -benchmem -count=4 func Benchmark_Ctx_Query(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=8") var res string b.ReportAllocs() @@ -2532,7 +2627,7 @@ func Benchmark_Ctx_Query(b *testing.B) { func Test_Ctx_QuerySignedInt(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=8") // int @@ -2579,7 +2674,7 @@ func Test_Ctx_QuerySignedInt(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_QuerySignedInt -benchmem -count=4 func Benchmark_Ctx_QuerySignedInt(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=8") var res int b.ReportAllocs() @@ -2594,7 +2689,7 @@ func Benchmark_Ctx_QuerySignedInt(b *testing.B) { func Test_Ctx_QueryBoundarySignedInt(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) var q string // int @@ -2639,7 +2734,7 @@ func Test_Ctx_QueryBoundarySignedInt(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_QueryBoundarySignedInt -benchmem -count=4 func Benchmark_Ctx_QueryBoundarySignedInt(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=8") var res int b.ReportAllocs() @@ -2654,7 +2749,7 @@ func Benchmark_Ctx_QueryBoundarySignedInt(b *testing.B) { func Test_Ctx_QueryUnsignedInt(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=8") // uint @@ -2701,7 +2796,7 @@ func Test_Ctx_QueryUnsignedInt(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_QueryUnsignedInt -benchmem -count=4 func Benchmark_Ctx_QueryUnsignedInt(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=8") var res uint b.ReportAllocs() @@ -2716,7 +2811,7 @@ func Benchmark_Ctx_QueryUnsignedInt(b *testing.B) { func Test_Ctx_QueryBoundaryUnsignedInt(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) var q string // uint @@ -2761,7 +2856,7 @@ func Test_Ctx_QueryBoundaryUnsignedInt(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_QueryBoundaryUnsignedInt -benchmem -count=4 func Benchmark_Ctx_QueryBoundaryUnsignedInt(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=8") var res uint b.ReportAllocs() @@ -2776,7 +2871,7 @@ func Benchmark_Ctx_QueryBoundaryUnsignedInt(b *testing.B) { func Test_Ctx_QueryFloat(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("name=alex&amount=32.23&id=") @@ -2800,7 +2895,7 @@ func Test_Ctx_QueryFloat(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_QueryFloat -benchmem -count=4 func Benchmark_Ctx_QueryFloat(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=8") var res float32 b.ReportAllocs() @@ -2815,7 +2910,7 @@ func Benchmark_Ctx_QueryFloat(b *testing.B) { func Test_Ctx_QueryBool(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("name=alex&want_pizza=false&id=") @@ -2830,7 +2925,7 @@ func Test_Ctx_QueryBool(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_QueryBool -benchmem -count=4 func Benchmark_Ctx_QueryBool(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=8") var res bool b.ReportAllocs() @@ -2845,7 +2940,7 @@ func Benchmark_Ctx_QueryBool(b *testing.B) { func Test_Ctx_QueryString(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("name=alex&amount=32.23&id=") @@ -2860,7 +2955,7 @@ func Test_Ctx_QueryString(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_QueryString -benchmem -count=4 func Benchmark_Ctx_QueryString(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=8") var res string b.ReportAllocs() @@ -2875,7 +2970,7 @@ func Benchmark_Ctx_QueryString(b *testing.B) { func Test_Ctx_QueryBytes(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("name=alex&amount=32.23&id=") @@ -2890,7 +2985,7 @@ func Test_Ctx_QueryBytes(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_QueryBytes -benchmem -count=4 func Benchmark_Ctx_QueryBytes(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=8") var res []byte b.ReportAllocs() @@ -2905,7 +3000,7 @@ func Benchmark_Ctx_QueryBytes(b *testing.B) { func Test_Ctx_QueryWithoutGenericDataType(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("name=alex&amount=32.23&isAgent=true&id=32") @@ -2944,7 +3039,7 @@ func Test_Ctx_QueryWithoutGenericDataType(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_QueryWithoutGenericDataType -benchmem -count=4 func Benchmark_Ctx_QueryWithoutGenericDataType(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=8") var res int b.ReportAllocs() @@ -2959,7 +3054,7 @@ func Benchmark_Ctx_QueryWithoutGenericDataType(b *testing.B) { func Test_Ctx_Range(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) testRange := func(header string, ranges ...RangeSet) { c.Request().Header.Set(HeaderRange, header) @@ -2992,7 +3087,7 @@ func Test_Ctx_Range(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Range -benchmem -count=4 func Benchmark_Ctx_Range(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) testCases := []struct { @@ -3036,7 +3131,7 @@ func Test_Ctx_Route(t *testing.T) { require.NoError(t, err, "app.Test(req)") require.Equal(t, StatusOK, resp.StatusCode, "Status code") - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.Equal(t, "/", c.Route().Path) require.Equal(t, MethodGet, c.Route().Method) @@ -3149,7 +3244,7 @@ func Test_Ctx_SaveFileToStorage(t *testing.T) { func Test_Ctx_Secure(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) // TODO Add TLS conn require.False(t, c.Secure()) @@ -3159,7 +3254,7 @@ func Test_Ctx_Secure(t *testing.T) { func Test_Ctx_Stale(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.True(t, c.Stale()) } @@ -3168,7 +3263,7 @@ func Test_Ctx_Stale(t *testing.T) { func Test_Ctx_Subdomains(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetHost("john.doe.is.awesome.google.com") require.Equal(t, []string{"john", "doe"}, c.Subdomains(4)) @@ -3180,7 +3275,7 @@ func Test_Ctx_Subdomains(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Subdomains -benchmem -count=4 func Benchmark_Ctx_Subdomains(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://john.doe.google.com") var res []string @@ -3196,7 +3291,7 @@ func Benchmark_Ctx_Subdomains(b *testing.B) { func Test_Ctx_ClearCookie(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderCookie, "john=doe") c.ClearCookie("john") @@ -3213,7 +3308,7 @@ func Test_Ctx_ClearCookie(t *testing.T) { func Test_Ctx_Download(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.NoError(t, c.Download("ctx.go", "Awesome File!")) @@ -3250,7 +3345,7 @@ func Test_Ctx_SendFile(t *testing.T) { require.NoError(t, err) // simple test case - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err = c.SendFile("ctx.go") // check expectation require.NoError(t, err) @@ -3259,7 +3354,7 @@ func Test_Ctx_SendFile(t *testing.T) { app.ReleaseCtx(c) // test with custom error code - c = app.NewCtx(&fasthttp.RequestCtx{}) + c = app.AcquireCtx(&fasthttp.RequestCtx{}) err = c.Status(StatusInternalServerError).SendFile("ctx.go") // check expectation require.NoError(t, err) @@ -3268,7 +3363,7 @@ func Test_Ctx_SendFile(t *testing.T) { app.ReleaseCtx(c) // test not modified - c = app.NewCtx(&fasthttp.RequestCtx{}) + c = app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderIfModifiedSince, fI.ModTime().Format(time.RFC1123)) err = c.SendFile("ctx.go") // check expectation @@ -3362,7 +3457,7 @@ func Test_Ctx_SendFile_RestoreOriginalURL(t *testing.T) { func Test_Ctx_JSON(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.Error(t, c.JSON(complex(1, 1))) @@ -3403,7 +3498,7 @@ func Test_Ctx_JSON(t *testing.T) { return []byte(`["custom","json"]`), nil }, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.JSON(Map{ // map has no order "Name": "Grame", @@ -3418,7 +3513,7 @@ func Test_Ctx_JSON(t *testing.T) { // go test -run=^$ -bench=Benchmark_Ctx_JSON -benchmem -count=4 func Benchmark_Ctx_JSON(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type SomeStruct struct { Name string @@ -3442,7 +3537,7 @@ func Benchmark_Ctx_JSON(b *testing.B) { func Benchmark_Ctx_JSON_Ctype(b *testing.B) { app := New() // TODO: Check extra allocs because of the interface stuff - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed type SomeStruct struct { Name string Age uint8 @@ -3466,7 +3561,7 @@ func Benchmark_Ctx_JSON_Ctype(b *testing.B) { func Test_Ctx_JSONP(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.Error(t, c.JSONP(complex(1, 1))) @@ -3494,7 +3589,7 @@ func Test_Ctx_JSONP(t *testing.T) { return []byte(`["custom","json"]`), nil }, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.JSONP(Map{ // map has no order "Name": "Grame", @@ -3509,7 +3604,7 @@ func Test_Ctx_JSONP(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_JSONP -benchmem -count=4 func Benchmark_Ctx_JSONP(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed type SomeStruct struct { Name string @@ -3534,7 +3629,7 @@ func Benchmark_Ctx_JSONP(b *testing.B) { func Test_Ctx_XML(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed require.Error(t, c.JSON(complex(1, 1))) @@ -3571,7 +3666,7 @@ func Test_Ctx_XML(t *testing.T) { return []byte(`xml`), nil }, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) type xmlResult struct { XMLName xml.Name `xml:"Users"` @@ -3593,7 +3688,7 @@ func Test_Ctx_XML(t *testing.T) { // go test -run=^$ -bench=Benchmark_Ctx_XML -benchmem -count=4 func Benchmark_Ctx_XML(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed type SomeStruct struct { Name string `xml:"Name"` Age uint8 `xml:"Age"` @@ -3617,7 +3712,7 @@ func Benchmark_Ctx_XML(b *testing.B) { func Test_Ctx_Links(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Links() require.Equal(t, "", string(c.Response().Header.Peek(HeaderLink))) @@ -3632,7 +3727,7 @@ func Test_Ctx_Links(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Links -benchmem -count=4 func Benchmark_Ctx_Links(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -3648,7 +3743,7 @@ func Benchmark_Ctx_Links(b *testing.B) { func Test_Ctx_Location(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Location("http://example.com") require.Equal(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) @@ -3690,7 +3785,7 @@ func Test_Ctx_Next_Error(t *testing.T) { func Test_Ctx_Render(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.Render("./.github/testdata/index.tmpl", Map{ "Title": "Hello, World!", @@ -3711,7 +3806,7 @@ func Test_Ctx_RenderWithoutLocals(t *testing.T) { app := New(Config{ PassLocalsToViews: false, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Locals("Title", "Hello, World!") @@ -3728,7 +3823,7 @@ func Test_Ctx_RenderWithLocals(t *testing.T) { t.Run("EmptyBind", func(t *testing.T) { t.Parallel() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Locals("Title", "Hello, World!") err := c.Render("./.github/testdata/index.tmpl", Map{}) @@ -3739,7 +3834,7 @@ func Test_Ctx_RenderWithLocals(t *testing.T) { t.Run("NilBind", func(t *testing.T) { t.Parallel() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Locals("Title", "Hello, World!") err := c.Render("./.github/testdata/index.tmpl", nil) @@ -3753,7 +3848,7 @@ func Test_Ctx_RenderWithBindVars(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.BindVars(Map{ "Title": "Hello, World!", @@ -3773,7 +3868,7 @@ func Test_Ctx_RenderWithBindVars(t *testing.T) { func Test_Ctx_RenderWithOverwrittenBind(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.BindVars(Map{ "Title": "Hello, World!", @@ -3798,7 +3893,7 @@ func Test_Ctx_RenderWithBindVarsLocals(t *testing.T) { PassLocalsToViews: true, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.BindVars(Map{ "Title": "Hello, World!", @@ -3824,7 +3919,7 @@ func Test_Ctx_RenderWithLocalsAndBinding(t *testing.T) { PassLocalsToViews: true, Views: engine, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Locals("Title", "This is a test.") @@ -3844,7 +3939,7 @@ func Benchmark_Ctx_RenderWithLocalsAndBindVars(b *testing.B) { PassLocalsToViews: true, Views: engine, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err = c.BindVars(Map{ "Title": "Hello, World!", @@ -3871,7 +3966,7 @@ func Benchmark_Ctx_RenderLocals(b *testing.B) { PassLocalsToViews: true, }) app.config.Views = engine - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Locals("Title", "Hello, World!") @@ -3892,7 +3987,7 @@ func Benchmark_Ctx_RenderBindVars(b *testing.B) { require.NoError(b, err) app := New() app.config.Views = engine - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err = c.BindVars(Map{ "Title": "Hello, World!", @@ -4011,7 +4106,7 @@ func Test_Ctx_Render_Engine(t *testing.T) { require.NoError(t, engine.Load()) app := New() app.config.Views = engine - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.Render("index.tmpl", Map{ "Title": "Hello, World!", @@ -4027,7 +4122,7 @@ func Test_Ctx_Render_Engine_With_View_Layout(t *testing.T) { require.NoError(t, engine.Load()) app := New(Config{ViewsLayout: "main.tmpl"}) app.config.Views = engine - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.Render("index.tmpl", Map{ "Title": "Hello, World!", @@ -4043,7 +4138,7 @@ func Benchmark_Ctx_Render_Engine(b *testing.B) { require.NoError(b, err) app := New() app.config.Views = engine - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) b.ReportAllocs() b.ResetTimer() @@ -4059,7 +4154,7 @@ func Benchmark_Ctx_Render_Engine(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_Get_Location_From_Route -benchmem -count=4 func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed app.Get("/user/:name", func(c Ctx) error { return c.SendString(c.Params("name")) @@ -4082,7 +4177,7 @@ func Test_Ctx_Get_Location_From_Route_name(t *testing.T) { t.Run("case insensitive", func(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) app.Get("/user/:name", func(c Ctx) error { return c.SendString(c.Params("name")) }).Name("User") @@ -4099,7 +4194,7 @@ func Test_Ctx_Get_Location_From_Route_name(t *testing.T) { t.Run("case sensitive", func(t *testing.T) { t.Parallel() app := New(Config{CaseSensitive: true}) - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) app.Get("/user/:name", func(c Ctx) error { return c.SendString(c.Params("name")) @@ -4119,7 +4214,7 @@ func Test_Ctx_Get_Location_From_Route_name(t *testing.T) { func Test_Ctx_Get_Location_From_Route_name_Optional_greedy(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) app.Get("/:phone/*/send/*", func(c Ctx) error { return c.SendString("Phone: " + c.Params("phone") + "\nFirst Param: " + c.Params("*1") + "\nSecond Param: " + c.Params("*2")) @@ -4138,7 +4233,7 @@ func Test_Ctx_Get_Location_From_Route_name_Optional_greedy(t *testing.T) { func Test_Ctx_Get_Location_From_Route_name_Optional_greedy_one_param(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) app.Get("/:phone/*/send", func(c Ctx) error { return c.SendString("Phone: " + c.Params("phone") + "\nFirst Param: " + c.Params("*1")) @@ -4165,7 +4260,7 @@ func Test_Ctx_Render_Engine_Error(t *testing.T) { t.Parallel() app := New() app.config.Views = errorTemplateEngine{} - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.Render("index.tmpl", nil) require.Error(t, err) @@ -4189,7 +4284,7 @@ func Test_Ctx_Render_Go_Template(t *testing.T) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err = c.Render(file.Name(), nil) require.NoError(t, err) @@ -4200,7 +4295,7 @@ func Test_Ctx_Render_Go_Template(t *testing.T) { func Test_Ctx_Send(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.NoError(t, c.Send([]byte("Hello, World"))) require.NoError(t, c.Send([]byte("Don't crash please"))) @@ -4211,7 +4306,7 @@ func Test_Ctx_Send(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Send -benchmem -count=4 func Benchmark_Ctx_Send(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) byt := []byte("Hello, World!") b.ReportAllocs() @@ -4229,7 +4324,7 @@ func Benchmark_Ctx_Send(b *testing.B) { func Test_Ctx_SendStatus(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.SendStatus(415) require.NoError(t, err) @@ -4241,7 +4336,7 @@ func Test_Ctx_SendStatus(t *testing.T) { func Test_Ctx_SendString(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.SendString("Don't crash please") require.NoError(t, err) @@ -4252,7 +4347,7 @@ func Test_Ctx_SendString(t *testing.T) { func Test_Ctx_SendStream(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.SendStream(bytes.NewReader([]byte("Don't crash please"))) require.NoError(t, err) @@ -4271,7 +4366,7 @@ func Test_Ctx_SendStream(t *testing.T) { func Test_Ctx_Set(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Set("X-1", "1") c.Set("X-2", "2") @@ -4286,7 +4381,7 @@ func Test_Ctx_Set(t *testing.T) { func Test_Ctx_Set_Splitter(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Set("Location", "foo\r\nSet-Cookie:%20SESSIONID=MaliciousValue\r\n") h := string(c.Response().Header.Peek("Location")) @@ -4300,7 +4395,7 @@ func Test_Ctx_Set_Splitter(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Set -benchmem -count=4 func Benchmark_Ctx_Set(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) val := "1431-15132-3423" b.ReportAllocs() @@ -4314,7 +4409,7 @@ func Benchmark_Ctx_Set(b *testing.B) { func Test_Ctx_Status(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Status(400) require.Equal(t, 400, c.Response().StatusCode()) @@ -4328,7 +4423,7 @@ func Test_Ctx_Status(t *testing.T) { func Test_Ctx_Type(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Type(".json") require.Equal(t, "application/json", string(c.Response().Header.Peek("Content-Type"))) @@ -4346,7 +4441,7 @@ func Test_Ctx_Type(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Type -benchmem -count=4 func Benchmark_Ctx_Type(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) b.ReportAllocs() b.ResetTimer() @@ -4359,7 +4454,7 @@ func Benchmark_Ctx_Type(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_Type_Charset -benchmem -count=4 func Benchmark_Ctx_Type_Charset(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -4373,7 +4468,7 @@ func Benchmark_Ctx_Type_Charset(b *testing.B) { func Test_Ctx_Vary(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Vary("Origin") c.Vary("User-Agent") @@ -4384,7 +4479,7 @@ func Test_Ctx_Vary(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Vary -benchmem -count=4 func Benchmark_Ctx_Vary(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -4397,7 +4492,7 @@ func Benchmark_Ctx_Vary(b *testing.B) { func Test_Ctx_Write(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) _, err := c.Write([]byte("Hello, ")) require.NoError(t, err) @@ -4409,7 +4504,7 @@ func Test_Ctx_Write(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Write -benchmem -count=4 func Benchmark_Ctx_Write(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) byt := []byte("Hello, World!") b.ReportAllocs() @@ -4426,7 +4521,7 @@ func Benchmark_Ctx_Write(b *testing.B) { func Test_Ctx_Writef(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) world := "World!" _, err := c.Writef("Hello, %s", world) @@ -4437,7 +4532,7 @@ func Test_Ctx_Writef(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Writef -benchmem -count=4 func Benchmark_Ctx_Writef(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed world := "World!" b.ReportAllocs() @@ -4454,7 +4549,7 @@ func Benchmark_Ctx_Writef(b *testing.B) { func Test_Ctx_WriteString(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) _, err := c.WriteString("Hello, ") require.NoError(t, err) @@ -4467,7 +4562,7 @@ func Test_Ctx_WriteString(t *testing.T) { func Test_Ctx_XHR(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXRequestedWith, "XMLHttpRequest") require.True(t, c.XHR()) @@ -4476,7 +4571,7 @@ func Test_Ctx_XHR(t *testing.T) { // go test -run=^$ -bench=Benchmark_Ctx_XHR -benchmem -count=4 func Benchmark_Ctx_XHR(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXRequestedWith, "XMLHttpRequest") var equal bool @@ -4491,7 +4586,7 @@ func Benchmark_Ctx_XHR(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_SendString_B -benchmem -count=4 func Benchmark_Ctx_SendString_B(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) body := "Hello, world!" b.ReportAllocs() @@ -4509,7 +4604,7 @@ func Benchmark_Ctx_SendString_B(b *testing.B) { func Test_Ctx_Queries(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().SetBody([]byte(``)) c.Request().Header.SetContentType("") @@ -4557,7 +4652,7 @@ func Test_Ctx_Queries(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Queries -benchmem -count=4 func Benchmark_Ctx_Queries(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) b.ReportAllocs() b.ResetTimer() @@ -4626,7 +4721,7 @@ func Benchmark_Ctx_BodyStreamWriter(b *testing.B) { func Test_Ctx_String(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) require.Equal(t, "#0000000000000000 - 0.0.0.0:0 <-> 0.0.0.0:0 - GET http:///", c.String()) } @@ -4635,7 +4730,7 @@ func Test_Ctx_String(t *testing.T) { func Benchmark_Ctx_String(b *testing.B) { var str string app := New() - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) b.ReportAllocs() b.ResetTimer() @@ -4736,7 +4831,7 @@ func Test_Ctx_IsFromLocal_X_Forwarded(t *testing.T) { // Test unset X-Forwarded-For header. { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) // fasthttp returns "0.0.0.0" as IP as there is no remote address. require.Equal(t, "0.0.0.0", c.IP()) require.False(t, c.IsFromLocal()) @@ -4744,7 +4839,7 @@ func Test_Ctx_IsFromLocal_X_Forwarded(t *testing.T) { // Test when setting X-Forwarded-For header to localhost "127.0.0.1" { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1") defer app.ReleaseCtx(c) require.False(t, c.IsFromLocal()) @@ -4752,7 +4847,7 @@ func Test_Ctx_IsFromLocal_X_Forwarded(t *testing.T) { // Test when setting X-Forwarded-For header to localhost "::1" { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "::1") defer app.ReleaseCtx(c) require.False(t, c.IsFromLocal()) @@ -4760,7 +4855,7 @@ func Test_Ctx_IsFromLocal_X_Forwarded(t *testing.T) { // Test when setting X-Forwarded-For to full localhost IPv6 address "0:0:0:0:0:0:0:1" { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "0:0:0:0:0:0:0:1") defer app.ReleaseCtx(c) require.False(t, c.IsFromLocal()) @@ -4768,7 +4863,7 @@ func Test_Ctx_IsFromLocal_X_Forwarded(t *testing.T) { // Test for a random IP address. { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "93.46.8.90") require.False(t, c.IsFromLocal()) @@ -4793,7 +4888,7 @@ func Test_Ctx_IsFromLocal_RemoteAddr(t *testing.T) { app := New() fastCtx := &fasthttp.RequestCtx{} fastCtx.SetRemoteAddr(localIPv4) - c := app.NewCtx(fastCtx) + c := app.AcquireCtx(fastCtx) require.Equal(t, "127.0.0.1", c.IP()) require.True(t, c.IsFromLocal()) @@ -4803,7 +4898,7 @@ func Test_Ctx_IsFromLocal_RemoteAddr(t *testing.T) { app := New() fastCtx := &fasthttp.RequestCtx{} fastCtx.SetRemoteAddr(localIPv6) - c := app.NewCtx(fastCtx) + c := app.AcquireCtx(fastCtx) require.Equal(t, "::1", c.IP()) require.True(t, c.IsFromLocal()) } @@ -4812,7 +4907,7 @@ func Test_Ctx_IsFromLocal_RemoteAddr(t *testing.T) { app := New() fastCtx := &fasthttp.RequestCtx{} fastCtx.SetRemoteAddr(localIPv6long) - c := app.NewCtx(fastCtx) + c := app.AcquireCtx(fastCtx) // fasthttp should return "::1" for "0:0:0:0:0:0:0:1". // otherwise IsFromLocal() will break. require.Equal(t, "::1", c.IP()) @@ -4823,7 +4918,7 @@ func Test_Ctx_IsFromLocal_RemoteAddr(t *testing.T) { app := New() fastCtx := &fasthttp.RequestCtx{} fastCtx.SetRemoteAddr(zeroIPv4) - c := app.NewCtx(fastCtx) + c := app.AcquireCtx(fastCtx) require.Equal(t, "0.0.0.0", c.IP()) require.False(t, c.IsFromLocal()) } @@ -4832,7 +4927,7 @@ func Test_Ctx_IsFromLocal_RemoteAddr(t *testing.T) { app := New() fastCtx := &fasthttp.RequestCtx{} fastCtx.SetRemoteAddr(someIPv4) - c := app.NewCtx(fastCtx) + c := app.AcquireCtx(fastCtx) require.Equal(t, "93.46.8.90", c.IP()) require.False(t, c.IsFromLocal()) } @@ -4841,7 +4936,7 @@ func Test_Ctx_IsFromLocal_RemoteAddr(t *testing.T) { app := New() fastCtx := &fasthttp.RequestCtx{} fastCtx.SetRemoteAddr(someIPv6) - c := app.NewCtx(fastCtx) + c := app.AcquireCtx(fastCtx) require.Equal(t, "2001:db8:85a3::8a2e:370:7334", c.IP()) require.False(t, c.IsFromLocal()) } @@ -4850,7 +4945,7 @@ func Test_Ctx_IsFromLocal_RemoteAddr(t *testing.T) { // go test -run Test_Ctx_extractIPsFromHeader -v func Test_Ctx_extractIPsFromHeader(t *testing.T) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("x-forwarded-for", "1.1.1.1,8.8.8.8 , /n, \n,1.1, a.c, 6.,6., , a,,42.118.81.169,10.0.137.108") ips := c.IPs() res := ips[len(ips)-2] @@ -4861,7 +4956,7 @@ func Test_Ctx_extractIPsFromHeader(t *testing.T) { func Test_Ctx_extractIPsFromHeader_EnableValidateIp(t *testing.T) { app := New() app.config.EnableIPValidation = true - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("x-forwarded-for", "1.1.1.1,8.8.8.8 , /n, \n,1.1, a.c, 6.,6., , a,,42.118.81.169,10.0.137.108") ips := c.IPs() res := ips[len(ips)-2] @@ -4872,7 +4967,7 @@ func Test_Ctx_extractIPsFromHeader_EnableValidateIp(t *testing.T) { func Test_Ctx_GetRespHeaders(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Set("test", "Hello, World πŸ‘‹!") c.Set("foo", "bar") @@ -4890,7 +4985,7 @@ func Test_Ctx_GetRespHeaders(t *testing.T) { func Benchmark_Ctx_GetRespHeaders(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Response().Header.Set("test", "Hello, World πŸ‘‹!") c.Response().Header.Set("foo", "bar") @@ -4915,7 +5010,7 @@ func Benchmark_Ctx_GetRespHeaders(b *testing.B) { func Test_Ctx_GetReqHeaders(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("test", "Hello, World πŸ‘‹!") c.Request().Header.Set("foo", "bar") @@ -4933,7 +5028,7 @@ func Test_Ctx_GetReqHeaders(t *testing.T) { func Benchmark_Ctx_GetReqHeaders(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("test", "Hello, World πŸ‘‹!") c.Request().Header.Set("foo", "bar") diff --git a/docs/api/app.md b/docs/api/app.md index 2e58a426d1..d44944f7dd 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -93,7 +93,7 @@ type Static struct { // Next defines a function to skip this middleware when returned true. // // Optional. Default: nil - Next func(c *Ctx) bool + Next func(c Ctx) bool } ``` @@ -205,7 +205,7 @@ func main() { app.Route("/test", func(api fiber.Router) { api.Get("/foo", handler).Name("foo") // /test/foo (name: test.foo) - api.Get("/bar", handler).Name("bar") // /test/bar (name: test.bar) + api.Get("/bar", handler).Name("bar") // /test/bar (name: test.bar) }, "test.") log.Fatal(app.Listen(":3000")) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 1b0661756c..3b12c7b35b 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1435,9 +1435,13 @@ app.Get("/", func(c fiber.Ctx) error { return err } - log.Println(p.Name) // john - log.Println(p.Pass) // doe - log.Println(p.Products) // [shoe, hat] + log.Println(p.Name) // john + log.Println(p.Pass) // doe + // fiber.Config{EnableSplittingOnParsers: false} - default + log.Println(p.Products) // ["shoe,hat"] + // fiber.Config{EnableSplittingOnParsers: true} + // log.Println(p.Products) // ["shoe", "hat"] + // ... }) @@ -1446,6 +1450,10 @@ app.Get("/", func(c fiber.Ctx) error { // curl "http://localhost:3000/?name=john&pass=doe&products=shoe,hat" ``` +:::info +For more parser settings please look here [Config](fiber.md#config) +::: + ## Range A struct containing the type and a slice of ranges will be returned. diff --git a/docs/api/middleware/cors.md b/docs/api/middleware/cors.md index 95645d8172..e01c51cc19 100644 --- a/docs/api/middleware/cors.md +++ b/docs/api/middleware/cors.md @@ -10,6 +10,8 @@ The middleware conforms to the `access-control-allow-origin` specification by pa For more control, `AllowOriginsFunc` can be used to programatically determine if an origin is allowed. If no match was found in `AllowOrigins` and if `AllowOriginsFunc` returns true then the 'access-control-allow-origin' response header is set to the 'origin' request header. +When defining your Origins make sure they are properly formatted. The middleware validates and normalizes the provided origins, ensuring they're in the correct format by checking for valid schemes (http or https), and removing any trailing slashes. + ## Signatures ```go @@ -56,18 +58,27 @@ app.Use(cors.New(cors.Config{ })) ``` +**Note: The following configuration is considered insecure and will result in a panic.** + +```go +app.Use(cors.New(cors.Config{ + AllowOrigins: "*", + AllowCredentials: true, +})) +``` + ## Config -| Property | Type | Description | Default | -|:-----------------|:---------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------| -| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | -| AllowOriginsFunc | `func(origin string) bool` | AllowOriginsFunc defines a function that will set the 'access-control-allow-origin' response header to the 'origin' request header when returned true. | `nil` | -| AllowOrigins | `string` | AllowOrigin defines a comma separated list of origins that may access the resource. | `"*"` | -| AllowMethods | `string` | AllowMethods defines a list of methods allowed when accessing the resource. This is used in response to a preflight request. | `"GET,POST,HEAD,PUT,DELETE,PATCH"` | -| AllowHeaders | `string` | AllowHeaders defines a list of request headers that can be used when making the actual request. This is in response to a preflight request. | `""` | -| AllowCredentials | `bool` | AllowCredentials indicates whether or not the response to the request can be exposed when the credentials flag is true. | `false` | -| ExposeHeaders | `string` | ExposeHeaders defines a whitelist headers that clients are allowed to access. | `""` | -| MaxAge | `int` | MaxAge indicates how long (in seconds) the results of a preflight request can be cached. If you pass MaxAge 0, Access-Control-Max-Age header will not be added and browser will use 5 seconds by default. To disable caching completely, pass MaxAge value negative. It will set the Access-Control-Max-Age header 0. | `0` | +| Property | Type | Description | Default | +|:-----------------|:---------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------| +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| AllowOriginsFunc | `func(origin string) bool` | AllowOriginsFunc defines a function that will set the 'access-control-allow-origin' response header to the 'origin' request header when returned true. This allows for dynamic evaluation of allowed origins. Note if AllowCredentials is true, wildcard origins will be not have the 'access-control-allow-credentials' header set to 'true'. | `nil` | +| AllowOrigins | `string` | AllowOrigin defines a comma separated list of origins that may access the resource. | `"*"` | +| AllowMethods | `string` | AllowMethods defines a list of methods allowed when accessing the resource. This is used in response to a preflight request. | `"GET,POST,HEAD,PUT,DELETE,PATCH"` | +| AllowHeaders | `string` | AllowHeaders defines a list of request headers that can be used when making the actual request. This is in response to a preflight request. | `""` | +| AllowCredentials | `bool` | AllowCredentials indicates whether or not the response to the request can be exposed when the credentials flag is true. When used as part of a response to a preflight request, this indicates whether or not the actual request can be made using credentials. Note: If true, AllowOrigins cannot be set to a wildcard ("*") to prevent security vulnerabilities. | `false` | +| ExposeHeaders | `string` | ExposeHeaders defines a whitelist headers that clients are allowed to access. | `""` | +| MaxAge | `int` | MaxAge indicates how long (in seconds) the results of a preflight request can be cached. If you pass MaxAge 0, Access-Control-Max-Age header will not be added and browser will use 5 seconds by default. To disable caching completely, pass MaxAge value negative. It will set the Access-Control-Max-Age header 0. | `0` | ## Default Config diff --git a/docs/api/middleware/encryptcookie.md b/docs/api/middleware/encryptcookie.md index 39dca0cec6..c153b6412d 100644 --- a/docs/api/middleware/encryptcookie.md +++ b/docs/api/middleware/encryptcookie.md @@ -4,7 +4,11 @@ id: encryptcookie # Encrypt Cookie -Encrypt middleware for [Fiber](https://github.com/gofiber/fiber) which encrypts cookie values. Note: this middleware does not encrypt cookie names. +Encrypt Cookie is a middleware for [Fiber](https://github.com/gofiber/fiber) that secures your cookie values through encryption. + +:::note +This middleware encrypts cookie values and not the cookie names. +::: ## Signatures @@ -18,7 +22,7 @@ func GenerateKey() string ## Examples -Import the middleware package that is part of the Fiber web framework +To use the Encrypt Cookie middleware, first, import the middleware package as part of the Fiber web framework: ```go import ( @@ -27,23 +31,20 @@ import ( ) ``` -After you initiate your Fiber app, you can use the following possibilities: +Once you've imported the middleware package, you can use it inside your Fiber app: ```go -// Provide a minimal config -// `Key` must be a 32 character string. It's used to encrypt the values, so make sure it is random and keep it secret. -// You can run `openssl rand -base64 32` or call `encryptcookie.GenerateKey()` to create a random key for you. -// Make sure not to set `Key` to `encryptcookie.GenerateKey()` because that will create a new key every run. +// Provide a minimal configuration app.Use(encryptcookie.New(encryptcookie.Config{ Key: "secret-thirty-2-character-string", })) -// Get / reading out the encrypted cookie +// Retrieve the encrypted cookie value app.Get("/", func(c fiber.Ctx) error { return c.SendString("value=" + c.Cookies("test")) }) -// Post / create the encrypted cookie +// Create an encrypted cookie app.Post("/", func(c fiber.Ctx) error { c.Cookie(&fiber.Cookie{ Name: "test", @@ -53,71 +54,48 @@ app.Post("/", func(c fiber.Ctx) error { }) ``` +:::note +`Key` must be a 32 character string. It's used to encrypt the values, so make sure it is random and keep it secret. +You can run `openssl rand -base64 32` or call `encryptcookie.GenerateKey()` to create a random key for you. +Make sure not to set `Key` to `encryptcookie.GenerateKey()` because that will create a new key every run. +::: + ## Config -<<<<<<< HEAD:middleware/encryptcookie/README.md -```go -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c fiber.Ctx) bool - - // Array of cookie keys that should not be encrypted. - // - // Optional. Default: ["csrf_"] - Except []string - - // Base64 encoded unique key to encode & decode cookies. - // - // Required. The key should be 32 bytes of random data in base64-encoded form. - // You may run `openssl rand -base64 32` or use `encryptcookie.GenerateKey()` to generate a new key. - Key string - - // Custom function to encrypt cookies. - // - // Optional. Default: EncryptCookie - Encryptor func(decryptedString, key string) (string, error) - - // Custom function to decrypt cookies. - // - // Optional. Default: DecryptCookie - Decryptor func(encryptedString, key string) (string, error) -} -``` -======= -| Property | Type | Description | Default | -|:----------|:----------------------------------------------------|:----------------------------------------------------------------------------------------------------|:-----------------------------| -| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | -| Except | `[]string` | Array of cookie keys that should not be encrypted. | `[]` | -| Key | `string` | Base64 encoded unique key to encode & decode cookies. Required. Key length should be 32 characters. | (No default, required field) | -| Encryptor | `func(decryptedString, key string) (string, error)` | Custom function to encrypt cookies. | `EncryptCookie` | -| Decryptor | `func(encryptedString, key string) (string, error)` | Custom function to decrypt cookies. | `DecryptCookie` | ->>>>>>> origin/master:docs/api/middleware/encryptcookie.md +| Property | Type | Description | Default | +|:----------|:----------------------------------------------------|:------------------------------------------------------------------------------------------------------|:-----------------------------| +| Next | `func(fiber.Ctx) bool` | A function to skip this middleware when returned true. | `nil` | +| Except | `[]string` | Array of cookie keys that should not be encrypted. | `[]` | +| Key | `string` | A base64-encoded unique key to encode & decode cookies. Required. Key length should be 32 characters. | (No default, required field) | +| Encryptor | `func(decryptedString, key string) (string, error)` | A custom function to encrypt cookies. | `EncryptCookie` | +| Decryptor | `func(encryptedString, key string) (string, error)` | A custom function to decrypt cookies. | `DecryptCookie` | ## Default Config ```go var ConfigDefault = Config{ Next: nil, - Except: []string{"csrf_"}, + Except: []string{}, Key: "", Encryptor: EncryptCookie, Decryptor: DecryptCookie, } ``` -## Usage of CSRF and Encryptcookie Middlewares with Custom Cookie Names -Normally, encryptcookie middleware skips `csrf_` cookies. However, it won't work when you use custom cookie names for CSRF. You should update `Except` config to avoid this problem. For example: +## Usage With Other Middlewares That Reads Or Modify Cookies +Place the `encryptcookie` middleware before any other middleware that reads or modifies cookies. For example, if you are using the CSRF middleware, ensure that the `encryptcookie` middleware is placed before it. Failure to do so may prevent the CSRF middleware from reading the encrypted cookie. + +You may also choose to exclude certain cookies from encryption. For instance, if you are using the `CSRF` middleware with a frontend framework like Angular, and the framework reads the token from a cookie, you should exclude that cookie from encryption. This can be achieved by adding the cookie name to the Except array in the configuration: ```go app.Use(encryptcookie.New(encryptcookie.Config{ - Key: "secret-thirty-2-character-string", - Except: []string{"csrf_1"}, // exclude CSRF cookie + Key: "secret-thirty-2-character-string", + Except: []string{csrf.ConfigDefault.CookieName}, // exclude CSRF cookie })) app.Use(csrf.New(csrf.Config{ - KeyLookup: "form:test", - CookieName: "csrf_1", - CookieHTTPOnly: true, + KeyLookup: "header:" + csrf.HeaderName, + CookieSameSite: "Lax", + CookieSecure: true, + CookieHTTPOnly: false, })) ``` diff --git a/docs/api/middleware/healthcheck.md b/docs/api/middleware/healthcheck.md new file mode 100644 index 0000000000..666d51ef12 --- /dev/null +++ b/docs/api/middleware/healthcheck.md @@ -0,0 +1,106 @@ +--- +id: healthcheck +--- + +# Health Check + +Liveness and readiness probes middleware for [Fiber](https://github.com/gofiber/fiber) that provides two endpoints for checking the liveness and readiness state of HTTP applications. + +## Overview + +- **Liveness Probe**: Checks if the server is up and running. + - **Default Endpoint**: `/livez` + - **Behavior**: By default returns `true` immediately when the server is operational. + +- **Readiness Probe**: Assesses if the application is ready to handle requests. + - **Default Endpoint**: `/readyz` + - **Behavior**: By default returns `true` immediately when the server is operational. + +- **HTTP Status Codes**: + - `200 OK`: Returned when the checker function evaluates to `true`. + - `503 Service Unavailable`: Returned when the checker function evaluates to `false`. + +## Signatures + +```go +func New(config Config) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the [Fiber](https://github.com/gofiber/fiber) web framework +```go +import ( + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/healthcheck" +) +``` + +After you initiate your [Fiber](https://github.com/gofiber/fiber) app, you can use the following possibilities: + +```go +// Provide a minimal config +app.Use(healthcheck.New()) + +// Or extend your config for customization +app.Use(healthcheck.New(healthcheck.Config{ + LivenessProbe: func(c fiber.Ctx) bool { + return true + }, + LivenessEndpoint: "/live", + ReadinessProbe: func(c fiber.Ctx) bool { + return serviceA.Ready() && serviceB.Ready() && ... + }, + ReadinessEndpoint: "/ready", +})) +``` + +## Config + +```go +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(fiber.Ctx) bool + + // Function used for checking the liveness of the application. Returns true if the application + // is running and false if it is not. The liveness probe is typically used to indicate if + // the application is in a state where it can handle requests (e.g., the server is up and running). + // + // Optional. Default: func(c fiber.Ctx) bool { return true } + LivenessProbe HealthChecker + + // HTTP endpoint at which the liveness probe will be available. + // + // Optional. Default: "/livez" + LivenessEndpoint string + + // Function used for checking the readiness of the application. Returns true if the application + // is ready to process requests and false otherwise. The readiness probe typically checks if all necessary + // services, databases, and other dependencies are available for the application to function correctly. + // + // Optional. Default: func(c fiber.Ctx) bool { return true } + ReadinessProbe HealthChecker + + // HTTP endpoint at which the readiness probe will be available. + // Optional. Default: "/readyz" + ReadinessEndpoint string +} +``` + +## Default Config + +The default configuration used by this middleware is defined as follows: +```go +func defaultLivenessProbe(fiber.Ctx) bool { return true } + +func defaultReadinessProbe(fiber.Ctx) bool { return true } + +var ConfigDefault = Config{ + LivenessProbe: defaultLivenessProbe, + ReadinessProbe: defaultReadinessProbe, + LivenessEndpoint: "/livez", + ReadinessEndpoint: "/readyz", +} +``` diff --git a/docs/api/middleware/logger.md b/docs/api/middleware/logger.md index 9208eb4677..7c147ba0dd 100644 --- a/docs/api/middleware/logger.md +++ b/docs/api/middleware/logger.md @@ -96,27 +96,27 @@ Writing to os.File is goroutine-safe, but if you are using a custom Output that ### Config -| Property | Type | Description | Default | -|:-----------------|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------| -| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | -| Done | `func(fiber.Ctx, []byte)` | Done is a function that is called after the log string for a request is written to Output, and pass the log string as parameter. | `nil` | -| CustomTags | `map[string]LogFunc` | tagFunctions defines the custom tag action. | `map[string]LogFunc` | -| Format | `string` | Format defines the logging tags. | `[${time}] ${status} - ${latency} ${method} ${path}\n` | -| TimeFormat | `string` | TimeFormat defines the time format for log timestamps. | `15:04:05` | -| TimeZone | `string` | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc | `"Local"` | -| TimeInterval | `time.Duration` | TimeInterval is the delay before the timestamp is updated. | `500 * time.Millisecond` | -| Output | `io.Writer` | Output is a writer where logs are written. | `os.Stdout` | -| DisableColors | `bool` | DisableColors defines if the logs output should be colorized. | `false` | -| enableColors | `bool` | Internal field for enabling colors in the log output. (This is not a user-configurable field) | - | -| enableLatency | `bool` | Internal field for enabling latency measurement in logs. (This is not a user-configurable field) | - | -| timeZoneLocation | `*time.Location` | Internal field for the time zone location. (This is not a user-configurable field) | - | +| Property | Type | Description | Default | +|:-----------------|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------| +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Done | `func(fiber.Ctx, []byte)` | Done is a function that is called after the log string for a request is written to Output, and pass the log string as parameter. | `nil` | +| CustomTags | `map[string]LogFunc` | tagFunctions defines the custom tag action. | `map[string]LogFunc` | +| Format | `string` | Format defines the logging tags. | `[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n` | +| TimeFormat | `string` | TimeFormat defines the time format for log timestamps. | `15:04:05` | +| TimeZone | `string` | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc | `"Local"` | +| TimeInterval | `time.Duration` | TimeInterval is the delay before the timestamp is updated. | `500 * time.Millisecond` | +| Output | `io.Writer` | Output is a writer where logs are written. | `os.Stdout` | +| DisableColors | `bool` | DisableColors defines if the logs output should be colorized. | `false` | +| enableColors | `bool` | Internal field for enabling colors in the log output. (This is not a user-configurable field) | - | +| enableLatency | `bool` | Internal field for enabling latency measurement in logs. (This is not a user-configurable field) | - | +| timeZoneLocation | `*time.Location` | Internal field for the time zone location. (This is not a user-configurable field) | - | ## Default Config ```go var ConfigDefault = Config{ Next: nil, Done: nil, - Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", + Format: "[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n", TimeFormat: "15:04:05", TimeZone: "Local", TimeInterval: 500 * time.Millisecond, diff --git a/docs/api/middleware/timeout.md b/docs/api/middleware/timeout.md index 059272fc21..e5f77546a0 100644 --- a/docs/api/middleware/timeout.md +++ b/docs/api/middleware/timeout.md @@ -8,14 +8,6 @@ There exist two distinct implementations of timeout middleware [Fiber](https://g **New** -Wraps a `fiber.Handler` with a timeout. If the handler takes longer than the given duration to return, the timeout error is set and forwarded to the centralized [ErrorHandler](https://docs.gofiber.io/error-handling). - -:::caution -This has been deprecated since it raises race conditions. -::: - -**NewWithContext** - As a `fiber.Handler` wrapper, it creates a context with `context.WithTimeout` and pass it in `UserContext`. If the context passed executions (eg. DB ops, Http calls) takes longer than the given duration to return, the timeout error is set and forwarded to the centralized `ErrorHandler`. @@ -27,7 +19,6 @@ It does not cancel long running executions. Underlying executions must handle ti ```go func New(handler fiber.Handler, timeout time.Duration, timeoutErrors ...error) fiber.Handler -func NewWithContext(handler fiber.Handler, timeout time.Duration, timeoutErrors ...error) fiber.Handler ``` ## Examples @@ -46,12 +37,7 @@ After you initiate your Fiber app, you can use the following possibilities: ```go func main() { app := fiber.New() -<<<<<<< HEAD:middleware/timeout/README.md - h := func(c fiber.Ctx) error { -======= - h := func(c fiber.Ctx) error { ->>>>>>> origin/master:docs/api/middleware/timeout.md sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") if err := sleepWithContext(c.UserContext(), sleepTime); err != nil { return fmt.Errorf("%w: execution error", err) @@ -105,7 +91,7 @@ func main() { return nil } - app.Get("/foo/:sleepTime", timeout.NewWithContext(h, 2*time.Second, ErrFooTimeOut)) + app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second, ErrFooTimeOut)) log.Fatal(app.Listen(":3000")) } @@ -144,7 +130,7 @@ func main() { return nil } - app.Get("/foo", timeout.NewWithContext(handler, 10*time.Second)) + app.Get("/foo", timeout.New(handler, 10*time.Second)) log.Fatal(app.Listen(":3000")) } ``` diff --git a/docs/guide/hooks.md b/docs/guide/hooks.md index 9fd0cf4726..7e51d9ae84 100644 --- a/docs/guide/hooks.md +++ b/docs/guide/hooks.md @@ -35,7 +35,7 @@ type OnMountHandler = func(*App) error OnRoute is a hook to execute user functions on each route registeration. Also you can get route properties by **route** parameter. ```go title="Signature" -func (app *App) OnRoute(handler ...OnRouteHandler) +func (h *Hooks) OnRoute(handler ...OnRouteHandler) ``` ## OnName @@ -47,7 +47,7 @@ OnName only works with naming routes, not groups. ::: ```go title="Signature" -func (app *App) OnName(handler ...OnNameHandler) +func (h *Hooks) OnName(handler ...OnNameHandler) ``` @@ -104,7 +104,7 @@ func main() { OnGroup is a hook to execute user functions on each group registeration. Also you can get group properties by **group** parameter. ```go title="Signature" -func (app *App) OnGroup(handler ...OnGroupHandler) +func (h *Hooks) OnGroup(handler ...OnGroupHandler) ``` ## OnGroupName @@ -116,7 +116,7 @@ OnGroupName only works with naming groups, not routes. ::: ```go title="Signature" -func (app *App) OnGroupName(handler ...OnGroupNameHandler) +func (h *Hooks) OnGroupName(handler ...OnGroupNameHandler) ``` ## OnListen @@ -124,7 +124,7 @@ func (app *App) OnGroupName(handler ...OnGroupNameHandler) OnListen is a hook to execute user functions on Listen, ListenTLS, Listener. ```go title="Signature" -func (app *App) OnListen(handler ...OnListenHandler) +func (h *Hooks) OnListen(handler ...OnListenHandler) ``` @@ -158,7 +158,7 @@ app.Listen(":5000") OnFork is a hook to execute user functions on Fork. ```go title="Signature" -func (app *App) OnFork(handler ...OnForkHandler) +func (h *Hooks) OnFork(handler ...OnForkHandler) ``` ## OnShutdown @@ -166,7 +166,7 @@ func (app *App) OnFork(handler ...OnForkHandler) OnShutdown is a hook to execute user functions after Shutdown. ```go title="Signature" -func (app *App) OnShutdown(handler ...OnShutdownHandler) +func (h *Hooks) OnShutdown(handler ...OnShutdownHandler) ``` ## OnMount diff --git a/log/default.go b/log/default.go index a7a60abd23..e9c3d1bbbd 100644 --- a/log/default.go +++ b/log/default.go @@ -31,6 +31,9 @@ func (l *defaultLogger) privateLog(lv Level, fmtArgs []any) { buf.WriteString(fmt.Sprint(fmtArgs...)) _ = l.stdlog.Output(l.depth, buf.String()) //nolint:errcheck // It is fine to ignore the error + if lv == LevelPanic { + panic(buf.String()) + } buf.Reset() bytebufferpool.Put(buf) if lv == LevelFatal { @@ -54,6 +57,9 @@ func (l *defaultLogger) privateLogf(lv Level, format string, fmtArgs []any) { _, _ = fmt.Fprint(buf, fmtArgs...) } _ = l.stdlog.Output(l.depth, buf.String()) //nolint:errcheck // It is fine to ignore the error + if lv == LevelPanic { + panic(buf.String()) + } buf.Reset() bytebufferpool.Put(buf) if lv == LevelFatal { @@ -92,6 +98,9 @@ func (l *defaultLogger) privateLogw(lv Level, format string, keysAndValues []any } _ = l.stdlog.Output(l.depth, buf.String()) //nolint:errcheck // It is fine to ignore the error + if lv == LevelPanic { + panic(buf.String()) + } buf.Reset() bytebufferpool.Put(buf) if lv == LevelFatal { diff --git a/log/default_test.go b/log/default_test.go index 9e820b6f4a..a0cb281497 100644 --- a/log/default_test.go +++ b/log/default_test.go @@ -28,6 +28,22 @@ func (w *byteSliceWriter) Write(p []byte) (int, error) { return len(p), nil } +func Test_WithContextCaller(t *testing.T) { + logger = &defaultLogger{ + stdlog: log.New(os.Stderr, "", log.Lshortfile), + depth: 4, + } + + var w byteSliceWriter + SetOutput(&w) + ctx := context.TODO() + + WithContext(ctx).Info("") + Info("") + + require.Equal(t, "default_test.go:41: [Info] \ndefault_test.go:42: [Info] \n", string(w.b)) +} + func Test_DefaultLogger(t *testing.T) { initDefaultLogger() @@ -39,7 +55,11 @@ func Test_DefaultLogger(t *testing.T) { Info("starting work") Warn("work may fail") Error("work failed") - Panic("work panic") + + require.Panics(t, func() { + Panic("work panic") + }) + require.Equal(t, "[Trace] trace work\n"+ "[Debug] received work order\n"+ "[Info] starting work\n"+ @@ -59,7 +79,10 @@ func Test_DefaultFormatLogger(t *testing.T) { Infof("starting %s", work) Warnf("%s may fail", work) Errorf("%s failed", work) - Panicf("%s panic", work) + + require.Panics(t, func() { + Panicf("%s panic", work) + }) require.Equal(t, "[Trace] trace work\n"+ "[Debug] received work order\n"+ @@ -82,7 +105,10 @@ func Test_CtxLogger(t *testing.T) { WithContext(ctx).Infof("starting %s", work) WithContext(ctx).Warnf("%s may fail", work) WithContext(ctx).Errorf("%s failed %d", work, 50) - WithContext(ctx).Panicf("%s panic", work) + + require.Panics(t, func() { + WithContext(ctx).Panicf("%s panic", work) + }) require.Equal(t, "[Trace] trace work\n"+ "[Debug] received work order\n"+ @@ -210,22 +236,6 @@ func BenchmarkLogfKeyAndValues(b *testing.B) { } } -func Test_WithContextCaller(t *testing.T) { - logger = &defaultLogger{ - stdlog: log.New(os.Stderr, "", log.Lshortfile), - depth: 4, - } - - var w byteSliceWriter - SetOutput(&w) - ctx := context.TODO() - - WithContext(ctx).Info("") - Info("") - - require.Equal(t, "default_test.go:223: [Info] \ndefault_test.go:224: [Info] \n", string(w.b)) -} - func Test_SetLevel(t *testing.T) { setLogger := &defaultLogger{ stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds), diff --git a/middleware/adaptor/adaptor.go b/middleware/adaptor/adaptor.go index 5b666e3caa..82059cf158 100644 --- a/middleware/adaptor/adaptor.go +++ b/middleware/adaptor/adaptor.go @@ -87,6 +87,7 @@ func HTTPMiddleware(mw func(http.Handler) http.Handler) fiber.Handler { c.Request().Header.SetMethod(r.Method) c.Request().SetRequestURI(r.RequestURI) c.Request().SetHost(r.Host) + c.Request().Header.SetHost(r.Host) for key, val := range r.Header { for _, v := range val { c.Request().Header.Set(key, v) @@ -139,6 +140,7 @@ func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc { req.Header.SetMethod(r.Method) req.SetRequestURI(r.RequestURI) req.SetHost(r.Host) + req.Header.SetHost(r.Host) for key, val := range r.Header { for _, v := range val { req.Header.Set(key, v) @@ -158,7 +160,7 @@ func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc { fctx.Init(req, remoteAddr, nil) if len(h) > 0 { // New fiber Ctx - ctx := app.NewCtx(&fctx) + ctx := app.AcquireCtx(&fctx) // Execute fiber Ctx err := h[0](ctx) if err != nil { diff --git a/middleware/adaptor/adaptor_test.go b/middleware/adaptor/adaptor_test.go index 621030c64e..a14ea60669 100644 --- a/middleware/adaptor/adaptor_test.go +++ b/middleware/adaptor/adaptor_test.go @@ -1,4 +1,4 @@ -//nolint:bodyclose, contextcheck, revive // Much easier to just ignore memory leaks in tests +//nolint:contextcheck, revive // Much easier to just ignore memory leaks in tests package adaptor import ( @@ -35,7 +35,8 @@ func Test_HTTPHandler(t *testing.T) { expectedURL, err := url.ParseRequestURI(expectedRequestURI) require.NoError(t, err) - expectedContextKey := "contextKey" + type contextKeyType string + expectedContextKey := contextKeyType("contextKey") expectedContextValue := "contextValue" callsCount := 0 @@ -86,7 +87,7 @@ func Test_HTTPHandler(t *testing.T) { fctx.Init(&req, remoteAddr, nil) app := fiber.New() - ctx := app.NewCtx(&fctx) + ctx := app.AcquireCtx(&fctx) defer app.ReleaseCtx(ctx) err = fiberH(ctx) @@ -114,6 +115,7 @@ var ( ) func Test_HTTPMiddleware(t *testing.T) { + const expectedHost = "foobar.com" tests := []struct { name string url string @@ -146,6 +148,7 @@ func Test_HTTPMiddleware(t *testing.T) { w.WriteHeader(http.StatusMethodNotAllowed) return } + r = r.WithContext(context.WithValue(r.Context(), TestContextKey, "okay")) r = r.WithContext(context.WithValue(r.Context(), TestContextSecondKey, "not_okay")) r = r.WithContext(context.WithValue(r.Context(), TestContextSecondKey, "okay")) @@ -178,6 +181,7 @@ func Test_HTTPMiddleware(t *testing.T) { for _, tt := range tests { req, err := http.NewRequestWithContext(context.Background(), tt.method, tt.url, nil) + req.Host = expectedHost require.NoError(t, err) resp, err := app.Test(req) @@ -186,6 +190,7 @@ func Test_HTTPMiddleware(t *testing.T) { } req, err := http.NewRequestWithContext(context.Background(), fiber.MethodPost, "/", nil) + req.Host = expectedHost require.NoError(t, err) resp, err := app.Test(req) @@ -237,6 +242,8 @@ func testFiberToHandlerFunc(t *testing.T, checkDefaultPort bool, app ...*fiber.A require.Equal(t, expectedRequestURI, string(c.Context().RequestURI()), "RequestURI") require.Equal(t, expectedContentLength, c.Context().Request.Header.ContentLength(), "ContentLength") require.Equal(t, expectedHost, c.Hostname(), "Host") + require.Equal(t, expectedHost, string(c.Request().Header.Host()), "Host") + require.Equal(t, "http://"+expectedHost, c.BaseURL(), "BaseURL") require.Equal(t, expectedRemoteAddr, c.Context().RemoteAddr().String(), "RemoteAddr") body := string(c.Body()) @@ -292,7 +299,7 @@ func testFiberToHandlerFunc(t *testing.T, checkDefaultPort bool, app ...*fiber.A require.Equal(t, expectedResponseBody, string(w.body), "Body") } -func setFiberContextValueMiddleware(next fiber.Handler, key string, value any) fiber.Handler { +func setFiberContextValueMiddleware(next fiber.Handler, key, value any) fiber.Handler { return func(c fiber.Ctx) error { c.Locals(key, value) return next(c) @@ -389,7 +396,7 @@ func Test_ConvertRequest(t *testing.T) { return c.SendString("Request URL: " + httpReq.URL.String()) }) - resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test?hello=world&another=test", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test?hello=world&another=test", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, http.StatusOK, resp.StatusCode, "Status code") @@ -446,7 +453,7 @@ func Benchmark_FiberHandlerFunc(b *testing.B) { r := http.Request{ Method: http.MethodPost, - Body: http.NoBody, + Body: nil, } // Replace the empty Body with our buffer @@ -513,7 +520,7 @@ func Benchmark_FiberHandlerFunc_Parallel(b *testing.B) { w := httptest.NewRecorder() r := http.Request{ Method: http.MethodPost, - Body: http.NoBody, + Body: nil, } // Replace the empty Body with our buffer diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index d33aeeadc6..de96c1a689 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -16,12 +16,14 @@ type Config struct { Next func(c fiber.Ctx) bool // AllowOriginsFunc defines a function that will set the 'access-control-allow-origin' - // response header to the 'origin' request header when returned true. + // response header to the 'origin' request header when returned true. This allows for + // dynamic evaluation of allowed origins. Note if AllowCredentials is true, wildcard origins + // will be not have the 'access-control-allow-credentials' header set to 'true'. // // Optional. Default: nil AllowOriginsFunc func(origin string) bool - // AllowOrigin defines a list of origins that may access the resource. + // AllowOrigin defines a comma separated list of origins that may access the resource. // // Optional. Default value "*" AllowOrigins string @@ -41,7 +43,8 @@ type Config struct { // AllowCredentials indicates whether or not the response to the request // can be exposed when the credentials flag is true. When used as part of // a response to a preflight request, this indicates whether or not the - // actual request can be made using credentials. + // actual request can be made using credentials. Note: If true, AllowOrigins + // cannot be set to a wildcard ("*") to prevent security vulnerabilities. // // Optional. Default value false. AllowCredentials bool @@ -94,16 +97,36 @@ func New(config ...Config) fiber.Handler { if cfg.AllowMethods == "" { cfg.AllowMethods = ConfigDefault.AllowMethods } - if cfg.AllowOrigins == "" { + // When none of the AllowOrigins or AllowOriginsFunc config was defined, set the default AllowOrigins value with "*" + if cfg.AllowOrigins == "" && cfg.AllowOriginsFunc == nil { cfg.AllowOrigins = ConfigDefault.AllowOrigins } } // Warning logs if both AllowOrigins and AllowOriginsFunc are set - if cfg.AllowOrigins != ConfigDefault.AllowOrigins && cfg.AllowOriginsFunc != nil { + if cfg.AllowOrigins != "" && cfg.AllowOriginsFunc != nil { log.Warn("[CORS] Both 'AllowOrigins' and 'AllowOriginsFunc' have been defined.") } + // Validate CORS credentials configuration + if cfg.AllowCredentials && cfg.AllowOrigins == "*" { + log.Panic("[CORS] Insecure setup, 'AllowCredentials' is set to true, and 'AllowOrigins' is set to a wildcard.") //nolint:revive // we want to exit the program + } + + // Validate and normalize static AllowOrigins if not using AllowOriginsFunc + if cfg.AllowOriginsFunc == nil && cfg.AllowOrigins != "" && cfg.AllowOrigins != "*" { + validatedOrigins := []string{} + for _, origin := range strings.Split(cfg.AllowOrigins, ",") { + isValid, normalizedOrigin := normalizeOrigin(origin) + if isValid { + validatedOrigins = append(validatedOrigins, normalizedOrigin) + } else { + log.Panicf("[CORS] Invalid origin format in configuration: %s", origin) //nolint:revive // we want to exit the program + } + } + cfg.AllowOrigins = strings.Join(validatedOrigins, ",") + } + // Convert string to slice allowOrigins := strings.Split(strings.ReplaceAll(cfg.AllowOrigins, " ", ""), ",") @@ -122,22 +145,18 @@ func New(config ...Config) fiber.Handler { return c.Next() } - // Get origin header - origin := c.Get(fiber.HeaderOrigin) + // Get originHeader header + originHeader := c.Get(fiber.HeaderOrigin) allowOrigin := "" // Check allowed origins - for _, o := range allowOrigins { - if o == "*" { + for _, origin := range allowOrigins { + if origin == "*" { allowOrigin = "*" break } - if o == origin { - allowOrigin = o - break - } - if matchSubdomain(origin, o) { - allowOrigin = origin + if validateDomain(originHeader, origin) { + allowOrigin = originHeader break } } @@ -145,9 +164,9 @@ func New(config ...Config) fiber.Handler { // Run AllowOriginsFunc if the logic for // handling the value in 'AllowOrigins' does // not result in allowOrigin being set. - if (allowOrigin == "" || allowOrigin == ConfigDefault.AllowOrigins) && cfg.AllowOriginsFunc != nil { - if cfg.AllowOriginsFunc(origin) { - allowOrigin = origin + if allowOrigin == "" && cfg.AllowOriginsFunc != nil { + if cfg.AllowOriginsFunc(originHeader) { + allowOrigin = originHeader } } @@ -172,9 +191,17 @@ func New(config ...Config) fiber.Handler { c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin) c.Set(fiber.HeaderAccessControlAllowMethods, allowMethods) - // Set Allow-Credentials if set to true if cfg.AllowCredentials { - c.Set(fiber.HeaderAccessControlAllowCredentials, "true") + // When AllowCredentials is true, set the Access-Control-Allow-Origin to the specific origin instead of '*' + if allowOrigin != "*" && allowOrigin != "" { + c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin) + c.Set(fiber.HeaderAccessControlAllowCredentials, "true") + } else if allowOrigin == "*" { + log.Warn("[CORS] 'AllowCredentials' is true. Ensure 'AllowOrigins' is not set to '*' in the configuration.") + } + } else { + // For non-credential requests, it's safe to set to '*' or specific origins + c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin) } // Set Allow-Headers if not empty diff --git a/middleware/cors/cors_test.go b/middleware/cors/cors_test.go index c83c9d96fd..3d3ae8c655 100644 --- a/middleware/cors/cors_test.go +++ b/middleware/cors/cors_test.go @@ -34,7 +34,7 @@ func Test_CORS_Negative_MaxAge(t *testing.T) { ctx := &fasthttp.RequestCtx{} ctx.Request.Header.SetMethod(fiber.MethodOptions) - ctx.Request.Header.Set(fiber.HeaderOrigin, "localhost") + ctx.Request.Header.Set(fiber.HeaderOrigin, "http://localhost") app.Handler()(ctx) require.Equal(t, "0", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlMaxAge))) @@ -71,7 +71,46 @@ func Test_CORS_Wildcard(t *testing.T) { app := fiber.New() // OPTIONS (preflight) response headers when AllowOrigins is * app.Use(New(Config{ - AllowOrigins: "*", + AllowOrigins: "*", + MaxAge: 3600, + ExposeHeaders: "X-Request-ID", + AllowHeaders: "Authentication", + })) + // Get handler pointer + handler := app.Handler() + + // Make request + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/") + ctx.Request.Header.Set(fiber.HeaderOrigin, "http://localhost") + ctx.Request.Header.SetMethod(fiber.MethodOptions) + + // Perform request + handler(ctx) + + // Check result + require.Equal(t, "*", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) // Validates request is not reflecting origin in the response + require.Equal(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowCredentials))) + require.Equal(t, "3600", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlMaxAge))) + require.Equal(t, "Authentication", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowHeaders))) + + // Test non OPTIONS (preflight) response headers + ctx = &fasthttp.RequestCtx{} + ctx.Request.Header.SetMethod(fiber.MethodGet) + handler(ctx) + + require.Equal(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowCredentials))) + require.Equal(t, "X-Request-ID", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlExposeHeaders))) +} + +// go test -run -v Test_CORS_Origin_AllowCredentials +func Test_CORS_Origin_AllowCredentials(t *testing.T) { + t.Parallel() + // New fiber instance + app := fiber.New() + // OPTIONS (preflight) response headers when AllowOrigins is * + app.Use(New(Config{ + AllowOrigins: "http://localhost", AllowCredentials: true, MaxAge: 3600, ExposeHeaders: "X-Request-ID", @@ -83,14 +122,14 @@ func Test_CORS_Wildcard(t *testing.T) { // Make request ctx := &fasthttp.RequestCtx{} ctx.Request.SetRequestURI("/") - ctx.Request.Header.Set(fiber.HeaderOrigin, "localhost") + ctx.Request.Header.Set(fiber.HeaderOrigin, "http://localhost") ctx.Request.Header.SetMethod(fiber.MethodOptions) // Perform request handler(ctx) // Check result - require.Equal(t, "*", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + require.Equal(t, "http://localhost", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) require.Equal(t, "true", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowCredentials))) require.Equal(t, "3600", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlMaxAge))) require.Equal(t, "Authentication", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowHeaders))) @@ -104,6 +143,57 @@ func Test_CORS_Wildcard(t *testing.T) { require.Equal(t, "X-Request-ID", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlExposeHeaders))) } +// go test -run -v Test_CORS_Wildcard_AllowCredentials_Panic +// Test for fiber-ghsa-fmg4-x8pw-hjhg +func Test_CORS_Wildcard_AllowCredentials_Panic(t *testing.T) { + t.Parallel() + // New fiber instance + app := fiber.New() + + didPanic := false + func() { + defer func() { + if r := recover(); r != nil { + didPanic = true + } + }() + + app.Use(New(Config{ + AllowOrigins: "*", + AllowCredentials: true, + })) + }() + + if !didPanic { + t.Errorf("Expected a panic when AllowOrigins is '*' and AllowCredentials is true") + } +} + +// go test -run -v Test_CORS_Invalid_Origin_Panic +func Test_CORS_Invalid_Origin_Panic(t *testing.T) { + t.Parallel() + // New fiber instance + app := fiber.New() + + didPanic := false + func() { + defer func() { + if r := recover(); r != nil { + didPanic = true + } + }() + + app.Use(New(Config{ + AllowOrigins: "localhost", + AllowCredentials: true, + })) + }() + + if !didPanic { + t.Errorf("Expected a panic when Origin is missing scheme") + } +} + // go test -run -v Test_CORS_Subdomain func Test_CORS_Subdomain(t *testing.T) { t.Parallel() @@ -192,12 +282,9 @@ func Test_CORS_AllowOriginScheme(t *testing.T) { shouldAllowOrigin: false, }, { - pattern: "http://*.example.com", - reqOrigin: `http://1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890\ - .1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890\ - .1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890\ - .1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.example.com`, - shouldAllowOrigin: false, + pattern: "http://*.example.com", + reqOrigin: "http://1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.1234567890.example.com", + shouldAllowOrigin: true, }, { pattern: "http://example.com", @@ -330,9 +417,9 @@ func Test_CORS_AllowOriginsFunc(t *testing.T) { // Perform request handler(ctx) - // Allow-Origin header should be "*" because http://google.com does not satisfy 'strings.Contains(origin, "example-2")' - // and AllowOrigins has not been set so the default "*" is used - require.Equal(t, "*", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + // Allow-Origin header should be empty because http://google.com does not satisfy 'strings.Contains(origin, "example-2")' + // and AllowOrigins has not been set + require.Equal(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) ctx.Request.Reset() ctx.Response.Reset() @@ -347,3 +434,215 @@ func Test_CORS_AllowOriginsFunc(t *testing.T) { // Allow-Origin header should be "http://example-2.com" require.Equal(t, "http://example-2.com", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) } + +func Test_CORS_AllowOriginsAndAllowOriginsFunc_AllUseCases(t *testing.T) { + testCases := []struct { + Name string + Config Config + RequestOrigin string + ResponseOrigin string + }{ + { + Name: "AllowOriginsDefined/AllowOriginsFuncUndefined/OriginAllowed", + Config: Config{ + AllowOrigins: "http://aaa.com", + AllowOriginsFunc: nil, + }, + RequestOrigin: "http://aaa.com", + ResponseOrigin: "http://aaa.com", + }, + { + Name: "AllowOriginsDefined/AllowOriginsFuncUndefined/OriginNotAllowed", + Config: Config{ + AllowOrigins: "http://aaa.com", + AllowOriginsFunc: nil, + }, + RequestOrigin: "http://bbb.com", + ResponseOrigin: "", + }, + { + Name: "AllowOriginsDefined/AllowOriginsFuncReturnsTrue/OriginAllowed", + Config: Config{ + AllowOrigins: "http://aaa.com", + AllowOriginsFunc: func(_ string) bool { + return true + }, + }, + RequestOrigin: "http://aaa.com", + ResponseOrigin: "http://aaa.com", + }, + { + Name: "AllowOriginsDefined/AllowOriginsFuncReturnsTrue/OriginNotAllowed", + Config: Config{ + AllowOrigins: "http://aaa.com", + AllowOriginsFunc: func(_ string) bool { + return true + }, + }, + RequestOrigin: "http://bbb.com", + ResponseOrigin: "http://bbb.com", + }, + { + Name: "AllowOriginsDefined/AllowOriginsFuncReturnsFalse/OriginAllowed", + Config: Config{ + AllowOrigins: "http://aaa.com", + AllowOriginsFunc: func(_ string) bool { + return false + }, + }, + RequestOrigin: "http://aaa.com", + ResponseOrigin: "http://aaa.com", + }, + { + Name: "AllowOriginsDefined/AllowOriginsFuncReturnsFalse/OriginNotAllowed", + Config: Config{ + AllowOrigins: "http://aaa.com", + AllowOriginsFunc: func(_ string) bool { + return false + }, + }, + RequestOrigin: "http://bbb.com", + ResponseOrigin: "", + }, + { + Name: "AllowOriginsEmpty/AllowOriginsFuncUndefined/OriginAllowed", + Config: Config{ + AllowOrigins: "", + AllowOriginsFunc: nil, + }, + RequestOrigin: "http://aaa.com", + ResponseOrigin: "*", + }, + { + Name: "AllowOriginsEmpty/AllowOriginsFuncReturnsTrue/OriginAllowed", + Config: Config{ + AllowOrigins: "", + AllowOriginsFunc: func(_ string) bool { + return true + }, + }, + RequestOrigin: "http://aaa.com", + ResponseOrigin: "http://aaa.com", + }, + { + Name: "AllowOriginsEmpty/AllowOriginsFuncReturnsFalse/OriginNotAllowed", + Config: Config{ + AllowOrigins: "", + AllowOriginsFunc: func(_ string) bool { + return false + }, + }, + RequestOrigin: "http://aaa.com", + ResponseOrigin: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + app := fiber.New() + app.Use("/", New(tc.Config)) + + handler := app.Handler() + + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/") + ctx.Request.Header.SetMethod(fiber.MethodOptions) + ctx.Request.Header.Set(fiber.HeaderOrigin, tc.RequestOrigin) + + handler(ctx) + + require.Equal(t, tc.ResponseOrigin, string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + }) + } +} + +// The fix for issue #2422 +func Test_CORS_AllowCredentials(t *testing.T) { + testCases := []struct { + Name string + Config Config + RequestOrigin string + ResponseOrigin string + ResponseCredentials string + }{ + { + Name: "AllowOriginsFuncDefined", + Config: Config{ + AllowCredentials: true, + AllowOriginsFunc: func(_ string) bool { + return true + }, + }, + RequestOrigin: "http://aaa.com", + // The AllowOriginsFunc config was defined, should use the real origin of the function + ResponseOrigin: "http://aaa.com", + ResponseCredentials: "true", + }, + { + Name: "fiber-ghsa-fmg4-x8pw-hjhg-wildcard-credentials", + Config: Config{ + AllowCredentials: true, + AllowOriginsFunc: func(_ string) bool { + return true + }, + }, + RequestOrigin: "*", + ResponseOrigin: "*", + // Middleware will validate that wildcard wont set credentials to true + ResponseCredentials: "", + }, + { + Name: "AllowOriginsFuncNotDefined", + Config: Config{ + // Setting this to true will cause the middleware to panic since default AllowOrigins is "*" + AllowCredentials: false, + }, + RequestOrigin: "http://aaa.com", + // None of the AllowOrigins or AllowOriginsFunc config was defined, should use the default origin of "*" + // which will cause the CORS error in the client: + // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' + // when the request's credentials mode is 'include'. + ResponseOrigin: "*", + ResponseCredentials: "", + }, + { + Name: "AllowOriginsDefined", + Config: Config{ + AllowCredentials: true, + AllowOrigins: "http://aaa.com", + }, + RequestOrigin: "http://aaa.com", + ResponseOrigin: "http://aaa.com", + ResponseCredentials: "true", + }, + { + Name: "AllowOriginsDefined/UnallowedOrigin", + Config: Config{ + AllowCredentials: true, + AllowOrigins: "http://aaa.com", + }, + RequestOrigin: "http://bbb.com", + ResponseOrigin: "", + ResponseCredentials: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + app := fiber.New() + app.Use("/", New(tc.Config)) + + handler := app.Handler() + + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/") + ctx.Request.Header.SetMethod(fiber.MethodOptions) + ctx.Request.Header.Set(fiber.HeaderOrigin, tc.RequestOrigin) + + handler(ctx) + + require.Equal(t, tc.ResponseCredentials, string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowCredentials))) + require.Equal(t, tc.ResponseOrigin, string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + }) + } +} diff --git a/middleware/cors/utils.go b/middleware/cors/utils.go index 8b6114bdab..a12ba85a49 100644 --- a/middleware/cors/utils.go +++ b/middleware/cors/utils.go @@ -1,56 +1,85 @@ package cors import ( + "net/url" "strings" ) +// matchScheme compares the scheme of the domain and pattern func matchScheme(domain, pattern string) bool { didx := strings.Index(domain, ":") pidx := strings.Index(pattern, ":") return didx != -1 && pidx != -1 && domain[:didx] == pattern[:pidx] } -// matchSubdomain compares authority with wildcard -func matchSubdomain(domain, pattern string) bool { - if !matchScheme(domain, pattern) { - return false +// validateDomain checks if the domain matches the pattern +func validateDomain(domain, pattern string) bool { + // Directly compare the domain and pattern for an exact match. + if domain == pattern { + return true } - didx := strings.Index(domain, "://") - pidx := strings.Index(pattern, "://") - if didx == -1 || pidx == -1 { - return false + + // Normalize domain and pattern to exclude schemes and ports for matching purposes + normalizedDomain := normalizeDomain(domain) + normalizedPattern := normalizeDomain(pattern) + + // Handling the case where pattern is a wildcard subdomain pattern. + if strings.HasPrefix(normalizedPattern, "*.") { + // Trim leading "*." from pattern for comparison. + trimmedPattern := normalizedPattern[2:] + + // Check if the domain ends with the trimmed pattern. + if strings.HasSuffix(normalizedDomain, trimmedPattern) { + // Ensure that the domain is not exactly the base domain. + if normalizedDomain != trimmedPattern { + // Special handling to prevent "example.com" matching "*.example.com". + if strings.TrimSuffix(normalizedDomain, trimmedPattern) != "" { + return true + } + } + } } - domAuth := domain[didx+3:] - // to avoid long loop by invalid long domain - const maxDomainLen = 253 - if len(domAuth) > maxDomainLen { - return false + + return false +} + +// normalizeDomain removes the scheme and port from the input domain +func normalizeDomain(input string) string { + // Remove scheme + input = strings.TrimPrefix(strings.TrimPrefix(input, "http://"), "https://") + + // Find and remove port, if present + if len(input) > 0 && input[0] != '[' { + if portIndex := strings.Index(input, ":"); portIndex != -1 { + input = input[:portIndex] + } } - patAuth := pattern[pidx+3:] - - domComp := strings.Split(domAuth, ".") - patComp := strings.Split(patAuth, ".") - 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] + + return input +} + +// normalizeOrigin checks if the provided origin is in a correct format +// and normalizes it by removing any path or trailing slash. +// It returns a boolean indicating whether the origin is valid +// and the normalized origin. +func normalizeOrigin(origin string) (bool, string) { + parsedOrigin, err := url.Parse(origin) + if err != nil { + return false, "" } - for i := len(patComp)/divHalf - 1; i >= 0; i-- { - opp := len(patComp) - 1 - i - patComp[i], patComp[opp] = patComp[opp], patComp[i] + + // Validate the scheme is either http or https + if parsedOrigin.Scheme != "http" && parsedOrigin.Scheme != "https" { + return false, "" } - for i, v := range domComp { - if len(patComp) <= i { - return false - } - p := patComp[i] - if p == "*" { - return true - } - if p != v { - return false - } + // Validate there is a host present. The presence of a path, query, or fragment components + // is checked, but a trailing "/" (indicative of the root) is allowed for the path and will be normalized + if parsedOrigin.Host == "" || (parsedOrigin.Path != "" && parsedOrigin.Path != "/") || parsedOrigin.RawQuery != "" || parsedOrigin.Fragment != "" { + return false, "" } - return false + + // Normalize the origin by constructing it from the scheme and host. + // The path or trailing slash is not included in the normalized origin. + return true, strings.ToLower(parsedOrigin.Scheme + "://" + parsedOrigin.Host) } diff --git a/middleware/cors/utils_test.go b/middleware/cors/utils_test.go new file mode 100644 index 0000000000..3acd692521 --- /dev/null +++ b/middleware/cors/utils_test.go @@ -0,0 +1,145 @@ +package cors + +import ( + "testing" +) + +// go test -run -v Test_normalizeOrigin +func Test_normalizeOrigin(t *testing.T) { + testCases := []struct { + origin string + expectedValid bool + expectedOrigin string + }{ + {"http://example.com", true, "http://example.com"}, // Simple case should work. + {"http://example.com/", true, "http://example.com"}, // Trailing slash should be removed. + {"http://example.com:3000", true, "http://example.com:3000"}, // Port should be preserved. + {"http://example.com:3000/", true, "http://example.com:3000"}, // Trailing slash should be removed. + {"http://", false, ""}, // Invalid origin should not be accepted. + {"http://example.com/path", false, ""}, // Path should not be accepted. + {"http://example.com?query=123", false, ""}, // Query should not be accepted. + {"http://example.com#fragment", false, ""}, // Fragment should not be accepted. + {"http://localhost", true, "http://localhost"}, // Localhost should be accepted. + {"http://127.0.0.1", true, "http://127.0.0.1"}, // IPv4 address should be accepted. + {"http://[::1]", true, "http://[::1]"}, // IPv6 address should be accepted. + {"http://[::1]:8080", true, "http://[::1]:8080"}, // IPv6 address with port should be accepted. + {"http://[::1]:8080/", true, "http://[::1]:8080"}, // IPv6 address with port and trailing slash should be accepted. + {"http://[::1]:8080/path", false, ""}, // IPv6 address with port and path should not be accepted. + {"http://[::1]:8080?query=123", false, ""}, // IPv6 address with port and query should not be accepted. + {"http://[::1]:8080#fragment", false, ""}, // IPv6 address with port and fragment should not be accepted. + {"http://[::1]:8080/path?query=123#fragment", false, ""}, // IPv6 address with port, path, query, and fragment should not be accepted. + {"http://[::1]:8080/path?query=123#fragment/", false, ""}, // IPv6 address with port, path, query, fragment, and trailing slash should not be accepted. + {"http://[::1]:8080/path?query=123#fragment/invalid", false, ""}, // IPv6 address with port, path, query, fragment, trailing slash, and invalid segment should not be accepted. + {"http://[::1]:8080/path?query=123#fragment/invalid/", false, ""}, // IPv6 address with port, path, query, fragment, trailing slash, and invalid segment with trailing slash should not be accepted. + {"http://[::1]:8080/path?query=123#fragment/invalid/segment", false, ""}, // IPv6 address with port, path, query, fragment, trailing slash, and invalid segment with additional segment should not be accepted. + } + + for _, tc := range testCases { + valid, normalizedOrigin := normalizeOrigin(tc.origin) + + if valid != tc.expectedValid { + t.Errorf("Expected origin '%s' to be valid: %v, but got: %v", tc.origin, tc.expectedValid, valid) + } + + if normalizedOrigin != tc.expectedOrigin { + t.Errorf("Expected normalized origin '%s' for origin '%s', but got: '%s'", tc.expectedOrigin, tc.origin, normalizedOrigin) + } + } +} + +// go test -run -v Test_matchScheme +func Test_matchScheme(t *testing.T) { + testCases := []struct { + domain string + pattern string + expected bool + }{ + {"http://example.com", "http://example.com", true}, // Exact match should work. + {"https://example.com", "http://example.com", false}, // Scheme mismatch should matter. + {"http://example.com", "https://example.com", false}, // Scheme mismatch should matter. + {"http://example.com", "http://example.org", true}, // Different domains should not matter. + {"http://example.com", "http://example.com:8080", true}, // Port should not matter. + {"http://example.com:8080", "http://example.com", true}, // Port should not matter. + {"http://example.com:8080", "http://example.com:8081", true}, // Different ports should not matter. + {"http://localhost", "http://localhost", true}, // Localhost should match. + {"http://127.0.0.1", "http://127.0.0.1", true}, // IPv4 address should match. + {"http://[::1]", "http://[::1]", true}, // IPv6 address should match. + } + + for _, tc := range testCases { + result := matchScheme(tc.domain, tc.pattern) + + if result != tc.expected { + t.Errorf("Expected matchScheme('%s', '%s') to be %v, but got %v", tc.domain, tc.pattern, tc.expected, result) + } + } +} + +// go test -run -v Test_validateOrigin +func Test_validateOrigin(t *testing.T) { + testCases := []struct { + domain string + pattern string + expected bool + }{ + {"http://example.com", "http://example.com", true}, // Exact match should work. + {"https://example.com", "http://example.com", false}, // Scheme mismatch should matter in CORS context. + {"http://example.com", "https://example.com", false}, // Scheme mismatch should matter in CORS context. + {"http://example.com", "http://example.org", false}, // Different domains should not match. + {"http://example.com", "http://example.com:8080", false}, // Port mismatch should matter. + {"http://example.com:8080", "http://example.com", false}, // Port mismatch should matter. + {"http://example.com:8080", "http://example.com:8081", false}, // Different ports should not match. + {"example.com", "example.com", true}, // Simplified form, assuming scheme and port are not considered here, but in practice, they are part of the origin. + {"sub.example.com", "example.com", false}, // Subdomain should not match the base domain directly. + {"sub.example.com", "*.example.com", true}, // Correct assumption for wildcard subdomain matching. + {"example.com", "*.example.com", false}, // Base domain should not match its wildcard subdomain pattern. + {"sub.example.com", "*.com", true}, // Technically correct for pattern matching, but broad wildcard use like this is not recommended for CORS. + {"sub.sub.example.com", "*.example.com", true}, // Nested subdomain should match the wildcard pattern. + {"example.com", "*.org", false}, // Different TLDs should not match. + {"example.com", "example.org", false}, // Different domains should not match. + {"example.com:8080", "*.example.com", false}, // Different ports mean different origins. + {"example.com", "sub.example.net", false}, // Different domains should not match. + {"http://localhost", "http://localhost", true}, // Localhost should match. + {"http://127.0.0.1", "http://127.0.0.1", true}, // IPv4 address should match. + {"http://[::1]", "http://[::1]", true}, // IPv6 address should match. + } + + for _, tc := range testCases { + result := validateDomain(tc.domain, tc.pattern) + + if result != tc.expected { + t.Errorf("Expected validateOrigin('%s', '%s') to be %v, but got %v", tc.domain, tc.pattern, tc.expected, result) + } + } +} + +// go test -run -v Test_normalizeDomain +func Test_normalizeDomain(t *testing.T) { + testCases := []struct { + input string + expectedOutput string + }{ + {"http://example.com", "example.com"}, // Simple case with http scheme. + {"https://example.com", "example.com"}, // Simple case with https scheme. + {"http://example.com:3000", "example.com"}, // Case with port. + {"https://example.com:3000", "example.com"}, // Case with port and https scheme. + {"http://example.com/path", "example.com/path"}, // Case with path. + {"http://example.com?query=123", "example.com?query=123"}, // Case with query. + {"http://example.com#fragment", "example.com#fragment"}, // Case with fragment. + {"example.com", "example.com"}, // Case without scheme. + {"example.com:8080", "example.com"}, // Case without scheme but with port. + {"sub.example.com", "sub.example.com"}, // Case with subdomain. + {"sub.sub.example.com", "sub.sub.example.com"}, // Case with nested subdomain. + {"http://localhost", "localhost"}, // Case with localhost. + {"http://127.0.0.1", "127.0.0.1"}, // Case with IPv4 address. + {"http://[::1]", "[::1]"}, // Case with IPv6 address. + } + + for _, tc := range testCases { + output := normalizeDomain(tc.input) + + if output != tc.expectedOutput { + t.Errorf("Expected normalized domain '%s' for input '%s', but got: '%s'", tc.expectedOutput, tc.input, output) + } + } +} diff --git a/middleware/csrf/csrf_test.go b/middleware/csrf/csrf_test.go index fe39271977..99cbf85e48 100644 --- a/middleware/csrf/csrf_test.go +++ b/middleware/csrf/csrf_test.go @@ -79,16 +79,16 @@ func Test_CSRF_WithSession(t *testing.T) { // fiber context ctx := &fasthttp.RequestCtx{} - defer app.ReleaseCtx(app.NewCtx(ctx)) + defer app.ReleaseCtx(app.AcquireCtx(ctx)) // get session - sess, err := store.Get(app.NewCtx(ctx)) + sess, err := store.Get(app.AcquireCtx(ctx)) require.NoError(t, err) require.True(t, sess.Fresh()) // the session string is no longer be 123 newSessionIDString := sess.ID() - app.NewCtx(ctx).Request().Header.SetCookie("_session", newSessionIDString) + app.AcquireCtx(ctx).Request().Header.SetCookie("_session", newSessionIDString) // middleware config config := Config{ @@ -212,16 +212,16 @@ func Test_CSRF_ExpiredToken_WithSession(t *testing.T) { // fiber context ctx := &fasthttp.RequestCtx{} - defer app.ReleaseCtx(app.NewCtx(ctx)) + defer app.ReleaseCtx(app.AcquireCtx(ctx)) // get session - sess, err := store.Get(app.NewCtx(ctx)) + sess, err := store.Get(app.AcquireCtx(ctx)) require.NoError(t, err) require.True(t, sess.Fresh()) // get session id newSessionIDString := sess.ID() - app.NewCtx(ctx).Request().Header.SetCookie("_session", newSessionIDString) + app.AcquireCtx(ctx).Request().Header.SetCookie("_session", newSessionIDString) // middleware config config := Config{ @@ -714,9 +714,9 @@ func Test_CSRF_DeleteToken(t *testing.T) { ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(HeaderName, token) ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) - handler := HandlerFromContext(app.NewCtx(ctx)) + handler := HandlerFromContext(app.AcquireCtx(ctx)) if handler != nil { - if err := handler.DeleteToken(app.NewCtx(ctx)); err != nil { + if err := handler.DeleteToken(app.AcquireCtx(ctx)); err != nil { t.Fatal(err) } } @@ -746,13 +746,13 @@ func Test_CSRF_DeleteToken_WithSession(t *testing.T) { ctx := &fasthttp.RequestCtx{} // get session - sess, err := store.Get(app.NewCtx(ctx)) + sess, err := store.Get(app.AcquireCtx(ctx)) require.NoError(t, err) require.True(t, sess.Fresh()) // the session string is no longer be 123 newSessionIDString := sess.ID() - app.NewCtx(ctx).Request().Header.SetCookie("_session", newSessionIDString) + app.AcquireCtx(ctx).Request().Header.SetCookie("_session", newSessionIDString) // middleware config config := Config{ @@ -781,9 +781,9 @@ func Test_CSRF_DeleteToken_WithSession(t *testing.T) { ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(HeaderName, token) ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) - handler := HandlerFromContext(app.NewCtx(ctx)) + handler := HandlerFromContext(app.AcquireCtx(ctx)) if handler != nil { - if err := handler.DeleteToken(app.NewCtx(ctx)); err != nil { + if err := handler.DeleteToken(app.AcquireCtx(ctx)); err != nil { t.Fatal(err) } } @@ -949,7 +949,7 @@ func Test_CSRF_Cookie_Injection_Exploit(t *testing.T) { // }) // resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) -// require.Equal(t, nil, err) +// require.NoError(t, err) // require.Equal(t, fiber.StatusOK, resp.StatusCode) // var token string diff --git a/middleware/earlydata/earlydata_test.go b/middleware/earlydata/earlydata_test.go index 9ec76d6b03..9a62bf2524 100644 --- a/middleware/earlydata/earlydata_test.go +++ b/middleware/earlydata/earlydata_test.go @@ -1,10 +1,8 @@ -//nolint:bodyclose // Much easier to just ignore memory leaks in tests package earlydata_test import ( "errors" "fmt" - "net/http" "net/http/httptest" "testing" @@ -90,7 +88,7 @@ func Test_EarlyData(t *testing.T) { t.Helper() { - req := httptest.NewRequest(fiber.MethodGet, "/", http.NoBody) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err := app.Test(req) require.NoError(t, err) @@ -108,7 +106,7 @@ func Test_EarlyData(t *testing.T) { } { - req := httptest.NewRequest(fiber.MethodPost, "/", http.NoBody) + req := httptest.NewRequest(fiber.MethodPost, "/", nil) resp, err := app.Test(req) require.NoError(t, err) @@ -130,7 +128,7 @@ func Test_EarlyData(t *testing.T) { t.Helper() { - req := httptest.NewRequest(fiber.MethodGet, "/", http.NoBody) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err := app.Test(req) require.NoError(t, err) @@ -148,7 +146,7 @@ func Test_EarlyData(t *testing.T) { } { - req := httptest.NewRequest(fiber.MethodPost, "/", http.NoBody) + req := httptest.NewRequest(fiber.MethodPost, "/", nil) resp, err := app.Test(req) require.NoError(t, err) diff --git a/middleware/encryptcookie/config.go b/middleware/encryptcookie/config.go index a68877308d..23c63bcd14 100644 --- a/middleware/encryptcookie/config.go +++ b/middleware/encryptcookie/config.go @@ -36,7 +36,7 @@ type Config struct { // ConfigDefault is the default config var ConfigDefault = Config{ Next: nil, - Except: []string{"csrf_"}, + Except: []string{}, Key: "", Encryptor: EncryptCookie, Decryptor: DecryptCookie, diff --git a/middleware/envvar/envvar_test.go b/middleware/envvar/envvar_test.go index c4964884a1..8c2fbeb05b 100644 --- a/middleware/envvar/envvar_test.go +++ b/middleware/envvar/envvar_test.go @@ -1,4 +1,3 @@ -//nolint:bodyclose // Much easier to just ignore memory leaks in tests package envvar import ( diff --git a/middleware/favicon/favicon_test.go b/middleware/favicon/favicon_test.go index 1a40684a5f..ecf550b2a3 100644 --- a/middleware/favicon/favicon_test.go +++ b/middleware/favicon/favicon_test.go @@ -1,4 +1,3 @@ -//nolint:bodyclose // Much easier to just ignore memory leaks in tests package favicon import ( diff --git a/middleware/filesystem/filesystem_test.go b/middleware/filesystem/filesystem_test.go index 8a2072b2b5..feccdd128f 100644 --- a/middleware/filesystem/filesystem_test.go +++ b/middleware/filesystem/filesystem_test.go @@ -1,4 +1,3 @@ -//nolint:bodyclose // Much easier to just ignore memory leaks in tests package filesystem import ( diff --git a/middleware/healthcheck/config.go b/middleware/healthcheck/config.go new file mode 100644 index 0000000000..59916fc869 --- /dev/null +++ b/middleware/healthcheck/config.go @@ -0,0 +1,84 @@ +package healthcheck + +import ( + "github.com/gofiber/fiber/v3" +) + +// Config defines the configuration options for the healthcheck middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(fiber.Ctx) bool + + // Function used for checking the liveness of the application. Returns true if the application + // is running and false if it is not. The liveness probe is typically used to indicate if + // the application is in a state where it can handle requests (e.g., the server is up and running). + // + // Optional. Default: func(c fiber.Ctx) bool { return true } + LivenessProbe HealthChecker + + // HTTP endpoint at which the liveness probe will be available. + // + // Optional. Default: "/livez" + LivenessEndpoint string + + // Function used for checking the readiness of the application. Returns true if the application + // is ready to process requests and false otherwise. The readiness probe typically checks if all necessary + // services, databases, and other dependencies are available for the application to function correctly. + // + // Optional. Default: func(c fiber.Ctx) bool { return true } + ReadinessProbe HealthChecker + + // HTTP endpoint at which the readiness probe will be available. + // Optional. Default: "/readyz" + ReadinessEndpoint string +} + +const ( + DefaultLivenessEndpoint = "/livez" + DefaultReadinessEndpoint = "/readyz" +) + +func defaultLivenessProbe(fiber.Ctx) bool { return true } + +func defaultReadinessProbe(fiber.Ctx) bool { return true } + +// ConfigDefault is the default config +var ConfigDefault = Config{ + LivenessProbe: defaultLivenessProbe, + ReadinessProbe: defaultReadinessProbe, + LivenessEndpoint: DefaultLivenessEndpoint, + ReadinessEndpoint: DefaultReadinessEndpoint, +} + +// defaultConfig returns a default config for the healthcheck middleware. +func defaultConfig(config ...Config) Config { + if len(config) < 1 { + return ConfigDefault + } + + cfg := config[0] + + if cfg.Next == nil { + cfg.Next = ConfigDefault.Next + } + + if cfg.LivenessProbe == nil { + cfg.LivenessProbe = defaultLivenessProbe + } + + if cfg.ReadinessProbe == nil { + cfg.ReadinessProbe = defaultReadinessProbe + } + + if cfg.LivenessEndpoint == "" { + cfg.LivenessEndpoint = DefaultLivenessEndpoint + } + + if cfg.ReadinessEndpoint == "" { + cfg.ReadinessEndpoint = DefaultReadinessEndpoint + } + + return cfg +} diff --git a/middleware/healthcheck/healthcheck.go b/middleware/healthcheck/healthcheck.go new file mode 100644 index 0000000000..222b6f4452 --- /dev/null +++ b/middleware/healthcheck/healthcheck.go @@ -0,0 +1,62 @@ +package healthcheck + +import ( + "strings" + + "github.com/gofiber/fiber/v3" +) + +// HealthChecker defines a function to check liveness or readiness of the application +type HealthChecker func(fiber.Ctx) bool + +// HealthCheckerHandler defines a function that returns a HealthChecker +type HealthCheckerHandler func(HealthChecker) fiber.Handler + +func healthCheckerHandler(checker HealthChecker) fiber.Handler { + return func(c fiber.Ctx) error { + if checker == nil { + return c.Next() + } + + if checker(c) { + return c.SendStatus(fiber.StatusOK) + } + + return c.SendStatus(fiber.StatusServiceUnavailable) + } +} + +func New(config ...Config) fiber.Handler { + cfg := defaultConfig(config...) + + isLiveHandler := healthCheckerHandler(cfg.LivenessProbe) + isReadyHandler := healthCheckerHandler(cfg.ReadinessProbe) + + return func(c fiber.Ctx) error { + // Don't execute middleware if Next returns true + if cfg.Next != nil && cfg.Next(c) { + return c.Next() + } + + if c.Method() != fiber.MethodGet { + return c.Next() + } + + prefixCount := len(strings.TrimRight(c.Route().Path, "/")) + if len(c.Path()) >= prefixCount { + checkPath := c.Path()[prefixCount:] + checkPathTrimmed := checkPath + if !c.App().Config().StrictRouting { + checkPathTrimmed = strings.TrimRight(checkPath, "/") + } + switch { + case checkPath == cfg.ReadinessEndpoint || checkPathTrimmed == cfg.ReadinessEndpoint: + return isReadyHandler(c) + case checkPath == cfg.LivenessEndpoint || checkPathTrimmed == cfg.LivenessEndpoint: + return isLiveHandler(c) + } + } + + return c.Next() + } +} diff --git a/middleware/healthcheck/healthcheck_test.go b/middleware/healthcheck/healthcheck_test.go new file mode 100644 index 0000000000..409b985a60 --- /dev/null +++ b/middleware/healthcheck/healthcheck_test.go @@ -0,0 +1,240 @@ +package healthcheck + +import ( + "net/http/httptest" + "strconv" + "testing" + + "github.com/gofiber/fiber/v3" + "github.com/stretchr/testify/require" + "github.com/valyala/fasthttp" +) + +func shouldGiveStatus(t *testing.T, app *fiber.App, path string, expectedStatus int) { + t.Helper() + req, err := app.Test(httptest.NewRequest(fiber.MethodGet, path, nil)) + require.NoError(t, err) + require.Equal(t, expectedStatus, req.StatusCode, "path: "+path+" should match "+strconv.Itoa(expectedStatus)) +} + +func shouldGiveOK(t *testing.T, app *fiber.App, path string) { + t.Helper() + shouldGiveStatus(t, app, path, fiber.StatusOK) +} + +func shouldGiveNotFound(t *testing.T, app *fiber.App, path string) { + t.Helper() + shouldGiveStatus(t, app, path, fiber.StatusNotFound) +} + +func Test_HealthCheck_Strict_Routing_Default(t *testing.T) { + t.Parallel() + + app := fiber.New(fiber.Config{ + StrictRouting: true, + }) + + app.Use(New()) + + shouldGiveOK(t, app, "/readyz") + shouldGiveOK(t, app, "/livez") + shouldGiveNotFound(t, app, "/readyz/") + shouldGiveNotFound(t, app, "/livez/") + shouldGiveNotFound(t, app, "/notDefined/readyz") + shouldGiveNotFound(t, app, "/notDefined/livez") +} + +func Test_HealthCheck_Group_Default(t *testing.T) { + t.Parallel() + + app := fiber.New() + app.Group("/v1", New()) + v2Group := app.Group("/v2/") + customer := v2Group.Group("/customer/") + customer.Use(New()) + + v3Group := app.Group("/v3/") + v3Group.Group("/todos/", New(Config{ReadinessEndpoint: "/readyz/", LivenessEndpoint: "/livez/"})) + + // Testing health check endpoints in versioned API groups + shouldGiveOK(t, app, "/v1/readyz") + shouldGiveOK(t, app, "/v1/livez") + shouldGiveOK(t, app, "/v1/readyz/") + shouldGiveOK(t, app, "/v1/livez/") + shouldGiveOK(t, app, "/v2/customer/readyz") + shouldGiveOK(t, app, "/v2/customer/livez") + shouldGiveOK(t, app, "/v2/customer/readyz/") + shouldGiveOK(t, app, "/v2/customer/livez/") + shouldGiveNotFound(t, app, "/v3/todos/readyz") + shouldGiveNotFound(t, app, "/v3/todos/livez") + shouldGiveOK(t, app, "/v3/todos/readyz/") + shouldGiveOK(t, app, "/v3/todos/livez/") + shouldGiveNotFound(t, app, "/notDefined/readyz") + shouldGiveNotFound(t, app, "/notDefined/livez") + shouldGiveNotFound(t, app, "/notDefined/readyz/") + shouldGiveNotFound(t, app, "/notDefined/livez/") + + // strict routing + app = fiber.New(fiber.Config{ + StrictRouting: true, + }) + app.Group("/v1", New()) + v2Group = app.Group("/v2/") + customer = v2Group.Group("/customer/") + customer.Use(New()) + + v3Group = app.Group("/v3/") + v3Group.Group("/todos/", New(Config{ReadinessEndpoint: "/readyz/", LivenessEndpoint: "/livez/"})) + + shouldGiveOK(t, app, "/v1/readyz") + shouldGiveOK(t, app, "/v1/livez") + shouldGiveNotFound(t, app, "/v1/readyz/") + shouldGiveNotFound(t, app, "/v1/livez/") + shouldGiveOK(t, app, "/v2/customer/readyz") + shouldGiveOK(t, app, "/v2/customer/livez") + shouldGiveNotFound(t, app, "/v2/customer/readyz/") + shouldGiveNotFound(t, app, "/v2/customer/livez/") + shouldGiveNotFound(t, app, "/v3/todos/readyz") + shouldGiveNotFound(t, app, "/v3/todos/livez") + shouldGiveOK(t, app, "/v3/todos/readyz/") + shouldGiveOK(t, app, "/v3/todos/livez/") + shouldGiveNotFound(t, app, "/notDefined/readyz") + shouldGiveNotFound(t, app, "/notDefined/livez") + shouldGiveNotFound(t, app, "/notDefined/readyz/") + shouldGiveNotFound(t, app, "/notDefined/livez/") +} + +func Test_HealthCheck_Default(t *testing.T) { + t.Parallel() + + app := fiber.New() + app.Use(New()) + + shouldGiveOK(t, app, "/readyz") + shouldGiveOK(t, app, "/livez") + shouldGiveOK(t, app, "/readyz/") + shouldGiveOK(t, app, "/livez/") + shouldGiveNotFound(t, app, "/notDefined/readyz") + shouldGiveNotFound(t, app, "/notDefined/livez") +} + +func Test_HealthCheck_Custom(t *testing.T) { + t.Parallel() + + app := fiber.New() + + c1 := make(chan struct{}, 1) + app.Use(New(Config{ + LivenessProbe: func(_ fiber.Ctx) bool { + return true + }, + LivenessEndpoint: "/live", + ReadinessProbe: func(_ fiber.Ctx) bool { + select { + case <-c1: + return true + default: + return false + } + }, + ReadinessEndpoint: "/ready", + })) + + // Setup custom liveness and readiness probes to simulate application health status + // Live should return 200 with GET request + shouldGiveOK(t, app, "/live") + // Live should return 404 with POST request + req, err := app.Test(httptest.NewRequest(fiber.MethodPost, "/live", nil)) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, req.StatusCode) + + // Ready should return 404 with POST request + req, err = app.Test(httptest.NewRequest(fiber.MethodPost, "/ready", nil)) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, req.StatusCode) + + // Ready should return 503 with GET request before the channel is closed + shouldGiveStatus(t, app, "/ready", fiber.StatusServiceUnavailable) + + // Ready should return 200 with GET request after the channel is closed + c1 <- struct{}{} + shouldGiveOK(t, app, "/ready") +} + +func Test_HealthCheck_Custom_Nested(t *testing.T) { + t.Parallel() + + app := fiber.New() + + c1 := make(chan struct{}, 1) + + app.Use(New(Config{ + LivenessProbe: func(_ fiber.Ctx) bool { + return true + }, + LivenessEndpoint: "/probe/live", + ReadinessProbe: func(_ fiber.Ctx) bool { + select { + case <-c1: + return true + default: + return false + } + }, + ReadinessEndpoint: "/probe/ready", + })) + + // Testing custom health check endpoints with nested paths + shouldGiveOK(t, app, "/probe/live") + shouldGiveStatus(t, app, "/probe/ready", fiber.StatusServiceUnavailable) + shouldGiveOK(t, app, "/probe/live/") + shouldGiveStatus(t, app, "/probe/ready/", fiber.StatusServiceUnavailable) + shouldGiveNotFound(t, app, "/probe/livez") + shouldGiveNotFound(t, app, "/probe/readyz") + shouldGiveNotFound(t, app, "/probe/livez/") + shouldGiveNotFound(t, app, "/probe/readyz/") + shouldGiveNotFound(t, app, "/livez") + shouldGiveNotFound(t, app, "/readyz") + shouldGiveNotFound(t, app, "/readyz/") + shouldGiveNotFound(t, app, "/livez/") + + c1 <- struct{}{} + shouldGiveOK(t, app, "/probe/ready") + c1 <- struct{}{} + shouldGiveOK(t, app, "/probe/ready/") +} + +func Test_HealthCheck_Next(t *testing.T) { + t.Parallel() + + app := fiber.New() + + app.Use(New(Config{ + Next: func(_ fiber.Ctx) bool { + return true + }, + })) + + shouldGiveNotFound(t, app, "/readyz") + shouldGiveNotFound(t, app, "/livez") +} + +func Benchmark_HealthCheck(b *testing.B) { + app := fiber.New() + + app.Use(New()) + + h := app.Handler() + fctx := &fasthttp.RequestCtx{} + fctx.Request.Header.SetMethod(fiber.MethodGet) + fctx.Request.SetRequestURI("/livez") + + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + h(fctx) + } + + require.Equal(b, fiber.StatusOK, fctx.Response.Header.StatusCode()) +} diff --git a/middleware/idempotency/idempotency_test.go b/middleware/idempotency/idempotency_test.go index c70e8fd522..f4d52d3c29 100644 --- a/middleware/idempotency/idempotency_test.go +++ b/middleware/idempotency/idempotency_test.go @@ -1,10 +1,8 @@ -//nolint:bodyclose // Much easier to just ignore memory leaks in tests package idempotency_test import ( "errors" "io" - "net/http" "net/http/httptest" "strconv" "sync" @@ -83,7 +81,7 @@ func Test_Idempotency(t *testing.T) { }) doReq := func(method, route, idempotencyKey string) string { - req := httptest.NewRequest(method, route, http.NoBody) + req := httptest.NewRequest(method, route, nil) if idempotencyKey != "" { req.Header.Set("X-Idempotency-Key", idempotencyKey) } diff --git a/middleware/keyauth/keyauth_test.go b/middleware/keyauth/keyauth_test.go index 2d955ac239..5b8d7d57a6 100644 --- a/middleware/keyauth/keyauth_test.go +++ b/middleware/keyauth/keyauth_test.go @@ -1,4 +1,3 @@ -//nolint:bodyclose // Much easier to just ignore memory leaks in tests package keyauth import ( diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 060a5de813..dcf4d45241 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -28,7 +28,7 @@ type Config struct { // Format defines the logging tags // - // Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n + // Optional. Default: [${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error} Format string // TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html @@ -109,7 +109,7 @@ var ConfigDefault = Config{ } // default logging format for Fiber's default logger -var defaultFormat = "[${time}] ${status} - ${latency} ${method} ${path}\n" +var defaultFormat = "[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n" // Helper function to set default values func configDefault(config ...Config) Config { diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 2694e5112d..0fb35ad121 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -1,4 +1,3 @@ -//nolint:bodyclose // Much easier to just ignore memory leaks in tests package logger import ( diff --git a/middleware/logger/tags.go b/middleware/logger/tags.go index 3c383dee7d..8d147fd05d 100644 --- a/middleware/logger/tags.go +++ b/middleware/logger/tags.go @@ -144,8 +144,12 @@ func createTagMap(cfg *Config) map[string]LogFunc { TagReset: func(output Buffer, c fiber.Ctx, _ *Data, _ string) (int, error) { return output.WriteString(c.App().Config().ColorScheme.Reset) }, - TagError: func(output Buffer, _ fiber.Ctx, data *Data, _ string) (int, error) { + TagError: func(output Buffer, c fiber.Ctx, data *Data, _ string) (int, error) { if data.ChainErr != nil { + if cfg.enableColors { + colors := c.App().Config().ColorScheme + return output.WriteString(fmt.Sprintf("%s%s%s", colors.Red, data.ChainErr.Error(), colors.Reset)) + } return output.WriteString(data.ChainErr.Error()) } return output.WriteString("-") diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index 86ea58efe6..408ee71a5f 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -299,17 +299,17 @@ func Test_Proxy_Timeout_Slow_Server(t *testing.T) { t.Parallel() _, addr := createProxyTestServer(t, func(c fiber.Ctx) error { - time.Sleep(2 * time.Second) + time.Sleep(300 * time.Millisecond) return c.SendString("fiber is awesome") }) app := fiber.New() app.Use(Balancer(Config{ Servers: []string{addr}, - Timeout: 3 * time.Second, + Timeout: 600 * time.Millisecond, })) - resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 5000) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 2000) require.NoError(t, err) require.Equal(t, fiber.StatusOK, resp.StatusCode) @@ -492,7 +492,7 @@ func Test_Proxy_DoTimeout_Timeout(t *testing.T) { return DoTimeout(c, "http://"+addr, time.Second) }) - _, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + _, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil), int((1*time.Second)/time.Millisecond)) require.Equal(t, errors.New("test: timeout error 1000ms"), err1) } @@ -532,7 +532,7 @@ func Test_Proxy_DoDeadline_PastDeadline(t *testing.T) { return DoDeadline(c, "http://"+addr, time.Now().Add(time.Second)) }) - _, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + _, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil), int((1*time.Second)/time.Millisecond)) require.Equal(t, errors.New("test: timeout error 1000ms"), err1) } diff --git a/middleware/redirect/redirect.go b/middleware/redirect/redirect.go index 267f010e69..4fb7bb8830 100644 --- a/middleware/redirect/redirect.go +++ b/middleware/redirect/redirect.go @@ -30,7 +30,11 @@ func New(config ...Config) fiber.Handler { for k, v := range cfg.rulesRegex { replacer := captureTokens(k, c.Path()) if replacer != nil { - return c.Redirect().Status(cfg.StatusCode).To(replacer.Replace(v)) + queryString := string(c.Context().QueryArgs().QueryString()) + if queryString != "" { + queryString = "?" + queryString + } + return c.Redirect().Status(cfg.StatusCode).To(replacer.Replace(v) + queryString) } } return c.Next() diff --git a/middleware/redirect/redirect_test.go b/middleware/redirect/redirect_test.go index b5692aa671..612454b8b3 100644 --- a/middleware/redirect/redirect_test.go +++ b/middleware/redirect/redirect_test.go @@ -1,4 +1,3 @@ -//nolint:bodyclose // Much easier to just ignore memory leaks in tests package redirect import ( @@ -44,6 +43,12 @@ func Test_Redirect(t *testing.T) { }, StatusCode: fiber.StatusMovedPermanently, })) + app.Use(New(Config{ + Rules: map[string]string{ + "/params": "/with_params", + }, + StatusCode: fiber.StatusMovedPermanently, + })) app.Get("/api/*", func(c fiber.Ctx) error { return c.SendString("API") @@ -104,6 +109,12 @@ func Test_Redirect(t *testing.T) { url: "/api/test", statusCode: fiber.StatusOK, }, + { + name: "redirect with query params", + url: "/params?query=abc", + redirectTo: "/with_params?query=abc", + statusCode: fiber.StatusMovedPermanently, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/middleware/rewrite/rewrite_test.go b/middleware/rewrite/rewrite_test.go index bedbbcfa47..4cd1c4d00b 100644 --- a/middleware/rewrite/rewrite_test.go +++ b/middleware/rewrite/rewrite_test.go @@ -1,4 +1,3 @@ -//nolint:bodyclose // Much easier to just ignore memory leaks in tests package rewrite import ( diff --git a/middleware/session/session_test.go b/middleware/session/session_test.go index 94ddd8b798..ab2741b95e 100644 --- a/middleware/session/session_test.go +++ b/middleware/session/session_test.go @@ -21,7 +21,8 @@ func Test_Session(t *testing.T) { app := fiber.New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) // set session ctx.Request().Header.SetCookie(store.sessionName, "123") @@ -69,7 +70,7 @@ func Test_Session(t *testing.T) { require.NoError(t, err) // requesting entirely new context to prevent falsy tests - ctx = app.NewCtx(&fasthttp.RequestCtx{}) + ctx = app.AcquireCtx(&fasthttp.RequestCtx{}) sess, err = store.Get(ctx) require.NoError(t, err) @@ -80,7 +81,9 @@ func Test_Session(t *testing.T) { // when we use the original session for the second time // the session be should be same if the session is not expired - ctx = app.NewCtx(&fasthttp.RequestCtx{}) + app.ReleaseCtx(ctx) + ctx = app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) // request the server with the old session ctx.Request().Header.SetCookie(store.sessionName, id) @@ -101,7 +104,8 @@ func Test_Session_Types(t *testing.T) { app := fiber.New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) // set cookie ctx.Request().Header.SetCookie(store.sessionName, "123") @@ -265,7 +269,8 @@ func Test_Session_Store_Reset(t *testing.T) { // fiber instance app := fiber.New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) // get session sess, err := store.Get(ctx) @@ -298,7 +303,7 @@ func Test_Session_Save(t *testing.T) { // fiber instance app := fiber.New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) // get session sess, err := store.Get(ctx) @@ -320,7 +325,8 @@ func Test_Session_Save(t *testing.T) { // fiber instance app := fiber.New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) // get session sess, err := store.Get(ctx) @@ -347,7 +353,8 @@ func Test_Session_Save_Expiration(t *testing.T) { // fiber instance app := fiber.New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) // get session sess, err := store.Get(ctx) @@ -389,7 +396,8 @@ func Test_Session_Destroy(t *testing.T) { // fiber instance app := fiber.New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) // get session sess, err := store.Get(ctx) @@ -410,7 +418,8 @@ func Test_Session_Destroy(t *testing.T) { // fiber instance app := fiber.New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) // get session sess, err := store.Get(ctx) @@ -449,7 +458,8 @@ func Test_Session_Cookie(t *testing.T) { // fiber instance app := fiber.New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) // get session sess, err := store.Get(ctx) @@ -467,7 +477,8 @@ func Test_Session_Cookie_In_Response(t *testing.T) { app := fiber.New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) // get session sess, err := store.Get(ctx) @@ -492,7 +503,8 @@ func Test_Session_Deletes_Single_Key(t *testing.T) { store := New() app := fiber.New() - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) sess, err := store.Get(ctx) require.NoError(t, err) @@ -521,11 +533,11 @@ func Test_Session_Reset(t *testing.T) { // session store store := New() - // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) - t.Run("reset session data and id, and set fresh to be true", func(t *testing.T) { t.Parallel() + // fiber context + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) // a random session uuid originalSessionUUIDString := "" @@ -590,7 +602,8 @@ func Test_Session_Regenerate(t *testing.T) { // a random session uuid originalSessionUUIDString := "" // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(ctx) // now the session is in the storage freshSession, err := store.Get(ctx) @@ -622,7 +635,7 @@ func Test_Session_Regenerate(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Session -benchmem -count=4 func Benchmark_Session(b *testing.B) { app, store := fiber.New(), New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) c.Request().Header.SetCookie(store.sessionName, "12356789") diff --git a/middleware/session/store_test.go b/middleware/session/store_test.go index bd7160fa2d..130f13feba 100644 --- a/middleware/session/store_test.go +++ b/middleware/session/store_test.go @@ -22,7 +22,7 @@ func TestStore_getSessionID(t *testing.T) { // session store store := New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) // set cookie ctx.Request().Header.SetCookie(store.sessionName, expectedID) @@ -37,7 +37,7 @@ func TestStore_getSessionID(t *testing.T) { KeyLookup: "header:session_id", }) // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) // set header ctx.Request().Header.Set(store.sessionName, expectedID) @@ -52,7 +52,7 @@ func TestStore_getSessionID(t *testing.T) { KeyLookup: "query:session_id", }) // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) // set url parameter ctx.Request().SetRequestURI(fmt.Sprintf("/path?%s=%s", store.sessionName, expectedID)) @@ -73,7 +73,7 @@ func TestStore_Get(t *testing.T) { // session store store := New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) // set cookie ctx.Request().Header.SetCookie(store.sessionName, unexpectedID) @@ -94,7 +94,7 @@ func TestStore_DeleteSession(t *testing.T) { store := New() // fiber context - ctx := app.NewCtx(&fasthttp.RequestCtx{}) + ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) // Create a new session session, err := store.Get(ctx) diff --git a/middleware/timeout/timeout.go b/middleware/timeout/timeout.go index 7baa0e7056..5a9711ce22 100644 --- a/middleware/timeout/timeout.go +++ b/middleware/timeout/timeout.go @@ -3,57 +3,13 @@ package timeout import ( "context" "errors" - "sync" "time" - "github.com/gofiber/fiber/v3/log" - "github.com/gofiber/fiber/v3" ) -var once sync.Once - -// New wraps a handler and aborts the process of the handler if the timeout is reached. -// -// Deprecated: This implementation contains data race issues. Use NewWithContext instead. -// Find documentation and sample usage on https://docs.gofiber.io/api/middleware/timeout -func New(handler fiber.Handler, timeout time.Duration) fiber.Handler { - once.Do(func() { - log.Warn("[TIMEOUT] timeout contains data race issues, not ready for production!") - }) - - if timeout <= 0 { - return handler - } - - // logic is from fasthttp.TimeoutWithCodeHandler https://github.com/valyala/fasthttp/blob/master/server.go#L418 - return func(c fiber.Ctx) error { - ch := make(chan struct{}, 1) - - go func() { - defer func() { - if err := recover(); err != nil { - log.Errorf("[TIMEOUT] recover error %v", err) - } - }() - if err := handler(c); err != nil { - log.Errorf("[TIMEOUT] handler error %v", err) - } - ch <- struct{}{} - }() - - select { - case <-ch: - case <-time.After(timeout): - return fiber.ErrRequestTimeout - } - - return nil - } -} - -// NewWithContext implementation of timeout middleware. Set custom errors(context.DeadlineExceeded vs) for get fiber.ErrRequestTimeout response. -func NewWithContext(h fiber.Handler, t time.Duration, tErrs ...error) fiber.Handler { +// New implementation of timeout middleware. Set custom errors(context.DeadlineExceeded vs) for get fiber.ErrRequestTimeout response. +func New(h fiber.Handler, t time.Duration, tErrs ...error) fiber.Handler { return func(ctx fiber.Ctx) error { timeoutContext, cancel := context.WithTimeout(ctx.UserContext(), t) defer cancel() diff --git a/middleware/timeout/timeout_test.go b/middleware/timeout/timeout_test.go index 87292c66f6..b08445eb2a 100644 --- a/middleware/timeout/timeout_test.go +++ b/middleware/timeout/timeout_test.go @@ -17,7 +17,7 @@ func Test_WithContextTimeout(t *testing.T) { t.Parallel() // fiber instance app := fiber.New() - h := NewWithContext(func(c fiber.Ctx) error { + h := New(func(c fiber.Ctx) error { sleepTime, err := time.ParseDuration(c.Params("sleepTime") + "ms") require.NoError(t, err) if err := sleepWithContext(c.UserContext(), sleepTime, context.DeadlineExceeded); err != nil { @@ -49,7 +49,7 @@ func Test_WithContextTimeoutWithCustomError(t *testing.T) { t.Parallel() // fiber instance app := fiber.New() - h := NewWithContext(func(c fiber.Ctx) error { + h := New(func(c fiber.Ctx) error { sleepTime, err := time.ParseDuration(c.Params("sleepTime") + "ms") require.NoError(t, err) if err := sleepWithContext(c.UserContext(), sleepTime, ErrFooTimeOut); err != nil { diff --git a/mount_test.go b/mount_test.go index 1ec62c0ef6..b2c82c2952 100644 --- a/mount_test.go +++ b/mount_test.go @@ -2,13 +2,11 @@ // πŸ€– 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 ( "errors" "io" - "net/http" "net/http/httptest" "testing" @@ -25,7 +23,7 @@ func Test_App_Mount(t *testing.T) { app := New() app.Use("/john", micro) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") require.Equal(t, uint32(1), app.handlersCount) @@ -45,7 +43,7 @@ func Test_App_Mount_RootPath_Nested(t *testing.T) { dynamic.Use("/api", apiserver) app.Use("/", dynamic) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/v1/home", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/v1/home", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") require.Equal(t, uint32(1), app.handlersCount) @@ -75,15 +73,15 @@ func Test_App_Mount_Nested(t *testing.T) { return c.SendStatus(StatusOK) }) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/one/doe", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/one/doe", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") - resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/nested", http.NoBody)) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/nested", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") - resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/three/test", http.NoBody)) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/three/test", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") @@ -100,7 +98,7 @@ func Test_App_Mount_Express_Behavior(t *testing.T) { } } testEndpoint := func(app *App, route, expectedBody string, expectedStatusCode int) { - resp, err := app.Test(httptest.NewRequest(MethodGet, route, http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, route, nil)) require.NoError(t, err, "app.Test(req)") body, err := io.ReadAll(resp.Body) require.NoError(t, err) @@ -145,7 +143,7 @@ func Test_App_Mount_Express_Behavior(t *testing.T) { func Test_App_Mount_RoutePositions(t *testing.T) { t.Parallel() testEndpoint := func(app *App, route, expectedBody string) { - resp, err := app.Test(httptest.NewRequest(MethodGet, route, http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, route, nil)) require.NoError(t, err, "app.Test(req)") body, err := io.ReadAll(resp.Body) require.NoError(t, err) @@ -245,7 +243,7 @@ func Test_App_ErrorHandler_GroupMount(t *testing.T) { v1 := app.Group("/v1") v1.Use("/john", micro) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) testErrorResponse(t, err, resp, "1: custom error") } @@ -265,7 +263,7 @@ func Test_App_ErrorHandler_GroupMountRootLevel(t *testing.T) { v1 := app.Group("/v1") v1.Use("/", micro) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) testErrorResponse(t, err, resp, "1: custom error") } @@ -281,7 +279,7 @@ func Test_App_Group_Mount(t *testing.T) { v1 := app.Group("/v1") v1.Use("/john", micro) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") require.Equal(t, uint32(1), app.handlersCount) @@ -302,7 +300,7 @@ func Test_App_UseParentErrorHandler(t *testing.T) { app.Use("/api", fiber) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) testErrorResponse(t, err, resp, "hi, i'm a custom error") } @@ -321,7 +319,7 @@ func Test_App_UseMountedErrorHandler(t *testing.T) { app.Use("/api", fiber) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) testErrorResponse(t, err, resp, "hi, i'm a custom error") } @@ -340,7 +338,7 @@ func Test_App_UseMountedErrorHandlerRootLevel(t *testing.T) { app.Use("/", fiber) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) testErrorResponse(t, err, resp, "hi, i'm a custom error") } @@ -382,7 +380,7 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { app.Use("/api", fiber) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub", nil)) require.NoError(t, err, "/api/sub req") require.Equal(t, 200, resp.StatusCode, "Status code") @@ -390,7 +388,7 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { require.NoError(t, err, "iotuil.ReadAll()") require.Equal(t, "hi, i'm a custom sub fiber error", string(b), "Response body") - resp2, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub/third", http.NoBody)) + resp2, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub/third", nil)) require.NoError(t, err, "/api/sub/third req") require.Equal(t, 200, resp.StatusCode, "Status code") @@ -493,7 +491,7 @@ func Test_Ctx_Render_Mount(t *testing.T) { app := New() app.Use("/hello", sub) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/a", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/a", nil)) require.Equal(t, StatusOK, resp.StatusCode, "Status code") require.NoError(t, err, "app.Test(req)") @@ -549,7 +547,7 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { sub.Use("/bruh", sub2) app.Use("/hello", sub) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/world/a", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/world/a", nil)) require.Equal(t, StatusOK, resp.StatusCode, "Status code") require.NoError(t, err, "app.Test(req)") @@ -557,7 +555,7 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { require.NoError(t, err) require.Equal(t, "

Hello a!

", string(body)) - resp, err = app.Test(httptest.NewRequest(MethodGet, "/test", http.NoBody)) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/test", nil)) require.Equal(t, StatusOK, resp.StatusCode, "Status code") require.NoError(t, err, "app.Test(req)") @@ -565,7 +563,7 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { require.NoError(t, err) require.Equal(t, "

Hello, World!

", string(body)) - resp, err = app.Test(httptest.NewRequest(MethodGet, "/hello/bruh/moment", http.NoBody)) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/hello/bruh/moment", nil)) require.Equal(t, StatusOK, resp.StatusCode, "Status code") require.NoError(t, err, "app.Test(req)") @@ -595,7 +593,7 @@ func Test_Ctx_Render_MountGroup(t *testing.T) { v1 := app.Group("/v1") v1.Use("/john", micro) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", http.NoBody)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") diff --git a/redirect_test.go b/redirect_test.go index 65f437f190..d49f526771 100644 --- a/redirect_test.go +++ b/redirect_test.go @@ -21,7 +21,7 @@ import ( func Test_Redirect_To(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.Redirect().To("http://default.com") require.NoError(t, err) @@ -41,7 +41,7 @@ func Test_Redirect_Route_WithParams(t *testing.T) { app.Get("/user/:name", func(c Ctx) error { return c.JSON(c.Params("name")) }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.Redirect().Route("user", RedirectConfig{ Params: Map{ @@ -60,7 +60,7 @@ func Test_Redirect_Route_WithParams_WithQueries(t *testing.T) { app.Get("/user/:name", func(c Ctx) error { return c.JSON(c.Params("name")) }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.Redirect().Route("user", RedirectConfig{ Params: Map{ @@ -84,7 +84,7 @@ func Test_Redirect_Route_WithOptionalParams(t *testing.T) { app.Get("/user/:name?", func(c Ctx) error { return c.JSON(c.Params("name")) }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.Redirect().Route("user", RedirectConfig{ Params: Map{ @@ -103,7 +103,7 @@ func Test_Redirect_Route_WithOptionalParamsWithoutValue(t *testing.T) { app.Get("/user/:name?", func(c Ctx) error { return c.JSON(c.Params("name")) }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.Redirect().Route("user") require.NoError(t, err) @@ -118,7 +118,7 @@ func Test_Redirect_Route_WithGreedyParameters(t *testing.T) { app.Get("/user/+", func(c Ctx) error { return c.JSON(c.Params("+")) }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.Redirect().Route("user", RedirectConfig{ Params: Map{ @@ -137,7 +137,7 @@ func Test_Redirect_Back(t *testing.T) { app.Get("/", func(c Ctx) error { return c.JSON("Home") }).Name("home") - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.Redirect().Back("/") require.NoError(t, err) @@ -159,7 +159,7 @@ func Test_Redirect_Back_WithReferer(t *testing.T) { app.Get("/back", func(c Ctx) error { return c.JSON("Back") }).Name("back") - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderReferer, "/back") err := c.Redirect().Back("/") @@ -178,7 +178,7 @@ func Test_Redirect_Route_WithFlashMessages(t *testing.T) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed err := c.Redirect().With("success", "1").With("message", "test").Route("user") require.NoError(t, err) @@ -201,7 +201,7 @@ func Test_Redirect_Route_WithOldInput(t *testing.T) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().URI().SetQueryString("id=1&name=tom") err := c.Redirect().With("success", "1").With("message", "test").WithInput().Route("user") @@ -229,7 +229,7 @@ func Test_Redirect_setFlash(t *testing.T) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") @@ -336,7 +336,7 @@ func Benchmark_Redirect_Route(b *testing.B) { return c.JSON(c.Params("name")) }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -363,7 +363,7 @@ func Benchmark_Redirect_Route_WithQueries(b *testing.B) { return c.JSON(c.Params("name")) }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -395,7 +395,7 @@ func Benchmark_Redirect_Route_WithFlashMessages(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -424,7 +424,7 @@ func Benchmark_Redirect_setFlash(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") @@ -453,7 +453,7 @@ func Benchmark_Redirect_Messages(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") c.Redirect().setFlash() @@ -478,7 +478,7 @@ func Benchmark_Redirect_OldInputs(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") c.Redirect().setFlash() @@ -503,7 +503,7 @@ func Benchmark_Redirect_Message(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") c.Redirect().setFlash() @@ -528,7 +528,7 @@ func Benchmark_Redirect_OldInput(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") c.Redirect().setFlash() diff --git a/router.go b/router.go index 42d5a1760b..4347c7ba9f 100644 --- a/router.go +++ b/router.go @@ -208,14 +208,14 @@ func (app *App) requestHandler(rctx *fasthttp.RequestCtx) { var c CustomCtx var ok bool if app.newCtxFunc != nil { - c, ok = app.AcquireCtx().(CustomCtx) + c, ok = app.AcquireCtx(rctx).(CustomCtx) if !ok { - panic(errors.New("failed to type-assert to CustomCtx")) + panic(errors.New("requestHandler: failed to type-assert to CustomCtx")) } } else { - c, ok = app.AcquireCtx().(*DefaultCtx) + c, ok = app.AcquireCtx(rctx).(*DefaultCtx) if !ok { - panic(errors.New("failed to type-assert to *DefaultCtx")) + panic(errors.New("requestHandler: failed to type-assert to *DefaultCtx")) } } c.Reset(rctx) diff --git a/router_test.go b/router_test.go index 743a58edac..5b29bbc3c2 100644 --- a/router_test.go +++ b/router_test.go @@ -2,7 +2,6 @@ // πŸ“ƒ 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 ( @@ -651,7 +650,7 @@ func Benchmark_Router_Next(b *testing.B) { var res bool var err error - c := app.NewCtx(request).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(request).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ResetTimer() for n := 0; n < b.N; n++ { @@ -832,8 +831,7 @@ func Benchmark_Router_Github_API(b *testing.B) { for n := 0; n < b.N; n++ { c.URI().SetPath(routesFixture.TestRoutes[i].Path) - ctx := app.AcquireCtx().(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed - ctx.Reset(c) + ctx := app.AcquireCtx(c).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed match, err = app.next(ctx) app.ReleaseCtx(ctx)