From eb7b00b4fb10d780ecf37486baea1b4b98d7a3bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Thu, 13 Mar 2025 01:44:57 +0800 Subject: [PATCH 1/6] fix: default value comment --- middleware/helmet/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/helmet/config.go b/middleware/helmet/config.go index c7fa3cab..c9367d81 100644 --- a/middleware/helmet/config.go +++ b/middleware/helmet/config.go @@ -28,7 +28,7 @@ type Config struct { ContentSecurityPolicy string // ReferrerPolicy - // Optional. Default value "ReferrerPolicy". + // Optional. Default value "no-referrer". ReferrerPolicy string // Permissions-Policy From b56a141d59cedd041c55cde66e696f4d8dde6bfb Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Fri, 14 Mar 2025 02:51:34 -0400 Subject: [PATCH 2/6] docs: Update helmet.md default values (#3350) Update helmet.md --- docs/middleware/helmet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/middleware/helmet.md b/docs/middleware/helmet.md index bc101350..369a4c2c 100644 --- a/docs/middleware/helmet.md +++ b/docs/middleware/helmet.md @@ -54,7 +54,7 @@ curl -I http://localhost:3000 | ContentSecurityPolicy | `string` | ContentSecurityPolicy | "" | | CSPReportOnly | `bool` | CSPReportOnly | false | | HSTSPreloadEnabled | `bool` | HSTSPreloadEnabled | false | -| ReferrerPolicy | `string` | ReferrerPolicy | "ReferrerPolicy" | +| ReferrerPolicy | `string` | ReferrerPolicy | "no-referrer" | | PermissionPolicy | `string` | Permissions-Policy | "" | | CrossOriginEmbedderPolicy | `string` | Cross-Origin-Embedder-Policy | "require-corp" | | CrossOriginOpenerPolicy | `string` | Cross-Origin-Opener-Policy | "same-origin" | From 395c8fafa96719cfcce808876c4b11914ca34efc Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Mon, 17 Mar 2025 03:29:51 -0400 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=A7=B9=20chore:=20Fix=20linter=20work?= =?UTF-8?q?flow=20failures=20(#3354)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix linter workflow failures * Bump golang.org/x/net to v0.36.0 * Try skipping golangci-lint cache * Update linter.yml * fix: directives and spaces * fix: better configuration * fix: golangci-lint install * Update golangci-lint version * Enable SA4023 * Update golangci-lint config * Remove duplicated rule --------- Co-authored-by: Fernandez Ludovic --- .github/workflows/linter.yml | 5 +- .golangci.yml | 14 +++-- Makefile | 2 +- app.go | 2 +- app_test.go | 4 +- bind_test.go | 4 +- client/helper_test.go | 2 +- client/hooks.go | 2 +- ctx_test.go | 76 +++++++++++++------------- go.mod | 2 +- go.sum | 2 + helpers.go | 8 +-- helpers_test.go | 2 +- internal/storage/memory/memory_test.go | 16 +++--- listen.go | 34 +++++------- log/default.go | 4 +- middleware/adaptor/adaptor_test.go | 4 +- middleware/logger/default_logger.go | 2 +- middleware/logger/logger_test.go | 6 +- middleware/proxy/proxy_test.go | 15 ++++- path.go | 2 +- redirect_test.go | 30 +++++----- router.go | 2 +- router_test.go | 4 +- 24 files changed, 129 insertions(+), 115 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index beed2126..2f7f8c84 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -37,4 +37,7 @@ jobs: uses: golangci/golangci-lint-action@v6 with: # NOTE: Keep this in sync with the version from .golangci.yml - version: v1.62.2 + version: v1.64.7 + # NOTE(ldez): temporary workaround + install-mode: goinstall + diff --git a/.golangci.yml b/.golangci.yml index 8b8d27b8..357676f7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -7,7 +7,6 @@ run: output: sort-results: true - uniq-by-line: false linters-settings: depguard: @@ -187,7 +186,7 @@ linters-settings: - name: unchecked-type-assertion disabled: true # TODO: Do not disable - name: unhandled-error - arguments: ['bytes\.Buffer\.Write'] + disabled: true stylecheck: checks: @@ -250,7 +249,10 @@ issues: max-issues-per-linter: 0 max-same-issues: 0 exclude-dirs: - - internal # TODO: Do not ignore interal packages + - internal # TODO: Do not ignore internal packages + exclude-files: + - '_msgp\.go' + - '_msgp_test\.go' exclude-rules: - linters: - err113 @@ -263,7 +265,10 @@ issues: linters: - bodyclose - err113 - # fix: true + - source: 'fmt.Fprintf?' + linters: + - errcheck + - revive linters: enable: @@ -358,7 +363,6 @@ linters: - stylecheck # - tagalign # TODO: Enable - tagliatelle - - tenv - testableexamples - testifylint # - testpackage # TODO: Enable diff --git a/Makefile b/Makefile index 669b3fbe..7e67dbc2 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.62.2 run ./... + golangci-lint run ## test: 🚦 Execute all tests .PHONY: test diff --git a/app.go b/app.go index ec55f06a..84059e79 100644 --- a/app.go +++ b/app.go @@ -1024,7 +1024,7 @@ func (app *App) Test(req *http.Request, config ...TestConfig) (*http.Response, e select { case err = <-channel: case <-time.After(cfg.Timeout): - conn.Close() //nolint:errcheck, revive // It is fine to ignore the error here + conn.Close() //nolint:errcheck // It is fine to ignore the error here if cfg.FailOnTimeout { return nil, os.ErrDeadlineExceeded } diff --git a/app_test.go b/app_test.go index 97d48fce..84606e6b 100644 --- a/app_test.go +++ b/app_test.go @@ -403,7 +403,7 @@ func Test_App_serverErrorHandler_Internal_Error(t *testing.T) { t.Parallel() app := New() msg := "test err" - c := app.AcquireCtx(&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) @@ -413,7 +413,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.AcquireCtx(&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", diff --git a/bind_test.go b/bind_test.go index b01086e6..89839e13 100644 --- a/bind_test.go +++ b/bind_test.go @@ -1378,7 +1378,7 @@ func Benchmark_Bind_URI(b *testing.B) { var err error app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.route = &Route{ Params: []string{ @@ -1415,7 +1415,7 @@ func Benchmark_Bind_URI_Map(b *testing.B) { var err error app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.route = &Route{ Params: []string{ diff --git a/client/helper_test.go b/client/helper_test.go index f25d0af1..1b644cbd 100644 --- a/client/helper_test.go +++ b/client/helper_test.go @@ -131,7 +131,7 @@ func testRequestFail(t *testing.T, handler fiber.Handler, wrapAgent func(agent * } } -func testClient(t *testing.T, handler fiber.Handler, wrapAgent func(agent *Client), excepted string, count ...int) { //nolint: unparam // maybe needed +func testClient(t *testing.T, handler fiber.Handler, wrapAgent func(agent *Client), excepted string, count ...int) { //nolint:unparam // maybe needed t.Helper() app, ln, start := createHelperServer(t) diff --git a/client/hooks.go b/client/hooks.go index ca6f5d6c..3d86930d 100644 --- a/client/hooks.go +++ b/client/hooks.go @@ -199,7 +199,7 @@ func parserRequestBody(c *Client, req *Request) error { case filesBody: return parserRequestBodyFile(req) case rawBody: - if body, ok := req.body.([]byte); ok { + if body, ok := req.body.([]byte); ok { //nolint:revive // ignore simplicity req.RawRequest.SetBody(body) } else { return ErrBodyType diff --git a/ctx_test.go b/ctx_test.go index ee272d24..5040f4f8 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -192,7 +192,7 @@ func Test_Ctx_AcceptsCharsets(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsCharsets -benchmem -count=4 func Benchmark_Ctx_AcceptsCharsets(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().Header.Set("Accept-Charset", "utf-8, iso-8859-1;q=0.5") var res string @@ -218,7 +218,7 @@ func Test_Ctx_AcceptsEncodings(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsEncodings -benchmem -count=4 func Benchmark_Ctx_AcceptsEncodings(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().Header.Set(HeaderAcceptEncoding, "deflate, gzip;q=1.0, *;q=0.5") var res string @@ -243,7 +243,7 @@ func Test_Ctx_AcceptsLanguages(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsLanguages -benchmem -count=4 func Benchmark_Ctx_AcceptsLanguages(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().Header.Set(HeaderAcceptLanguage, "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5") var res string @@ -304,7 +304,7 @@ func Test_Ctx_Append(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Append -benchmem -count=4 func Benchmark_Ctx_Append(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -337,7 +337,7 @@ func Test_Ctx_Attachment(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Attachment -benchmem -count=4 func Benchmark_Ctx_Attachment(b *testing.B) { app := New() - c := app.AcquireCtx(&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 Test_Ctx_BaseURL(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_BaseURL -benchmem func Benchmark_Ctx_BaseURL(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().SetHost("google.com:1337") c.Request().URI().SetPath("/haha/oke/lol") @@ -380,7 +380,7 @@ func Benchmark_Ctx_BaseURL(b *testing.B) { func Test_Ctx_Body(t *testing.T) { t.Parallel() app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().SetBody([]byte("john=doe")) require.Equal(t, []byte("john=doe"), c.Body()) @@ -390,7 +390,7 @@ func Test_Ctx_Body(t *testing.T) { func Test_Ctx_BodyRaw(t *testing.T) { t.Parallel() app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().SetBodyRaw([]byte("john=doe")) require.Equal(t, []byte("john=doe"), c.BodyRaw()) @@ -400,7 +400,7 @@ func Test_Ctx_BodyRaw(t *testing.T) { func Test_Ctx_BodyRaw_Immutable(t *testing.T) { t.Parallel() app := New(Config{Immutable: true}) - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().SetBodyRaw([]byte("john=doe")) require.Equal(t, []byte("john=doe"), c.BodyRaw()) @@ -411,7 +411,7 @@ func Benchmark_Ctx_Body(b *testing.B) { const input = "john=doe" app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().SetBody([]byte(input)) b.ReportAllocs() @@ -428,7 +428,7 @@ func Benchmark_Ctx_BodyRaw(b *testing.B) { const input = "john=doe" app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().SetBodyRaw([]byte(input)) b.ReportAllocs() @@ -445,7 +445,7 @@ func Benchmark_Ctx_BodyRaw_Immutable(b *testing.B) { const input = "john=doe" app := New(Config{Immutable: true}) - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().SetBodyRaw([]byte(input)) b.ReportAllocs() @@ -462,7 +462,7 @@ func Test_Ctx_Body_Immutable(t *testing.T) { t.Parallel() app := New() app.config.Immutable = true - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().SetBody([]byte("john=doe")) require.Equal(t, []byte("john=doe"), c.Body()) @@ -474,7 +474,7 @@ func Benchmark_Ctx_Body_Immutable(b *testing.B) { app := New() app.config.Immutable = true - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().SetBody([]byte(input)) b.ReportAllocs() @@ -527,7 +527,7 @@ func Test_Ctx_Body_With_Compression(t *testing.T) { t.Run(tCase.name, func(t *testing.T) { t.Parallel() app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().Header.Set("Content-Encoding", tCase.contentEncoding) if strings.Contains(tCase.contentEncoding, "gzip") { @@ -720,7 +720,7 @@ func Test_Ctx_Body_With_Compression_Immutable(t *testing.T) { t.Parallel() app := New() app.config.Immutable = true - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().Header.Set("Content-Encoding", tCase.contentEncoding) if strings.Contains(tCase.contentEncoding, "gzip") { @@ -897,7 +897,7 @@ func Test_Ctx_Context(t *testing.T) { t.Parallel() testKey := struct{}{} testValue := "Test Value" - ctx := context.WithValue(context.Background(), testKey, testValue) //nolint: staticcheck // not needed for tests + ctx := context.WithValue(context.Background(), testKey, testValue) //nolint:staticcheck // not needed for tests require.Equal(t, testValue, ctx.Value(testKey)) }) } @@ -910,7 +910,7 @@ func Test_Ctx_SetContext(t *testing.T) { testKey := struct{}{} testValue := "Test Value" - ctx := context.WithValue(context.Background(), testKey, testValue) //nolint: staticcheck // not needed for tests + ctx := context.WithValue(context.Background(), testKey, testValue) //nolint:staticcheck // not needed for tests c.SetContext(ctx) require.Equal(t, testValue, c.Context().Value(testKey)) } @@ -930,7 +930,7 @@ func Test_Ctx_Context_Multiple_Requests(t *testing.T) { } input := utils.CopyString(Query(c, "input", "NO_VALUE")) - ctx = context.WithValue(ctx, testKey, fmt.Sprintf("%s_%s", testValue, input)) //nolint: staticcheck // not needed for tests + ctx = context.WithValue(ctx, testKey, fmt.Sprintf("%s_%s", testValue, input)) //nolint:staticcheck // not needed for tests c.SetContext(ctx) return c.Status(StatusOK).SendString(fmt.Sprintf("resp_%s_returned", input)) @@ -1013,7 +1013,7 @@ func Test_Ctx_Cookie(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Cookie -benchmem -count=4 func Benchmark_Ctx_Cookie(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -1544,12 +1544,12 @@ func Test_Ctx_Binders(t *testing.T) { t.Run("URI", func(t *testing.T) { t.Skip("URI is not ready for v3") //nolint:gocritic // TODO: uncomment - //t.Parallel() - //withValues(t, func(c Ctx, testStruct *TestStruct) error { + // t.Parallel() + // withValues(t, func(c Ctx, testStruct *TestStruct) error { // c.Route().Params = []string{"name", "name2", "class", "class2"} // c.Params().value = [30]string{"foo", "bar", "111", "222"} // return c.Bind().URI(testStruct) - //}) + // }) }) t.Run("ReqHeader", func(t *testing.T) { t.Parallel() @@ -2566,7 +2566,7 @@ func Test_Ctx_Params_Case_Sensitive(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Params -benchmem -count=4 func Benchmark_Ctx_Params(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.route = &Route{ Params: []string{ @@ -3746,7 +3746,7 @@ func Benchmark_Ctx_CBOR(b *testing.B) { func Benchmark_Ctx_JSON_Ctype(b *testing.B) { app := New() // TODO: Check extra allocs because of the interface stuff - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed type SomeStruct struct { Name string Age uint8 @@ -3813,7 +3813,7 @@ func Test_Ctx_JSONP(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_JSONP -benchmem -count=4 func Benchmark_Ctx_JSONP(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed type SomeStruct struct { Name string @@ -3838,7 +3838,7 @@ func Benchmark_Ctx_JSONP(b *testing.B) { func Test_Ctx_XML(t *testing.T) { t.Parallel() app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed require.Error(t, c.JSON(complex(1, 1))) @@ -3897,7 +3897,7 @@ func Test_Ctx_XML(t *testing.T) { // go test -run=^$ -bench=Benchmark_Ctx_XML -benchmem -count=4 func Benchmark_Ctx_XML(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed type SomeStruct struct { Name string `xml:"Name"` Age uint8 `xml:"Age"` @@ -3936,7 +3936,7 @@ func Test_Ctx_Links(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Links -benchmem -count=4 func Benchmark_Ctx_Links(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -4363,7 +4363,7 @@ func Benchmark_Ctx_Render_Engine(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_Get_Location_From_Route -benchmem -count=4 func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed app.Get("/user/:name", func(c Ctx) error { return c.SendString(c.Params("name")) @@ -4578,14 +4578,14 @@ func Test_Ctx_SendStreamWriter(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) err := c.SendStreamWriter(func(w *bufio.Writer) { - w.WriteString("Don't crash please") //nolint:errcheck, revive // It is fine to ignore the error + w.WriteString("Don't crash please") //nolint:errcheck // It is fine to ignore the error }) require.NoError(t, err) require.Equal(t, "Don't crash please", string(c.Response().Body())) err = c.SendStreamWriter(func(w *bufio.Writer) { for lineNum := 1; lineNum <= 5; lineNum++ { - fmt.Fprintf(w, "Line %d\n", lineNum) //nolint:errcheck, revive // It is fine to ignore the error + fmt.Fprintf(w, "Line %d\n", lineNum) if err := w.Flush(); err != nil { t.Errorf("unexpected error: %s", err) return @@ -4607,7 +4607,7 @@ func Test_Ctx_SendStreamWriter_Interrupted(t *testing.T) { app.Get("/", func(c Ctx) error { return c.SendStreamWriter(func(w *bufio.Writer) { for lineNum := 1; lineNum <= 5; lineNum++ { - fmt.Fprintf(w, "Line %d\n", lineNum) //nolint:errcheck // It is fine to ignore the error + fmt.Fprintf(w, "Line %d\n", lineNum) if err := w.Flush(); err != nil { if lineNum < 3 { @@ -4728,7 +4728,7 @@ func Benchmark_Ctx_Type(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_Type_Charset -benchmem -count=4 func Benchmark_Ctx_Type_Charset(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -4753,7 +4753,7 @@ func Test_Ctx_Vary(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Vary -benchmem -count=4 func Benchmark_Ctx_Vary(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -4806,7 +4806,7 @@ func Test_Ctx_Writef(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Writef -benchmem -count=4 func Benchmark_Ctx_Writef(b *testing.B) { app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed world := "World!" b.ReportAllocs() @@ -4951,11 +4951,11 @@ func Test_Ctx_BodyStreamWriter(t *testing.T) { ctx := &fasthttp.RequestCtx{} ctx.SetBodyStreamWriter(func(w *bufio.Writer) { - fmt.Fprintf(w, "body writer line 1\n") //nolint: errcheck // It is fine to ignore the error + fmt.Fprintf(w, "body writer line 1\n") if err := w.Flush(); err != nil { t.Errorf("unexpected error: %s", err) } - fmt.Fprintf(w, "body writer line 2\n") //nolint: errcheck // It is fine to ignore the error + fmt.Fprintf(w, "body writer line 2\n") }) require.True(t, ctx.IsBodyStream()) diff --git a/go.mod b/go.mod index 5367e203..f4cd17c1 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/net v0.35.0 // indirect + golang.org/x/net v0.37.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index e586535a..71824581 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,8 @@ golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= diff --git a/helpers.go b/helpers.go index a452e13c..584553a7 100644 --- a/helpers.go +++ b/helpers.go @@ -51,16 +51,16 @@ func getTLSConfig(ln net.Listener) *tls.Config { } // Copy value from pointer - if val := reflect.Indirect(pointer); val.Type() != nil { + if val := reflect.Indirect(pointer); val.IsValid() { // Get private field from value - if field := val.FieldByName("config"); field.Type() != nil { + if field := val.FieldByName("config"); field.IsValid() { // Copy value from pointer field (unsafe) newval := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())) //nolint:gosec // Probably the only way to extract the *tls.Config from a net.Listener. TODO: Verify there really is no easier way without using unsafe. - if newval.Type() == nil { + if !newval.IsValid() { return nil } // Get element from pointer - if elem := newval.Elem(); elem.Type() != nil { + if elem := newval.Elem(); elem.IsValid() { // Cast value to *tls.Config c, ok := elem.Interface().(*tls.Config) if !ok { diff --git a/helpers_test.go b/helpers_test.go index 5e56bade..12d6b60f 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -566,7 +566,7 @@ func Test_Utils_TestConn_Closed_Write(t *testing.T) { require.NoError(t, err) // Close early, write should fail - conn.Close() //nolint:errcheck, revive // It is fine to ignore the error here + conn.Close() //nolint:errcheck // It is fine to ignore the error here _, err = conn.Write([]byte("Response 2\n")) require.ErrorIs(t, err, errTestConnClosed) diff --git a/internal/storage/memory/memory_test.go b/internal/storage/memory/memory_test.go index 347e9f5f..1952ddcf 100644 --- a/internal/storage/memory/memory_test.go +++ b/internal/storage/memory/memory_test.go @@ -209,7 +209,7 @@ func Benchmark_Memory_Set(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _ = testStore.Set("john", []byte("doe"), 0) //nolint: errcheck // error not needed for benchmark + _ = testStore.Set("john", []byte("doe"), 0) //nolint:errcheck // error not needed for benchmark } } @@ -220,7 +220,7 @@ func Benchmark_Memory_Set_Parallel(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { - _ = testStore.Set("john", []byte("doe"), 0) //nolint: errcheck // error not needed for benchmark + _ = testStore.Set("john", []byte("doe"), 0) //nolint:errcheck // error not needed for benchmark } }) } @@ -259,7 +259,7 @@ func Benchmark_Memory_Get(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _, _ = testStore.Get("john") //nolint: errcheck // error not needed for benchmark + _, _ = testStore.Get("john") //nolint:errcheck // error not needed for benchmark } } @@ -273,7 +273,7 @@ func Benchmark_Memory_Get_Parallel(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { - _, _ = testStore.Get("john") //nolint: errcheck // error not needed for benchmark + _, _ = testStore.Get("john") //nolint:errcheck // error not needed for benchmark } }) } @@ -315,8 +315,8 @@ func Benchmark_Memory_SetAndDelete(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - _ = testStore.Set("john", []byte("doe"), 0) //nolint: errcheck // error not needed for benchmark - _ = testStore.Delete("john") //nolint: errcheck // error not needed for benchmark + _ = testStore.Set("john", []byte("doe"), 0) //nolint:errcheck // error not needed for benchmark + _ = testStore.Delete("john") //nolint:errcheck // error not needed for benchmark } } @@ -327,8 +327,8 @@ func Benchmark_Memory_SetAndDelete_Parallel(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { - _ = testStore.Set("john", []byte("doe"), 0) //nolint: errcheck // error not needed for benchmark - _ = testStore.Delete("john") //nolint: errcheck // error not needed for benchmark + _ = testStore.Set("john", []byte("doe"), 0) //nolint:errcheck // error not needed for benchmark + _ = testStore.Delete("john") //nolint:errcheck // error not needed for benchmark } }) } diff --git a/listen.go b/listen.go index f33c9daf..83ed655b 100644 --- a/listen.go +++ b/listen.go @@ -328,7 +328,7 @@ func (*App) prepareListenData(addr string, isTLS bool, cfg ListenConfig) ListenD } // startupMessage prepares the startup message with the handler number, port, address and other information -func (app *App) startupMessage(addr string, isTLS bool, pids string, cfg ListenConfig) { //nolint: revive // Accepting a bool param named isTLS if fine here +func (app *App) startupMessage(addr string, isTLS bool, pids string, cfg ListenConfig) { //nolint:revive // Accepting a bool param named isTLS if fine here // ignore child processes if IsChild() { return @@ -366,38 +366,35 @@ func (app *App) startupMessage(addr string, isTLS bool, pids string, cfg ListenC out = colorable.NewNonColorable(os.Stdout) } - fmt.Fprintf(out, "%s\n", fmt.Sprintf(figletFiberText, colors.Red+"v"+Version+colors.Reset)) //nolint:errcheck,revive // ignore error - fmt.Fprintf(out, strings.Repeat("-", 50)+"\n") //nolint:errcheck,revive,govet // ignore error + fmt.Fprintf(out, "%s\n", fmt.Sprintf(figletFiberText, colors.Red+"v"+Version+colors.Reset)) + fmt.Fprintf(out, strings.Repeat("-", 50)+"\n") if host == "0.0.0.0" { - //nolint:errcheck,revive // ignore error fmt.Fprintf(out, "%sINFO%s Server started on: \t%s%s://127.0.0.1:%s%s (bound on host 0.0.0.0 and port %s)\n", colors.Green, colors.Reset, colors.Blue, scheme, port, colors.Reset, port) } else { - //nolint:errcheck,revive // ignore error fmt.Fprintf(out, "%sINFO%s Server started on: \t%s%s%s\n", colors.Green, colors.Reset, colors.Blue, fmt.Sprintf("%s://%s:%s", scheme, host, port), colors.Reset) } if app.config.AppName != "" { - fmt.Fprintf(out, "%sINFO%s Application name: \t\t%s%s%s\n", colors.Green, colors.Reset, colors.Blue, app.config.AppName, colors.Reset) //nolint:errcheck,revive // ignore error + fmt.Fprintf(out, "%sINFO%s Application name: \t\t%s%s%s\n", colors.Green, colors.Reset, colors.Blue, app.config.AppName, colors.Reset) } - //nolint:errcheck,revive // ignore error fmt.Fprintf(out, "%sINFO%s Total handlers count: \t%s%s%s\n", colors.Green, colors.Reset, colors.Blue, strconv.Itoa(int(app.handlersCount)), colors.Reset) if isPrefork == "Enabled" { - fmt.Fprintf(out, "%sINFO%s Prefork: \t\t\t%s%s%s\n", colors.Green, colors.Reset, colors.Blue, isPrefork, colors.Reset) //nolint:errcheck,revive // ignore error + fmt.Fprintf(out, "%sINFO%s Prefork: \t\t\t%s%s%s\n", colors.Green, colors.Reset, colors.Blue, isPrefork, colors.Reset) } else { - fmt.Fprintf(out, "%sINFO%s Prefork: \t\t\t%s%s%s\n", colors.Green, colors.Reset, colors.Red, isPrefork, colors.Reset) //nolint:errcheck,revive // ignore error + fmt.Fprintf(out, "%sINFO%s Prefork: \t\t\t%s%s%s\n", colors.Green, colors.Reset, colors.Red, isPrefork, colors.Reset) } - fmt.Fprintf(out, "%sINFO%s PID: \t\t\t%s%v%s\n", colors.Green, colors.Reset, colors.Blue, os.Getpid(), colors.Reset) //nolint:errcheck,revive // ignore error - fmt.Fprintf(out, "%sINFO%s Total process count: \t%s%s%s\n", colors.Green, colors.Reset, colors.Blue, procs, colors.Reset) //nolint:errcheck,revive // ignore error + fmt.Fprintf(out, "%sINFO%s PID: \t\t\t%s%v%s\n", colors.Green, colors.Reset, colors.Blue, os.Getpid(), colors.Reset) + fmt.Fprintf(out, "%sINFO%s Total process count: \t%s%s%s\n", colors.Green, colors.Reset, colors.Blue, procs, colors.Reset) if cfg.EnablePrefork { // Turn the `pids` variable (in the form ",a,b,c,d,e,f,etc") into a slice of PIDs @@ -408,7 +405,7 @@ func (app *App) startupMessage(addr string, isTLS bool, pids string, cfg ListenC } } - fmt.Fprintf(out, "%sINFO%s Child PIDs: \t\t%s", colors.Green, colors.Reset, colors.Blue) //nolint:errcheck,revive // ignore error + fmt.Fprintf(out, "%sINFO%s Child PIDs: \t\t%s", colors.Green, colors.Reset, colors.Blue) totalPids := len(pidSlice) rowTotalPidCount := 10 @@ -421,17 +418,17 @@ func (app *App) startupMessage(addr string, isTLS bool, pids string, cfg ListenC } for n, pid := range pidSlice[start:end] { - fmt.Fprintf(out, "%s", pid) //nolint:errcheck,revive // ignore error + fmt.Fprintf(out, "%s", pid) if n+1 != len(pidSlice[start:end]) { - fmt.Fprintf(out, ", ") //nolint:errcheck,revive // ignore error + fmt.Fprintf(out, ", ") } } - fmt.Fprintf(out, "\n%s", colors.Reset) //nolint:errcheck,revive // ignore error + fmt.Fprintf(out, "\n%s", colors.Reset) } } // add new Line as spacer - fmt.Fprintf(out, "\n%s", colors.Reset) //nolint:errcheck,revive // ignore error + fmt.Fprintf(out, "\n%s", colors.Reset) } // printRoutesMessage print all routes with method, path, name and handlers @@ -473,11 +470,10 @@ func (app *App) printRoutesMessage() { return routes[i].path < routes[j].path }) - fmt.Fprintf(w, "%smethod\t%s| %spath\t%s| %sname\t%s| %shandlers\t%s\n", colors.Blue, colors.White, colors.Green, colors.White, colors.Cyan, colors.White, colors.Yellow, colors.Reset) //nolint:errcheck,revive // ignore error - fmt.Fprintf(w, "%s------\t%s| %s----\t%s| %s----\t%s| %s--------\t%s\n", colors.Blue, colors.White, colors.Green, colors.White, colors.Cyan, colors.White, colors.Yellow, colors.Reset) //nolint:errcheck,revive // ignore error + fmt.Fprintf(w, "%smethod\t%s| %spath\t%s| %sname\t%s| %shandlers\t%s\n", colors.Blue, colors.White, colors.Green, colors.White, colors.Cyan, colors.White, colors.Yellow, colors.Reset) + fmt.Fprintf(w, "%s------\t%s| %s----\t%s| %s----\t%s| %s--------\t%s\n", colors.Blue, colors.White, colors.Green, colors.White, colors.Cyan, colors.White, colors.Yellow, colors.Reset) for _, route := range routes { - //nolint:errcheck,revive // ignore error fmt.Fprintf(w, "%s%s\t%s| %s%s\t%s| %s%s\t%s| %s%s%s\n", colors.Blue, route.method, colors.White, colors.Green, route.path, colors.White, colors.Cyan, route.name, colors.White, colors.Yellow, route.handlers, colors.Reset) } diff --git a/log/default.go b/log/default.go index 6de940a4..f376a1a0 100644 --- a/log/default.go +++ b/log/default.go @@ -53,9 +53,9 @@ func (l *defaultLogger) privateLogf(lv Level, format string, fmtArgs []any) { buf.WriteString(level) if len(fmtArgs) > 0 { - _, _ = fmt.Fprintf(buf, format, fmtArgs...) //nolint: errcheck // It is fine to ignore the error + _, _ = fmt.Fprintf(buf, format, fmtArgs...) } else { - _, _ = fmt.Fprint(buf, fmtArgs...) //nolint: errcheck // It is fine to ignore the error + _, _ = fmt.Fprint(buf, fmtArgs...) } _ = l.stdlog.Output(l.depth, buf.String()) //nolint:errcheck // It is fine to ignore the error diff --git a/middleware/adaptor/adaptor_test.go b/middleware/adaptor/adaptor_test.go index 67c306fe..7bede0a8 100644 --- a/middleware/adaptor/adaptor_test.go +++ b/middleware/adaptor/adaptor_test.go @@ -1,4 +1,4 @@ -//nolint: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 ( @@ -68,7 +68,7 @@ func Test_HTTPHandler(t *testing.T) { w.Header().Set("Header1", "value1") w.Header().Set("Header2", "value2") w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, "request body is %q", body) //nolint:errcheck // not needed + fmt.Fprintf(w, "request body is %q", body) } fiberH := HTTPHandlerFunc(http.HandlerFunc(nethttpH)) fiberH = setFiberContextValueMiddleware(fiberH, expectedContextKey, expectedContextValue) diff --git a/middleware/logger/default_logger.go b/middleware/logger/default_logger.go index e4de79bf..c70a3e0e 100644 --- a/middleware/logger/default_logger.go +++ b/middleware/logger/default_logger.go @@ -166,7 +166,7 @@ func writeLog(w io.Writer, msg []byte) { // Write error to output if _, err := w.Write([]byte(err.Error())); err != nil { // There is something wrong with the given io.Writer - _, _ = fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) //nolint: errcheck // It is fine to ignore the error + _, _ = fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) } } } diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 011a0ead..eb1b6a44 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -865,7 +865,7 @@ func Test_Logger_ByteSent_Streaming(t *testing.T) { for { i++ msg := fmt.Sprintf("%d - the time is %v", i, time.Now()) - fmt.Fprintf(w, "data: Message: %s\n\n", msg) //nolint:errcheck // ignore error + fmt.Fprintf(w, "data: Message: %s\n\n", msg) err := w.Flush() if err != nil { break @@ -1049,7 +1049,7 @@ func Benchmark_Logger(b *testing.B) { for { i++ msg := fmt.Sprintf("%d - the time is %v", i, time.Now()) - fmt.Fprintf(w, "data: Message: %s\n\n", msg) //nolint:errcheck // ignore error + fmt.Fprintf(w, "data: Message: %s\n\n", msg) err := w.Flush() if err != nil { break @@ -1217,7 +1217,7 @@ func Benchmark_Logger_Parallel(b *testing.B) { for { i++ msg := fmt.Sprintf("%d - the time is %v", i, time.Now()) - fmt.Fprintf(w, "data: Message: %s\n\n", msg) //nolint:errcheck // ignore error + fmt.Fprintf(w, "data: Message: %s\n\n", msg) err := w.Flush() if err != nil { break diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index a8108251..532af09e 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -506,7 +506,10 @@ func Test_Proxy_Do_WithRealURL(t *testing.T) { return Do(c, "https://www.google.com") }) - resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil), fiber.TestConfig{ + Timeout: 2 * time.Second, + FailOnTimeout: true, + }) require.NoError(t, err1) require.Equal(t, fiber.StatusOK, resp.StatusCode) require.Equal(t, "/test", resp.Request.URL.String()) @@ -523,7 +526,10 @@ func Test_Proxy_Do_WithRedirect(t *testing.T) { return Do(c, "https://google.com") }) - resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil), fiber.TestConfig{ + Timeout: 2 * time.Second, + FailOnTimeout: true, + }) require.NoError(t, err1) body, err := io.ReadAll(resp.Body) require.NoError(t, err) @@ -558,7 +564,10 @@ func Test_Proxy_DoRedirects_TooManyRedirects(t *testing.T) { return DoRedirects(c, "http://google.com", 0) }) - resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil), fiber.TestConfig{ + Timeout: 2 * time.Second, + FailOnTimeout: true, + }) require.NoError(t, err1) body, err := io.ReadAll(resp.Body) require.NoError(t, err) diff --git a/path.go b/path.go index 3876943d..b188a41c 100644 --- a/path.go +++ b/path.go @@ -487,7 +487,7 @@ func splitNonEscaped(s, sep string) []string { } // getMatch parses the passed url and tries to match it against the route segments and determine the parameter positions -func (parser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { //nolint: revive // Accepting a bool param is fine here +func (parser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { //nolint:revive // Accepting a bool param is fine here var i, paramsIterator, partLen int for _, segment := range parser.segs { partLen = len(detectionPath) diff --git a/redirect_test.go b/redirect_test.go index 1570d05f..82cfb36b 100644 --- a/redirect_test.go +++ b/redirect_test.go @@ -178,7 +178,7 @@ func Test_Redirect_Back_WithFlashMessages(t *testing.T) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&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").Back("/") require.NoError(t, err) @@ -225,7 +225,7 @@ func Test_Redirect_Route_WithFlashMessages(t *testing.T) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&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") @@ -259,7 +259,7 @@ func Test_Redirect_Route_WithOldInput(t *testing.T) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&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") @@ -294,7 +294,7 @@ func Test_Redirect_Route_WithOldInput(t *testing.T) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Request().Header.Set(HeaderContentType, MIMEApplicationForm) c.Request().SetBodyString("id=1&name=tom") @@ -330,7 +330,7 @@ func Test_Redirect_Route_WithOldInput(t *testing.T) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed body := &bytes.Buffer{} writer := multipart.NewWriter(body) @@ -376,7 +376,7 @@ func Test_Redirect_parseAndClearFlashMessages(t *testing.T) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed msgs := redirectionMsgs{ { @@ -464,7 +464,7 @@ func Benchmark_Redirect_Route(b *testing.B) { return c.JSON(c.Params("name")) }).Name("user") - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -491,7 +491,7 @@ func Benchmark_Redirect_Route_WithQueries(b *testing.B) { return c.JSON(c.Params("name")) }).Name("user") - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -523,7 +523,7 @@ func Benchmark_Redirect_Route_WithFlashMessages(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -576,7 +576,7 @@ func Benchmark_Redirect_parseAndClearFlashMessages(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed val, err := testredirectionMsgs.MarshalMsg(nil) require.NoError(b, err) @@ -618,7 +618,7 @@ func Benchmark_Redirect_processFlashMessages(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed c.Redirect().With("success", "1").With("message", "test") @@ -647,7 +647,7 @@ func Benchmark_Redirect_Messages(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed val, err := testredirectionMsgs.MarshalMsg(nil) require.NoError(b, err) @@ -684,7 +684,7 @@ func Benchmark_Redirect_OldInputs(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed val, err := testredirectionMsgs.MarshalMsg(nil) require.NoError(b, err) @@ -719,7 +719,7 @@ func Benchmark_Redirect_Message(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed val, err := testredirectionMsgs.MarshalMsg(nil) require.NoError(b, err) @@ -750,7 +750,7 @@ func Benchmark_Redirect_OldInput(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed val, err := testredirectionMsgs.MarshalMsg(nil) require.NoError(b, err) diff --git a/router.go b/router.go index a14d2edd..795529bd 100644 --- a/router.go +++ b/router.go @@ -108,7 +108,7 @@ func (r *Route) match(detectionPath, path string, params *[maxParams]string) boo return false } -func (app *App) nextCustom(c CustomCtx) (bool, error) { //nolint: unparam // bool param might be useful for testing +func (app *App) nextCustom(c CustomCtx) (bool, error) { //nolint:unparam // bool param might be useful for testing // Get stack length tree, ok := app.treeStack[c.getMethodINT()][c.getTreePath()] if !ok { diff --git a/router_test.go b/router_test.go index 0ac0c212..0e6019e2 100644 --- a/router_test.go +++ b/router_test.go @@ -600,7 +600,7 @@ func Benchmark_Router_Next(b *testing.B) { var res bool var err error - c := app.AcquireCtx(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++ { @@ -825,7 +825,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(c).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + ctx := app.AcquireCtx(c).(*DefaultCtx) //nolint:errcheck,forcetypeassert // not needed match, err = app.next(ctx) app.ReleaseCtx(ctx) From 87f3f0c8b658978cebe29f4099a02d1fa00532d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Wed, 19 Mar 2025 13:00:16 +0300 Subject: [PATCH 4/6] :bug: bug: fix client iterators when using break statement (#3357) * :bug: bug: fix client iterators when using break statement * fix linter --- client/request.go | 13 +++++++------ client/request_test.go | 19 ++++++++++++++++++- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/client/request.go b/client/request.go index 80a03b09..daa7e60b 100644 --- a/client/request.go +++ b/client/request.go @@ -298,11 +298,12 @@ func (r *Request) Cookie(key string) string { // Use maps.Collect() to gather them into a map if needed. func (r *Request) Cookies() iter.Seq2[string, string] { return func(yield func(string, string) bool) { - r.cookies.VisitAll(func(key, val string) { - if !yield(key, val) { + for k, v := range *r.cookies { + res := yield(k, v) + if !res { return } - }) + } } } @@ -343,11 +344,11 @@ func (r *Request) PathParam(key string) string { // Use maps.Collect() to gather them into a map if needed. func (r *Request) PathParams() iter.Seq2[string, string] { return func(yield func(string, string) bool) { - r.path.VisitAll(func(key, val string) { - if !yield(key, val) { + for k, v := range *r.path { + if !yield(k, v) { return } - }) + } } } diff --git a/client/request_test.go b/client/request_test.go index c13dbbd8..73bae29d 100644 --- a/client/request_test.go +++ b/client/request_test.go @@ -1,3 +1,4 @@ +//nolint:goconst // Much easier to just ignore memory leaks in tests package client import ( @@ -451,6 +452,14 @@ func Test_Request_Cookies(t *testing.T) { require.Equal(t, "bar", cookies["foo"]) require.Equal(t, "foo", cookies["bar"]) + require.NotPanics(t, func() { + for _, v := range req.Cookies() { + if v == "bar" { + break + } + } + }) + require.Len(t, cookies, 2) } @@ -564,6 +573,14 @@ func Test_Request_PathParams(t *testing.T) { require.Equal(t, "foo", pathParams["bar"]) require.Len(t, pathParams, 2) + + require.NotPanics(t, func() { + for _, v := range req.PathParams() { + if v == "bar" { + break + } + } + }) } func Benchmark_Request_PathParams(b *testing.B) { @@ -1579,7 +1596,7 @@ func Test_SetValWithStruct(t *testing.T) { require.True(t, func() bool { for _, v := range p.PeekMulti("TSlice") { - if string(v) == "bar" { //nolint:goconst // test + if string(v) == "bar" { return true } } From f6ac929fde9c5502d2b80abc0fe679d5e6fe5dc3 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Thu, 20 Mar 2025 06:35:59 -0700 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=A7=B9=20chore:=20Mark=20unused=20tes?= =?UTF-8?q?ts=20with=20t.SkipNow=20(#3366)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🚨 test: uncomment test and mark it with t.SkipNow * fix lint issues --- middleware/csrf/csrf_test.go | 91 ++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/middleware/csrf/csrf_test.go b/middleware/csrf/csrf_test.go index 090082f4..7f586cd9 100644 --- a/middleware/csrf/csrf_test.go +++ b/middleware/csrf/csrf_test.go @@ -1331,56 +1331,65 @@ func Test_CSRF_Cookie_Injection_Exploit(t *testing.T) { } // TODO: use this test case and make the unsafe header value bug from https://github.com/gofiber/fiber/issues/2045 reproducible and permanently fixed/tested by this testcase -// func Test_CSRF_UnsafeHeaderValue(t *testing.T) { -// t.Parallel() -// app := fiber.New() +func Test_CSRF_UnsafeHeaderValue(t *testing.T) { + t.SkipNow() + t.Parallel() + app := fiber.New() -// app.Use(New()) -// app.Get("/", func(c fiber.Ctx) error { -// return c.SendStatus(fiber.StatusOK) -// }) -// app.Get("/test", func(c fiber.Ctx) error { -// return c.SendStatus(fiber.StatusOK) -// }) -// app.Post("/", func(c fiber.Ctx) error { -// return c.SendStatus(fiber.StatusOK) -// }) + app.Use(New()) + app.Get("/", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + app.Get("/test", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + app.Post("/", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) -// resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) -// require.NoError(t, err) -// require.Equal(t, fiber.StatusOK, resp.StatusCode) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) -// var token string -// for _, c := range resp.Cookies() { -// if c.Name != ConfigDefault.CookieName { -// continue -// } -// token = c.Value -// break -// } + var token string + for _, c := range resp.Cookies() { + if c.Name != ConfigDefault.CookieName { + continue + } + token = c.Value + break + } -// fmt.Println("token", token) + t.Log("token", token) -// getReq := httptest.NewRequest(fiber.MethodGet, "/", nil) -// getReq.Header.Set(HeaderName, token) -// resp, err = app.Test(getReq) + getReq := httptest.NewRequest(fiber.MethodGet, "/", nil) + getReq.Header.Set(HeaderName, token) + resp, err = app.Test(getReq) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) -// getReq = httptest.NewRequest(fiber.MethodGet, "/test", nil) -// getReq.Header.Set("X-Requested-With", "XMLHttpRequest") -// getReq.Header.Set(fiber.HeaderCacheControl, "no") -// getReq.Header.Set(HeaderName, token) + getReq = httptest.NewRequest(fiber.MethodGet, "/test", nil) + getReq.Header.Set("X-Requested-With", "XMLHttpRequest") + getReq.Header.Set(fiber.HeaderCacheControl, "no") + getReq.Header.Set(HeaderName, token) -// resp, err = app.Test(getReq) + resp, err = app.Test(getReq) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) -// getReq.Header.Set(fiber.HeaderAccept, "*/*") -// getReq.Header.Del(HeaderName) -// resp, err = app.Test(getReq) + getReq.Header.Set(fiber.HeaderAccept, "*/*") + getReq.Header.Del(HeaderName) + resp, err = app.Test(getReq) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) -// postReq := httptest.NewRequest(fiber.MethodPost, "/", nil) -// postReq.Header.Set("X-Requested-With", "XMLHttpRequest") -// postReq.Header.Set(HeaderName, token) -// resp, err = app.Test(postReq) -// } + postReq := httptest.NewRequest(fiber.MethodPost, "/", nil) + postReq.Header.Set("X-Requested-With", "XMLHttpRequest") + postReq.Header.Set(HeaderName, token) + resp, err = app.Test(postReq) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) +} // go test -v -run=^$ -bench=Benchmark_Middleware_CSRF_Check -benchmem -count=4 func Benchmark_Middleware_CSRF_Check(b *testing.B) { From e947e03ed2d072b683aa69757109dee8cf565c08 Mon Sep 17 00:00:00 2001 From: Edvard <75655486+edvardsanta@users.noreply.github.com> Date: Fri, 21 Mar 2025 12:13:21 -0300 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=94=A5=20feat(logger):=20Add=20predef?= =?UTF-8?q?ined=20log=20formats=20(#3359)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(logger): Add predefined log formats This commit introduces predefined log formats for the logger middleware, enhancing its flexibility and ease of use. Users can now specify formats like "common", "combined", and "json" in addition to the default format. Changes: - Added a `format.go` file to store predefined log format constants. - Updated `config.go` to include documentation for the `Format` configuration option, explaining the available placeholders and predefined formats. - Modified `logger.go` to utilize the predefined formats based on the `Format` configuration. - Added a new test case `Test_Logger_CLF` in `logger_test.go` to verify the "common" log format. * feat(logger): Use predefined formats and fix default format This commit updates the logger middleware to utilize the predefined log formats introduced in a previous commit. It also fixes the default format to use the `FormatDefault` constant. Changes: - Updated `config.go` to use `FormatDefault` constant for the default format. - Updated `default_logger.go` to use `FormatDefault` constant for the default format. - Added new test cases in `logger_test.go` to verify the "common", "combined" and "json" log formats. - Updated `format.go` to add newline character to the end of the default format. * feat(logger): Document and exemplify predefined formats * fix(logger): Improve test assertions based on golangci-lint * docs(logger): Improve documentation and formatting logger.md based on markdownlint-cli2 * docs(logger): Improve documentation based on markdownlint-cli2 * fix(logger): Improve combined and JSON format tests * feat(logger): Add ECS log format * feat(logger): Add CustomFormat option This commit introduces a `CustomFormat` option to the `Config` struct, allowing users to specify a predefined format (like "common", "combined", "json", or "ecs") * feat(logger): Add ECS log format to examples and config * docs(logger): Update examples in whats_new.md * feat(logger): Remove CustomFormat option and renamed Format consts - Removed `CustomFormat` field from `Config`. - Removed `LoggerConfig` map. - Rename predefined formats constants. * docs(logger): Update documentation and examples after format refactor --------- Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> --- docs/middleware/logger.md | 78 ++++++++++++------ docs/whats_new.md | 16 ++++ middleware/logger/config.go | 23 ++++-- middleware/logger/default_logger.go | 2 +- middleware/logger/format.go | 14 ++++ middleware/logger/logger.go | 1 - middleware/logger/logger_test.go | 118 ++++++++++++++++++++++++++++ 7 files changed, 220 insertions(+), 32 deletions(-) create mode 100644 middleware/logger/format.go diff --git a/docs/middleware/logger.md b/docs/middleware/logger.md index af16f384..c4c60cbc 100644 --- a/docs/middleware/logger.md +++ b/docs/middleware/logger.md @@ -79,7 +79,7 @@ app.Use(logger.New(logger.Config{ TimeZone: "Asia/Shanghai", Done: func(c fiber.Ctx, logString []byte) { if c.Response().StatusCode() != fiber.StatusOK { - reporter.SendToSlack(logString) + reporter.SendToSlack(logString) } }, })) @@ -88,6 +88,23 @@ app.Use(logger.New(logger.Config{ app.Use(logger.New(logger.Config{ DisableColors: true, })) + +// Use predefined formats +app.Use(logger.New(logger.Config{ + Format: logger.FormatCommon, +})) + +app.Use(logger.New(logger.Config{ + Format: logger.FormatCombined, +})) + +app.Use(logger.New(logger.Config{ + Format: logger.FormatJSON, +})) + +app.Use(logger.New(logger.Config{ + Format: logger.FormatECS, +})) ``` ### Use Logger Middleware with Other Loggers @@ -136,37 +153,50 @@ Writing to os.File is goroutine-safe, but if you are using a custom Stream that ### Config -| Property | Type | Description | Default | -|:-----------------|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------| -| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | -| Skip | `func(fiber.Ctx) bool` | Skip is a function to determine if logging is skipped or written to Stream. | `nil` | -| Done | `func(fiber.Ctx, []byte)` | Done is a function that is called after the log string for a request is written to Stream, and pass the log string as parameter. | `nil` | -| CustomTags | `map[string]LogFunc` | tagFunctions defines the custom tag action. | `map[string]LogFunc` | -| Format | `string` | Format defines the logging tags. | `[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n` | -| TimeFormat | `string` | TimeFormat defines the time format for log timestamps. | `15:04:05` | -| TimeZone | `string` | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc | `"Local"` | -| TimeInterval | `time.Duration` | TimeInterval is the delay before the timestamp is updated. | `500 * time.Millisecond` | -| Stream | `io.Writer` | Stream is a writer where logs are written. | `os.Stdout` | -| LoggerFunc | `func(c fiber.Ctx, data *Data, cfg Config) error` | Custom logger function for integration with logging libraries (Zerolog, Zap, Logrus, etc). Defaults to Fiber's default logger if not defined. | `see default_logger.go defaultLoggerInstance` | -| DisableColors | `bool` | DisableColors defines if the logs output should be colorized. | `false` | +| Property | Type | Description | Default | +| :------------ | :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | +| Next | `func(fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Skip | `func(fiber.Ctx) bool` | Skip is a function to determine if logging is skipped or written to Stream. | `nil` | +| Done | `func(fiber.Ctx, []byte)` | Done is a function that is called after the log string for a request is written to Stream, and pass the log string as parameter. | `nil` | +| CustomTags | `map[string]LogFunc` | tagFunctions defines the custom tag action. | `map[string]LogFunc` | +| `Format` | `string` | Defines the logging tags. See more in [Predefined Formats](#predefined-formats), or create your own using [Tags](#constants). | `[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n` (same as `DefaultFormat`) | +| 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` | +| Stream | `io.Writer` | Stream is a writer where logs are written. | `os.Stdout` | +| LoggerFunc | `func(c fiber.Ctx, data *Data, cfg Config) error` | Custom logger function for integration with logging libraries (Zerolog, Zap, Logrus, etc). Defaults to Fiber's default logger if not defined. | `see default_logger.go defaultLoggerInstance` | +| DisableColors | `bool` | DisableColors defines if the logs output should be colorized. | `false` | ## Default Config ```go var ConfigDefault = Config{ - Next: nil, - Skip nil, - Done: nil, - Format: "[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n", - TimeFormat: "15:04:05", - TimeZone: "Local", - TimeInterval: 500 * time.Millisecond, - Stream: os.Stdout, - DisableColors: false, - LoggerFunc: defaultLoggerInstance, + Next: nil, + Skip: nil, + Done: nil, + Format: DefaultFormat, + TimeFormat: "15:04:05", + TimeZone: "Local", + TimeInterval: 500 * time.Millisecond, + Stream: os.Stdout, + BeforeHandlerFunc: beforeHandlerFunc, + LoggerFunc: defaultLoggerInstance, + enableColors: true, } ``` +## Predefined Formats + +Logger provides predefined formats that you can use by name or directly by specifying the format string. + +| **Format Constant** | **Format String** | **Description** | +|---------------------|--------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| +| `DefaultFormat` | `"[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n"` | Fiber's default logger format. | +| `CommonFormat` | `"${ip} - - [${time}] "${method} ${url} ${protocol}" ${status} ${bytesSent}\n"` | Common Log Format (CLF) used in web server logs. | +| `CombinedFormat` | `"${ip} - - [${time}] "${method} ${url} ${protocol}" ${status} ${bytesSent} "${referer}" "${ua}"\n"` | CLF format plus the `referer` and `user agent` fields. | +| `JSONFormat` | `"{time: ${time}, ip: ${ip}, method: ${method}, url: ${url}, status: ${status}, bytesSent: ${bytesSent}}\n"` | JSON format for structured logging. | +| `ECSFormat` | `"{\"@timestamp\":\"${time}\",\"ecs\":{\"version\":\"1.6.0\"},\"client\":{\"ip\":\"${ip}\"},\"http\":{\"request\":{\"method\":\"${method}\",\"url\":\"${url}\",\"protocol\":\"${protocol}\"},\"response\":{\"status_code\":${status},\"body\":{\"bytes\":${bytesSent}}}},\"log\":{\"level\":\"INFO\",\"logger\":\"fiber\"},\"message\":\"${method} ${url} responded with ${status}\"}\n"` | Elastic Common Schema (ECS) format for structured logging. | + ## Constants ```go diff --git a/docs/whats_new.md b/docs/whats_new.md index 8528dc41..bc569d3e 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -937,6 +937,22 @@ app.Use(logger.New(logger.Config{ +#### Predefined Formats + +Logger provides predefined formats that you can use by name or directly by specifying the format string. +
+ +Example Usage + +```go +app.Use(logger.New(logger.Config{ + Format: logger.FormatCombined, +})) +``` + +See more in [Logger](./middleware/logger.md#predefined-formats) +
+ ### Filesystem We've decided to remove filesystem middleware to clear up the confusion between static and filesystem middleware. diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 2df814eb..d543acfa 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -50,9 +50,23 @@ type Config struct { timeZoneLocation *time.Location - // Format defines the logging tags + // Format defines the logging format for the middleware. // - // Optional. Default: [${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error} + // You can customize the log output by defining a format string with placeholders + // such as: ${time}, ${ip}, ${status}, ${method}, ${path}, ${latency}, ${error}, etc. + // The full list of available placeholders can be found in 'tags.go' or at + // 'https://docs.gofiber.io/api/middleware/logger/#constants'. + // + // Fiber provides predefined logging formats that can be used directly: + // + // - DefaultFormat → Uses the default log format: "[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}" + // - CommonFormat → Uses the Apache Common Log Format (CLF): "${ip} - - [${time}] \"${method} ${url} ${protocol}\" ${status} ${bytesSent}\n" + // - CombinedFormat → Uses the Apache Combined Log Format: "${ip} - - [${time}] \"${method} ${url} ${protocol}\" ${status} ${bytesSent} \"${referer}\" \"${ua}\"\n" + // - JSONFormat → Uses the JSON log format: "{\"time\":\"${time}\",\"ip\":\"${ip}\",\"method\":\"${method}\",\"url\":\"${url}\",\"status\":${status},\"bytesSent\":${bytesSent}}\n" + // - ECSFormat → Uses the Elastic Common Schema (ECS) log format: {\"@timestamp\":\"${time}\",\"ecs\":{\"version\":\"1.6.0\"},\"client\":{\"ip\":\"${ip}\"},\"http\":{\"request\":{\"method\":\"${method}\",\"url\":\"${url}\",\"protocol\":\"${protocol}\"},\"response\":{\"status_code\":${status},\"body\":{\"bytes\":${bytesSent}}}},\"log\":{\"level\":\"INFO\",\"logger\":\"fiber\"},\"message\":\"${method} ${url} responded with ${status}\"}" + // If both `Format` and `CustomFormat` are provided, the `CustomFormat` will be used, and the `Format` field will be ignored. + // If no format is specified, the default format is used: + // "[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}" Format string // TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html @@ -105,7 +119,7 @@ var ConfigDefault = Config{ Next: nil, Skip: nil, Done: nil, - Format: defaultFormat, + Format: DefaultFormat, TimeFormat: "15:04:05", TimeZone: "Local", TimeInterval: 500 * time.Millisecond, @@ -115,9 +129,6 @@ var ConfigDefault = Config{ enableColors: true, } -// default logging format for Fiber's default logger -var defaultFormat = "[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n" - // Helper function to set default values func configDefault(config ...Config) Config { // Return default config if nothing provided diff --git a/middleware/logger/default_logger.go b/middleware/logger/default_logger.go index c70a3e0e..a2cbfa1f 100644 --- a/middleware/logger/default_logger.go +++ b/middleware/logger/default_logger.go @@ -28,7 +28,7 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error { buf := bytebufferpool.Get() // Default output when no custom Format or io.Writer is given - if cfg.Format == defaultFormat { + if cfg.Format == DefaultFormat { // Format error if exist formatErr := "" if cfg.enableColors { diff --git a/middleware/logger/format.go b/middleware/logger/format.go new file mode 100644 index 00000000..901c2409 --- /dev/null +++ b/middleware/logger/format.go @@ -0,0 +1,14 @@ +package logger + +const ( + // Fiber's default logger + DefaultFormat = "[${time}] ${ip} ${status} - ${latency} ${method} ${path} ${error}\n" + // Apache Common Log Format (CLF) + CommonFormat = "${ip} - - [${time}] \"${method} ${url} ${protocol}\" ${status} ${bytesSent}\n" + // Apache Combined Log Format + CombinedFormat = "${ip} - - [${time}] \"${method} ${url} ${protocol}\" ${status} ${bytesSent} \"${referer}\" \"${ua}\"\n" + // JSON log formats + JSONFormat = "{\"time\":\"${time}\",\"ip\":\"${ip}\",\"method\":\"${method}\",\"url\":\"${url}\",\"status\":${status},\"bytesSent\":${bytesSent}}\n" + // Elastic Common Schema (ECS) Log Format + ECSFormat = "{\"@timestamp\":\"${time}\",\"ecs\":{\"version\":\"1.6.0\"},\"client\":{\"ip\":\"${ip}\"},\"http\":{\"request\":{\"method\":\"${method}\",\"url\":\"${url}\",\"protocol\":\"${protocol}\"},\"response\":{\"status_code\":${status},\"body\":{\"bytes\":${bytesSent}}}},\"log\":{\"level\":\"INFO\",\"logger\":\"fiber\"},\"message\":\"${method} ${url} responded with ${status}\"}\n" +) diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 793c16c2..7d4befc9 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -40,7 +40,6 @@ func New(config ...Config) fiber.Handler { } }() } - // Set PID once pid := strconv.Itoa(os.Getpid()) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index eb1b6a44..edce174c 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -467,6 +467,124 @@ func Test_Logger_All(t *testing.T) { require.Equal(t, expected, buf.String()) } +func Test_Logger_CLF_Format(t *testing.T) { + t.Parallel() + buf := bytebufferpool.Get() + defer bytebufferpool.Put(buf) + + app := fiber.New() + + app.Use(New(Config{ + Format: CommonFormat, + Stream: buf, + })) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/?foo=bar", nil)) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) + + expected := fmt.Sprintf("0.0.0.0 - - [%s] \"%s %s %s\" %d %d\n", + time.Now().Format("15:04:05"), + fiber.MethodGet, "/?foo=bar", "HTTP/1.1", + fiber.StatusNotFound, + 0) + logResponse := buf.String() + require.Equal(t, expected, logResponse) +} + +func Test_Logger_Combined_CLF_Format(t *testing.T) { + t.Parallel() + buf := bytebufferpool.Get() + defer bytebufferpool.Put(buf) + + app := fiber.New() + + app.Use(New(Config{ + Format: CombinedFormat, + Stream: buf, + })) + const expectedUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36" + const expectedReferer = "http://example.com" + req := httptest.NewRequest(fiber.MethodGet, "/?foo=bar", nil) + req.Header.Set("Referer", expectedReferer) + req.Header.Set("User-Agent", expectedUA) + resp, err := app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) + + expected := fmt.Sprintf("0.0.0.0 - - [%s] %q %d %d %q %q\n", + time.Now().Format("15:04:05"), + fmt.Sprintf("%s %s %s", fiber.MethodGet, "/?foo=bar", "HTTP/1.1"), + fiber.StatusNotFound, + 0, + expectedReferer, + expectedUA) + logResponse := buf.String() + require.Equal(t, expected, logResponse) +} + +func Test_Logger_Json_Format(t *testing.T) { + t.Parallel() + buf := bytebufferpool.Get() + defer bytebufferpool.Put(buf) + + app := fiber.New() + + app.Use(New(Config{ + Format: JSONFormat, + Stream: buf, + })) + + req := httptest.NewRequest(fiber.MethodGet, "/?foo=bar", nil) + resp, err := app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) + + expected := fmt.Sprintf( + "{\"time\":%q,\"ip\":%q,\"method\":%q,\"url\":%q,\"status\":%d,\"bytesSent\":%d}\n", + time.Now().Format("15:04:05"), + "0.0.0.0", + fiber.MethodGet, + "/?foo=bar", + fiber.StatusNotFound, + 0, + ) + logResponse := buf.String() + require.Equal(t, expected, logResponse) +} + +func Test_Logger_ECS_Format(t *testing.T) { + t.Parallel() + buf := bytebufferpool.Get() + defer bytebufferpool.Put(buf) + + app := fiber.New() + + app.Use(New(Config{ + Format: ECSFormat, + Stream: buf, + })) + + req := httptest.NewRequest(fiber.MethodGet, "/?foo=bar", nil) + resp, err := app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) + + expected := fmt.Sprintf( + "{\"@timestamp\":%q,\"ecs\":{\"version\":\"1.6.0\"},\"client\":{\"ip\":%q},\"http\":{\"request\":{\"method\":%q,\"url\":%q,\"protocol\":%q},\"response\":{\"status_code\":%d,\"body\":{\"bytes\":%d}}},\"log\":{\"level\":\"INFO\",\"logger\":\"fiber\"},\"message\":%q}\n", + time.Now().Format("15:04:05"), + "0.0.0.0", + fiber.MethodGet, + "/?foo=bar", + "HTTP/1.1", + fiber.StatusNotFound, + 0, + fmt.Sprintf("%s %s responded with %d", fiber.MethodGet, "/?foo=bar", fiber.StatusNotFound), + ) + logResponse := buf.String() + require.Equal(t, expected, logResponse) +} + func getLatencyTimeUnits() []struct { unit string div time.Duration