diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 6c378e40..dc04fec6 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -37,4 +37,4 @@ jobs: uses: golangci/golangci-lint-action@v6 with: # NOTE: Keep this in sync with the version from .golangci.yml - version: v1.60.3 + version: v1.61.0 diff --git a/.github/workflows/markdown.yml b/.github/workflows/markdown.yml index cc131e7f..d8d74905 100644 --- a/.github/workflows/markdown.yml +++ b/.github/workflows/markdown.yml @@ -15,7 +15,7 @@ jobs: uses: actions/checkout@v4 - name: Run markdownlint-cli2 - uses: DavidAnson/markdownlint-cli2-action@v16 + uses: DavidAnson/markdownlint-cli2-action@v17 with: globs: | **/*.md diff --git a/Makefile b/Makefile index 1d7f57d0..33526991 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ markdown: ## lint: 🚨 Run lint checks .PHONY: lint lint: - go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.60.3 run ./... + go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.61.0 run ./... ## test: 🚦 Execute all tests .PHONY: test diff --git a/binder/mapping.go b/binder/mapping.go index 07af94a1..36821be0 100644 --- a/binder/mapping.go +++ b/binder/mapping.go @@ -1,13 +1,15 @@ package binder import ( + "errors" "reflect" "strings" "sync" - "github.com/gofiber/fiber/v3/internal/schema" "github.com/gofiber/utils/v2" "github.com/valyala/bytebufferpool" + + "github.com/gofiber/fiber/v3/internal/schema" ) // ParserConfig form decoder config for SetParserDecoder @@ -132,15 +134,24 @@ func parseParamSquareBrackets(k string) (string, error) { defer bytebufferpool.Put(bb) kbytes := []byte(k) + openBracketsCount := 0 for i, b := range kbytes { - if b == '[' && kbytes[i+1] != ']' { - if err := bb.WriteByte('.'); err != nil { - return "", err //nolint:wrapcheck // unnecessary to wrap it + if b == '[' { + openBracketsCount++ + if i+1 < len(kbytes) && kbytes[i+1] != ']' { + if err := bb.WriteByte('.'); err != nil { + return "", err //nolint:wrapcheck // unnecessary to wrap it + } } + continue } - if b == '[' || b == ']' { + if b == ']' { + openBracketsCount-- + if openBracketsCount < 0 { + return "", errors.New("unmatched brackets") + } continue } @@ -149,6 +160,10 @@ func parseParamSquareBrackets(k string) (string, error) { } } + if openBracketsCount > 0 { + return "", errors.New("unmatched brackets") + } + return bb.String(), nil } diff --git a/binder/mapping_test.go b/binder/mapping_test.go index aec91ff2..e6fc8146 100644 --- a/binder/mapping_test.go +++ b/binder/mapping_test.go @@ -1,6 +1,7 @@ package binder import ( + "errors" "reflect" "testing" @@ -29,3 +30,70 @@ func Test_EqualFieldType(t *testing.T) { require.True(t, equalFieldType(&user, reflect.Int, "AGE")) require.True(t, equalFieldType(&user, reflect.Int, "age")) } + +func Test_ParseParamSquareBrackets(t *testing.T) { + tests := []struct { + err error + input string + expected string + }{ + { + err: nil, + input: "foo[bar]", + expected: "foo.bar", + }, + { + err: nil, + input: "foo[bar][baz]", + expected: "foo.bar.baz", + }, + { + err: errors.New("unmatched brackets"), + input: "foo[bar", + expected: "", + }, + { + err: errors.New("unmatched brackets"), + input: "foo[bar][baz", + expected: "", + }, + { + err: errors.New("unmatched brackets"), + input: "foo]bar[", + expected: "", + }, + { + err: nil, + input: "foo[bar[baz]]", + expected: "foo.bar.baz", + }, + { + err: nil, + input: "", + expected: "", + }, + { + err: nil, + input: "[]", + expected: "", + }, + { + err: nil, + input: "foo[]", + expected: "foo", + }, + } + + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + result, err := parseParamSquareBrackets(tt.input) + if tt.err != nil { + require.Error(t, err) + require.EqualError(t, err, tt.err.Error()) + } else { + require.NoError(t, err) + require.Equal(t, tt.expected, result) + } + }) + } +} diff --git a/ctx.go b/ctx.go index 4d7417ee..607a678f 100644 --- a/ctx.go +++ b/ctx.go @@ -624,7 +624,7 @@ func (c *DefaultCtx) Fresh() bool { if err != nil { return false } - return lastModifiedTime.Before(modifiedSinceTime) + return lastModifiedTime.Compare(modifiedSinceTime) != 1 } } } @@ -1841,21 +1841,9 @@ func (c *DefaultCtx) IsProxyTrusted() bool { return false } -var localHosts = [...]string{"127.0.0.1", "::1"} - -// IsLocalHost will return true if address is a localhost address. -func (*DefaultCtx) isLocalHost(address string) bool { - for _, h := range localHosts { - if address == h { - return true - } - } - return false -} - // IsFromLocal will return true if request came from local. func (c *DefaultCtx) IsFromLocal() bool { - return c.isLocalHost(c.fasthttp.RemoteIP().String()) + return c.fasthttp.RemoteIP().IsLoopback() } // Bind You can bind body, cookie, headers etc. into the map, map slice, struct easily by using Binding method. diff --git a/ctx_interface_gen.go b/ctx_interface_gen.go index 7709f7c9..62f2d368 100644 --- a/ctx_interface_gen.go +++ b/ctx_interface_gen.go @@ -318,8 +318,6 @@ type Ctx interface { // If EnableTrustedProxyCheck false, it returns true // IsProxyTrusted can check remote ip by proxy ranges and ip map. IsProxyTrusted() bool - // IsLocalHost will return true if address is a localhost address. - isLocalHost(address string) bool // IsFromLocal will return true if request came from local. IsFromLocal() bool // Bind You can bind body, cookie, headers etc. into the map, map slice, struct easily by using Binding method. diff --git a/ctx_test.go b/ctx_test.go index 6dcd7410..a94e4cb4 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -1397,6 +1397,10 @@ func Test_Ctx_Fresh(t *testing.T) { require.False(t, c.Fresh()) c.Request().Header.Set(HeaderIfModifiedSince, "Wed, 21 Oct 2015 07:28:00 GMT") + require.True(t, c.Fresh()) + + c.Request().Header.Set(HeaderIfModifiedSince, "Wed, 21 Oct 2015 07:27:59 GMT") + c.Response().Header.Set(HeaderLastModified, "Wed, 21 Oct 2015 07:28:00 GMT") require.False(t, c.Fresh()) } @@ -1412,6 +1416,18 @@ func Benchmark_Ctx_Fresh_WithNoCache(b *testing.B) { } } +// go test -v -run=^$ -bench=Benchmark_Ctx_Fresh_LastModified -benchmem -count=4 +func Benchmark_Ctx_Fresh_LastModified(b *testing.B) { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + + c.Response().Header.Set(HeaderLastModified, "Wed, 21 Oct 2015 07:28:00 GMT") + c.Request().Header.Set(HeaderIfModifiedSince, "Wed, 21 Oct 2015 07:28:00 GMT") + for n := 0; n < b.N; n++ { + c.Fresh() + } +} + // go test -run Test_Ctx_Binders -v func Test_Ctx_Binders(t *testing.T) { t.Parallel() @@ -6352,3 +6368,61 @@ func Benchmark_Ctx_IsProxyTrusted(b *testing.B) { }) }) } + +func Benchmark_Ctx_IsFromLocalhost(b *testing.B) { + // Scenario without localhost check + b.Run("Non_Localhost", func(b *testing.B) { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + c.Request().SetRequestURI("http://google.com:8080/test") + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + c.IsFromLocal() + } + app.ReleaseCtx(c) + }) + + // Scenario without localhost check in parallel + b.Run("Non_Localhost_Parallel", func(b *testing.B) { + app := New() + b.ReportAllocs() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + c.Request().SetRequestURI("http://google.com:8080/test") + for pb.Next() { + c.IsFromLocal() + } + app.ReleaseCtx(c) + }) + }) + + // Scenario with localhost check + b.Run("Localhost", func(b *testing.B) { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + c.Request().SetRequestURI("http://localhost:8080/test") + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + c.IsFromLocal() + } + app.ReleaseCtx(c) + }) + + // Scenario with localhost check in parallel + b.Run("Localhost_Parallel", func(b *testing.B) { + app := New() + b.ReportAllocs() + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + c.Request().SetRequestURI("http://localhost:8080/test") + for pb.Next() { + c.IsFromLocal() + } + app.ReleaseCtx(c) + }) + }) +} diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 2640512a..00c2422c 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -788,7 +788,7 @@ Improper use of the X-Forwarded-For header can be a security risk. For details, ## Is -Returns the matching **content type**, if the incoming request’s [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) HTTP header field matches the [MIME type](https://developer.mozilla.org/ru/docs/Web/HTTP/Basics_of_HTTP/MIME_types) specified by the type parameter. +Returns the matching **content type**, if the incoming request’s [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) HTTP header field matches the [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types) specified by the type parameter. :::info If the request has **no** body, it returns **false**. diff --git a/docs/client/hooks.md b/docs/client/hooks.md index 8616eac7..ccbc7d40 100644 --- a/docs/client/hooks.md +++ b/docs/client/hooks.md @@ -2,7 +2,7 @@ id: hooks title: 🎣 Hooks description: >- - Hooks are used to manipulate request/response proccess of Fiber client. + Hooks are used to manipulate request/response process of Fiber client. sidebar_position: 4 --- @@ -77,7 +77,7 @@ There are also some builtin request hooks provide some functionalities for Fiber - [parserRequestURL](https://github.com/gofiber/fiber/blob/main/client/hooks.go#L62): parserRequestURL customizes the URL according to the path params and query params. It's necessary for `PathParam` and `QueryParam` methods. -- [parserRequestHeader](https://github.com/gofiber/fiber/blob/main/client/hooks.go#L113): parserRequestHeader sets request headers, cookies, body type, referer, user agent according to client and request proeprties. It's necessary to make request header and cookiejar methods functional. +- [parserRequestHeader](https://github.com/gofiber/fiber/blob/main/client/hooks.go#L113): parserRequestHeader sets request headers, cookies, body type, referer, user agent according to client and request properties. It's necessary to make request header and cookiejar methods functional. - [parserRequestBody](https://github.com/gofiber/fiber/blob/main/client/hooks.go#L178): parserRequestBody serializes the body automatically. It is useful for XML, JSON, form, file bodies. diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md index f9972ec2..360aaacf 100644 --- a/docs/middleware/logger.md +++ b/docs/middleware/logger.md @@ -44,7 +44,7 @@ app.Use(logger.New(logger.Config{ app.Use(requestid.New()) app.Use(logger.New(logger.Config{ // For more options, see the Config section - Format: "${pid} ${locals:requestid} ${status} - ${method} ${path}​\n", + Format: "${pid} ${locals:requestid} ${status} - ${method} ${path}\n", })) // Changing TimeZone & TimeFormat diff --git a/docs/middleware/recover.md b/docs/middleware/recover.md index e8b4870a..0ae5cfcf 100644 --- a/docs/middleware/recover.md +++ b/docs/middleware/recover.md @@ -19,7 +19,7 @@ Import the middleware package that is part of the Fiber web framework ```go import ( "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/middleware/recover" + recoverer "github.com/gofiber/fiber/v3/middleware/recover" ) ``` @@ -27,7 +27,7 @@ After you initiate your Fiber app, you can use the following possibilities: ```go // Initialize default config -app.Use(recover.New()) +app.Use(recoverer.New()) // This panic will be caught by the middleware app.Get("/", func(c fiber.Ctx) error { diff --git a/go.mod b/go.mod index dd88f5b1..85002a6e 100644 --- a/go.mod +++ b/go.mod @@ -8,20 +8,20 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.20 github.com/stretchr/testify v1.9.0 - github.com/tinylib/msgp v1.1.8 + github.com/tinylib/msgp v1.2.1 github.com/valyala/bytebufferpool v1.0.0 - github.com/valyala/fasthttp v1.55.0 + github.com/valyala/fasthttp v1.56.0 ) require ( github.com/andybalholm/brotli v1.1.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/klauspost/compress v1.17.9 // indirect - github.com/philhofer/fwd v1.1.2 // indirect + github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sys v0.25.0 // indirect + golang.org/x/text v0.18.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1c4e23e7..48867e3c 100644 --- a/go.sum +++ b/go.sum @@ -13,58 +13,28 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= -github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= +github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 h1:jYi87L8j62qkXzaYHAQAhEapgukhenIMZRBKTNRLHJ4= +github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= -github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= +github.com/tinylib/msgp v1.2.1 h1:6ypy2qcCznxpP4hpORzhtXyTqrBs7cfM9MCCWY8zsmU= +github.com/tinylib/msgp v1.2.1/go.mod h1:2vIGs3lcUo8izAATNobrCHevYZC/LMsJtw4JPiYPHro= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= -github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM= +github.com/valyala/fasthttp v1.56.0 h1:bEZdJev/6LCBlpdORfrLu/WOZXXxvrUQSiyniuaoW8U= +github.com/valyala/fasthttp v1.56.0/go.mod h1:sReBt3XZVnudxuLOx4J/fMrJVorWRiWY2koQKgABiVI= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/middleware/adaptor/adaptor.go b/middleware/adaptor/adaptor.go index 539aa3e2..03c6287e 100644 --- a/middleware/adaptor/adaptor.go +++ b/middleware/adaptor/adaptor.go @@ -14,6 +14,11 @@ import ( "github.com/valyala/fasthttp/fasthttpadaptor" ) +type disableLogger struct{} + +func (*disableLogger) Printf(string, ...any) { +} + var ctxPool = sync.Pool{ New: func() any { return new(fasthttp.RequestCtx) @@ -96,6 +101,8 @@ func HTTPMiddleware(mw func(http.Handler) http.Handler) fiber.Handler { c.Request().SetHost(r.Host) c.Request().Header.SetHost(r.Host) + // Remove all cookies before setting, see https://github.com/valyala/fasthttp/pull/1864 + c.Request().Header.DelAllCookies() for key, val := range r.Header { for _, v := range val { c.Request().Header.Set(key, v) @@ -171,7 +178,7 @@ func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc { fctx.Response.Reset() fctx.Request.Reset() defer ctxPool.Put(fctx) - fctx.Init(req, remoteAddr, nil) + fctx.Init(req, remoteAddr, &disableLogger{}) if len(h) > 0 { // New fiber Ctx diff --git a/middleware/adaptor/adaptor_test.go b/middleware/adaptor/adaptor_test.go index 32abc64d..990d421d 100644 --- a/middleware/adaptor/adaptor_test.go +++ b/middleware/adaptor/adaptor_test.go @@ -10,6 +10,7 @@ import ( "net/http" "net/http/httptest" "net/url" + "strings" "testing" "github.com/gofiber/fiber/v3" @@ -86,7 +87,7 @@ func Test_HTTPHandler(t *testing.T) { remoteAddr, err := net.ResolveTCPAddr("tcp", expectedRemoteAddr) require.NoError(t, err) - fctx.Init(&req, remoteAddr, nil) + fctx.Init(&req, remoteAddr, &disableLogger{}) app := fiber.New() ctx := app.AcquireCtx(&fctx) defer app.ReleaseCtx(ctx) @@ -200,6 +201,81 @@ func Test_HTTPMiddleware(t *testing.T) { require.Equal(t, "okay", resp.Header.Get("context_second_okay")) } +func Test_HTTPMiddlewareWithCookies(t *testing.T) { + const ( + cookieHeader = "Cookie" + setCookieHeader = "Set-Cookie" + cookieOneName = "cookieOne" + cookieTwoName = "cookieTwo" + cookieOneValue = "valueCookieOne" + cookieTwoValue = "valueCookieTwo" + ) + nethttpMW := func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + next.ServeHTTP(w, r) + }) + } + + app := fiber.New() + app.Use(HTTPMiddleware(nethttpMW)) + app.Post("/", func(c fiber.Ctx) error { + // RETURNING CURRENT COOKIES TO RESPONSE + var cookies []string = strings.Split(c.Get(cookieHeader), "; ") + for _, cookie := range cookies { + c.Set(setCookieHeader, cookie) + } + return c.SendStatus(fiber.StatusOK) + }) + + // Test case for POST request with cookies + t.Run("POST request with cookies", func(t *testing.T) { + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodPost, "/", nil) + require.NoError(t, err) + req.AddCookie(&http.Cookie{Name: cookieOneName, Value: cookieOneValue}) + req.AddCookie(&http.Cookie{Name: cookieTwoName, Value: cookieTwoValue}) + + resp, err := app.Test(req) + require.NoError(t, err) + cookies := resp.Cookies() + require.Len(t, cookies, 2) + for _, cookie := range cookies { + switch cookie.Name { + case cookieOneName: + require.Equal(t, cookieOneValue, cookie.Value) + case cookieTwoName: + require.Equal(t, cookieTwoValue, cookie.Value) + default: + t.Error("unexpected cookie key") + } + } + }) + + // New test case for GET request + t.Run("GET request", func(t *testing.T) { + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/", nil) + require.NoError(t, err) + + resp, err := app.Test(req) + require.NoError(t, err) + require.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode) + }) + + // New test case for request without cookies + t.Run("POST request without cookies", func(t *testing.T) { + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodPost, "/", nil) + require.NoError(t, err) + + resp, err := app.Test(req) + require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode) + require.Empty(t, resp.Cookies()) + }) +} + func Test_FiberHandler(t *testing.T) { testFiberToHandlerFunc(t, false) } @@ -412,6 +488,10 @@ func Benchmark_FiberHandlerFunc(b *testing.B) { name string bodyContent []byte }{ + { + name: "No Content", + bodyContent: nil, // No body content case + }, { name: "100KB", bodyContent: make([]byte, 100*1024), @@ -450,7 +530,14 @@ func Benchmark_FiberHandlerFunc(b *testing.B) { for _, bm := range benchmarks { b.Run(bm.name, func(b *testing.B) { w := httptest.NewRecorder() - bodyBuffer := bytes.NewBuffer(bm.bodyContent) + var bodyBuffer *bytes.Buffer + + // Handle the "No Content" case where bodyContent is nil + if bm.bodyContent != nil { + bodyBuffer = bytes.NewBuffer(bm.bodyContent) + } else { + bodyBuffer = bytes.NewBuffer([]byte{}) // Empty buffer for no content + } r := http.Request{ Method: http.MethodPost, @@ -476,6 +563,10 @@ func Benchmark_FiberHandlerFunc_Parallel(b *testing.B) { name string bodyContent []byte }{ + { + name: "No Content", + bodyContent: nil, // No body content case + }, { name: "100KB", bodyContent: make([]byte, 100*1024), @@ -513,7 +604,15 @@ func Benchmark_FiberHandlerFunc_Parallel(b *testing.B) { for _, bm := range benchmarks { b.Run(bm.name, func(b *testing.B) { - bodyBuffer := bytes.NewBuffer(bm.bodyContent) + var bodyBuffer *bytes.Buffer + + // Handle the "No Content" case where bodyContent is nil + if bm.bodyContent != nil { + bodyBuffer = bytes.NewBuffer(bm.bodyContent) + } else { + bodyBuffer = bytes.NewBuffer([]byte{}) // Empty buffer for no content + } + b.ReportAllocs() b.ResetTimer() diff --git a/middleware/cache/cache.go b/middleware/cache/cache.go index 69c3fd5c..5c832f0b 100644 --- a/middleware/cache/cache.go +++ b/middleware/cache/cache.go @@ -63,7 +63,7 @@ func New(config ...Config) fiber.Handler { var ( // Cache settings mux = &sync.RWMutex{} - timestamp = uint64(time.Now().Unix()) + timestamp = uint64(time.Now().Unix()) //nolint:gosec //Not a concern ) // Create manager to simplify storage operations ( see manager.go ) manager := newManager(cfg.Storage) @@ -75,7 +75,7 @@ func New(config ...Config) fiber.Handler { // Update timestamp in the configured interval go func() { for { - atomic.StoreUint64(×tamp, uint64(time.Now().Unix())) + atomic.StoreUint64(×tamp, uint64(time.Now().Unix())) //nolint:gosec //Not a concern time.Sleep(timestampUpdatePeriod) } }() @@ -117,7 +117,7 @@ func New(config ...Config) fiber.Handler { // Get timestamp ts := atomic.LoadUint64(×tamp) - // Cache Entry not found + // Cache Entry found if e != nil { // Invalidate cache if requested if cfg.CacheInvalidator != nil && cfg.CacheInvalidator(c) { diff --git a/middleware/etag/etag.go b/middleware/etag/etag.go index aca57bc1..f28a33ce 100644 --- a/middleware/etag/etag.go +++ b/middleware/etag/etag.go @@ -65,7 +65,7 @@ func New(config ...Config) fiber.Handler { return c.SendStatus(fiber.StatusRequestEntityTooLarge) } - bb.B = appendUint(bb.Bytes(), uint32(bodyLength)) //nolint:gosec // Body length is validated above + bb.B = appendUint(bb.Bytes(), uint32(bodyLength)) bb.WriteByte('-') bb.B = appendUint(bb.Bytes(), crc32.Checksum(body, crc32q)) bb.WriteByte('"')