mirror of https://github.com/gofiber/fiber.git
V2 to v3 merge (#2864)
* 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] <support@github.com>
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 🐛 (#2751)
* Revert "Revert "🐛 requestid.Config.ContextKey is interface{} (#2369)" (#2742)"
This reverts commit 28be17f929
.
* 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] <support@github.com>
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] <support@github.com>
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)
* ✨ feat: add liveness and readiness checks (#2509)
* ✨ feat: add liveness and readiness checkers
* 📝 docs: add docs for liveness and readiness
* ✨ feat: add options method for probe checkers
* ✅ tests: add tests for liveness and readiness
* ♻️ refactor: change default endpoint values
* ♻️ refactor: change default value for liveness endpoint
* 📝 docs: add return status for liveness and readiness probes
* ♻️ refactor: change probechecker to middleware
* 📝 docs: move docs to middleware session
* ♻️ refactor: apply gofumpt formatting
* ♻️ 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 <efectn@protonmail.com>
Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com>
Co-authored-by: Juan Calderon-Perez <jgcalderonperez@protonmail.com>
* 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
* 📚 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 <rene@gofiber.io>
* 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 <rene@gofiber.io>
* 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 <renanbastos.tec@gmail.com>
* 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] <support@github.com>
Co-authored-by: tokelo-12 <113810058+tokelo-12@users.noreply.github.com>
Co-authored-by: Jason McNeil <sixcolors@mac.com>
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 <ste3ls@gmail.com>
Co-authored-by: Mehmet Firat KOMURCU <mehmetfiratkomurcu@hotmail.com>
Co-authored-by: Bruno <bdm2943@icloud.com>
Co-authored-by: Muhammad Kholid B <muhammadkholidb@gmail.com>
Co-authored-by: gilwo <gilwo@users.noreply.github.com>
Co-authored-by: Lucas Lemos <lucashenriqueblemos@gmail.com>
Co-authored-by: Muhammed Efe Cetin <efectn@protonmail.com>
Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com>
Co-authored-by: Juan Calderon-Perez <jgcalderonperez@protonmail.com>
Co-authored-by: Jongmin Kim <kjongmin26@gmail.com>
Co-authored-by: Giovanni Rivera <rivera.giovanni271@gmail.com>
Co-authored-by: Renan Bastos <renanbastos.tec@gmail.com>
pull/2884/head^2
parent
8c413d0652
commit
ec48a76f14
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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/
|
||||
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
6
app.go
6
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 (
|
||||
|
|
15
app_test.go
15
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)
|
||||
|
|
76
bind_test.go
76
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 {
|
||||
|
|
|
@ -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
|
||||
|
|
503
ctx_test.go
503
ctx_test.go
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
}
|
||||
```
|
||||
|
||||
|
|
|
@ -1437,7 +1437,11 @@ app.Get("/", func(c fiber.Ctx) error {
|
|||
|
||||
log.Println(p.Name) // john
|
||||
log.Println(p.Pass) // doe
|
||||
log.Println(p.Products) // [shoe, hat]
|
||||
// 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.
|
||||
|
|
|
@ -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,16 +58,25 @@ 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` |
|
||||
| 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. | `false` |
|
||||
| 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` |
|
||||
|
||||
|
|
|
@ -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` |
|
||||
|:----------|:----------------------------------------------------|:------------------------------------------------------------------------------------------------------|:-----------------------------|
|
||||
| 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` | 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
|
||||
| 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
|
||||
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,
|
||||
}))
|
||||
```
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
```
|
|
@ -97,11 +97,11 @@ 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` |
|
||||
| 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` |
|
||||
|
@ -116,7 +116,7 @@ Writing to os.File is goroutine-safe, but if you are using a custom Output that
|
|||
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,
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
```
|
||||
|
|
|
@ -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)
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
|
@ -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)
|
||||
```
|
||||
|
||||
<Tabs>
|
||||
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
// 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
|
||||
|
|
|
@ -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)))
|
||||
|
@ -72,6 +72,45 @@ func Test_CORS_Wildcard(t *testing.T) {
|
|||
// OPTIONS (preflight) response headers when AllowOrigins is *
|
||||
app.Use(New(Config{
|
||||
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()
|
||||
|
@ -193,11 +283,8 @@ func Test_CORS_AllowOriginScheme(t *testing.T) {
|
|||
},
|
||||
{
|
||||
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,
|
||||
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)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
didx := strings.Index(domain, "://")
|
||||
pidx := strings.Index(pattern, "://")
|
||||
if didx == -1 || pidx == -1 {
|
||||
return false
|
||||
}
|
||||
domAuth := domain[didx+3:]
|
||||
// to avoid long loop by invalid long domain
|
||||
const maxDomainLen = 253
|
||||
if len(domAuth) > maxDomainLen {
|
||||
return false
|
||||
}
|
||||
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]
|
||||
}
|
||||
for i := len(patComp)/divHalf - 1; i >= 0; i-- {
|
||||
opp := len(patComp) - 1 - i
|
||||
patComp[i], patComp[opp] = patComp[opp], patComp[i]
|
||||
}
|
||||
|
||||
for i, v := range domComp {
|
||||
if len(patComp) <= i {
|
||||
return false
|
||||
}
|
||||
p := patComp[i]
|
||||
if p == "*" {
|
||||
// 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
|
||||
}
|
||||
if p != v {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
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, ""
|
||||
}
|
||||
|
||||
// Validate the scheme is either http or https
|
||||
if parsedOrigin.Scheme != "http" && parsedOrigin.Scheme != "https" {
|
||||
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, ""
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
//nolint:bodyclose // Much easier to just ignore memory leaks in tests
|
||||
package envvar
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
//nolint:bodyclose // Much easier to just ignore memory leaks in tests
|
||||
package favicon
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
//nolint:bodyclose // Much easier to just ignore memory leaks in tests
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
//nolint:bodyclose // Much easier to just ignore memory leaks in tests
|
||||
package keyauth
|
||||
|
||||
import (
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
//nolint:bodyclose // Much easier to just ignore memory leaks in tests
|
||||
package logger
|
||||
|
||||
import (
|
||||
|
|
|
@ -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("-")
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
//nolint:bodyclose // Much easier to just ignore memory leaks in tests
|
||||
package rewrite
|
||||
|
||||
import (
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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, "<h1>Hello a!</h1>", 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, "<h1>Hello, World!</h1>", 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")
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue