From 54439a5bde246577331b6e345c9d640b289568c2 Mon Sep 17 00:00:00 2001 From: Iliya Date: Thu, 9 Feb 2023 09:14:13 +0330 Subject: [PATCH 001/162] =?UTF-8?q?=F0=9F=94=A5=20Feature:=20add=20queryFl?= =?UTF-8?q?oat=20parser=20(#2328)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ctx.go | 22 ++++++++++++++++++++++ ctx_test.go | 15 +++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/ctx.go b/ctx.go index 046307e1ff..6606d314b2 100644 --- a/ctx.go +++ b/ctx.go @@ -1126,6 +1126,28 @@ func (c *Ctx) QueryInt(key string, defaultValue ...int) int { return value } +// QueryFloat returns float64 value of key string parameter in the url. +// Default to empty or invalid key is 0. +// +// GET /?name=alex&amount=32.23&id= +// QueryFloat("amount") = 32.23 +// QueryFloat("amount", 3) = 32.23 +// QueryFloat("name", 1) = 1 +// QueryFloat("name") = 0 +// QueryFloat("id", 3) = 3 +func (c *Ctx) QueryFloat(key string, defaultValue ...float64) float64 { + // use strconv.ParseFloat to convert the param to a float or return zero and an error. + value, err := strconv.ParseFloat(c.app.getString(c.fasthttp.QueryArgs().Peek(key)), 64) + if err != nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return 0 + } + + return value +} + // QueryParser binds the query string to a struct. func (c *Ctx) QueryParser(out interface{}) error { data := make(map[string][]string) diff --git a/ctx_test.go b/ctx_test.go index 7fc59dc89c..ac15b65a93 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2149,6 +2149,21 @@ func Test_Ctx_QueryInt(t *testing.T) { utils.AssertEqual(t, 2, c.QueryInt("id", 2)) } +func Test_Ctx_QueryFloat(t *testing.T) { + t.Parallel() + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Request().URI().SetQueryString("name=alex&amount=32.23&id=") + + utils.AssertEqual(t, 32.23, c.QueryFloat("amount")) + utils.AssertEqual(t, 32.23, c.QueryFloat("amount", 3.123)) + utils.AssertEqual(t, 87.123, c.QueryFloat("name", 87.123)) + utils.AssertEqual(t, float64(0), c.QueryFloat("name")) + utils.AssertEqual(t, 12.87, c.QueryFloat("id", 12.87)) + utils.AssertEqual(t, float64(0), c.QueryFloat("id")) +} + // go test -run Test_Ctx_Range func Test_Ctx_Range(t *testing.T) { t.Parallel() From c3b151a1fe058abd91650cdd905e63dbf2409165 Mon Sep 17 00:00:00 2001 From: Iliya Date: Thu, 9 Feb 2023 18:03:09 +0330 Subject: [PATCH 002/162] =?UTF-8?q?=F0=9F=94=A5=20Feature:=20add=20queryBo?= =?UTF-8?q?ol=20parser=20(#2329)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔥 Feature: add queryBool parser * 🩹 pass linter --- ctx.go | 22 +++++++++++++++++++++- ctx_test.go | 15 +++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/ctx.go b/ctx.go index 6606d314b2..2a99b8384f 100644 --- a/ctx.go +++ b/ctx.go @@ -1126,6 +1126,27 @@ func (c *Ctx) QueryInt(key string, defaultValue ...int) int { return value } +// QueryBool returns bool value of key string parameter in the url. +// Default to empty or invalid key is true. +// +// Get /?name=alex&want_pizza=false&id= +// QueryBool("want_pizza") == false +// QueryBool("want_pizza", true) == false +// QueryBool("alex") == true +// QueryBool("alex", false) == false +// QueryBool("id") == true +// QueryBool("id", false) == false +func (c *Ctx) QueryBool(key string, defaultValue ...bool) bool { + value, err := strconv.ParseBool(c.app.getString(c.fasthttp.QueryArgs().Peek(key))) + if err != nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return true + } + return value +} + // QueryFloat returns float64 value of key string parameter in the url. // Default to empty or invalid key is 0. // @@ -1144,7 +1165,6 @@ func (c *Ctx) QueryFloat(key string, defaultValue ...float64) float64 { } return 0 } - return value } diff --git a/ctx_test.go b/ctx_test.go index ac15b65a93..cecef6ab9f 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2149,6 +2149,21 @@ func Test_Ctx_QueryInt(t *testing.T) { utils.AssertEqual(t, 2, c.QueryInt("id", 2)) } +func Test_Ctx_QueryBool(t *testing.T) { + t.Parallel() + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Request().URI().SetQueryString("name=alex&want_pizza=false&id=") + + utils.AssertEqual(t, false, c.QueryBool("want_pizza")) + utils.AssertEqual(t, false, c.QueryBool("want_pizza", true)) + utils.AssertEqual(t, true, c.QueryBool("name")) + utils.AssertEqual(t, false, c.QueryBool("name", false)) + utils.AssertEqual(t, true, c.QueryBool("id")) + utils.AssertEqual(t, false, c.QueryBool("id", false)) +} + func Test_Ctx_QueryFloat(t *testing.T) { t.Parallel() app := New() From 497eb02b48190484d842748a08b2a84aba2ced62 Mon Sep 17 00:00:00 2001 From: Michail Safronov Date: Tue, 14 Feb 2023 02:48:55 +0500 Subject: [PATCH 003/162] Basic auth alloc (#2333) * basic_auth: extend benchmark for uppercase Basic * basic_auth: check space after basic (and avoid alloc if Basic) * fixup! basic_auth: check space after basic (and avoid alloc if Basic) --- middleware/basicauth/basicauth.go | 2 +- middleware/basicauth/basicauth_test.go | 30 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/middleware/basicauth/basicauth.go b/middleware/basicauth/basicauth.go index 3017be09b9..c3ed62bb9d 100644 --- a/middleware/basicauth/basicauth.go +++ b/middleware/basicauth/basicauth.go @@ -24,7 +24,7 @@ func New(config Config) fiber.Handler { auth := c.Get(fiber.HeaderAuthorization) // Check if the header contains content besides "basic". - if len(auth) <= 6 || strings.ToLower(auth[:5]) != "basic" { + if len(auth) <= 6 || !utils.EqualFold(auth[:6], "basic ") { return cfg.Unauthorized(c) } diff --git a/middleware/basicauth/basicauth_test.go b/middleware/basicauth/basicauth_test.go index 1284163203..0722516a40 100644 --- a/middleware/basicauth/basicauth_test.go +++ b/middleware/basicauth/basicauth_test.go @@ -122,3 +122,33 @@ func Benchmark_Middleware_BasicAuth(b *testing.B) { utils.AssertEqual(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) } + +// go test -v -run=^$ -bench=Benchmark_Middleware_BasicAuth -benchmem -count=4 +func Benchmark_Middleware_BasicAuth_Upper(b *testing.B) { + app := fiber.New() + + app.Use(New(Config{ + Users: map[string]string{ + "john": "doe", + }, + })) + app.Get("/", func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusTeapot) + }) + + h := app.Handler() + + fctx := &fasthttp.RequestCtx{} + fctx.Request.Header.SetMethod(fiber.MethodGet) + fctx.Request.SetRequestURI("/") + fctx.Request.Header.Set(fiber.HeaderAuthorization, "Basic am9objpkb2U=") // john:doe + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + h(fctx) + } + + utils.AssertEqual(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) +} From b634ba0a58315308f488a4a5a920d46f095465fc Mon Sep 17 00:00:00 2001 From: Ryan Devenney Date: Mon, 20 Feb 2023 16:36:34 -0500 Subject: [PATCH 004/162] fix cors * behavior #2338 (#2339) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛- fix cors * behavior #2338 --- middleware/cors/cors.go | 6 +++--- middleware/cors/cors_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index d7325d5b2f..011ca25459 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -112,11 +112,11 @@ func New(config ...Config) fiber.Handler { // Check allowed origins for _, o := range allowOrigins { - if o == "*" && cfg.AllowCredentials { - allowOrigin = origin + if o == "*" { + allowOrigin = "*" break } - if o == "*" || o == origin { + if o == origin { allowOrigin = o break } diff --git a/middleware/cors/cors_test.go b/middleware/cors/cors_test.go index 3600282331..4343cc2313 100644 --- a/middleware/cors/cors_test.go +++ b/middleware/cors/cors_test.go @@ -76,7 +76,7 @@ func Test_CORS_Wildcard(t *testing.T) { handler(ctx) // Check result - utils.AssertEqual(t, "localhost", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + utils.AssertEqual(t, "*", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) utils.AssertEqual(t, "true", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowCredentials))) utils.AssertEqual(t, "3600", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlMaxAge))) utils.AssertEqual(t, "Authentication", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowHeaders))) From dc038d8233b486960a87d30253d4a888e376ff0b Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Fri, 24 Feb 2023 09:09:00 -0500 Subject: [PATCH 005/162] Feature: Add DoRedirects, DoTimeout and DoDeadline to Proxy middleware (#2332) * Add support for DoRedirects Signed-off-by: Juan Calderon-Perez * Fix linter issues Signed-off-by: Juan Calderon-Perez * Add example to README * Add support for DoDeadline and DoTimeout. Expand unit-tests * Fix linter errors Signed-off-by: Juan Calderon-Perez * Add examples for Proxy Middleware --------- Signed-off-by: Juan Calderon-Perez --- middleware/proxy/README.md | 36 +++++++ middleware/proxy/proxy.go | 47 ++++++++- middleware/proxy/proxy_test.go | 179 +++++++++++++++++++++++++++++++-- 3 files changed, 246 insertions(+), 16 deletions(-) diff --git a/middleware/proxy/README.md b/middleware/proxy/README.md index 6cc03e8b9d..e8663878fc 100644 --- a/middleware/proxy/README.md +++ b/middleware/proxy/README.md @@ -18,6 +18,12 @@ func Balancer(config Config) fiber.Handler func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler // Do performs the given http request and fills the given http response. func Do(c *fiber.Ctx, addr string, clients ...*fasthttp.Client) error +// DoRedirects performs the given http request and fills the given http response while following up to maxRedirectsCount redirects. +func DoRedirects(c *fiber.Ctx, addr string, maxRedirectsCount int, clients ...*fasthttp.Client) error +// DoDeadline performs the given request and waits for response until the given deadline. +func DoDeadline(c *fiber.Ctx, addr string, deadline time.Time, clients ...*fasthttp.Client) error +// DoTimeout performs the given request and waits for response during the given timeout duration. +func DoTimeout(c *fiber.Ctx, addr string, timeout time.Duration, clients ...*fasthttp.Client) error // DomainForward the given http request based on the given domain and fills the given http response func DomainForward(hostname string, addr string, clients ...*fasthttp.Client) fiber.Handler // BalancerForward performs the given http request based round robin balancer and fills the given http response @@ -73,6 +79,36 @@ app.Get("/:id", func(c *fiber.Ctx) error { return nil }) +// Make proxy requests while following redirects +app.Get("/proxy", func(c *fiber.Ctx) error { + if err := proxy.DoRedirects(c, "http://google.com", 3); err != nil { + return err + } + // Remove Server header from response + c.Response().Header.Del(fiber.HeaderServer) + return nil +}) + +// Make proxy requests and wait up to 5 seconds before timing out +app.Get("/proxy", func(c *fiber.Ctx) error { + if err := proxy.DoTimeout(c, "http://localhost:3000", time.Second * 5); err != nil { + return err + } + // Remove Server header from response + c.Response().Header.Del(fiber.HeaderServer) + return nil +}) + +// Make proxy requests, timeout a minute from now +app.Get("/proxy", func(c *fiber.Ctx) error { + if err := DoDeadline(c, "http://localhost", time.Now().Add(time.Minute)); err != nil { + return err + } + // Remove Server header from response + c.Response().Header.Del(fiber.HeaderServer) + return nil +}) + // Minimal round robin balancer app.Use(proxy.Balancer(proxy.Config{ Servers: []string{ diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index 008342631f..3a464d48d2 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -7,6 +7,7 @@ import ( "net/url" "strings" "sync" + "time" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" @@ -139,16 +140,53 @@ func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler { // Do performs the given http request and fills the given http response. // This method can be used within a fiber.Handler func Do(c *fiber.Ctx, addr string, clients ...*fasthttp.Client) error { + return doAction(c, addr, func(cli *fasthttp.Client, req *fasthttp.Request, resp *fasthttp.Response) error { + return cli.Do(req, resp) + }, clients...) +} + +// DoRedirects performs the given http request and fills the given http response, following up to maxRedirectsCount redirects. +// When the redirect count exceeds maxRedirectsCount, ErrTooManyRedirects is returned. +// This method can be used within a fiber.Handler +func DoRedirects(c *fiber.Ctx, addr string, maxRedirectsCount int, clients ...*fasthttp.Client) error { + return doAction(c, addr, func(cli *fasthttp.Client, req *fasthttp.Request, resp *fasthttp.Response) error { + return cli.DoRedirects(req, resp, maxRedirectsCount) + }, clients...) +} + +// DoDeadline performs the given request and waits for response until the given deadline. +// This method can be used within a fiber.Handler +func DoDeadline(c *fiber.Ctx, addr string, deadline time.Time, clients ...*fasthttp.Client) error { + return doAction(c, addr, func(cli *fasthttp.Client, req *fasthttp.Request, resp *fasthttp.Response) error { + return cli.DoDeadline(req, resp, deadline) + }, clients...) +} + +// DoTimeout performs the given request and waits for response during the given timeout duration. +// This method can be used within a fiber.Handler +func DoTimeout(c *fiber.Ctx, addr string, timeout time.Duration, clients ...*fasthttp.Client) error { + return doAction(c, addr, func(cli *fasthttp.Client, req *fasthttp.Request, resp *fasthttp.Response) error { + return cli.DoTimeout(req, resp, timeout) + }, clients...) +} + +func doAction( + c *fiber.Ctx, + addr string, + action func(cli *fasthttp.Client, req *fasthttp.Request, resp *fasthttp.Response) error, + clients ...*fasthttp.Client, +) error { var cli *fasthttp.Client + + // set local or global client if len(clients) != 0 { - // Set local client cli = clients[0] } else { - // Set global client lock.RLock() cli = client lock.RUnlock() } + req := c.Request() res := c.Response() originalURL := utils.CopyString(c.OriginalURL()) @@ -157,14 +195,13 @@ func Do(c *fiber.Ctx, addr string, clients ...*fasthttp.Client) error { copiedURL := utils.CopyString(addr) req.SetRequestURI(copiedURL) // NOTE: if req.isTLS is true, SetRequestURI keeps the scheme as https. - // issue reference: - // https://github.com/gofiber/fiber/issues/1762 + // Reference: https://github.com/gofiber/fiber/issues/1762 if scheme := getScheme(utils.UnsafeBytes(copiedURL)); len(scheme) > 0 { req.URI().SetSchemeBytes(scheme) } req.Header.Del(fiber.HeaderConnection) - if err := cli.Do(req, res); err != nil { + if err := action(cli, req, res); err != nil { return err } res.Header.Del(fiber.HeaderConnection) diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index 6ed169ed69..49be2a2c95 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -2,6 +2,7 @@ package proxy import ( "crypto/tls" + "errors" "io" "net" "net/http/httptest" @@ -48,6 +49,19 @@ func Test_Proxy_Empty_Upstream_Servers(t *testing.T) { app.Use(Balancer(Config{Servers: []string{}})) } +// go test -run Test_Proxy_Empty_Config +func Test_Proxy_Empty_Config(t *testing.T) { + t.Parallel() + + defer func() { + if r := recover(); r != nil { + utils.AssertEqual(t, "Servers cannot be empty", r) + } + }() + app := fiber.New() + app.Use(New(Config{})) +} + // go test -run Test_Proxy_Next func Test_Proxy_Next(t *testing.T) { t.Parallel() @@ -345,24 +359,167 @@ func Test_Proxy_Buffer_Size_Response(t *testing.T) { // go test -race -run Test_Proxy_Do_RestoreOriginalURL func Test_Proxy_Do_RestoreOriginalURL(t *testing.T) { t.Parallel() + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { + return c.SendString("proxied") + }) + app := fiber.New() - app.Get("/proxy", func(c *fiber.Ctx) error { - return c.SendString("ok") + app.Get("/test", func(c *fiber.Ctx) error { + return Do(c, "http://"+addr) }) + resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + utils.AssertEqual(t, nil, err1) + utils.AssertEqual(t, "/test", resp.Request.URL.String()) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "proxied", string(body)) +} + +// go test -race -run Test_Proxy_Do_WithRealURL +func Test_Proxy_Do_WithRealURL(t *testing.T) { + t.Parallel() + app := fiber.New() app.Get("/test", func(c *fiber.Ctx) error { - originalURL := utils.CopyString(c.OriginalURL()) - if err := Do(c, "/proxy"); err != nil { - return err - } - utils.AssertEqual(t, originalURL, c.OriginalURL()) - return c.SendString("ok") + return Do(c, "https://www.google.com") + }) + + resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + utils.AssertEqual(t, nil, err1) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + utils.AssertEqual(t, "/test", resp.Request.URL.String()) + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, true, strings.Contains(string(body), "https://www.google.com/")) +} + +// go test -race -run Test_Proxy_Do_WithRedirect +func Test_Proxy_Do_WithRedirect(t *testing.T) { + t.Parallel() + app := fiber.New() + app.Get("/test", func(c *fiber.Ctx) error { + return Do(c, "https://google.com") + }) + + resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + utils.AssertEqual(t, nil, err1) + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, true, strings.Contains(string(body), "https://www.google.com/")) + utils.AssertEqual(t, 301, resp.StatusCode) +} + +// go test -race -run Test_Proxy_DoRedirects_RestoreOriginalURL +func Test_Proxy_DoRedirects_RestoreOriginalURL(t *testing.T) { + t.Parallel() + app := fiber.New() + app.Get("/test", func(c *fiber.Ctx) error { + return DoRedirects(c, "http://google.com", 1) + }) + + resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + utils.AssertEqual(t, nil, err1) + _, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + utils.AssertEqual(t, "/test", resp.Request.URL.String()) +} + +// go test -race -run Test_Proxy_DoRedirects_TooManyRedirects +func Test_Proxy_DoRedirects_TooManyRedirects(t *testing.T) { + t.Parallel() + app := fiber.New() + app.Get("/test", func(c *fiber.Ctx) error { + return DoRedirects(c, "http://google.com", 0) }) + + resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + utils.AssertEqual(t, nil, err1) + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "too many redirects detected when doing the request", string(body)) + utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) + utils.AssertEqual(t, "/test", resp.Request.URL.String()) +} + +// go test -race -run Test_Proxy_DoTimeout_RestoreOriginalURL +func Test_Proxy_DoTimeout_RestoreOriginalURL(t *testing.T) { + t.Parallel() + + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { + return c.SendString("proxied") + }) + + app := fiber.New() + app.Get("/test", func(c *fiber.Ctx) error { + return DoTimeout(c, "http://"+addr, time.Second) + }) + + resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + utils.AssertEqual(t, nil, err1) + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "proxied", string(body)) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + utils.AssertEqual(t, "/test", resp.Request.URL.String()) +} + +// go test -race -run Test_Proxy_DoTimeout_Timeout +func Test_Proxy_DoTimeout_Timeout(t *testing.T) { + t.Parallel() + + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { + time.Sleep(time.Second * 5) + return c.SendString("proxied") + }) + + app := fiber.New() + app.Get("/test", func(c *fiber.Ctx) error { + return DoTimeout(c, "http://"+addr, time.Second) + }) + _, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) - // This test requires multiple requests due to zero allocation used in fiber - _, err2 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + utils.AssertEqual(t, errors.New("test: timeout error 1000ms"), err1) +} +// go test -race -run Test_Proxy_DoDeadline_RestoreOriginalURL +func Test_Proxy_DoDeadline_RestoreOriginalURL(t *testing.T) { + t.Parallel() + + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { + return c.SendString("proxied") + }) + + app := fiber.New() + app.Get("/test", func(c *fiber.Ctx) error { + return DoDeadline(c, "http://"+addr, time.Now().Add(time.Second)) + }) + + resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) utils.AssertEqual(t, nil, err1) - utils.AssertEqual(t, nil, err2) + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "proxied", string(body)) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + utils.AssertEqual(t, "/test", resp.Request.URL.String()) +} + +// go test -race -run Test_Proxy_DoDeadline_PastDeadline +func Test_Proxy_DoDeadline_PastDeadline(t *testing.T) { + t.Parallel() + + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { + time.Sleep(time.Second * 5) + return c.SendString("proxied") + }) + + app := fiber.New() + app.Get("/test", func(c *fiber.Ctx) error { + return DoDeadline(c, "http://"+addr, time.Now().Add(time.Second)) + }) + + _, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + utils.AssertEqual(t, errors.New("test: timeout error 1000ms"), err1) } // go test -race -run Test_Proxy_Do_HTTP_Prefix_URL From af69fa5a56b921a02e041d45a61024a37c53e564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sat, 25 Feb 2023 10:01:26 +0100 Subject: [PATCH 006/162] use another labeler --- .github/release-drafter.yml | 55 --------------------------- .github/workflows/auto-labeler.yml | 21 ++++++++++ .github/workflows/release-drafter.yml | 5 +-- 3 files changed, 22 insertions(+), 59 deletions(-) create mode 100644 .github/workflows/auto-labeler.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 6df748953c..134447d7bc 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -32,61 +32,6 @@ version-resolver: - '🤖 Dependencies' - '🧹 Updates' default: patch -autolabeler: - - label: '📒 Documentation' - title: - - '/docs/i' - - '/doc:/i' - - '/\[doc\]/i' - - '/README/i' - - '/typos/i' - - '/comment/i' - - '/📚/i' - - '/📒/i' - - '/📝/i' - - '/documentation/i' - - label: '☢️ Bug' - title: - - '/fix/i' - - '/race/i' - - '/bug/i' - - '/missing/i' - - '/correct/i' - - '/🐛/i' - - '/☢/i' - - '/🩹/i' - - '/🚨/i' - - label: '🧹 Updates' - title: - - '/improve/i' - - '/update/i' - - '/refactor/i' - - '/deprecated/i' - - '/remove/i' - - '/unused/i' - - '/test/i' - - '/⚡/i' - - '/👷/i' - - '/🚧/i' - - '/♻️/i' - - '/🎨/i' - - '/🧪/i' - - '/🧹/i' - - label: '🤖 Dependencies' - title: - - '/bumb/i' - - '/dependencies/i' - - '/📦/i' - - '/🤖/i' - - label: '✏️ Feature' - title: - - '/feature/i' - - '/create/i' - - '/implement/i' - - '/add/i' - - '/🚀/i' - - '/✨/i' - - '/🔥/i' template: | $CHANGES diff --git a/.github/workflows/auto-labeler.yml b/.github/workflows/auto-labeler.yml new file mode 100644 index 0000000000..c86bd68a48 --- /dev/null +++ b/.github/workflows/auto-labeler.yml @@ -0,0 +1,21 @@ +name: Auto labeler +on: + issues: + types: [ opened, edited, milestoned ] + pull_request_target: + types: [ opened ] +permissions: + contents: read + issues: write + pull-requests: write + statuses: write + checks: write +jobs: + labeler: + runs-on: ubuntu-latest + steps: + - name: Check Labels + id: labeler + uses: fuxingloh/multi-labeler@v1 + with: + github-token: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 1669279191..f4a5cea982 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -5,10 +5,7 @@ on: # branches to consider in the event; optional, defaults to all branches: - master - # pull_request event is required only for autolabeler - pull_request: - # Only following types are handled by the action, but one can default to all as well - types: [opened, reopened, synchronize] + - main jobs: update_release_draft: From b3643198f8e9a8126b2681afdd374cc4ff7219b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Sat, 25 Feb 2023 12:29:07 +0300 Subject: [PATCH 007/162] :memo: docs: automated synchronization with `gofiber/docs` (#2344) Automated Synchronization with gofiber/docs repo --- .github/scripts/sync_docs.sh | 38 + .github/workflows/sync-docs.yml | 25 + docs/api/_category_.json | 8 + docs/api/app.md | 608 +++++++++ docs/api/client.md | 567 ++++++++ docs/api/constants.md | 291 ++++ docs/api/ctx.md | 1890 ++++++++++++++++++++++++++ docs/api/fiber.md | 119 ++ docs/api/middleware/_category_.json | 9 + docs/api/middleware/basicauth.md | 121 ++ docs/api/middleware/cache.md | 138 ++ docs/api/middleware/compress.md | 85 ++ docs/api/middleware/cors.md | 99 ++ docs/api/middleware/csrf.md | 136 ++ docs/api/middleware/earlydata.md | 93 ++ docs/api/middleware/encryptcookie.md | 108 ++ docs/api/middleware/envvar.md | 74 + docs/api/middleware/etag.md | 80 ++ docs/api/middleware/expvar.md | 84 ++ docs/api/middleware/favicon.md | 85 ++ docs/api/middleware/filesystem.md | 237 ++++ docs/api/middleware/idempotency.md | 110 ++ docs/api/middleware/limiter.md | 146 ++ docs/api/middleware/logger.md | 193 +++ docs/api/middleware/monitor.md | 104 ++ docs/api/middleware/pprof.md | 70 + docs/api/middleware/proxy.md | 150 ++ docs/api/middleware/recover.md | 67 + docs/api/middleware/requestid.md | 79 ++ docs/api/middleware/session.md | 151 ++ docs/api/middleware/skip.md | 25 + docs/api/middleware/timeout.md | 128 ++ docs/extra/_category_.json | 8 + docs/extra/benchmarks.md | 112 ++ docs/extra/faq.md | 67 + docs/guide/_category_.json | 8 + docs/guide/error-handling.md | 128 ++ docs/guide/faster-fiber.md | 36 + docs/guide/grouping.md | 75 + docs/guide/hooks.md | 192 +++ docs/guide/routing.md | 285 ++++ docs/guide/templates.md | 105 ++ docs/guide/validation.md | 83 ++ docs/intro.md | 196 +++ docs/partials/routing/handler.md | 69 + 45 files changed, 7482 insertions(+) create mode 100755 .github/scripts/sync_docs.sh create mode 100644 .github/workflows/sync-docs.yml create mode 100644 docs/api/_category_.json create mode 100644 docs/api/app.md create mode 100644 docs/api/client.md create mode 100644 docs/api/constants.md create mode 100644 docs/api/ctx.md create mode 100644 docs/api/fiber.md create mode 100644 docs/api/middleware/_category_.json create mode 100644 docs/api/middleware/basicauth.md create mode 100644 docs/api/middleware/cache.md create mode 100644 docs/api/middleware/compress.md create mode 100644 docs/api/middleware/cors.md create mode 100644 docs/api/middleware/csrf.md create mode 100644 docs/api/middleware/earlydata.md create mode 100644 docs/api/middleware/encryptcookie.md create mode 100644 docs/api/middleware/envvar.md create mode 100644 docs/api/middleware/etag.md create mode 100644 docs/api/middleware/expvar.md create mode 100644 docs/api/middleware/favicon.md create mode 100644 docs/api/middleware/filesystem.md create mode 100644 docs/api/middleware/idempotency.md create mode 100644 docs/api/middleware/limiter.md create mode 100644 docs/api/middleware/logger.md create mode 100644 docs/api/middleware/monitor.md create mode 100644 docs/api/middleware/pprof.md create mode 100644 docs/api/middleware/proxy.md create mode 100644 docs/api/middleware/recover.md create mode 100644 docs/api/middleware/requestid.md create mode 100644 docs/api/middleware/session.md create mode 100644 docs/api/middleware/skip.md create mode 100644 docs/api/middleware/timeout.md create mode 100644 docs/extra/_category_.json create mode 100644 docs/extra/benchmarks.md create mode 100644 docs/extra/faq.md create mode 100644 docs/guide/_category_.json create mode 100644 docs/guide/error-handling.md create mode 100644 docs/guide/faster-fiber.md create mode 100644 docs/guide/grouping.md create mode 100644 docs/guide/hooks.md create mode 100644 docs/guide/routing.md create mode 100644 docs/guide/templates.md create mode 100644 docs/guide/validation.md create mode 100644 docs/intro.md create mode 100644 docs/partials/routing/handler.md diff --git a/.github/scripts/sync_docs.sh b/.github/scripts/sync_docs.sh new file mode 100755 index 0000000000..38b636ea83 --- /dev/null +++ b/.github/scripts/sync_docs.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +# Some env variables +BRANCH="master" +MAJOR_VERSION="v2" +REPO_URL="github.com/gofiber/docs.git" +AUTHOR_EMAIL="github-actions[bot]@users.noreply.github.com" +AUTHOR_USERNAME="github-actions[bot]" + +# Set commit author +git config --global user.email "${AUTHOR_EMAIL}" +git config --global user.name "${AUTHOR_USERNAME}" + +if [[ $EVENT == "push" ]]; then + latest_commit=$(git rev-parse --short HEAD) + log_output=$(git log --oneline ${BRANCH} HEAD~1..HEAD --name-status -- docs/) + + if [[ $log_output != "" ]]; then + git clone https://${TOKEN}@${REPO_URL} fiber-docs + cp -a docs/* fiber-docs/docs + + # Push changes for next docs + cd fiber-docs/ || return + git add . + git commit -m "Add docs from https://github.com/gofiber/fiber/commit/${latest_commit}" + git push https://${TOKEN}@${REPO_URL} + fi +elif [[ $EVENT == "release" ]]; then + latest_tag=$(git describe --tags --abbrev=0) + + # Push changes for stable docs + git clone https://${TOKEN}@${REPO_URL} fiber-docs + cd fiber-docs/ || return + cp -a docs/* versioned_docs/version-${MAJOR_VERSION}.x + git add . + git commit -m "Sync docs for ${latest_tag} release" + git push https://${TOKEN}@${REPO_URL} +fi \ No newline at end of file diff --git a/.github/workflows/sync-docs.yml b/.github/workflows/sync-docs.yml new file mode 100644 index 0000000000..25ad5a2cdb --- /dev/null +++ b/.github/workflows/sync-docs.yml @@ -0,0 +1,25 @@ +name: 'Sync docs' + +on: + push: + branches: + - master + - main + release: + types: [published] + +jobs: + sync-docs: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 2 + + - name: Sync docs + run: ./.github/scripts/sync_docs.sh + env: + EVENT: ${{ github.event_name }} + TOKEN: ${{ secrets.TOKEN }} \ No newline at end of file diff --git a/docs/api/_category_.json b/docs/api/_category_.json new file mode 100644 index 0000000000..c0fc66388a --- /dev/null +++ b/docs/api/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "API", + "position": 2, + "link": { + "type": "generated-index", + "description": "API documentation for Fiber." + } +} diff --git a/docs/api/app.md b/docs/api/app.md new file mode 100644 index 0000000000..314ea83a5c --- /dev/null +++ b/docs/api/app.md @@ -0,0 +1,608 @@ +--- +id: app +title: 🚀 App +description: The app instance conventionally denotes the Fiber application. +sidebar_position: 2 +--- + +import RoutingHandler from './../partials/routing/handler.md'; + +## Static + +Use the **Static** method to serve static files such as **images**, **CSS,** and **JavaScript**. + +:::info +By default, **Static** will serve `index.html` files in response to a request on a directory. +::: + +```go title="Signature" +func (app *App) Static(prefix, root string, config ...Static) Router +``` + +Use the following code to serve files in a directory named `./public` + +```go +app.Static("/", "./public") + +// => http://localhost:3000/hello.html +// => http://localhost:3000/js/jquery.js +// => http://localhost:3000/css/style.css +``` + +```go title="Examples" +// Serve files from multiple directories +app.Static("/", "./public") + +// Serve files from "./files" directory: +app.Static("/", "./files") +``` + +You can use any virtual path prefix \(_where the path does not actually exist in the file system_\) for files that are served by the **Static** method, specify a prefix path for the static directory, as shown below: + +```go title="Examples" +app.Static("/static", "./public") + +// => http://localhost:3000/static/hello.html +// => http://localhost:3000/static/js/jquery.js +// => http://localhost:3000/static/css/style.css +``` + +If you want to have a little bit more control regarding the settings for serving static files. You could use the `fiber.Static` struct to enable specific settings. + +```go title="fiber.Static{}" +// Static defines configuration options when defining static assets. +type Static struct { + // When set to true, the server tries minimizing CPU usage by caching compressed files. + // This works differently than the github.com/gofiber/compression middleware. + // Optional. Default value false + Compress bool `json:"compress"` + + // When set to true, enables byte range requests. + // Optional. Default value false + ByteRange bool `json:"byte_range"` + + // When set to true, enables directory browsing. + // Optional. Default value false. + Browse bool `json:"browse"` + + // When set to true, enables direct download. + // Optional. Default value false. + Download bool `json:"download"` + + // The name of the index file for serving a directory. + // Optional. Default value "index.html". + Index string `json:"index"` + + // Expiration duration for inactive file handlers. + // Use a negative time.Duration to disable it. + // + // Optional. Default value 10 * time.Second. + CacheDuration time.Duration `json:"cache_duration"` + + // The value for the Cache-Control HTTP-header + // that is set on the file response. MaxAge is defined in seconds. + // + // Optional. Default value 0. + MaxAge int `json:"max_age"` + + // ModifyResponse defines a function that allows you to alter the response. + // + // Optional. Default: nil + ModifyResponse Handler + + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *Ctx) bool +} +``` + +```go title="Example" +// Custom config +app.Static("/", "./public", fiber.Static{ + Compress: true, + ByteRange: true, + Browse: true, + Index: "john.html", + CacheDuration: 10 * time.Second, + MaxAge: 3600, +}) +``` + +## Route Handlers + + + +## Mount + +You can Mount Fiber instance by creating a `*Mount` + +```go title="Signature" +func (a *App) Mount(prefix string, app *App) Router +``` + +```go title="Examples" +func main() { + app := fiber.New() + micro := fiber.New() + app.Mount("/john", micro) // GET /john/doe -> 200 OK + + micro.Get("/doe", func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +## MountPath + +The `MountPath` property contains one or more path patterns on which a sub-app was mounted. + +```go title="Signature" +func (app *App) MountPath() string +``` + +```go title="Examples" +func main() { + app := New() + one := New() + two := New() + three := New() + + two.Mount("/three", three) + one.Mount("/two", two) + app.Mount("/one", one) + + one.MountPath() // "/one" + two.MountPath() // "/one/two" + three.MountPath() // "/one/two/three" + app.MountPath() // "" +} +``` + +:::caution +Mounting order is important for MountPath. If you want to get mount paths properly, you should start mounting from the deepest app. +::: + +## Group + +You can group routes by creating a `*Group` struct. + +```go title="Signature" +func (app *App) Group(prefix string, handlers ...Handler) Router +``` + +```go title="Examples" +func main() { + app := fiber.New() + + api := app.Group("/api", handler) // /api + + v1 := api.Group("/v1", handler) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + v2 := api.Group("/v2", handler) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + log.Fatal(app.Listen(":3000")) +} +``` + +## Route + +You can define routes with a common prefix inside the common function. + +```go title="Signature" +func (app *App) Route(prefix string, fn func(router Router), name ...string) Router +``` + +```go title="Examples" +func main() { + app := fiber.New() + + app.Route("/test", func(api fiber.Router) { + api.Get("/foo", handler).Name("foo") // /test/foo (name: test.foo) + api.Get("/bar", handler).Name("bar") // /test/bar (name: test.bar) + }, "test.") + + log.Fatal(app.Listen(":3000")) +} +``` + +## Server + +Server returns the underlying [fasthttp server](https://godoc.org/github.com/valyala/fasthttp#Server) + +```go title="Signature" +func (app *App) Server() *fasthttp.Server +``` + +```go title="Examples" +func main() { + app := fiber.New() + + app.Server().MaxConnsPerIP = 1 + + // ... +} +``` + +## Server Shutdown + +Shutdown gracefully shuts down the server without interrupting any active connections. Shutdown works by first closing all open listeners and then waits indefinitely for all connections to return to idle before shutting down. + +ShutdownWithTimeout will forcefully close any active connections after the timeout expires. + +```go +func (app *App) Shutdown() error +func (app *App) ShutdownWithTimeout(timeout time.Duration) error +``` + +## HandlersCount + +This method returns the amount of registered handlers. + +```go title="Signature" +func (app *App) HandlersCount() uint32 +``` + +## Stack + +This method returns the original router stack + +```go title="Signature" +func (app *App) Stack() [][]*Route +``` + +```go title="Examples" +var handler = func(c *fiber.Ctx) error { return nil } + +func main() { + app := fiber.New() + + app.Get("/john/:age", handler) + app.Post("/register", handler) + + data, _ := json.MarshalIndent(app.Stack(), "", " ") + fmt.Println(string(data)) + + app.Listen(":3000") +} +``` + +```javascript title="Result" +[ + [ + { + "method": "GET", + "path": "/john/:age", + "params": [ + "age" + ] + } + ], + [ + { + "method": "HEAD", + "path": "/john/:age", + "params": [ + "age" + ] + } + ], + [ + { + "method": "POST", + "path": "/register", + "params": null + } + ] +] +``` + +## Name + +This method assigns the name of latest created route. + +```go title="Signature" +func (app *App) Name(name string) Router +``` + +```go title="Examples" +var handler = func(c *fiber.Ctx) error { return nil } + +func main() { + app := fiber.New() + + app.Get("/", handler) + app.Name("index") + + app.Get("/doe", handler).Name("home") + + app.Trace("/tracer", handler).Name("tracert") + + app.Delete("/delete", handler).Name("delete") + + a := app.Group("/a") + a.Name("fd.") + + a.Get("/test", handler).Name("test") + + data, _ := json.MarshalIndent(app.Stack(), "", " ") + fmt.Print(string(data)) + + app.Listen(":3000") + +} +``` + +```javascript title="Result" +[ + [ + { + "method": "GET", + "name": "index", + "path": "/", + "params": null + }, + { + "method": "GET", + "name": "home", + "path": "/doe", + "params": null + }, + { + "method": "GET", + "name": "fd.test", + "path": "/a/test", + "params": null + } + ], + [ + { + "method": "HEAD", + "name": "", + "path": "/", + "params": null + }, + { + "method": "HEAD", + "name": "", + "path": "/doe", + "params": null + }, + { + "method": "HEAD", + "name": "", + "path": "/a/test", + "params": null + } + ], + null, + null, + [ + { + "method": "DELETE", + "name": "delete", + "path": "/delete", + "params": null + } + ], + null, + null, + [ + { + "method": "TRACE", + "name": "tracert", + "path": "/tracer", + "params": null + } + ], + null +] +``` + +## GetRoute + +This method gets the route by name. + +```go title="Signature" +func (app *App) GetRoute(name string) Route +``` + +```go title="Examples" +var handler = func(c *fiber.Ctx) error { return nil } + +func main() { + app := fiber.New() + + app.Get("/", handler).Name("index") + + data, _ := json.MarshalIndent(app.GetRoute("index"), "", " ") + fmt.Print(string(data)) + + + app.Listen(":3000") + +} +``` + +```javascript title="Result" +{ + "method": "GET", + "name": "index", + "path": "/", + "params": null +} +``` + +## GetRoutes + +This method gets all routes. + +```go title="Signature" +func (app *App) GetRoutes(filterUseOption ...bool) []Route +``` + +When filterUseOption equal to true, it will filter the routes registered by the middleware. +```go title="Examples" +func main() { + app := fiber.New() + app.Post("/", func (c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }).Name("index") + data, _ := json.MarshalIndent(app.GetRoutes(true), "", " ") + fmt.Print(string(data)) +} +``` + +```javascript title="Result" +[ + { + "method": "POST", + "name": "index", + "path": "/", + "params": null + } +] +``` + +## Config + +Config returns the app config as value \( read-only \). + +```go title="Signature" +func (app *App) Config() Config +``` + +## Handler + +Handler returns the server handler that can be used to serve custom \*fasthttp.RequestCtx requests. + +```go title="Signature" +func (app *App) Handler() fasthttp.RequestHandler +``` + +## Listen + +Listen serves HTTP requests from the given address. + +```go title="Signature" +func (app *App) Listen(addr string) error +``` + +```go title="Examples" +// Listen on port :8080 +app.Listen(":8080") + +// Custom host +app.Listen("127.0.0.1:8080") +``` + +## ListenTLS + +ListenTLS serves HTTPs requests from the given address using certFile and keyFile paths to as TLS certificate and key file. + +```go title="Signature" +func (app *App) ListenTLS(addr, certFile, keyFile string) error +``` + +```go title="Examples" +app.ListenTLS(":443", "./cert.pem", "./cert.key"); +``` + +Using `ListenTLS` defaults to the following config \( use `Listener` to provide your own config \) + +```go title="Default \*tls.Config" +&tls.Config{ + MinVersion: tls.VersionTLS12, + Certificates: []tls.Certificate{ + cert, + }, +} +``` + +## ListenMutualTLS + +ListenMutualTLS serves HTTPs requests from the given address using certFile, keyFile and clientCertFile are the paths to TLS certificate and key file + +```go title="Signature" +func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) error +``` + +```go title="Examples" +app.ListenMutualTLS(":443", "./cert.pem", "./cert.key", "./ca-chain-cert.pem"); +``` + +Using `ListenMutualTLS` defaults to the following config \( use `Listener` to provide your own config \) + +```go title="Default \*tls.Config" +&tls.Config{ + MinVersion: tls.VersionTLS12, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: clientCertPool, + Certificates: []tls.Certificate{ + cert, + }, +} +``` + +## Listener + +You can pass your own [`net.Listener`](https://pkg.go.dev/net/#Listener) using the `Listener` method. This method can be used to enable **TLS/HTTPS** with a custom tls.Config. + +```go title="Signature" +func (app *App) Listener(ln net.Listener) error +``` + +```go title="Examples" +ln, _ := net.Listen("tcp", ":3000") + +cer, _:= tls.LoadX509KeyPair("server.crt", "server.key") + +ln = tls.NewListener(ln, &tls.Config{Certificates: []tls.Certificate{cer}}) + +app.Listener(ln) +``` + +## Test + +Testing your application is done with the **Test** method. Use this method for creating `_test.go` files or when you need to debug your routing logic. The default timeout is `1s` if you want to disable a timeout altogether, pass `-1` as a second argument. + +```go title="Signature" +func (app *App) Test(req *http.Request, msTimeout ...int) (*http.Response, error) +``` + +```go title="Examples" +// Create route with GET method for test: +app.Get("/", func(c *fiber.Ctx) error { + fmt.Println(c.BaseURL()) // => http://google.com + fmt.Println(c.Get("X-Custom-Header")) // => hi + + return c.SendString("hello, World!") +}) + +// http.Request +req := httptest.NewRequest("GET", "http://google.com", nil) +req.Header.Set("X-Custom-Header", "hi") + +// http.Response +resp, _ := app.Test(req) + +// Do something with results: +if resp.StatusCode == fiber.StatusOK { + body, _ := ioutil.ReadAll(resp.Body) + fmt.Println(string(body)) // => Hello, World! +} +``` + +## Hooks + +Hooks is a method to return [hooks](../guide/hooks.md) property. + +```go title="Signature" +func (app *App) Hooks() *Hooks +``` diff --git a/docs/api/client.md b/docs/api/client.md new file mode 100644 index 0000000000..0e17dbbd79 --- /dev/null +++ b/docs/api/client.md @@ -0,0 +1,567 @@ +--- +id: client +title: 🌎 Client +description: The Client struct represents the Fiber HTTP Client. +sidebar_position: 5 +--- + +## Start request + +Start a http request with http method and url. + +```go title="Signatures" +// Client http methods +func (c *Client) Get(url string) *Agent +func (c *Client) Head(url string) *Agent +func (c *Client) Post(url string) *Agent +func (c *Client) Put(url string) *Agent +func (c *Client) Patch(url string) *Agent +func (c *Client) Delete(url string) *Agent +``` + +## ✨ Agent + +`Agent` is built on top of FastHTTP's [`HostClient`](https://github.com/valyala/fasthttp/blob/master/client.go#L603) which has lots of convenient helper methods such as dedicated methods for request methods. + +### Parse + +Parse initializes a HostClient. + +```go title="Parse" +a := AcquireAgent() +req := a.Request() +req.Header.SetMethod(MethodGet) +req.SetRequestURI("http://example.com") + +if err := a.Parse(); err != nil { + panic(err) +} + +code, body, errs := a.Bytes() // ... +``` + +### Set + +Set sets the given `key: value` header. + +```go title="Signature" +func (a *Agent) Set(k, v string) *Agent +func (a *Agent) SetBytesK(k []byte, v string) *Agent +func (a *Agent) SetBytesV(k string, v []byte) *Agent +func (a *Agent) SetBytesKV(k []byte, v []byte) *Agent +``` + +```go title="Example" +agent.Set("k1", "v1"). + SetBytesK([]byte("k1"), "v1"). + SetBytesV("k1", []byte("v1")). + SetBytesKV([]byte("k2"), []byte("v2")) +// ... +``` + +### Add + +Add adds the given `key: value` header. Multiple headers with the same key may be added with this function. + +```go title="Signature" +func (a *Agent) Add(k, v string) *Agent +func (a *Agent) AddBytesK(k []byte, v string) *Agent +func (a *Agent) AddBytesV(k string, v []byte) *Agent +func (a *Agent) AddBytesKV(k []byte, v []byte) *Agent +``` + +```go title="Example" +agent.Add("k1", "v1"). + AddBytesK([]byte("k1"), "v1"). + AddBytesV("k1", []byte("v1")). + AddBytesKV([]byte("k2"), []byte("v2")) +// Headers: +// K1: v1 +// K1: v1 +// K1: v1 +// K2: v2 +``` + +### ConnectionClose + +ConnectionClose adds the `Connection: close` header. + +```go title="Signature" +func (a *Agent) ConnectionClose() *Agent +``` + +```go title="Example" +agent.ConnectionClose() +// ... +``` + +### UserAgent + +UserAgent sets `User-Agent` header value. + +```go title="Signature" +func (a *Agent) UserAgent(userAgent string) *Agent +func (a *Agent) UserAgentBytes(userAgent []byte) *Agent +``` + +```go title="Example" +agent.UserAgent("fiber") +// ... +``` + +### Cookie + +Cookie sets a cookie in `key: value` form. `Cookies` can be used to set multiple cookies. + +```go title="Signature" +func (a *Agent) Cookie(key, value string) *Agent +func (a *Agent) CookieBytesK(key []byte, value string) *Agent +func (a *Agent) CookieBytesKV(key, value []byte) *Agent +func (a *Agent) Cookies(kv ...string) *Agent +func (a *Agent) CookiesBytesKV(kv ...[]byte) *Agent +``` + +```go title="Example" +agent.Cookie("k", "v") +agent.Cookies("k1", "v1", "k2", "v2") +// ... +``` + +### Referer + +Referer sets the Referer header value. + +```go title="Signature" +func (a *Agent) Referer(referer string) *Agent +func (a *Agent) RefererBytes(referer []byte) *Agent +``` + +```go title="Example" +agent.Referer("https://docs.gofiber.io") +// ... +``` + +### ContentType + +ContentType sets Content-Type header value. + +```go title="Signature" +func (a *Agent) ContentType(contentType string) *Agent +func (a *Agent) ContentTypeBytes(contentType []byte) *Agent +``` + +```go title="Example" +agent.ContentType("custom-type") +// ... +``` + +### Host + +Host sets the Host header. + +```go title="Signature" +func (a *Agent) Host(host string) *Agent +func (a *Agent) HostBytes(host []byte) *Agent +``` + +```go title="Example" +agent.Host("example.com") +// ... +``` + +### QueryString + +QueryString sets the URI query string. + +```go title="Signature" +func (a *Agent) QueryString(queryString string) *Agent +func (a *Agent) QueryStringBytes(queryString []byte) *Agent +``` + +```go title="Example" +agent.QueryString("foo=bar") +// ... +``` + +### BasicAuth + +BasicAuth sets the URI username and password using HTTP Basic Auth. + +```go title="Signature" +func (a *Agent) BasicAuth(username, password string) *Agent +func (a *Agent) BasicAuthBytes(username, password []byte) *Agent +``` + +```go title="Example" +agent.BasicAuth("foo", "bar") +// ... +``` + +### Body + +There are several ways to set request body. + +```go title="Signature" +func (a *Agent) BodyString(bodyString string) *Agent +func (a *Agent) Body(body []byte) *Agent + +// BodyStream sets request body stream and, optionally body size. +// +// If bodySize is >= 0, then the bodyStream must provide exactly bodySize bytes +// before returning io.EOF. +// +// If bodySize < 0, then bodyStream is read until io.EOF. +// +// bodyStream.Close() is called after finishing reading all body data +// if it implements io.Closer. +// +// Note that GET and HEAD requests cannot have body. +func (a *Agent) BodyStream(bodyStream io.Reader, bodySize int) *Agent +``` + +```go title="Example" +agent.BodyString("foo=bar") +agent.Body([]byte("bar=baz")) +agent.BodyStream(strings.NewReader("body=stream"), -1) +// ... +``` + +### JSON + +JSON sends a JSON request by setting the Content-Type header to `application/json`. + +```go title="Signature" +func (a *Agent) JSON(v interface{}) *Agent +``` + +```go title="Example" +agent.JSON(fiber.Map{"success": true}) +// ... +``` + +### XML + +XML sends an XML request by setting the Content-Type header to `application/xml`. + +```go title="Signature" +func (a *Agent) XML(v interface{}) *Agent +``` + +```go title="Example" +agent.XML(fiber.Map{"success": true}) +// ... +``` + +### Form + +Form sends a form request by setting the Content-Type header to `application/x-www-form-urlencoded`. + +```go title="Signature" +// Form sends form request with body if args is non-nil. +// +// It is recommended obtaining args via AcquireArgs and release it +// manually in performance-critical code. +func (a *Agent) Form(args *Args) *Agent +``` + +```go title="Example" +args := AcquireArgs() +args.Set("foo", "bar") + +agent.Form(args) +// ... +ReleaseArgs(args) +``` + +### MultipartForm + +MultipartForm sends multipart form request by setting the Content-Type header to `multipart/form-data`. These requests can include key-value's and files. + +```go title="Signature" +// MultipartForm sends multipart form request with k-v and files. +// +// It is recommended to obtain args via AcquireArgs and release it +// manually in performance-critical code. +func (a *Agent) MultipartForm(args *Args) *Agent +``` + +```go title="Example" +args := AcquireArgs() +args.Set("foo", "bar") + +agent.MultipartForm(args) +// ... +ReleaseArgs(args) +``` + +Fiber provides several methods for sending files. Note that they must be called before `MultipartForm`. + +#### Boundary + +Boundary sets boundary for multipart form request. + +```go title="Signature" +func (a *Agent) Boundary(boundary string) *Agent +``` + +```go title="Example" +agent.Boundary("myBoundary") + .MultipartForm(nil) +// ... +``` + +#### SendFile\(s\) + +SendFile read a file and appends it to a multipart form request. Sendfiles can be used to append multiple files. + +```go title="Signature" +func (a *Agent) SendFile(filename string, fieldname ...string) *Agent +func (a *Agent) SendFiles(filenamesAndFieldnames ...string) *Agent +``` + +```go title="Example" +agent.SendFile("f", "field name") + .SendFiles("f1", "field name1", "f2"). + .MultipartForm(nil) +// ... +``` + +#### FileData + +FileData appends file data for multipart form request. + +```go +// FormFile represents multipart form file +type FormFile struct { + // Fieldname is form file's field name + Fieldname string + // Name is form file's name + Name string + // Content is form file's content + Content []byte +} +``` + +```go title="Signature" +// FileData appends files for multipart form request. +// +// It is recommended obtaining formFile via AcquireFormFile and release it +// manually in performance-critical code. +func (a *Agent) FileData(formFiles ...*FormFile) *Agent +``` + +```go title="Example" +ff1 := &FormFile{"filename1", "field name1", []byte("content")} +ff2 := &FormFile{"filename2", "field name2", []byte("content")} +agent.FileData(ff1, ff2). + MultipartForm(nil) +// ... +``` + +### Debug + +Debug mode enables logging request and response detail to `io.writer`\(default is `os.Stdout`\). + +```go title="Signature" +func (a *Agent) Debug(w ...io.Writer) *Agent +``` + +```go title="Example" +agent.Debug() +// ... +``` + +### Timeout + +Timeout sets request timeout duration. + +```go title="Signature" +func (a *Agent) Timeout(timeout time.Duration) *Agent +``` + +```go title="Example" +agent.Timeout(time.Second) +// ... +``` + +### Reuse + +Reuse enables the Agent instance to be used again after one request. If agent is reusable, then it should be released manually when it is no longer used. + +```go title="Signature" +func (a *Agent) Reuse() *Agent +``` + +```go title="Example" +agent.Reuse() +// ... +``` + +### InsecureSkipVerify + +InsecureSkipVerify controls whether the Agent verifies the server certificate chain and host name. + +```go title="Signature" +func (a *Agent) InsecureSkipVerify() *Agent +``` + +```go title="Example" +agent.InsecureSkipVerify() +// ... +``` + +### TLSConfig + +TLSConfig sets tls config. + +```go title="Signature" +func (a *Agent) TLSConfig(config *tls.Config) *Agent +``` + +```go title="Example" +// Create tls certificate +cer, _ := tls.LoadX509KeyPair("pem", "key") + +config := &tls.Config{ + Certificates: []tls.Certificate{cer}, +} + +agent.TLSConfig(config) +// ... +``` + +### MaxRedirectsCount + +MaxRedirectsCount sets max redirect count for GET and HEAD. + +```go title="Signature" +func (a *Agent) MaxRedirectsCount(count int) *Agent +``` + +```go title="Example" +agent.MaxRedirectsCount(7) +// ... +``` + +### JSONEncoder + +JSONEncoder sets custom json encoder. + +```go title="Signature" +func (a *Agent) JSONEncoder(jsonEncoder utils.JSONMarshal) *Agent +``` + +```go title="Example" +agent.JSONEncoder(json.Marshal) +// ... +``` + +### JSONDecoder + +JSONDecoder sets custom json decoder. + +```go title="Signature" +func (a *Agent) JSONDecoder(jsonDecoder utils.JSONUnmarshal) *Agent +``` + +```go title="Example" +agent.JSONDecoder(json.Unmarshal) +// ... +``` + +### Request + +Request returns Agent request instance. + +```go title="Signature" +func (a *Agent) Request() *Request +``` + +```go title="Example" +req := agent.Request() +// ... +``` + +### SetResponse + +SetResponse sets custom response for the Agent instance. It is recommended obtaining custom response via AcquireResponse and release it manually in performance-critical code. + +```go title="Signature" +func (a *Agent) SetResponse(customResp *Response) *Agent +``` + +```go title="Example" +resp := AcquireResponse() +agent.SetResponse(resp) +// ... +ReleaseResponse(resp) +``` + +### Dest + +Dest sets custom dest. The contents of dest will be replaced by the response body, if the dest is too small a new slice will be allocated. + +```go title="Signature" +func (a *Agent) Dest(dest []byte) *Agent { +``` + +```go title="Example" +agent.Dest(nil) +// ... +``` + +### Bytes + +Bytes returns the status code, bytes body and errors of url. + +```go title="Signature" +func (a *Agent) Bytes() (code int, body []byte, errs []error) +``` + +```go title="Example" +code, body, errs := agent.Bytes() +// ... +``` + +### String + +String returns the status code, string body and errors of url. + +```go title="Signature" +func (a *Agent) String() (int, string, []error) +``` + +```go title="Example" +code, body, errs := agent.String() +// ... +``` + +### Struct + +Struct returns the status code, bytes body and errors of url. And bytes body will be unmarshalled to given v. + +```go title="Signature" +func (a *Agent) Struct(v interface{}) (code int, body []byte, errs []error) +``` + +```go title="Example" +var d data +code, body, errs := agent.Struct(&d) +// ... +``` + +### RetryIf + +RetryIf controls whether a retry should be attempted after an error. +By default, will use isIdempotent function from fasthttp + +```go title="Signature" +func (a *Agent) RetryIf(retryIf RetryIfFunc) *Agent +``` + +```go title="Example" +agent.Get("https://example.com").RetryIf(func (req *fiber.Request) bool { + return req.URI() == "https://example.com" +}) +// ... +``` diff --git a/docs/api/constants.md b/docs/api/constants.md new file mode 100644 index 0000000000..8a436a9f3b --- /dev/null +++ b/docs/api/constants.md @@ -0,0 +1,291 @@ +--- +id: constants +title: 📋 Constants +description: Some constants for Fiber. +sidebar_position: 4 +--- + +HTTP methods were copied from net/http. + +```go +const ( + MethodGet = "GET" // RFC 7231, 4.3.1 + MethodHead = "HEAD" // RFC 7231, 4.3.2 + MethodPost = "POST" // RFC 7231, 4.3.3 + MethodPut = "PUT" // RFC 7231, 4.3.4 + MethodPatch = "PATCH" // RFC 5789 + MethodDelete = "DELETE" // RFC 7231, 4.3.5 + MethodConnect = "CONNECT" // RFC 7231, 4.3.6 + MethodOptions = "OPTIONS" // RFC 7231, 4.3.7 + MethodTrace = "TRACE" // RFC 7231, 4.3.8 + methodUse = "USE" +) +``` + +MIME types that are commonly used + +```go +const ( + MIMETextXML = "text/xml" + MIMETextHTML = "text/html" + MIMETextPlain = "text/plain" + MIMEApplicationXML = "application/xml" + MIMEApplicationJSON = "application/json" + MIMEApplicationJavaScript = "application/javascript" + MIMEApplicationForm = "application/x-www-form-urlencoded" + MIMEOctetStream = "application/octet-stream" + MIMEMultipartForm = "multipart/form-data" + + MIMETextXMLCharsetUTF8 = "text/xml; charset=utf-8" + MIMETextHTMLCharsetUTF8 = "text/html; charset=utf-8" + MIMETextPlainCharsetUTF8 = "text/plain; charset=utf-8" + MIMEApplicationXMLCharsetUTF8 = "application/xml; charset=utf-8" + MIMEApplicationJSONCharsetUTF8 = "application/json; charset=utf-8" + MIMEApplicationJavaScriptCharsetUTF8 = "application/javascript; charset=utf-8" +) +``` + +HTTP status codes were copied from net/http. + +```go +const ( + StatusContinue = 100 // RFC 7231, 6.2.1 + StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2 + StatusProcessing = 102 // RFC 2518, 10.1 + StatusEarlyHints = 103 // RFC 8297 + StatusOK = 200 // RFC 7231, 6.3.1 + StatusCreated = 201 // RFC 7231, 6.3.2 + StatusAccepted = 202 // RFC 7231, 6.3.3 + StatusNonAuthoritativeInformation = 203 // RFC 7231, 6.3.4 + StatusNoContent = 204 // RFC 7231, 6.3.5 + StatusResetContent = 205 // RFC 7231, 6.3.6 + StatusPartialContent = 206 // RFC 7233, 4.1 + StatusMultiStatus = 207 // RFC 4918, 11.1 + StatusAlreadyReported = 208 // RFC 5842, 7.1 + StatusIMUsed = 226 // RFC 3229, 10.4.1 + StatusMultipleChoices = 300 // RFC 7231, 6.4.1 + StatusMovedPermanently = 301 // RFC 7231, 6.4.2 + StatusFound = 302 // RFC 7231, 6.4.3 + StatusSeeOther = 303 // RFC 7231, 6.4.4 + StatusNotModified = 304 // RFC 7232, 4.1 + StatusUseProxy = 305 // RFC 7231, 6.4.5 + StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7 + StatusPermanentRedirect = 308 // RFC 7538, 3 + StatusBadRequest = 400 // RFC 7231, 6.5.1 + StatusUnauthorized = 401 // RFC 7235, 3.1 + StatusPaymentRequired = 402 // RFC 7231, 6.5.2 + StatusForbidden = 403 // RFC 7231, 6.5.3 + StatusNotFound = 404 // RFC 7231, 6.5.4 + StatusMethodNotAllowed = 405 // RFC 7231, 6.5.5 + StatusNotAcceptable = 406 // RFC 7231, 6.5.6 + StatusProxyAuthRequired = 407 // RFC 7235, 3.2 + StatusRequestTimeout = 408 // RFC 7231, 6.5.7 + StatusConflict = 409 // RFC 7231, 6.5.8 + StatusGone = 410 // RFC 7231, 6.5.9 + StatusLengthRequired = 411 // RFC 7231, 6.5.10 + StatusPreconditionFailed = 412 // RFC 7232, 4.2 + StatusRequestEntityTooLarge = 413 // RFC 7231, 6.5.11 + StatusRequestURITooLong = 414 // RFC 7231, 6.5.12 + StatusUnsupportedMediaType = 415 // RFC 7231, 6.5.13 + StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4 + StatusExpectationFailed = 417 // RFC 7231, 6.5.14 + StatusTeapot = 418 // RFC 7168, 2.3.3 + StatusMisdirectedRequest = 421 // RFC 7540, 9.1.2 + StatusUnprocessableEntity = 422 // RFC 4918, 11.2 + StatusLocked = 423 // RFC 4918, 11.3 + StatusFailedDependency = 424 // RFC 4918, 11.4 + StatusTooEarly = 425 // RFC 8470, 5.2. + StatusUpgradeRequired = 426 // RFC 7231, 6.5.15 + StatusPreconditionRequired = 428 // RFC 6585, 3 + StatusTooManyRequests = 429 // RFC 6585, 4 + StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5 + StatusUnavailableForLegalReasons = 451 // RFC 7725, 3 + StatusInternalServerError = 500 // RFC 7231, 6.6.1 + StatusNotImplemented = 501 // RFC 7231, 6.6.2 + StatusBadGateway = 502 // RFC 7231, 6.6.3 + StatusServiceUnavailable = 503 // RFC 7231, 6.6.4 + StatusGatewayTimeout = 504 // RFC 7231, 6.6.5 + StatusHTTPVersionNotSupported = 505 // RFC 7231, 6.6.6 + StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1 + StatusInsufficientStorage = 507 // RFC 4918, 11.5 + StatusLoopDetected = 508 // RFC 5842, 7.2 + StatusNotExtended = 510 // RFC 2774, 7 + StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6 +) +``` + +Errors + +```go +var ( + ErrBadRequest = NewError(StatusBadRequest) // RFC 7231, 6.5.1 + ErrUnauthorized = NewError(StatusUnauthorized) // RFC 7235, 3.1 + ErrPaymentRequired = NewError(StatusPaymentRequired) // RFC 7231, 6.5.2 + ErrForbidden = NewError(StatusForbidden) // RFC 7231, 6.5.3 + ErrNotFound = NewError(StatusNotFound) // RFC 7231, 6.5.4 + ErrMethodNotAllowed = NewError(StatusMethodNotAllowed) // RFC 7231, 6.5.5 + ErrNotAcceptable = NewError(StatusNotAcceptable) // RFC 7231, 6.5.6 + ErrProxyAuthRequired = NewError(StatusProxyAuthRequired) // RFC 7235, 3.2 + ErrRequestTimeout = NewError(StatusRequestTimeout) // RFC 7231, 6.5.7 + ErrConflict = NewError(StatusConflict) // RFC 7231, 6.5.8 + ErrGone = NewError(StatusGone) // RFC 7231, 6.5.9 + ErrLengthRequired = NewError(StatusLengthRequired) // RFC 7231, 6.5.10 + ErrPreconditionFailed = NewError(StatusPreconditionFailed) // RFC 7232, 4.2 + ErrRequestEntityTooLarge = NewError(StatusRequestEntityTooLarge) // RFC 7231, 6.5.11 + ErrRequestURITooLong = NewError(StatusRequestURITooLong) // RFC 7231, 6.5.12 + ErrUnsupportedMediaType = NewError(StatusUnsupportedMediaType) // RFC 7231, 6.5.13 + ErrRequestedRangeNotSatisfiable = NewError(StatusRequestedRangeNotSatisfiable) // RFC 7233, 4.4 + ErrExpectationFailed = NewError(StatusExpectationFailed) // RFC 7231, 6.5.14 + ErrTeapot = NewError(StatusTeapot) // RFC 7168, 2.3.3 + ErrMisdirectedRequest = NewError(StatusMisdirectedRequest) // RFC 7540, 9.1.2 + ErrUnprocessableEntity = NewError(StatusUnprocessableEntity) // RFC 4918, 11.2 + ErrLocked = NewError(StatusLocked) // RFC 4918, 11.3 + ErrFailedDependency = NewError(StatusFailedDependency) // RFC 4918, 11.4 + ErrTooEarly = NewError(StatusTooEarly) // RFC 8470, 5.2. + ErrUpgradeRequired = NewError(StatusUpgradeRequired) // RFC 7231, 6.5.15 + ErrPreconditionRequired = NewError(StatusPreconditionRequired) // RFC 6585, 3 + ErrTooManyRequests = NewError(StatusTooManyRequests) // RFC 6585, 4 + ErrRequestHeaderFieldsTooLarge = NewError(StatusRequestHeaderFieldsTooLarge) // RFC 6585, 5 + ErrUnavailableForLegalReasons = NewError(StatusUnavailableForLegalReasons) // RFC 7725, 3 + ErrInternalServerError = NewError(StatusInternalServerError) // RFC 7231, 6.6.1 + ErrNotImplemented = NewError(StatusNotImplemented) // RFC 7231, 6.6.2 + ErrBadGateway = NewError(StatusBadGateway) // RFC 7231, 6.6.3 + ErrServiceUnavailable = NewError(StatusServiceUnavailable) // RFC 7231, 6.6.4 + ErrGatewayTimeout = NewError(StatusGatewayTimeout) // RFC 7231, 6.6.5 + ErrHTTPVersionNotSupported = NewError(StatusHTTPVersionNotSupported) // RFC 7231, 6.6.6 + ErrVariantAlsoNegotiates = NewError(StatusVariantAlsoNegotiates) // RFC 2295, 8.1 + ErrInsufficientStorage = NewError(StatusInsufficientStorage) // RFC 4918, 11.5 + ErrLoopDetected = NewError(StatusLoopDetected) // RFC 5842, 7.2 + ErrNotExtended = NewError(StatusNotExtended) // RFC 2774, 7 + ErrNetworkAuthenticationRequired = NewError(StatusNetworkAuthenticationRequired) // RFC 6585, 6 +) +``` + +HTTP Headers were copied from net/http. + +```go +const ( + HeaderAuthorization = "Authorization" + HeaderProxyAuthenticate = "Proxy-Authenticate" + HeaderProxyAuthorization = "Proxy-Authorization" + HeaderWWWAuthenticate = "WWW-Authenticate" + HeaderAge = "Age" + HeaderCacheControl = "Cache-Control" + HeaderClearSiteData = "Clear-Site-Data" + HeaderExpires = "Expires" + HeaderPragma = "Pragma" + HeaderWarning = "Warning" + HeaderAcceptCH = "Accept-CH" + HeaderAcceptCHLifetime = "Accept-CH-Lifetime" + HeaderContentDPR = "Content-DPR" + HeaderDPR = "DPR" + HeaderEarlyData = "Early-Data" + HeaderSaveData = "Save-Data" + HeaderViewportWidth = "Viewport-Width" + HeaderWidth = "Width" + HeaderETag = "ETag" + HeaderIfMatch = "If-Match" + HeaderIfModifiedSince = "If-Modified-Since" + HeaderIfNoneMatch = "If-None-Match" + HeaderIfUnmodifiedSince = "If-Unmodified-Since" + HeaderLastModified = "Last-Modified" + HeaderVary = "Vary" + HeaderConnection = "Connection" + HeaderKeepAlive = "Keep-Alive" + HeaderAccept = "Accept" + HeaderAcceptCharset = "Accept-Charset" + HeaderAcceptEncoding = "Accept-Encoding" + HeaderAcceptLanguage = "Accept-Language" + HeaderCookie = "Cookie" + HeaderExpect = "Expect" + HeaderMaxForwards = "Max-Forwards" + HeaderSetCookie = "Set-Cookie" + HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials" + HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers" + HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods" + HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin" + HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers" + HeaderAccessControlMaxAge = "Access-Control-Max-Age" + HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers" + HeaderAccessControlRequestMethod = "Access-Control-Request-Method" + HeaderOrigin = "Origin" + HeaderTimingAllowOrigin = "Timing-Allow-Origin" + HeaderXPermittedCrossDomainPolicies = "X-Permitted-Cross-Domain-Policies" + HeaderDNT = "DNT" + HeaderTk = "Tk" + HeaderContentDisposition = "Content-Disposition" + HeaderContentEncoding = "Content-Encoding" + HeaderContentLanguage = "Content-Language" + HeaderContentLength = "Content-Length" + HeaderContentLocation = "Content-Location" + HeaderContentType = "Content-Type" + HeaderForwarded = "Forwarded" + HeaderVia = "Via" + HeaderXForwardedFor = "X-Forwarded-For" + HeaderXForwardedHost = "X-Forwarded-Host" + HeaderXForwardedProto = "X-Forwarded-Proto" + HeaderXForwardedProtocol = "X-Forwarded-Protocol" + HeaderXForwardedSsl = "X-Forwarded-Ssl" + HeaderXUrlScheme = "X-Url-Scheme" + HeaderLocation = "Location" + HeaderFrom = "From" + HeaderHost = "Host" + HeaderReferer = "Referer" + HeaderReferrerPolicy = "Referrer-Policy" + HeaderUserAgent = "User-Agent" + HeaderAllow = "Allow" + HeaderServer = "Server" + HeaderAcceptRanges = "Accept-Ranges" + HeaderContentRange = "Content-Range" + HeaderIfRange = "If-Range" + HeaderRange = "Range" + HeaderContentSecurityPolicy = "Content-Security-Policy" + HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only" + HeaderCrossOriginResourcePolicy = "Cross-Origin-Resource-Policy" + HeaderExpectCT = "Expect-CT" + HeaderFeaturePolicy = "Feature-Policy" + HeaderPublicKeyPins = "Public-Key-Pins" + HeaderPublicKeyPinsReportOnly = "Public-Key-Pins-Report-Only" + HeaderStrictTransportSecurity = "Strict-Transport-Security" + HeaderUpgradeInsecureRequests = "Upgrade-Insecure-Requests" + HeaderXContentTypeOptions = "X-Content-Type-Options" + HeaderXDownloadOptions = "X-Download-Options" + HeaderXFrameOptions = "X-Frame-Options" + HeaderXPoweredBy = "X-Powered-By" + HeaderXXSSProtection = "X-XSS-Protection" + HeaderLastEventID = "Last-Event-ID" + HeaderNEL = "NEL" + HeaderPingFrom = "Ping-From" + HeaderPingTo = "Ping-To" + HeaderReportTo = "Report-To" + HeaderTE = "TE" + HeaderTrailer = "Trailer" + HeaderTransferEncoding = "Transfer-Encoding" + HeaderSecWebSocketAccept = "Sec-WebSocket-Accept" + HeaderSecWebSocketExtensions = "Sec-WebSocket-Extensions" + HeaderSecWebSocketKey = "Sec-WebSocket-Key" + HeaderSecWebSocketProtocol = "Sec-WebSocket-Protocol" + HeaderSecWebSocketVersion = "Sec-WebSocket-Version" + HeaderAcceptPatch = "Accept-Patch" + HeaderAcceptPushPolicy = "Accept-Push-Policy" + HeaderAcceptSignature = "Accept-Signature" + HeaderAltSvc = "Alt-Svc" + HeaderDate = "Date" + HeaderIndex = "Index" + HeaderLargeAllocation = "Large-Allocation" + HeaderLink = "Link" + HeaderPushPolicy = "Push-Policy" + HeaderRetryAfter = "Retry-After" + HeaderServerTiming = "Server-Timing" + HeaderSignature = "Signature" + HeaderSignedHeaders = "Signed-Headers" + HeaderSourceMap = "SourceMap" + HeaderUpgrade = "Upgrade" + HeaderXDNSPrefetchControl = "X-DNS-Prefetch-Control" + HeaderXPingback = "X-Pingback" + HeaderXRequestID = "X-Request-ID" + HeaderXRequestedWith = "X-Requested-With" + HeaderXRobotsTag = "X-Robots-Tag" + HeaderXUACompatible = "X-UA-Compatible" +) +``` \ No newline at end of file diff --git a/docs/api/ctx.md b/docs/api/ctx.md new file mode 100644 index 0000000000..4ba2c4cd71 --- /dev/null +++ b/docs/api/ctx.md @@ -0,0 +1,1890 @@ +--- +id: ctx +title: 🧠 Ctx +description: >- + The Ctx struct represents the Context which hold the HTTP request and + response. It has methods for the request query string, parameters, body, HTTP + headers, and so on. +sidebar_position: 3 +--- + +## Accepts + +Checks, if the specified **extensions** or **content** **types** are acceptable. + +:::info +Based on the request’s [Accept](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) HTTP header. +::: + +```go title="Signature" +func (c *Ctx) Accepts(offers ...string) string +func (c *Ctx) AcceptsCharsets(offers ...string) string +func (c *Ctx) AcceptsEncodings(offers ...string) string +func (c *Ctx) AcceptsLanguages(offers ...string) string +``` + +```go title="Example" +// Accept: text/*, application/json + +app.Get("/", func(c *fiber.Ctx) error { + c.Accepts("html") // "html" + c.Accepts("text/html") // "text/html" + c.Accepts("json", "text") // "json" + c.Accepts("application/json") // "application/json" + c.Accepts("image/png") // "" + c.Accepts("png") // "" + // ... +}) +``` + +Fiber provides similar functions for the other accept headers. + +```go +// Accept-Charset: utf-8, iso-8859-1;q=0.2 +// Accept-Encoding: gzip, compress;q=0.2 +// Accept-Language: en;q=0.8, nl, ru + +app.Get("/", func(c *fiber.Ctx) error { + c.AcceptsCharsets("utf-16", "iso-8859-1") + // "iso-8859-1" + + c.AcceptsEncodings("compress", "br") + // "compress" + + c.AcceptsLanguages("pt", "nl", "ru") + // "nl" + // ... +}) +``` + +## AllParams + +Params is used to get all route parameters. +Using Params method to get params. + +```go title="Signature" +func (c *Ctx) AllParams() map[string]string +``` + +```go title="Example" +// GET http://example.com/user/fenny +app.Get("/user/:name", func(c *fiber.Ctx) error { + c.AllParams() // "{"name": "fenny"}" + + // ... +}) + +// GET http://example.com/user/fenny/123 +app.Get("/user/*", func(c *fiber.Ctx) error { + c.AllParams() // "{"*1": "fenny/123"}" + + // ... +}) +``` + +## App + +Returns the [\*App](ctx.md) reference so you could easily access all application settings. + +```go title="Signature" +func (c *Ctx) App() *App +``` + +```go title="Example" +app.Get("/stack", func(c *fiber.Ctx) error { + return c.JSON(c.App().Stack()) +}) +``` + +## Append + +Appends the specified **value** to the HTTP response header field. + +:::caution +If the header is **not** already set, it creates the header with the specified value. +::: + +```go title="Signature" +func (c *Ctx) Append(field string, values ...string) +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.Append("Link", "http://google.com", "http://localhost") + // => Link: http://localhost, http://google.com + + c.Append("Link", "Test") + // => Link: http://localhost, http://google.com, Test + + // ... +}) +``` + +## Attachment + +Sets the HTTP response [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) header field to `attachment`. + +```go title="Signature" +func (c *Ctx) Attachment(filename ...string) +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.Attachment() + // => Content-Disposition: attachment + + c.Attachment("./upload/images/logo.png") + // => Content-Disposition: attachment; filename="logo.png" + // => Content-Type: image/png + + // ... +}) +``` + +## BaseURL + +Returns the base URL \(**protocol** + **host**\) as a `string`. + +```go title="Signature" +func (c *Ctx) BaseURL() string +``` + +```go title="Example" +// GET https://example.com/page#chapter-1 + +app.Get("/", func(c *fiber.Ctx) error { + c.BaseURL() // https://example.com + // ... +}) +``` + +## Bind +Add vars to default view var map binding to template engine. +Variables are read by the Render method and may be overwritten. + +```go title="Signature" +func (c *Ctx) Bind(vars Map) error +``` + +```go title="Example" +app.Use(func(c *fiber.Ctx) error { + c.Bind(fiber.Map{ + "Title": "Hello, World!", + }) +}) + +app.Get("/", func(c *fiber.Ctx) error { + return c.Render("xxx.tmpl", fiber.Map{}) // Render will use Title variable +}) +``` + +## Body + +Returns the raw request **body**. + +```go title="Signature" +func (c *Ctx) Body() []byte +``` + +```go title="Example" +// curl -X POST http://localhost:8080 -d user=john + +app.Post("/", func(c *fiber.Ctx) error { + // Get raw body from POST request: + return c.Send(c.Body()) // []byte("user=john") +}) +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + +## BodyParser + +Binds the request body to a struct. + +It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a JSON body with a field called Pass, you would use a struct field of `json:"pass"`. + +| content-type | struct tag | +|---|---| +| `application/x-www-form-urlencoded` | form | +| `multipart/form-data` | form | +| `application/json` | json | +| `application/xml` | xml | +| `text/xml` | xml | + +```go title="Signature" +func (c *Ctx) BodyParser(out interface{}) error +``` + +```go title="Example" +// Field names should start with an uppercase letter +type Person struct { + Name string `json:"name" xml:"name" form:"name"` + Pass string `json:"pass" xml:"pass" form:"pass"` +} + +app.Post("/", func(c *fiber.Ctx) error { + p := new(Person) + + if err := c.BodyParser(p); err != nil { + return err + } + + log.Println(p.Name) // john + log.Println(p.Pass) // doe + + // ... +}) + +// Run tests with the following curl commands + +// curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"pass\":\"doe\"}" localhost:3000 + +// curl -X POST -H "Content-Type: application/xml" --data "johndoe" localhost:3000 + +// curl -X POST -H "Content-Type: application/x-www-form-urlencoded" --data "name=john&pass=doe" localhost:3000 + +// curl -X POST -F name=john -F pass=doe http://localhost:3000 + +// curl -X POST "http://localhost:3000/?name=john&pass=doe" +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + +## ClearCookie + +Expire a client cookie \(_or all cookies if left empty\)_ + +```go title="Signature" +func (c *Ctx) ClearCookie(key ...string) +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + // Clears all cookies: + c.ClearCookie() + + // Expire specific cookie by name: + c.ClearCookie("user") + + // Expire multiple cookies by names: + c.ClearCookie("token", "session", "track_id", "version") + // ... +}) +``` + +:::caution +Web browsers and other compliant clients will only clear the cookie if the given options are identical to those when creating the cookie, excluding expires and maxAge. ClearCookie will not set these values for you - a technique similar to the one shown below should be used to ensure your cookie is deleted. +::: + +```go title="Example" +app.Get("/set", func(c *fiber.Ctx) error { + c.Cookie(&fiber.Cookie{ + Name: "token", + Value: "randomvalue", + Expires: time.Now().Add(24 * time.Hour), + HTTPOnly: true, + SameSite: "lax", + }) + + // ... +}) + +app.Get("/delete", func(c *fiber.Ctx) error { + c.Cookie(&fiber.Cookie{ + Name: "token", + // Set expiry date to the past + Expires: time.Now().Add(-(time.Hour * 2)), + HTTPOnly: true, + SameSite: "lax", + }) + + // ... +}) +``` + +## ClientHelloInfo + +ClientHelloInfo contains information from a ClientHello message in order to guide application logic in the GetCertificate and GetConfigForClient callbacks. +You can refer to the [ClientHelloInfo](https://golang.org/pkg/crypto/tls/#ClientHelloInfo) struct documentation for more information on the returned struct. + +```go title="Signature" +func (c *Ctx) ClientHelloInfo() *tls.ClientHelloInfo +``` + +```go title="Example" +// GET http://example.com/hello +app.Get("/hello", func(c *fiber.Ctx) error { + chi := c.ClientHelloInfo() + // ... +}) +``` + +## Context + +Returns [\*fasthttp.RequestCtx](https://godoc.org/github.com/valyala/fasthttp#RequestCtx) that is compatible with the context.Context interface that requires a deadline, a cancellation signal, and other values across API boundaries. + +```go title="Signature" +func (c *Ctx) Context() *fasthttp.RequestCtx +``` + +:::info +Please read the [Fasthttp Documentation](https://pkg.go.dev/github.com/valyala/fasthttp?tab=doc) for more information. +::: + +## Cookie + +Set cookie + +```go title="Signature" +func (c *Ctx) Cookie(cookie *Cookie) +``` + +```go +type Cookie struct { + Name string `json:"name"` + Value string `json:"value"` + Path string `json:"path"` + Domain string `json:"domain"` + MaxAge int `json:"max_age"` + Expires time.Time `json:"expires"` + Secure bool `json:"secure"` + HTTPOnly bool `json:"http_only"` + SameSite string `json:"same_site"` + SessionOnly bool `json:"session_only"` +} +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + // Create cookie + cookie := new(fiber.Cookie) + cookie.Name = "john" + cookie.Value = "doe" + cookie.Expires = time.Now().Add(24 * time.Hour) + + // Set cookie + c.Cookie(cookie) + // ... +}) +``` + +## Cookies + +Get cookie value by key, you could pass an optional default value that will be returned if the cookie key does not exist. + +```go title="Signature" +func (c *Ctx) Cookies(key string, defaultValue ...string) string +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + // Get cookie by key: + c.Cookies("name") // "john" + c.Cookies("empty", "doe") // "doe" + // ... +}) +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + +## Download + +Transfers the file from path as an `attachment`. + +Typically, browsers will prompt the user to download. By default, the [Content-Disposition](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition) header `filename=` parameter is the file path \(_this typically appears in the browser dialog_\). + +Override this default with the **filename** parameter. + +```go title="Signature" +func (c *Ctx) Download(file string, filename ...string) error +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + return c.Download("./files/report-12345.pdf"); + // => Download report-12345.pdf + + return c.Download("./files/report-12345.pdf", "report.pdf"); + // => Download report.pdf +}) +``` + +## Format + +Performs content-negotiation on the [Accept](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) HTTP header. It uses [Accepts](ctx.md#accepts) to select a proper format. + +:::info +If the header is **not** specified or there is **no** proper format, **text/plain** is used. +::: + +```go title="Signature" +func (c *Ctx) Format(body interface{}) error +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + // Accept: text/plain + c.Format("Hello, World!") + // => Hello, World! + + // Accept: text/html + c.Format("Hello, World!") + // =>

Hello, World!

+ + // Accept: application/json + c.Format("Hello, World!") + // => "Hello, World!" + // .. +}) +``` + +## FormFile + +MultipartForm files can be retrieved by name, the **first** file from the given key is returned. + +```go title="Signature" +func (c *Ctx) FormFile(key string) (*multipart.FileHeader, error) +``` + +```go title="Example" +app.Post("/", func(c *fiber.Ctx) error { + // Get first file from form field "document": + file, err := c.FormFile("document") + + // Save file to root directory: + return c.SaveFile(file, fmt.Sprintf("./%s", file.Filename)) +}) +``` + +## FormValue + +Any form values can be retrieved by name, the **first** value from the given key is returned. + +```go title="Signature" +func (c *Ctx) FormValue(key string, defaultValue ...string) string +``` + +```go title="Example" +app.Post("/", func(c *fiber.Ctx) error { + // Get first value from form field "name": + c.FormValue("name") + // => "john" or "" if not exist + + // .. +}) +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + +## Fresh + +[https://expressjs.com/en/4x/api.html\#req.fresh](https://expressjs.com/en/4x/api.html#req.fresh) + +```go title="Signature" +func (c *Ctx) Fresh() bool +``` + +## Get + +Returns the HTTP request header specified by the field. + +:::tip +The match is **case-insensitive**. +::: + +```go title="Signature" +func (c *Ctx) Get(key string, defaultValue ...string) string +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.Get("Content-Type") // "text/plain" + c.Get("CoNtEnT-TypE") // "text/plain" + c.Get("something", "john") // "john" + // .. +}) +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + +## GetReqHeaders + +Returns the HTTP request headers. + +```go title="Signature" +func (c *Ctx) GetReqHeaders() map[string]string +``` + +## GetRespHeader + +Returns the HTTP response header specified by the field. + +:::tip +The match is **case-insensitive**. +::: + +```go title="Signature" +func (c *Ctx) GetRespHeader(key string, defaultValue ...string) string +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.GetRespHeader("X-Request-Id") // "8d7ad5e3-aaf3-450b-a241-2beb887efd54" + c.GetRespHeader("Content-Type") // "text/plain" + c.GetRespHeader("something", "john") // "john" + // .. +}) +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + +## GetRespHeaders + +Returns the HTTP response headers. + +```go title="Signature" +func (c *Ctx) GetRespHeaders() map[string]string +``` + +## GetRouteURL + +Generates URLs to named routes, with parameters. URLs are relative, for example: "/user/1831" + +```go title="Signature" +func (c *Ctx) GetRouteURL(routeName string, params Map) (string, error) +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Home page") +}).Name("home") + +app.Get("/user/:id", func(c *fiber.Ctx) error { + return c.SendString(c.Params("id")) +}).Name("user.show") + +app.Get("/test", func(c *fiber.Ctx) error { + location, _ := c.GetRouteURL("user.show", fiber.Map{"id": 1}) + return c.SendString(location) +}) + +// /test returns "/user/1" +``` + +## Hostname + +Returns the hostname derived from the [Host](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host) HTTP header. + +```go title="Signature" +func (c *Ctx) Hostname() string +``` + +```go title="Example" +// GET http://google.com/search + +app.Get("/", func(c *fiber.Ctx) error { + c.Hostname() // "google.com" + + // ... +}) +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + +## IP + +Returns the remote IP address of the request. + +```go title="Signature" +func (c *Ctx) IP() string +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.IP() // "127.0.0.1" + + // ... +}) +``` + +## IPs + +Returns an array of IP addresses specified in the [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) request header. + +```go title="Signature" +func (c *Ctx) IPs() []string +``` + +```go title="Example" +// X-Forwarded-For: proxy1, 127.0.0.1, proxy3 + +app.Get("/", func(c *fiber.Ctx) error { + c.IPs() // ["proxy1", "127.0.0.1", "proxy3"] + + // ... +}) +``` + +## 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. + +:::info +If the request has **no** body, it returns **false**. +::: + +```go title="Signature" +func (c *Ctx) Is(extension string) bool +``` + +```go title="Example" +// Content-Type: text/html; charset=utf-8 + +app.Get("/", func(c *fiber.Ctx) error { + c.Is("html") // true + c.Is(".html") // true + c.Is("json") // false + + // ... +}) +``` + +## IsFromLocal + +Returns true if request came from localhost +```go title="Signature" +func (c *Ctx) IsFromLocal() bool { +``` + +```go title="Example" + +app.Get("/", func(c *fiber.Ctx) error { + // If request came from localhost, return true else return false + c.IsFromLocal() + + // ... +}) +``` + +## JSON + +Converts any **interface** or **string** to JSON using the [goccy/go-json](https://github.com/goccy/go-json) package. + +:::info +JSON also sets the content header to **application/json**. +::: + +```go title="Signature" +func (c *Ctx) JSON(data interface{}) error +``` + +```go title="Example" +type SomeStruct struct { + Name string + Age uint8 +} + +app.Get("/json", func(c *fiber.Ctx) error { + // Create data struct: + data := SomeStruct{ + Name: "Grame", + Age: 20, + } + + return c.JSON(data) + // => Content-Type: application/json + // => "{"Name": "Grame", "Age": 20}" + + return c.JSON(fiber.Map{ + "name": "Grame", + "age": 20, + }) + // => Content-Type: application/json + // => "{"name": "Grame", "age": 20}" +}) +``` + +## JSONP + +Sends a JSON response with JSONP support. This method is identical to [JSON](ctx.md#json), except that it opts-in to JSONP callback support. By default, the callback name is simply callback. + +Override this by passing a **named string** in the method. + +```go title="Signature" +func (c *Ctx) JSONP(data interface{}, callback ...string) error +``` + +```go title="Example" +type SomeStruct struct { + name string + age uint8 +} + +app.Get("/", func(c *fiber.Ctx) error { + // Create data struct: + data := SomeStruct{ + name: "Grame", + age: 20, + } + + return c.JSONP(data) + // => callback({"name": "Grame", "age": 20}) + + return c.JSONP(data, "customFunc") + // => customFunc({"name": "Grame", "age": 20}) +}) +``` + +## Links + +Joins the links followed by the property to populate the response’s [Link](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link) HTTP header field. + +```go title="Signature" +func (c *Ctx) Links(link ...string) +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.Links( + "http://api.example.com/users?page=2", "next", + "http://api.example.com/users?page=5", "last", + ) + // Link: ; rel="next", + // ; rel="last" + + // ... +}) +``` + +## Locals + +A method that stores variables scoped to the request and, therefore, are available only to the routes that match the request. + +:::tip +This is useful if you want to pass some **specific** data to the next middleware. +::: + +```go title="Signature" +func (c *Ctx) Locals(key interface{}, value ...interface{}) interface{} +``` + +```go title="Example" +app.Use(func(c *fiber.Ctx) error { + c.Locals("user", "admin") + return c.Next() +}) + +app.Get("/admin", func(c *fiber.Ctx) error { + if c.Locals("user") == "admin" { + return c.Status(fiber.StatusOK).SendString("Welcome, admin!") + } + return c.SendStatus(fiber.StatusForbidden) + +}) +``` + +## Location + +Sets the response [Location](https://developer.mozilla.org/ru/docs/Web/HTTP/Headers/Location) HTTP header to the specified path parameter. + +```go title="Signature" +func (c *Ctx) Location(path string) +``` + +```go title="Example" +app.Post("/", func(c *fiber.Ctx) error { + c.Location("http://example.com") + + c.Location("/foo/bar") + + return nil +}) +``` + +## Method + +Returns a string corresponding to the HTTP method of the request: `GET`, `POST`, `PUT`, and so on. +Optionally, you could override the method by passing a string. + +```go title="Signature" +func (c *Ctx) Method(override ...string) string +``` + +```go title="Example" +app.Post("/", func(c *fiber.Ctx) error { + c.Method() // "POST" + + c.Method("GET") + c.Method() // GET + + // ... +}) +``` + +## MultipartForm + +To access multipart form entries, you can parse the binary with `MultipartForm()`. This returns a `map[string][]string`, so given a key, the value will be a string slice. + +```go title="Signature" +func (c *Ctx) MultipartForm() (*multipart.Form, error) +``` + +```go title="Example" +app.Post("/", func(c *fiber.Ctx) error { + // Parse the multipart form: + if form, err := c.MultipartForm(); err == nil { + // => *multipart.Form + + if token := form.Value["token"]; len(token) > 0 { + // Get key value: + fmt.Println(token[0]) + } + + // Get all files from "documents" key: + files := form.File["documents"] + // => []*multipart.FileHeader + + // Loop through files: + for _, file := range files { + fmt.Println(file.Filename, file.Size, file.Header["Content-Type"][0]) + // => "tutorial.pdf" 360641 "application/pdf" + + // Save the files to disk: + if err := c.SaveFile(file, fmt.Sprintf("./%s", file.Filename)); err != nil { + return err + } + } + } + + return err +}) +``` + +## Next + +When **Next** is called, it executes the next method in the stack that matches the current route. You can pass an error struct within the method that will end the chaining and call the [error handler](https://docs.gofiber.io/guide/error-handling). + +```go title="Signature" +func (c *Ctx) Next() error +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + fmt.Println("1st route!") + return c.Next() +}) + +app.Get("*", func(c *fiber.Ctx) error { + fmt.Println("2nd route!") + return c.Next() +}) + +app.Get("/", func(c *fiber.Ctx) error { + fmt.Println("3rd route!") + return c.SendString("Hello, World!") +}) +``` + +## OriginalURL + +Returns the original request URL. + +```go title="Signature" +func (c *Ctx) OriginalURL() string +``` + +```go title="Example" +// GET http://example.com/search?q=something + +app.Get("/", func(c *fiber.Ctx) error { + c.OriginalURL() // "/search?q=something" + + // ... +}) +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + +## Params + +Method can be used to get the route parameters, you could pass an optional default value that will be returned if the param key does not exist. + +:::info +Defaults to empty string \(`""`\), if the param **doesn't** exist. +::: + +```go title="Signature" +func (c *Ctx) Params(key string, defaultValue ...string) string +``` + +```go title="Example" +// GET http://example.com/user/fenny +app.Get("/user/:name", func(c *fiber.Ctx) error { + c.Params("name") // "fenny" + + // ... +}) + +// GET http://example.com/user/fenny/123 +app.Get("/user/*", func(c *fiber.Ctx) error { + c.Params("*") // "fenny/123" + c.Params("*1") // "fenny/123" + + // ... +}) +``` + +Unnamed route parameters\(\*, +\) can be fetched by the **character** and the **counter** in the route. + +```go title="Example" +// ROUTE: /v1/*/shop/* +// GET: /v1/brand/4/shop/blue/xs +c.Params("*1") // "brand/4" +c.Params("*2") // "blue/xs" +``` + +For reasons of **downward compatibility**, the first parameter segment for the parameter character can also be accessed without the counter. + +```go title="Example" +app.Get("/v1/*/shop/*", func(c *fiber.Ctx) error { + c.Params("*") // outputs the values of the first wildcard segment +}) +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + +## ParamsInt + +Method can be used to get an integer from the route parameters. +Please note if that parameter is not in the request, zero +will be returned. If the parameter is NOT a number, zero and an error +will be returned + +:::info +Defaults to the integer zero \(`0`\), if the param **doesn't** exist. +::: + +```go title="Signature" +func (c *Ctx) ParamsInt(key string) (int, error) +``` + +```go title="Example" +// GET http://example.com/user/123 +app.Get("/user/:id", func(c *fiber.Ctx) error { + id, err := c.ParamsInt("id") // int 123 and no error + + // ... +}) + +``` + +This method is equivalent of using `atoi` with ctx.Params + +## ParamsParser +This method is similar to BodyParser, but for path parameters. It is important to use the struct tag "params". For example, if you want to parse a path parameter with a field called Pass, you would use a struct field of params:"pass" + +```go title="Signature" +func (c *Ctx) ParamsParser(out interface{}) error +``` + +```go title="Example" +// GET http://example.com/user/111 +app.Get("/user/:id", func(c *fiber.Ctx) error { + param := struct {ID uint `params:"id"`}{} + + c.ParamsParser(¶m) // "{"id": 111}" + + // ... +}) + +``` + +## Path + +Contains the path part of the request URL. Optionally, you could override the path by passing a string. For internal redirects, you might want to call [RestartRouting](ctx.md#restartrouting) instead of [Next](ctx.md#next). + +```go title="Signature" +func (c *Ctx) Path(override ...string) string +``` + +```go title="Example" +// GET http://example.com/users?sort=desc + +app.Get("/users", func(c *fiber.Ctx) error { + c.Path() // "/users" + + c.Path("/john") + c.Path() // "/john" + + // ... +}) +``` + +## Protocol + +Contains the request protocol string: `http` or `https` for **TLS** requests. + +```go title="Signature" +func (c *Ctx) Protocol() string +``` + +```go title="Example" +// GET http://example.com + +app.Get("/", func(c *fiber.Ctx) error { + c.Protocol() // "http" + + // ... +}) +``` + +## Query + +This property is an object containing a property for each query string parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. + +:::info +If there is **no** query string, it returns an **empty string**. +::: + +```go title="Signature" +func (c *Ctx) Query(key string, defaultValue ...string) string +``` + +```go title="Example" +// GET http://example.com/?order=desc&brand=nike + +app.Get("/", func(c *fiber.Ctx) error { + c.Query("order") // "desc" + c.Query("brand") // "nike" + c.Query("empty", "nike") // "nike" + + // ... +}) +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + +## QueryInt + +This property is an object containing a property for each query integer parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. + + +:::caution +Please note if that parameter is not in the request, zero will be returned. +If the parameter is not a number, it is still tried to be converted and usually returned as 1. +::: + +:::info +Defaults to the integer zero \(`0`\), if the param **doesn't** exist. +::: + +```go title="Signature" +func (c *Ctx) QueryInt(key string, defaultValue ...int) int +``` + +```go title="Example" +// GET http://example.com/?name=alex&wanna_cake=2&id= + +app.Get("/", func(c *fiber.Ctx) error { + QueryInt("wanna_cake", 1) // 2 + QueryInt("name", 1) // 1 + QueryInt("id", 1) // 1 + QueryInt("id") // 0 + + // ... +}) +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + +## QueryParser + +This method is similar to [BodyParser](ctx.md#bodyparser), but for query parameters. +It is important to use the struct tag "query". For example, if you want to parse a query parameter with a field called Pass, you would use a struct field of `query:"pass"`. + +```go title="Signature" +func (c *Ctx) QueryParser(out interface{}) error +``` + +```go title="Example" +// Field names should start with an uppercase letter +type Person struct { + Name string `query:"name"` + Pass string `query:"pass"` + Products []string `query:"products"` +} + +app.Get("/", func(c *fiber.Ctx) error { + p := new(Person) + + if err := c.QueryParser(p); err != nil { + return err + } + + log.Println(p.Name) // john + log.Println(p.Pass) // doe + log.Println(p.Products) // [shoe, hat] + + // ... +}) +// Run tests with the following curl command + +// curl "http://localhost:3000/?name=john&pass=doe&products=shoe,hat" +``` + +## Range + +A struct containing the type and a slice of ranges will be returned. + +```go title="Signature" +func (c *Ctx) Range(size int) (Range, error) +``` + +```go title="Example" +// Range: bytes=500-700, 700-900 +app.Get("/", func(c *fiber.Ctx) error { + b := c.Range(1000) + if b.Type == "bytes" { + for r := range r.Ranges { + fmt.Println(r) + // [500, 700] + } + } +}) +``` + +## Redirect + +Redirects to the URL derived from the specified path, with specified status, a positive integer that corresponds to an HTTP status code. + +:::info +If **not** specified, status defaults to **302 Found**. +::: + +```go title="Signature" +func (c *Ctx) Redirect(location string, status ...int) error +``` + +```go title="Example" +app.Get("/coffee", func(c *fiber.Ctx) error { + return c.Redirect("/teapot") +}) + +app.Get("/teapot", func(c *fiber.Ctx) error { + return c.Status(fiber.StatusTeapot).Send("🍵 short and stout 🍵") +}) +``` + +```go title="More examples" +app.Get("/", func(c *fiber.Ctx) error { + return c.Redirect("/foo/bar") + return c.Redirect("../login") + return c.Redirect("http://example.com") + return c.Redirect("http://example.com", 301) +}) +``` + +## RedirectToRoute + +Redirects to the specific route along with the parameters and with specified status, a positive integer that corresponds to an HTTP status code. + +:::info +If **not** specified, status defaults to **302 Found**. +::: + +:::info +If you want to send queries to route, you must add **"queries"** key typed as **map[string]string** to params. +::: + +```go title="Signature" +func (c *Ctx) RedirectToRoute(routeName string, params fiber.Map, status ...int) error +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + // /user/fiber + return c.RedirectToRoute("user", fiber.Map{ + "name": "fiber" + }) +}) + +app.Get("/with-queries", func(c *fiber.Ctx) error { + // /user/fiber?data[0][name]=john&data[0][age]=10&test=doe + return c.RedirectToRoute("user", fiber.Map{ + "name": "fiber", + "queries": map[string]string{"data[0][name]": "john", "data[0][age]": "10", "test": "doe"}, + }) +}) + +app.Get("/user/:name", func(c *fiber.Ctx) error { + return c.SendString(c.Params("name")) +}).Name("user") +``` + +## RedirectBack + +Redirects back to refer URL. It redirects to fallback URL if refer header doesn't exists, with specified status, a positive integer that corresponds to an HTTP status code. + +:::info +If **not** specified, status defaults to **302 Found**. +::: + +```go title="Signature" +func (c *Ctx) RedirectBack(fallback string, status ...int) error +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Home page") +}) +app.Get("/test", func(c *fiber.Ctx) error { + c.Set("Content-Type", "text/html") + return c.SendString(`Back`) +}) + +app.Get("/back", func(c *fiber.Ctx) error { + return c.RedirectBack("/") +}) +``` + +## Render + +Renders a view with data and sends a `text/html` response. By default `Render` uses the default [**Go Template engine**](https://pkg.go.dev/html/template/). If you want to use another View engine, please take a look at our [**Template middleware**](https://github.com/gofiber/template). + +```go title="Signature" +func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error +``` + +## Request + +Request return the [\*fasthttp.Request](https://godoc.org/github.com/valyala/fasthttp#Request) pointer + +```go title="Signature" +func (c *Ctx) Request() *fasthttp.Request +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.Request().Header.Method() + // => []byte("GET") +}) +``` + +## ReqHeaderParser + +This method is similar to [BodyParser](ctx.md#bodyparser), but for request headers. +It is important to use the struct tag "reqHeader". For example, if you want to parse a request header with a field called Pass, you would use a struct field of `reqHeader:"pass"`. + +```go title="Signature" +func (c *Ctx) ReqHeaderParser(out interface{}) error +``` + +```go title="Example" +// Field names should start with an uppercase letter +type Person struct { + Name string `reqHeader:"name"` + Pass string `reqHeader:"pass"` + Products []string `reqHeader:"products"` +} + +app.Get("/", func(c *fiber.Ctx) error { + p := new(Person) + + if err := c.ReqHeaderParser(p); err != nil { + return err + } + + log.Println(p.Name) // john + log.Println(p.Pass) // doe + log.Println(p.Products) // [shoe, hat] + + // ... +}) +// Run tests with the following curl command + +// curl "http://localhost:3000/" -H "name: john" -H "pass: doe" -H "products: shoe,hat" +``` + +## Response + +Response return the [\*fasthttp.Response](https://godoc.org/github.com/valyala/fasthttp#Response) pointer + +```go title="Signature" +func (c *Ctx) Response() *fasthttp.Response +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.Response().BodyWriter().Write([]byte("Hello, World!")) + // => "Hello, World!" + return nil +}) +``` + +## RestartRouting + +Instead of executing the next method when calling [Next](ctx.md#next), **RestartRouting** restarts execution from the first method that matches the current route. This may be helpful after overriding the path, i. e. an internal redirect. Note that handlers might be executed again which could result in an infinite loop. + +```go title="Signature" +func (c *Ctx) RestartRouting() error +``` + +```go title="Example" +app.Get("/new", func(c *fiber.Ctx) error { + return c.SendString("From /new") +}) + +app.Get("/old", func(c *fiber.Ctx) error { + c.Path("/new") + return c.RestartRouting() +}) +``` + +## Route + +Returns the matched [Route](https://pkg.go.dev/github.com/gofiber/fiber?tab=doc#Route) struct. + +```go title="Signature" +func (c *Ctx) Route() *Route +``` + +```go title="Example" +// http://localhost:8080/hello + + +app.Get("/hello/:name", func(c *fiber.Ctx) error { + r := c.Route() + fmt.Println(r.Method, r.Path, r.Params, r.Handlers) + // GET /hello/:name handler [name] + + // ... +}) +``` + +:::caution +Do not rely on `c.Route()` in middlewares **before** calling `c.Next()` - `c.Route()` returns the **last executed route**. +::: + +```go title="Example" +func MyMiddleware() fiber.Handler { + return func(c *fiber.Ctx) error { + beforeNext := c.Route().Path // Will be '/' + err := c.Next() + afterNext := c.Route().Path // Will be '/hello/:name' + return err + } +} +``` + +## SaveFile + +Method is used to save **any** multipart file to disk. + +```go title="Signature" +func (c *Ctx) SaveFile(fh *multipart.FileHeader, path string) error +``` + +```go title="Example" +app.Post("/", func(c *fiber.Ctx) error { + // Parse the multipart form: + if form, err := c.MultipartForm(); err == nil { + // => *multipart.Form + + // Get all files from "documents" key: + files := form.File["documents"] + // => []*multipart.FileHeader + + // Loop through files: + for _, file := range files { + fmt.Println(file.Filename, file.Size, file.Header["Content-Type"][0]) + // => "tutorial.pdf" 360641 "application/pdf" + + // Save the files to disk: + if err := c.SaveFile(file, fmt.Sprintf("./%s", file.Filename)); err != nil { + return err + } + } + return err + } +}) +``` + +## SaveFileToStorage + +Method is used to save **any** multipart file to an external storage system. + +```go title="Signature" +func (c *Ctx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, storage Storage) error +``` + +```go title="Example" +storage := memory.New() + +app.Post("/", func(c *fiber.Ctx) error { + // Parse the multipart form: + if form, err := c.MultipartForm(); err == nil { + // => *multipart.Form + + // Get all files from "documents" key: + files := form.File["documents"] + // => []*multipart.FileHeader + + // Loop through files: + for _, file := range files { + fmt.Println(file.Filename, file.Size, file.Header["Content-Type"][0]) + // => "tutorial.pdf" 360641 "application/pdf" + + // Save the files to storage: + if err := c.SaveFileToStorage(file, fmt.Sprintf("./%s", file.Filename), storage); err != nil { + return err + } + } + return err + } +}) +``` + +## Secure + +A boolean property that is `true` , if a **TLS** connection is established. + +```go title="Signature" +func (c *Ctx) Secure() bool +``` + +```go title="Example" +// Secure() method is equivalent to: +c.Protocol() == "https" +``` + +## Send + +Sets the HTTP response body. + +```go title="Signature" +func (c *Ctx) Send(body []byte) error +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + return c.Send([]byte("Hello, World!")) // => "Hello, World!" +}) +``` + +Fiber also provides `SendString` and `SendStream` methods for raw inputs. + +:::tip +Use this if you **don't need** type assertion, recommended for **faster** performance. +::: + +```go title="Signature" +func (c *Ctx) SendString(body string) error +func (c *Ctx) SendStream(stream io.Reader, size ...int) error +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + // => "Hello, World!" + + return c.SendStream(bytes.NewReader([]byte("Hello, World!"))) + // => "Hello, World!" +}) +``` + +## SendFile + +Transfers the file from the given path. Sets the [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) response HTTP header field based on the **filenames** extension. + +:::caution +Method doesn´t use **gzipping** by default, set it to **true** to enable. +::: + +```go title="Signature" title="Signature" +func (c *Ctx) SendFile(file string, compress ...bool) error +``` + +```go title="Example" +app.Get("/not-found", func(c *fiber.Ctx) error { + return c.SendFile("./public/404.html"); + + // Disable compression + return c.SendFile("./static/index.html", false); +}) +``` + +:::info +If the file contains an url specific character you have to escape it before passing the file path into the `sendFile` function. +::: + +```go title="Example" +app.Get("/file-with-url-chars", func(c *fiber.Ctx) error { + return c.SendFile(url.PathEscape("hash_sign_#.txt")) +}) +``` + +## SendStatus + +Sets the status code and the correct status message in the body, if the response body is **empty**. + +:::tip +You can find all used status codes and messages [here](https://github.com/gofiber/fiber/blob/dffab20bcdf4f3597d2c74633a7705a517d2c8c2/utils.go#L183-L244). +::: + +```go title="Signature" +func (c *Ctx) SendStatus(status int) error +``` + +```go title="Example" +app.Get("/not-found", func(c *fiber.Ctx) error { + return c.SendStatus(415) + // => 415 "Unsupported Media Type" + + c.SendString("Hello, World!") + return c.SendStatus(415) + // => 415 "Hello, World!" +}) +``` + +## Set + +Sets the response’s HTTP header field to the specified `key`, `value`. + +```go title="Signature" +func (c *Ctx) Set(key string, val string) +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.Set("Content-Type", "text/plain") + // => "Content-type: text/plain" + + // ... +}) +``` + +## SetParserDecoder + +Allow you to config BodyParser/QueryParser decoder, base on schema's options, providing possibility to add custom type for pausing. + +```go title="Signature" +func SetParserDecoder(parserConfig fiber.ParserConfig{ + IgnoreUnknownKeys bool, + ParserType []fiber.ParserType{ + Customtype interface{}, + Converter func(string) reflect.Value, + }, + ZeroEmpty bool, + SetAliasTag string, +}) +``` + +```go title="Example" + +type CustomTime time.Time + +// String() returns the time in string +func (ct *CustomTime) String() string { + t := time.Time(*ct).String() + return t +} + +// Register the converter for CustomTime type format as 2006-01-02 +var timeConverter = func(value string) reflect.Value { + fmt.Println("timeConverter", value) + if v, err := time.Parse("2006-01-02", value); err == nil { + return reflect.ValueOf(v) + } + return reflect.Value{} +} + +customTime := fiber.ParserType{ + Customtype: CustomTime{}, + Converter: timeConverter, +} + +// Add setting to the Decoder +fiber.SetParserDecoder(fiber.ParserConfig{ + IgnoreUnknownKeys: true, + ParserType: []fiber.ParserType{customTime}, + ZeroEmpty: true, +}) + +// Example to use CustomType, you pause custom time format not in RFC3339 +type Demo struct { + Date CustomTime `form:"date" query:"date"` + Title string `form:"title" query:"title"` + Body string `form:"body" query:"body"` +} + +app.Post("/body", func(c *fiber.Ctx) error { + var d Demo + c.BodyParser(&d) + fmt.Println("d.Date", d.Date.String()) + return c.JSON(d) +}) + +app.Get("/query", func(c *fiber.Ctx) error { + var d Demo + c.QueryParser(&d) + fmt.Println("d.Date", d.Date.String()) + return c.JSON(d) +}) + +// curl -X POST -F title=title -F body=body -F date=2021-10-20 http://localhost:3000/body + +// curl -X GET "http://localhost:3000/query?title=title&body=body&date=2021-10-20" + +``` + + +## SetUserContext + +Sets the user specified implementation for context interface. + +```go title="Signature" +func (c *Ctx) SetUserContext(ctx context.Context) +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + ctx := context.Background() + c.SetUserContext(ctx) + // Here ctx could be any context implementation + + // ... +}) +``` + +## Stale + +[https://expressjs.com/en/4x/api.html\#req.stale](https://expressjs.com/en/4x/api.html#req.stale) + +```go title="Signature" +func (c *Ctx) Stale() bool +``` + +## Status + +Sets the HTTP status for the response. + +:::info +Method is a **chainable**. +::: + +```go title="Signature" +func (c *Ctx) Status(status int) *Ctx +``` + +```go title="Example" +app.Get("/fiber", func(c *fiber.Ctx) error { + c.Status(fiber.StatusOK) + return nil +} + +app.Get("/hello", func(c *fiber.Ctx) error { + return c.Status(fiber.StatusBadRequest).SendString("Bad Request") +} + +app.Get("/world", func(c *fiber.Ctx) error { + return c.Status(fiber.StatusNotFound).SendFile("./public/gopher.png") +}) +``` + +## Subdomains + +Returns a string slice of subdomains in the domain name of the request. + +The application property subdomain offset, which defaults to `2`, is used for determining the beginning of the subdomain segments. + +```go title="Signature" +func (c *Ctx) Subdomains(offset ...int) []string +``` + +```go title="Example" +// Host: "tobi.ferrets.example.com" + +app.Get("/", func(c *fiber.Ctx) error { + c.Subdomains() // ["ferrets", "tobi"] + c.Subdomains(1) // ["tobi"] + + // ... +}) +``` + +## Type + +Sets the [Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) HTTP header to the MIME type listed [here](https://github.com/nginx/nginx/blob/master/conf/mime.types) specified by the file **extension**. + +```go title="Signature" +func (c *Ctx) Type(ext string, charset ...string) *Ctx +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.Type(".html") // => "text/html" + c.Type("html") // => "text/html" + c.Type("png") // => "image/png" + + c.Type("json", "utf-8") // => "application/json; charset=utf-8" + + // ... +}) +``` + +## UserContext + +UserContext returns a context implementation that was set by user earlier +or returns a non-nil, empty context, if it was not set earlier. + +```go title="Signature" +func (c *Ctx) UserContext() context.Context +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + ctx := c.UserContext() + // ctx is context implementation set by user + + // ... +}) +``` + +## Vary + +Adds the given header field to the [Vary](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary) response header. This will append the header, if not already listed, otherwise leaves it listed in the current location. + +:::info +Multiple fields are **allowed**. +::: + +```go title="Signature" +func (c *Ctx) Vary(fields ...string) +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.Vary("Origin") // => Vary: Origin + c.Vary("User-Agent") // => Vary: Origin, User-Agent + + // No duplicates + c.Vary("Origin") // => Vary: Origin, User-Agent + + c.Vary("Accept-Encoding", "Accept") + // => Vary: Origin, User-Agent, Accept-Encoding, Accept + + // ... +}) +``` + +## Write + +Write adopts the Writer interface + +```go title="Signature" +func (c *Ctx) Write(p []byte) (n int, err error) +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.Write([]byte("Hello, World!")) // => "Hello, World!" + + fmt.Fprintf(c, "%s\n", "Hello, World!") // "Hello, World!Hello, World!" +}) +``` + +## Writef + +Writef adopts the string with variables + +```go title="Signature" +func (c *Ctx) Writef(f string, a ...interface{}) (n int, err error) +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + world := "World!" + c.Writef("Hello, %s", world) // => "Hello, World!" + + fmt.Fprintf(c, "%s\n", "Hello, World!") // "Hello, World!Hello, World!" +}) +``` + +## WriteString + +WriteString adopts the string + +```go title="Signature" +func (c *Ctx) WriteString(s string) (n int, err error) +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + c.WriteString("Hello, World!") // => "Hello, World!" + + fmt.Fprintf(c, "%s\n", "Hello, World!") // "Hello, World!Hello, World!" +}) +``` + +## XHR + +A Boolean property, that is `true`, if the request’s [X-Requested-With](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers) header field is [XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest), indicating that the request was issued by a client library \(such as [jQuery](https://api.jquery.com/jQuery.ajax/)\). + +```go title="Signature" +func (c *Ctx) XHR() bool +``` + +```go title="Example" +// X-Requested-With: XMLHttpRequest + +app.Get("/", func(c *fiber.Ctx) error { + c.XHR() // true + + // ... +}) +``` + +## XML + +Converts any **interface** or **string** to XML using the standard `encoding/xml` package. + +:::info +XML also sets the content header to **application/xml**. +::: + +```go title="Signature" +func (c *Ctx) XML(data interface{}) error +``` + +```go title="Example" +type SomeStruct struct { + XMLName xml.Name `xml:"Fiber"` + Name string `xml:"Name"` + Age uint8 `xml:"Age"` +} + +app.Get("/", func(c *fiber.Ctx) error { + // Create data struct: + data := SomeStruct{ + Name: "Grame", + Age: 20, + } + + return c.XML(data) + // + // Grame + // 20 + // +}) +``` diff --git a/docs/api/fiber.md b/docs/api/fiber.md new file mode 100644 index 0000000000..5e0c4deef8 --- /dev/null +++ b/docs/api/fiber.md @@ -0,0 +1,119 @@ +--- +id: fiber +title: 📦 Fiber +description: Fiber represents the fiber package where you start to create an instance. +sidebar_position: 1 +--- + +## New + +This method creates a new **App** named instance. You can pass optional [config ](#config)when creating a new instance. + +```go title="Signature" +func New(config ...Config) *App +``` + +```go title="Example" +// Default config +app := fiber.New() + +// ... +``` + +## Config + +You can pass an optional Config when creating a new Fiber instance. + +```go title="Example" +// Custom config +app := fiber.New(fiber.Config{ + Prefork: true, + CaseSensitive: true, + StrictRouting: true, + ServerHeader: "Fiber", + AppName: "Test App v1.0.1" +}) + +// ... +``` + +**Config fields** + +| Property | Type | Description | Default | +| ---------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------- | +| AppName | `string` | This allows to setup app name for the app | `""` | +| BodyLimit | `int` | Sets the maximum allowed size for a request body, if the size exceeds the configured limit, it sends `413 - Request Entity Too Large` response. | `4 * 1024 * 1024` | +| CaseSensitive | `bool` | When enabled, `/Foo` and `/foo` are different routes. When disabled, `/Foo`and `/foo` are treated the same. | `false` | +| ColorScheme | `Colors` | You can define custom color scheme. They'll be used for startup message, route list and some middlewares. | `DefaultColors` | +| CompressedFileSuffix | `string` | Adds a suffix to the original file name and tries saving the resulting compressed file under the new file name. | `".fiber.gz"` | +| Concurrency | `int` | Maximum number of concurrent connections. | `256 * 1024` | +| DisableDefaultContentType | `bool` | When set to true, causes the default Content-Type header to be excluded from the Response. | `false` | +| DisableDefaultDate | `bool` | When set to true causes the default date header to be excluded from the response. | `false` | +| DisableHeaderNormalizing | `bool` | By default all header names are normalized: conteNT-tYPE -> Content-Type | `false` | +| DisableKeepalive | `bool` | Disable keep-alive connections, the server will close incoming connections after sending the first response to the client | `false` | +| DisablePreParseMultipartForm | `bool` | Will not pre parse Multipart Form data if set to true. This option is useful for servers that desire to treat multipart form data as a binary blob, or choose when to parse the data. | `false` | +| DisableStartupMessage | `bool` | When set to true, it will not print out debug information | `false` | +| ETag | `bool` | Enable or disable ETag header generation, since both weak and strong etags are generated using the same hashing method \(CRC-32\). Weak ETags are the default when enabled. | `false` | +| EnableIPValidation | `bool` | If set to true, `c.IP()` and `c.IPs()` will validate IP addresses before returning them. Also, `c.IP()` will return only the first valid IP rather than just the raw header value that may be a comma seperated string.

**WARNING:** There is a small performance cost to doing this validation. Keep disabled if speed is your only concern and your application is behind a trusted proxy that already validates this header. | `false` | +| EnablePrintRoutes | `bool` | EnablePrintRoutes enables print all routes with their method, path, name and handler.. | `false` | +| EnableTrustedProxyCheck | `bool` | When set to true, fiber will check whether proxy is trusted, using TrustedProxies list.

By default `c.Protocol()` will get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header, `c.IP()` will get value from `ProxyHeader` header, `c.Hostname()` will get value from X-Forwarded-Host header.
If `EnableTrustedProxyCheck` is true, and `RemoteIP` is in the list of `TrustedProxies` `c.Protocol()`, `c.IP()`, and `c.Hostname()` will have the same behaviour when `EnableTrustedProxyCheck` disabled, if `RemoteIP` isn't in the list, `c.Protocol()` will return https in case when tls connection is handled by the app, or http otherwise, `c.IP()` will return RemoteIP() from fasthttp context, `c.Hostname()` will return `fasthttp.Request.URI().Host()` | `false` | +| ErrorHandler | `ErrorHandler` | ErrorHandler is executed when an error is returned from fiber.Handler. Mounted fiber error handlers are retained by the top-level app and applied on prefix associated requests. | `DefaultErrorHandler` | +| GETOnly | `bool` | Rejects all non-GET requests if set to true. This option is useful as anti-DoS protection for servers accepting only GET requests. The request size is limited by ReadBufferSize if GETOnly is set. | `false` | +| IdleTimeout | `time.Duration` | The maximum amount of time to wait for the next request when keep-alive is enabled. If IdleTimeout is zero, the value of ReadTimeout is used. | `nil` | +| Immutable | `bool` | When enabled, all values returned by context methods are immutable. By default, they are valid until you return from the handler; see issue [\#185](https://github.com/gofiber/fiber/issues/185). | `false` | +| JSONDecoder | `utils.JSONUnmarshal` | Allowing for flexibility in using another json library for decoding. | `json.Unmarshal` | +| JSONEncoder | `utils.JSONMarshal` | Allowing for flexibility in using another json library for encoding. | `json.Marshal` | +| Network | `string` | Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only)

**WARNING:** When prefork is set to true, only "tcp4" and "tcp6" can be chosen. | `NetworkTCP4` | +| PassLocalsToViews | `bool` | PassLocalsToViews Enables passing of the locals set on a fiber.Ctx to the template engine. See our **Template Middleware** for supported engines. | `false` | +| Prefork | `bool` | Enables use of the[`SO_REUSEPORT`](https://lwn.net/Articles/542629/)socket option. This will spawn multiple Go processes listening on the same port. learn more about [socket sharding](https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/). **NOTE: if enabled, the application will need to be ran through a shell because prefork mode sets environment variables. If you're using Docker, make sure the app is ran with `CMD ./app` or `CMD ["sh", "-c", "/app"]`. For more info, see** [**this**](https://github.com/gofiber/fiber/issues/1021#issuecomment-730537971) **issue comment.** | `false` | +| ProxyHeader | `string` | This will enable `c.IP()` to return the value of the given header key. By default `c.IP()`will return the Remote IP from the TCP connection, this property can be useful if you are behind a load balancer e.g. _X-Forwarded-\*_. | `""` | +| ReadBufferSize | `int` | per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers \(for example, BIG cookies\). | `4096` | +| ReadTimeout | `time.Duration` | The amount of time allowed to read the full request, including the body. The default timeout is unlimited. | `nil` | +| RequestMethods | `[]string` | RequestMethods provides customizibility for HTTP methods. You can add/remove methods as you wish. | `DefaultMethods` | +| ServerHeader | `string` | Enables the `Server` HTTP header with the given value. | `""` | +| StreamRequestBody | `bool` | StreamRequestBody enables request body streaming, and calls the handler sooner when given body is larger then the current limit. | `false` | +| StrictRouting | `bool` | When enabled, the router treats `/foo` and `/foo/` as different. Otherwise, the router treats `/foo` and `/foo/` as the same. | `false` | +| TrustedProxies | `[]string` | Contains the list of trusted proxy IP's. Look at `EnableTrustedProxyCheck` doc.

It can take IP or IP range addresses. If it gets IP range, it iterates all possible addresses. | `[]string*__*` | +| UnescapePath | `bool` | Converts all encoded characters in the route back before setting the path for the context, so that the routing can also work with URL encoded special characters | `false` | +| Views | `Views` | Views is the interface that wraps the Render function. See our **Template Middleware** for supported engines. | `nil` | +| ViewsLayout | `string` | Views Layout is the global layout for all template render until override on Render function. See our **Template Middleware** for supported engines. | `""` | +| WriteBufferSize | `int` | Per-connection buffer size for responses' writing. | `4096` | +| WriteTimeout | `time.Duration` | The maximum duration before timing out writes of the response. The default timeout is unlimited. | `nil` | +| XMLEncoder | `utils.XMLMarshal` | Allowing for flexibility in using another XML library for encoding. | `xml.Marshal` | + +## NewError + +NewError creates a new HTTPError instance with an optional message. + +```go title="Signature" +func NewError(code int, message ...string) *Error +``` + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + return fiber.NewError(782, "Custom error message") +}) +``` + +## IsChild + +IsChild determines if the current process is a result of Prefork. + +```go title="Signature" +func IsChild() bool +``` + +```go title="Example" +// Prefork will spawn child processes +app := fiber.New(fiber.Config{ + Prefork: true, +}) + +if !fiber.IsChild() { + fmt.Println("I'm the parent process") +} else { + fmt.Println("I'm a child process") +} + +// ... +``` diff --git a/docs/api/middleware/_category_.json b/docs/api/middleware/_category_.json new file mode 100644 index 0000000000..133ac5147a --- /dev/null +++ b/docs/api/middleware/_category_.json @@ -0,0 +1,9 @@ +{ + "label": "🧬 Middleware", + "position": 7, + "collapsed": true, + "link": { + "type": "generated-index", + "description": "Middleware is a function chained in the HTTP request cycle with access to the Context which it uses to perform a specific action, for example, logging every request or enabling CORS." + } +} \ No newline at end of file diff --git a/docs/api/middleware/basicauth.md b/docs/api/middleware/basicauth.md new file mode 100644 index 0000000000..268f5b246e --- /dev/null +++ b/docs/api/middleware/basicauth.md @@ -0,0 +1,121 @@ +--- +id: basicauth +title: BasicAuth +--- + +Basic Authentication middleware for [Fiber](https://github.com/gofiber/fiber) that provides an HTTP basic authentication. It calls the next handler for valid credentials and [401 Unauthorized](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) or a custom response for missing or invalid credentials. + +## Signatures + +```go +func New(config Config) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/basicauth" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Provide a minimal config +app.Use(basicauth.New(basicauth.Config{ + Users: map[string]string{ + "john": "doe", + "admin": "123456", + }, +})) + +// Or extend your config for customization +app.Use(basicauth.New(basicauth.Config{ + Users: map[string]string{ + "john": "doe", + "admin": "123456", + }, + Realm: "Forbidden", + Authorizer: func(user, pass string) bool { + if user == "john" && pass == "doe" { + return true + } + if user == "admin" && pass == "123456" { + return true + } + return false + }, + Unauthorized: func(c *fiber.Ctx) error { + return c.SendFile("./unauthorized.html") + }, + ContextUsername: "_user", + ContextPassword: "_pass", +})) +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Users defines the allowed credentials + // + // Required. Default: map[string]string{} + Users map[string]string + + // Realm is a string to define realm attribute of BasicAuth. + // the realm identifies the system to authenticate against + // and can be used by clients to save credentials + // + // Optional. Default: "Restricted". + Realm string + + // Authorizer defines a function you can pass + // to check the credentials however you want. + // It will be called with a username and password + // and is expected to return true or false to indicate + // that the credentials were approved or not. + // + // Optional. Default: nil. + Authorizer func(string, string) bool + + // Unauthorized defines the response body for unauthorized responses. + // By default it will return with a 401 Unauthorized and the correct WWW-Auth header + // + // Optional. Default: nil + Unauthorized fiber.Handler + + // ContextUser is the key to store the username in Locals + // + // Optional. Default: "username" + ContextUsername string + + // ContextPass is the key to store the password in Locals + // + // Optional. Default: "password" + ContextPassword string +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + Next: nil, + Users: map[string]string{}, + Realm: "Restricted", + Authorizer: nil, + Unauthorized: nil, + ContextUsername: "username", + ContextPassword: "password", +} +``` diff --git a/docs/api/middleware/cache.md b/docs/api/middleware/cache.md new file mode 100644 index 0000000000..c4d5375982 --- /dev/null +++ b/docs/api/middleware/cache.md @@ -0,0 +1,138 @@ +--- +id: cache +title: Cache +--- + +Cache middleware for [Fiber](https://github.com/gofiber/fiber) designed to intercept responses and cache them. This middleware will cache the `Body`, `Content-Type` and `StatusCode` using the `c.Path()` as unique identifier. Special thanks to [@codemicro](https://github.com/codemicro/fiber-cache) for creating this middleware for Fiber core! + +Request Directives
+`Cache-Control: no-cache` will return the up-to-date response but still caches it. You will always get a `miss` cache status.
+`Cache-Control: no-store` will refrain from caching. You will always get the up-to-date response. + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cache" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Initialize default config +app.Use(cache.New()) + +// Or extend your config for customization +app.Use(cache.New(cache.Config{ + Next: func(c *fiber.Ctx) bool { + return c.Query("refresh") == "true" + }, + Expiration: 30 * time.Minute, + CacheControl: true, +})) +``` + +Or you can custom key and expire time like this: + +```go +app.Use(New(Config{ + ExpirationGenerator: func(c *fiber.Ctx, cfg *Config) time.Duration { + newCacheTime, _ := strconv.Atoi(c.GetRespHeader("Cache-Time", "600")) + return time.Second * time.Duration(newCacheTime) + }, + KeyGenerator: func(c *fiber.Ctx) string { + return c.Path() + } +})) + +app.Get("/", func(c *fiber.Ctx) error { + c.Response().Header.Add("Cache-Time", "6000") + return c.SendString("hi") +}) +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Expiration is the time that an cached response will live + // + // Optional. Default: 1 * time.Minute + Expiration time.Duration + + // CacheControl enables client side caching if set to true + // + // Optional. Default: false + CacheControl bool + + // Key allows you to generate custom keys, by default c.Path() is used + // + // Default: func(c *fiber.Ctx) string { + // return utils.CopyString(c.Path()) + // } + KeyGenerator func(*fiber.Ctx) string + + // allows you to generate custom Expiration Key By Key, default is Expiration (Optional) + // + // Default: nil + ExpirationGenerator func(*fiber.Ctx, *Config) time.Duration + + // Store is used to store the state of the middleware + // + // Default: an in memory store for this process only + Storage fiber.Storage + + // allows you to store additional headers generated by next middlewares & handler + // + // Default: false + StoreResponseHeaders bool + + // Max number of bytes of response bodies simultaneously stored in cache. When limit is reached, + // entries with the nearest expiration are deleted to make room for new. + // 0 means no limit + // + // Default: 0 + MaxBytes uint + + // You can specify HTTP methods to cache. + // The middleware just caches the routes of its methods in this slice. + // + // Default: []string{fiber.MethodGet, fiber.MethodHead} + Methods []string +} +``` + +## Default Config + +```go +// ConfigDefault is the default config +var ConfigDefault = Config{ + Next: nil, + Expiration: 1 * time.Minute, + CacheControl: false, + KeyGenerator: func(c *fiber.Ctx) string { + return utils.CopyString(c.Path()) + }, + ExpirationGenerator: nil, + StoreResponseHeaders: false, + Storage: nil, + MaxBytes: 0, + Methods: []string{fiber.MethodGet, fiber.MethodHead}, +} +``` diff --git a/docs/api/middleware/compress.md b/docs/api/middleware/compress.md new file mode 100644 index 0000000000..1ea51f5751 --- /dev/null +++ b/docs/api/middleware/compress.md @@ -0,0 +1,85 @@ +--- +id: compress +title: Compress +--- + +Compression middleware for [Fiber](https://github.com/gofiber/fiber) that will compress the response using `gzip`, `deflate` and `brotli` compression depending on the [Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header. + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/compress" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Default middleware config +app.Use(compress.New()) + +// Provide a custom compression level +app.Use(compress.New(compress.Config{ + Level: compress.LevelBestSpeed, // 1 +})) + +// Skip middleware for specific routes +app.Use(compress.New(compress.Config{ + Next: func(c *fiber.Ctx) bool { + return c.Path() == "/dont_compress" + }, + Level: compress.LevelBestSpeed, // 1 +})) +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // CompressLevel determines the compression algoritm + // + // Optional. Default: LevelDefault + // LevelDisabled: -1 + // LevelDefault: 0 + // LevelBestSpeed: 1 + // LevelBestCompression: 2 + Level int +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + Next: nil, + Level: LevelDefault, +} +``` + +## Constants + +```go +// Compression levels +const ( + LevelDisabled = -1 + LevelDefault = 0 + LevelBestSpeed = 1 + LevelBestCompression = 2 +) +``` diff --git a/docs/api/middleware/cors.md b/docs/api/middleware/cors.md new file mode 100644 index 0000000000..38783f7ca9 --- /dev/null +++ b/docs/api/middleware/cors.md @@ -0,0 +1,99 @@ +--- +id: cors +title: CORS +--- + +CORS middleware for [Fiber](https://github.com/gofiber/fiber) that can be used to enable [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) with various options. + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cors" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Default config +app.Use(cors.New()) + +// Or extend your config for customization +app.Use(cors.New(cors.Config{ + AllowOrigins: "https://gofiber.io, https://gofiber.net", + AllowHeaders: "Origin, Content-Type, Accept", +})) +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // AllowOrigin defines a list of origins that may access the resource. + // + // Optional. Default value "*" + AllowOrigins string + + // AllowMethods defines a list of methods allowed when accessing the resource. + // This is used in response to a preflight request. + // + // Optional. Default value "GET,POST,HEAD,PUT,DELETE,PATCH" + AllowMethods string + + // AllowHeaders defines a list of request headers that can be used when + // making the actual request. This is in response to a preflight request. + // + // Optional. Default value "". + AllowHeaders string + + // AllowCredentials indicates whether or not the response to the request + // can be exposed when the credentials flag is true. When used as part of + // a response to a preflight request, this indicates whether or not the + // actual request can be made using credentials. + // + // Optional. Default value false. + AllowCredentials bool + + // ExposeHeaders defines a whitelist headers that clients are allowed to + // access. + // + // Optional. Default value "". + ExposeHeaders string + + // MaxAge indicates how long (in seconds) the results of a preflight request + // can be cached. + // + // Optional. Default value 0. + MaxAge int +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + Next: nil, + AllowOrigins: "*", + AllowMethods: "GET,POST,HEAD,PUT,DELETE,PATCH", + AllowHeaders: "", + AllowCredentials: false, + ExposeHeaders: "", + MaxAge: 0, +} +``` diff --git a/docs/api/middleware/csrf.md b/docs/api/middleware/csrf.md new file mode 100644 index 0000000000..0ccce01ed1 --- /dev/null +++ b/docs/api/middleware/csrf.md @@ -0,0 +1,136 @@ +--- +id: csrf +title: CSRF +--- + +CSRF middleware for [Fiber](https://github.com/gofiber/fiber) that provides [Cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) protection by passing a csrf token via cookies. This cookie value will be used to compare against the client csrf token on requests, other than those defined as "safe" by RFC7231 \(GET, HEAD, OPTIONS, or TRACE\). When the csrf token is invalid, this middleware will return the `fiber.ErrForbidden` error. When no `_csrf` cookie is set, or the token has expired, a new token will be generated and `_csrf` cookie set. + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/csrf" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Initialize default config +app.Use(csrf.New()) + +// Or extend your config for customization +app.Use(csrf.New(csrf.Config{ + KeyLookup: "header:X-Csrf-Token", + CookieName: "csrf_", + CookieSameSite: "Strict", + Expiration: 1 * time.Hour, + KeyGenerator: utils.UUID, + Extractor: func(c *fiber.Ctx) (string, error) { ... }, +})) +``` + +Note: KeyLookup will be ignored if Extractor is explicitly set. + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // KeyLookup is a string in the form of ":" that is used + // to create an Extractor that extracts the token from the request. + // Possible values: + // - "header:" + // - "query:" + // - "param:" + // - "form:" + // - "cookie:" + // + // Ignored if an Extractor is explicitly set. + // + // Optional. Default: "header:X-CSRF-Token" + KeyLookup string + + // Name of the session cookie. This cookie will store session key. + // Optional. Default value "_csrf". + CookieName string + + // Domain of the CSRF cookie. + // Optional. Default value "". + CookieDomain string + + // Path of the CSRF cookie. + // Optional. Default value "". + CookiePath string + + // Indicates if CSRF cookie is secure. + // Optional. Default value false. + CookieSecure bool + + // Indicates if CSRF cookie is HTTP only. + // Optional. Default value false. + CookieHTTPOnly bool + + // Indicates if CSRF cookie is requested by SameSite. + // Optional. Default value "Lax". + CookieSameSite string + + // Decides whether cookie should last for only the browser sesison. + // Ignores Expiration if set to true + CookieSessionOnly bool + + // Expiration is the duration before csrf token will expire + // + // Optional. Default: 1 * time.Hour + Expiration time.Duration + + // Store is used to store the state of the middleware + // + // Optional. Default: memory.New() + Storage fiber.Storage + + // Context key to store generated CSRF token into context. + // If left empty, token will not be stored in context. + // + // Optional. Default: "" + ContextKey string + + // KeyGenerator creates a new CSRF token + // + // Optional. Default: utils.UUID + KeyGenerator func() string + + // Extractor returns the csrf token + // + // If set this will be used in place of an Extractor based on KeyLookup. + // + // Optional. Default will create an Extractor based on KeyLookup. + Extractor func(c *fiber.Ctx) (string, error) +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + KeyLookup: "header:X-Csrf-Token", + CookieName: "csrf_", + CookieSameSite: "Lax", + Expiration: 1 * time.Hour, + KeyGenerator: utils.UUID, +} +``` diff --git a/docs/api/middleware/earlydata.md b/docs/api/middleware/earlydata.md new file mode 100644 index 0000000000..46da54b573 --- /dev/null +++ b/docs/api/middleware/earlydata.md @@ -0,0 +1,93 @@ +--- +id: earlydata +title: EarlyData +--- + +The Early Data middleware for [Fiber](https://github.com/gofiber/fiber) adds support for TLS 1.3's early data ("0-RTT") feature. +Citing [RFC 8446](https://datatracker.ietf.org/doc/html/rfc8446#section-2-3), when a client and server share a PSK, TLS 1.3 allows clients to send data on the first flight ("early data") to speed up the request, effectively reducing the regular 1-RTT request to a 0-RTT request. + +Make sure to enable fiber's `EnableTrustedProxyCheck` config option before using this middleware in order to not trust bogus HTTP request headers of the client. + +Also be aware that enabling support for early data in your reverse proxy (e.g. nginx, as done with a simple `ssl_early_data on;`) makes requests replayable. Refer to the following documents before continuing: + +- https://datatracker.ietf.org/doc/html/rfc8446#section-8 +- https://blog.trailofbits.com/2019/03/25/what-application-developers-need-to-know-about-tls-early-data-0rtt/ + +By default, this middleware allows early data requests on safe HTTP request methods only and rejects the request otherwise, i.e. aborts the request before executing your handler. This behavior can be controlled by the `AllowEarlyData` config option. +Safe HTTP methods — `GET`, `HEAD`, `OPTIONS` and `TRACE` — should not modify a state on the server. + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +First import the middleware from Fiber, + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/earlydata" +) +``` + +Then create a Fiber app with `app := fiber.New()`. + +### Default Config + +```go +app.Use(earlydata.New()) +``` + +### Custom Config + +```go +app.Use(earlydata.New(earlydata.Config{ + Error: fiber.ErrTooEarly, + // ... +})) +``` + +### Config + +```go +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // IsEarlyData returns whether the request is an early-data request. + // + // Optional. Default: a function which checks if the "Early-Data" request header equals "1". + IsEarlyData func(c *fiber.Ctx) bool + + // AllowEarlyData returns whether the early-data request should be allowed or rejected. + // + // Optional. Default: a function which rejects the request on unsafe and allows the request on safe HTTP request methods. + AllowEarlyData func(c *fiber.Ctx) bool + + // Error is returned in case an early-data request is rejected. + // + // Optional. Default: fiber.ErrTooEarly. + Error error +} +``` + +### Default Config + +```go +var ConfigDefault = Config{ + IsEarlyData: func(c *fiber.Ctx) bool { + return c.Get("Early-Data") == "1" + }, + + AllowEarlyData: func(c *fiber.Ctx) bool { + return fiber.IsMethodSafe(c.Method()) + }, + + Error: fiber.ErrTooEarly, +} +``` diff --git a/docs/api/middleware/encryptcookie.md b/docs/api/middleware/encryptcookie.md new file mode 100644 index 0000000000..9a8f3141f6 --- /dev/null +++ b/docs/api/middleware/encryptcookie.md @@ -0,0 +1,108 @@ +--- +id: encryptcookie +title: Encrypt Cookie +--- + +Encrypt middleware for [Fiber](https://github.com/gofiber/fiber) which encrypts cookie values. Note: this middleware does not encrypt cookie names. + +## Signatures + +```go +// Intitializes the middleware +func New(config ...Config) fiber.Handler + +// Returns a random 32 character long string +func GenerateKey() string +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/encryptcookie" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Default middleware config +app.Use(encryptcookie.New(encryptcookie.Config{ + Key: "secret-thirty-2-character-string", +})) + +// Get / reading out the encrypted cookie +app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("value=" + c.Cookies("test")) +}) + +// Post / create the encrypted cookie +app.Post("/", func(c *fiber.Ctx) error { + c.Cookie(&fiber.Cookie{ + Name: "test", + Value: "SomeThing", + }) + return nil +}) +``` + +## Config + +```go +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Array of cookie keys that should not be encrypted. + // + // Optional. Default: ["csrf_"] + Except []string + + // Base64 encoded unique key to encode & decode cookies. + // + // Required. Key length should be 32 characters. + // You may use `encryptcookie.GenerateKey()` to generate a new key. + Key string + + // Custom function to encrypt cookies. + // + // Optional. Default: EncryptCookie + Encryptor func(decryptedString, key string) (string, error) + + // Custom function to decrypt cookies. + // + // Optional. Default: DecryptCookie + Decryptor func(encryptedString, key string) (string, error) +} +``` + +## Default Config + +```go +// `Key` must be a 32 character string. It's used to encrpyt the values, so make sure it is random and keep it secret. +// You can call `encryptcookie.GenerateKey()` to create a random key for you. +// Make sure not to set `Key` to `encryptcookie.GenerateKey()` because that will create a new key every run. +app.Use(encryptcookie.New(encryptcookie.Config{ + Key: "secret-thirty-2-character-string", +})) +``` + +## Usage of CSRF and Encryptcookie Middlewares with Custom Cookie Names +Normally, encryptcookie middleware skips `csrf_` cookies. However, it won't work when you use custom cookie names for CSRF. You should update `Except` config to avoid this problem. For example: + +```go +app.Use(encryptcookie.New(encryptcookie.Config{ + Key: "secret-thirty-2-character-string", + Except: []string{"csrf_1"}, // exclude CSRF cookie +})) +app.Use(csrf.New(csrf.Config{ + KeyLookup: "form:test", + CookieName: "csrf_1", + CookieHTTPOnly: true, +})) +``` diff --git a/docs/api/middleware/envvar.md b/docs/api/middleware/envvar.md new file mode 100644 index 0000000000..8cb8dd0f48 --- /dev/null +++ b/docs/api/middleware/envvar.md @@ -0,0 +1,74 @@ +--- +id: envvar +title: EnvVar +--- + +EnvVar middleware for [Fiber](https://github.com/gofiber/fiber) that can be used to expose environment variables with various options. + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +First import the middleware from Fiber, + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/envvar" +) +``` + +Then create a Fiber app with `app := fiber.New()`. + +**Note**: You need to provide a path to use envvar middleware. + +### Default Config + +```go +app.Use("/expose/envvars", envvar.New()) +``` + +### Custom Config + +```go +app.Use("/expose/envvars", envvar.New(envvar.Config{ + ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, + ExcludeVars: map[string]string{"excludeKey": ""}} +})) +``` + +### Response + +Http response contract: +``` +{ + "vars": { + "someEnvVariable": "someValue", + "anotherEnvVariable": "anotherValue", + } +} + +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // ExportVars specifies the environment variables that should export + ExportVars map[string]string + // ExcludeVars specifies the environment variables that should not export + ExcludeVars map[string]string +} + +``` + +## Default Config + +```go +Config{} +``` diff --git a/docs/api/middleware/etag.md b/docs/api/middleware/etag.md new file mode 100644 index 0000000000..ee2fa4ee28 --- /dev/null +++ b/docs/api/middleware/etag.md @@ -0,0 +1,80 @@ +--- +id: etag +title: ETag +--- + +ETag middleware for [Fiber](https://github.com/gofiber/fiber) that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/etag" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +### Default Config + +```go +app.Use(etag.New()) + +// Get / receives Etag: "13-1831710635" in response header +app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") +}) +``` + +### Custom Config + +```go +app.Use(etag.New(etag.Config{ + Weak: true, +})) + +// Get / receives Etag: "W/"13-1831710635" in response header +app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") +}) +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Weak indicates that a weak validator is used. Weak etags are easy + // to generate, but are far less useful for comparisons. Strong + // validators are ideal for comparisons but can be very difficult + // to generate efficiently. Weak ETag values of two representations + // of the same resources might be semantically equivalent, but not + // byte-for-byte identical. This means weak etags prevent caching + // when byte range requests are used, but strong etags mean range + // requests can still be cached. + Weak bool +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + Next: nil, + Weak: false, +} +``` diff --git a/docs/api/middleware/expvar.md b/docs/api/middleware/expvar.md new file mode 100644 index 0000000000..a4ecb3c421 --- /dev/null +++ b/docs/api/middleware/expvar.md @@ -0,0 +1,84 @@ +--- +id: expvar +title: ExpVar +--- + +Expvar middleware for [Fiber](https://github.com/gofiber/fiber) that serves via its HTTP server runtime exposed variants in the JSON format. The package is typically only imported for the side effect of registering its HTTP handlers. The handled path is `/debug/vars`. + +## Signatures + +```go +func New() fiber.Handler +``` + +## Example + +Import the expvar package that is part of the Fiber web framework + +```go +package main + +import ( + "expvar" + "fmt" + + "github.com/gofiber/fiber/v2" + expvarmw "github.com/gofiber/fiber/v2/middleware/expvar" +) + +var count = expvar.NewInt("count") + +func main() { + app := fiber.New() + app.Use(expvarmw.New()) + app.Get("/", func(c *fiber.Ctx) error { + count.Add(1) + + return c.SendString(fmt.Sprintf("hello expvar count %d", count.Value())) + }) + + fmt.Println(app.Listen(":3000")) +} +``` + +Visit path `/debug/vars` to see all vars and use query `r=key` to filter exposed variables. + +```bash +curl 127.0.0.1:3000 +hello expvar count 1 + +curl 127.0.0.1:3000/debug/vars +{ + "cmdline": ["xxx"], + "count": 1, + "expvarHandlerCalls": 33, + "expvarRegexpErrors": 0, + "memstats": {...} +} + +curl 127.0.0.1:3000/debug/vars?r=c +{ + "cmdline": ["xxx"], + "count": 1 +} +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + Next: nil, +} +``` diff --git a/docs/api/middleware/favicon.md b/docs/api/middleware/favicon.md new file mode 100644 index 0000000000..20fe6682b3 --- /dev/null +++ b/docs/api/middleware/favicon.md @@ -0,0 +1,85 @@ +--- +id: favicon +title: Favicon +--- + +Favicon middleware for [Fiber](https://github.com/gofiber/fiber) that ignores favicon requests or caches a provided icon in memory to improve performance by skipping disk access. User agents request favicon.ico frequently and indiscriminately, so you may wish to exclude these requests from your logs by using this middleware before your logger middleware. + +:::note +This middleware is exclusively for serving the default, implicit favicon, which is GET /favicon.ico or [custom favicon URL](#config). +::: + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/favicon" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Provide a minimal config +app.Use(favicon.New()) + +// Or extend your config for customization +app.Use(favicon.New(favicon.Config{ + File: "./favicon.ico", + URL: "/favicon.ico", +})) +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // File holds the path to an actual favicon that will be cached + // + // Optional. Default: "" + File string + + // URL for favicon handler + // + // Optional. Default: "/favicon.ico" + URL string + + // FileSystem is an optional alternate filesystem to search for the favicon in. + // An example of this could be an embedded or network filesystem + // + // Optional. Default: nil + FileSystem http.FileSystem + + // CacheControl defines how the Cache-Control header in the response should be set + // + // Optional. Default: "public, max-age=31536000" + CacheControl string +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + Next: nil, + File: "", + URL: "/favicon.ico", + FileSystem: nil, + CacheControl: "public, max-age=31536000", +} +``` diff --git a/docs/api/middleware/filesystem.md b/docs/api/middleware/filesystem.md new file mode 100644 index 0000000000..71bdc92972 --- /dev/null +++ b/docs/api/middleware/filesystem.md @@ -0,0 +1,237 @@ +--- +id: filesystem +title: FileSystem +--- + +Filesystem middleware for [Fiber](https://github.com/gofiber/fiber) that enables you to serve files from a directory. + +:::caution +**`:params` & `:optionals?` within the prefix path are not supported!** + +**To handle paths with spaces (or other url encoded values) make sure to set `fiber.Config{ UnescapePath: true}`** +::: + +### Table of Contents + +* [Signatures](filesystem.md#signatures) +* [Examples](filesystem.md#examples) +* [Config](filesystem.md#config) +* [Default Config](filesystem.md#default-config) + +### Signatures + +```go +func New(config Config) fiber.Handler +``` + +### Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/filesystem" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Provide a minimal config +app.Use(filesystem.New(filesystem.Config{ + Root: http.Dir("./assets") +})) + +// Or extend your config for customization +app.Use(filesystem.New(filesystem.Config{ + Root: http.Dir("./assets"), + Browse: true, + Index: "index.html", + NotFoundFile: "404.html", + MaxAge: 3600, +})) +``` + +## pkger + +[https://github.com/markbates/pkger](https://github.com/markbates/pkger) + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/filesystem" + + "github.com/markbates/pkger" +) + +func main() { + app := fiber.New() + + app.Use("/assets", filesystem.New(filesystem.Config{ + Root: pkger.Dir("/assets"), + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +## packr + +[https://github.com/gobuffalo/packr](https://github.com/gobuffalo/packr) + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/filesystem" + + "github.com/gobuffalo/packr/v2" +) + +func main() { + app := fiber.New() + + app.Use("/assets", filesystem.New(filesystem.Config{ + Root: packr.New("Assets Box", "/assets"), + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +## go.rice + +[https://github.com/GeertJohan/go.rice](https://github.com/GeertJohan/go.rice) + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/filesystem" + + "github.com/GeertJohan/go.rice" +) + +func main() { + app := fiber.New() + + app.Use("/assets", filesystem.New(filesystem.Config{ + Root: rice.MustFindBox("assets").HTTPBox(), + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +## fileb0x + +[https://github.com/UnnoTed/fileb0x](https://github.com/UnnoTed/fileb0x) + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/filesystem" + + "/myEmbeddedFiles" +) + +func main() { + app := fiber.New() + + app.Use("/assets", filesystem.New(filesystem.Config{ + Root: myEmbeddedFiles.HTTP, + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +## statik + +[https://github.com/rakyll/statik](https://github.com/rakyll/statik) + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/filesystem" + + // Use blank to invoke init function and register data to statik + _ "/statik" + "github.com/rakyll/statik/fs" +) + +func main() { + statikFS, err := fs.New() + if err != nil { + panic(err) + } + + app := fiber.New() + + app.Use("/", filesystem.New(filesystem.Config{ + Root: statikFS, + })) + + log.Fatal(app.Listen(":3000")) +} +``` + +### Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Root is a FileSystem that provides access + // to a collection of files and directories. + // + // Required. Default: nil + Root http.FileSystem `json:"-"` + + // Enable directory browsing. + // + // Optional. Default: false + Browse bool `json:"browse"` + + // Index file for serving a directory. + // + // Optional. Default: "index.html" + Index string `json:"index"` + + // The value for the Cache-Control HTTP-header + // that is set on the file response. MaxAge is defined in seconds. + // + // Optional. Default value 0. + MaxAge int `json:"max_age"` + + // File to return if path is not found. Useful for SPA's. + // + // Optional. Default: "" + NotFoundFile string `json:"not_found_file"` +} +``` + +### Default Config + +```go +var ConfigDefault = Config{ + Next: nil, + Root: nil, + Browse: false, + Index: "/index.html", + MaxAge: 0, +} +``` diff --git a/docs/api/middleware/idempotency.md b/docs/api/middleware/idempotency.md new file mode 100644 index 0000000000..72eee4b4bb --- /dev/null +++ b/docs/api/middleware/idempotency.md @@ -0,0 +1,110 @@ +--- +id: idempotency +title: Idempotency +--- + +Idempotency middleware for [Fiber](https://github.com/gofiber/fiber) allows for fault-tolerant APIs where duplicate requests — for example due to networking issues on the client-side — do not erroneously cause the same action performed multiple times on the server-side. + +Refer to https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-02 for a better understanding. + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +First import the middleware from Fiber, + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/idempotency" +) +``` + +Then create a Fiber app with `app := fiber.New()`. + +### Default Config + +```go +app.Use(idempotency.New()) +``` + +### Custom Config + +```go +app.Use(idempotency.New(idempotency.Config{ + Lifetime: 42 * time.Minute, + // ... +})) +``` + +### Config + +```go +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: a function which skips the middleware on safe HTTP request method. + Next func(c *fiber.Ctx) bool + + // Lifetime is the maximum lifetime of an idempotency key. + // + // Optional. Default: 30 * time.Minute + Lifetime time.Duration + + // KeyHeader is the name of the header that contains the idempotency key. + // + // Optional. Default: X-Idempotency-Key + KeyHeader string + // KeyHeaderValidate defines a function to validate the syntax of the idempotency header. + // + // Optional. Default: a function which ensures the header is 36 characters long (the size of an UUID). + KeyHeaderValidate func(string) error + + // KeepResponseHeaders is a list of headers that should be kept from the original response. + // + // Optional. Default: nil (to keep all headers) + KeepResponseHeaders []string + + // Lock locks an idempotency key. + // + // Optional. Default: an in-memory locker for this process only. + Lock Locker + + // Storage stores response data by idempotency key. + // + // Optional. Default: an in-memory storage for this process only. + Storage fiber.Storage +} +``` + +### Default Config + +```go +var ConfigDefault = Config{ + Next: func(c *fiber.Ctx) bool { + // Skip middleware if the request was done using a safe HTTP method + return fiber.IsMethodSafe(c.Method()) + }, + + Lifetime: 30 * time.Minute, + + KeyHeader: "X-Idempotency-Key", + KeyHeaderValidate: func(k string) error { + if l, wl := len(k), 36; l != wl { // UUID length is 36 chars + return fmt.Errorf("%w: invalid length: %d != %d", ErrInvalidIdempotencyKey, l, wl) + } + + return nil + }, + + KeepResponseHeaders: nil, + + Lock: nil, // Set in configDefault so we don't allocate data here. + + Storage: nil, // Set in configDefault so we don't allocate data here. +} +``` diff --git a/docs/api/middleware/limiter.md b/docs/api/middleware/limiter.md new file mode 100644 index 0000000000..8680f57014 --- /dev/null +++ b/docs/api/middleware/limiter.md @@ -0,0 +1,146 @@ +--- +id: limiter +title: Limiter +--- + +Limiter middleware for [Fiber](https://github.com/gofiber/fiber) used to limit repeated requests to public APIs and/or endpoints such as password reset etc. Also useful for API clients, web crawling, or other tasks that need to be throttled. + +:::note +This module does not share state with other processes/servers by default. +::: + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/limiter" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Default middleware config +app.Use(limiter.New()) + +// Or extend your config for customization +app.Use(limiter.New(limiter.Config{ + Next: func(c *fiber.Ctx) bool { + return c.IP() == "127.0.0.1" + }, + Max: 20, + Expiration: 30 * time.Second, + KeyGenerator: func(c *fiber.Ctx) string { + return c.Get("x-forwarded-for") + }, + LimitReached: func(c *fiber.Ctx) error { + return c.SendFile("./toofast.html") + }, + Storage: myCustomStorage{} +})) +``` + +## Sliding window + +Instead of using the standard fixed window algorithm, you can enable the [sliding window](https://en.wikipedia.org/wiki/Sliding_window_protocol) algorithm. + +A example of such configuration is: + +```go +app.Use(limiter.New(limiter.Config{ + Max: 20, + Expiration: 30 * time.Second, + LimiterMiddleware: limiter.SlidingWindow{} +})) +``` + +This means that every window will take into account the previous window(if there was any). The given formula for the rate is: +``` +weightOfPreviousWindpw = previous window's amount request * (whenNewWindow / Expiration) +rate = weightOfPreviousWindpw + current window's amount request. +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Max number of recent connections during `Expiration` seconds before sending a 429 response + // + // Default: 5 + Max int + + // KeyGenerator allows you to generate custom keys, by default c.IP() is used + // + // Default: func(c *fiber.Ctx) string { + // return c.IP() + // } + KeyGenerator func(*fiber.Ctx) string + + // Expiration is the time on how long to keep records of requests in memory + // + // Default: 1 * time.Minute + Expiration time.Duration + + // LimitReached is called when a request hits the limit + // + // Default: func(c *fiber.Ctx) error { + // return c.SendStatus(fiber.StatusTooManyRequests) + // } + LimitReached fiber.Handler + + // When set to true, requests with StatusCode >= 400 won't be counted. + // + // Default: false + SkipFailedRequests bool + + // When set to true, requests with StatusCode < 400 won't be counted. + // + // Default: false + SkipSuccessfulRequests bool + + // Store is used to store the state of the middleware + // + // Default: an in memory store for this process only + Storage fiber.Storage + + // LimiterMiddleware is the struct that implements limiter middleware. + // + // Default: a new Fixed Window Rate Limiter + LimiterMiddleware LimiterHandler +} +``` + +A custom store can be used if it implements the `Storage` interface - more details and an example can be found in `store.go`. + +## Default Config + +```go +var ConfigDefault = Config{ + Max: 5, + Expiration: 1 * time.Minute, + KeyGenerator: func(c *fiber.Ctx) string { + return c.IP() + }, + LimitReached: func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusTooManyRequests) + }, + SkipFailedRequests: false, + SkipSuccessfulRequests: false, + LimiterMiddleware: FixedWindow{}, +} +``` diff --git a/docs/api/middleware/logger.md b/docs/api/middleware/logger.md new file mode 100644 index 0000000000..1da3342431 --- /dev/null +++ b/docs/api/middleware/logger.md @@ -0,0 +1,193 @@ +--- +id: logger +title: Logger +--- + +Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP request/response details. + +## Signatures +```go +func New(config ...Config) fiber.Handler +``` +## Examples +First ensure the appropriate packages are imported +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/logger" +) +``` + +:::tip + +The order of registration plays a role. Only all routes that are registered after this one will be logged. +The middleware should therefore be one of the first to be registered. + +::: + +### Default Config +```go +// Default middleware config +app.Use(logger.New()) +``` +### Logging remote IP and Port +```go +app.Use(logger.New(logger.Config{ + Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", +})) +``` + +### Logging Request ID +```go +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", +})) +``` + +### Changing TimeZone & TimeFormat + +```go +app.Use(logger.New(logger.Config{ + Format: "${pid} ${status} - ${method} ${path}\n", + TimeFormat: "02-Jan-2006", + TimeZone: "America/New_York", +})) +``` + +### Custom File Writer +```go +file, err := os.OpenFile("./123.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) +if err != nil { + log.Fatalf("error opening file: %v", err) +} +defer file.Close() +app.Use(logger.New(logger.Config{ + Output: file, +})) +``` +### Add Custom Tags +```go +app.Use(logger.New(logger.Config{ + CustomTags: map[string]logger.LogFunc{ + "custom_tag": func(output logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) { + return output.WriteString("it is a custom tag") + }, + }, +})) +``` + +### Callback after log is written + +```go +app.Use(logger.New(logger.Config{ + TimeFormat: time.RFC3339Nano, + TimeZone: "Asia/Shanghai", + Done: func(c *fiber.Ctx, logString []byte) { + if c.Response().StatusCode() != fiber.StatusOK { + reporter.SendToSlack(logString) + } + }, +})) +``` + +## Config +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + // Done is a function that is called after the log string for a request is written to Output, + // and pass the log string as parameter. + // + // Optional. Default: nil + Done func(c *fiber.Ctx, logString []byte) + // tagFunctions defines the custom tag action + // + // Optional. Default: map[string]LogFunc + CustomTags map[string]LogFunc + // Format defines the logging tags + // + // Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n + Format string + // TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html + // + // Optional. Default: 15:04:05 + TimeFormat string + // TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc + // + // Optional. Default: "Local" + TimeZone string + // TimeInterval is the delay before the timestamp is updated + // + // Optional. Default: 500 * time.Millisecond + TimeInterval time.Duration + // Output is a writer where logs are written + // + // Default: os.Stdout + Output io.Writer +} +type LogFunc func(buf logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) +``` +## Default Config +```go +var ConfigDefault = Config{ + Next: nil, + Done: nil, + Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", + TimeFormat: "15:04:05", + TimeZone: "Local", + TimeInterval: 500 * time.Millisecond, + Output: os.Stdout, +} +``` + +## Constants +```go +// Logger variables +const ( + TagPid = "pid" + TagTime = "time" + TagReferer = "referer" + TagProtocol = "protocol" + TagPort = "port" + TagIP = "ip" + TagIPs = "ips" + TagHost = "host" + TagMethod = "method" + TagPath = "path" + TagURL = "url" + TagUA = "ua" + TagLatency = "latency" + TagStatus = "status" // response status + TagResBody = "resBody" // response body + TagReqHeaders = "reqHeaders" + TagQueryStringParams = "queryParams" // request query parameters + TagBody = "body" // request body + TagBytesSent = "bytesSent" + TagBytesReceived = "bytesReceived" + TagRoute = "route" + TagError = "error" + // DEPRECATED: Use TagReqHeader instead + TagHeader = "header:" // request header + TagReqHeader = "reqHeader:" // request header + TagRespHeader = "respHeader:" // response header + TagQuery = "query:" // request query + TagForm = "form:" // request form + TagCookie = "cookie:" // request cookie + TagLocals = "locals:" + // colors + TagBlack = "black" + TagRed = "red" + TagGreen = "green" + TagYellow = "yellow" + TagBlue = "blue" + TagMagenta = "magenta" + TagCyan = "cyan" + TagWhite = "white" + TagReset = "reset" +) +``` diff --git a/docs/api/middleware/monitor.md b/docs/api/middleware/monitor.md new file mode 100644 index 0000000000..66353ea49e --- /dev/null +++ b/docs/api/middleware/monitor.md @@ -0,0 +1,104 @@ +--- +id: monitor +title: Monitor +--- + +Monitor middleware for [Fiber](https://github.com/gofiber/fiber) that reports server metrics, inspired by [express-status-monitor](https://github.com/RafalWilinski/express-status-monitor) + +:::caution +Monitor is still in beta, API might change in the future! +::: + +![](https://i.imgur.com/nHAtBpJ.gif) + +### Signatures +```go +func New() fiber.Handler +``` + +### Examples +Import the middleware package and assign it to a route. +```go +package main + +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/monitor" +) + +func main() { + app := fiber.New() + + app.Get("/metrics", monitor.New(monitor.Config{Title: "MyService Metrics Page"})) + + log.Fatal(app.Listen(":3000")) +} +``` +You can also access the API endpoint with +`curl -X GET -H "Accept: application/json" http://localhost:3000/metrics` which returns: +```json +{"pid":{ "cpu":0.4568381746582226, "ram":20516864, "conns":3 }, + "os": { "cpu":8.759124087593099, "ram":3997155328, "conns":44, + "total_ram":8245489664, "load_avg":0.51 }} +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Metrics page title + // + // Optional. Default: "Fiber Monitor" + Title string + + // Refresh period + // + // Optional. Default: 3 seconds + Refresh time.Duration + + // Whether the service should expose only the monitoring API. + // + // Optional. Default: false + APIOnly bool + + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Custom HTML Code to Head Section(Before End) + // + // Optional. Default: empty + CustomHead string + + // FontURL for specify font resource path or URL . also you can use relative path + // + // Optional. Default: https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap + + FontURL string + // ChartJsURL for specify ChartJS library path or URL . also you can use relative path + // + // Optional. Default: https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js + + ChartJsURL string + +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + Title: "Fiber Monitor", + Refresh: 3 * time.Second, + APIOnly: false, + Next: nil, + CustomHead:"", + FontURL:"https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap", + ChartJsURL:"https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js" + +} +``` diff --git a/docs/api/middleware/pprof.md b/docs/api/middleware/pprof.md new file mode 100644 index 0000000000..fbcb47e6a0 --- /dev/null +++ b/docs/api/middleware/pprof.md @@ -0,0 +1,70 @@ +--- +id: pprof +title: Pprof +--- + +Pprof middleware for [Fiber](https://github.com/gofiber/fiber) that serves via its HTTP server runtime profiling data in the format expected by the pprof visualization tool. The package is typically only imported for the side effect of registering its HTTP handlers. The handled paths all begin with /debug/pprof/. + +* [Signatures](pprof.md#signatures) +* [Examples](pprof.md#examples) + +## Signatures + +```go +func New() fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/pprof" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Default middleware +app.Use(pprof.New()) +``` + +In systems where you have multiple ingress endpoints, it is common to add a URL prefix, like so: + +```go +// Default middleware +app.Use(pprof.New(pprof.Config{Prefix: "/endpoint-prefix"})) +``` + +This prefix will be added to the default path of "/debug/pprof/", for a resulting URL of: +"/endpoint-prefix/debug/pprof/". + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Prefix defines a URL prefix added before "/debug/pprof". + // Note that it should start with (but not end with) a slash. + // Example: "/federated-fiber" + // + // Optional. Default: "" + Prefix string +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + Next: nil, +} +``` diff --git a/docs/api/middleware/proxy.md b/docs/api/middleware/proxy.md new file mode 100644 index 0000000000..f1f159f0cc --- /dev/null +++ b/docs/api/middleware/proxy.md @@ -0,0 +1,150 @@ +--- +id: proxy +title: Proxy +--- + +Proxy middleware for [Fiber](https://github.com/gofiber/fiber) that allows you to proxy requests to multiple servers. + +## Signatures + +```go +// Balancer create a load balancer among multiple upstrem servers. +func Balancer(config Config) fiber.Handler +// Forward performs the given http request and fills the given http response. +func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler +// Do performs the given http request and fills the given http response. +func Do(c *fiber.Ctx, addr string, clients ...*fasthttp.Client) error +// DomainForward the given http request based on the given domain and fills the given http response +func DomainForward(hostname string, addr string, clients ...*fasthttp.Client) fiber.Handler +// BalancerForward performs the given http request based round robin balancer and fills the given http response +func BalancerForward(servers []string, clients ...*fasthttp.Client) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/proxy" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// if target https site uses a self-signed certificate, you should +// call WithTlsConfig before Do and Forward +proxy.WithTlsConfig(&tls.Config{ + InsecureSkipVerify: true, +}) +// if you need to use global self-custom client, you should use proxy.WithClient. +proxy.WithClient(&fasthttp.Client{ + NoDefaultUserAgentHeader: true, + DisablePathNormalizing: true, +}) + +// Forward to url +app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif")) + +// If you want to forward with a specific domain. You have to use proxy.DomainForward. +app.Get("/payments", proxy.DomainForward("docs.gofiber.io", "http://localhost:8000")) + +// Forward to url with local custom client +app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif", &fasthttp.Client{ + NoDefaultUserAgentHeader: true, + DisablePathNormalizing: true, +})) + +// Make request within handler +app.Get("/:id", func(c *fiber.Ctx) error { + url := "https://i.imgur.com/"+c.Params("id")+".gif" + if err := proxy.Do(c, url); err != nil { + return err + } + // Remove Server header from response + c.Response().Header.Del(fiber.HeaderServer) + return nil +}) + +// Minimal round robin balancer +app.Use(proxy.Balancer(proxy.Config{ + Servers: []string{ + "http://localhost:3001", + "http://localhost:3002", + "http://localhost:3003", + }, +})) + +// Or extend your balancer for customization +app.Use(proxy.Balancer(proxy.Config{ + Servers: []string{ + "http://localhost:3001", + "http://localhost:3002", + "http://localhost:3003", + }, + ModifyRequest: func(c *fiber.Ctx) error { + c.Request().Header.Add("X-Real-IP", c.IP()) + return nil + }, + ModifyResponse: func(c *fiber.Ctx) error { + c.Response().Header.Del(fiber.HeaderServer) + return nil + }, +})) + +// Or this way if the balancer is using https and the destination server is only using http. +app.Use(proxy.BalancerForward([]string{ + "http://localhost:3001", + "http://localhost:3002", + "http://localhost:3003", +})) +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Servers defines a list of :// HTTP servers, + // + // which are used in a round-robin manner. + // i.e.: "https://foobar.com, http://www.foobar.com" + // + // Required + Servers []string + + // ModifyRequest allows you to alter the request + // + // Optional. Default: nil + ModifyRequest fiber.Handler + + // ModifyResponse allows you to alter the response + // + // Optional. Default: nil + ModifyResponse fiber.Handler + + // tls config for the http client. + TlsConfig *tls.Config + + // Client is custom client when client config is complex. + // Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig + // will not be used if the client are set. + Client *fasthttp.LBClient +} +``` + +## Default Config + +```go +// ConfigDefault is the default config +var ConfigDefault = Config{ + Next: nil, +} +``` diff --git a/docs/api/middleware/recover.md b/docs/api/middleware/recover.md new file mode 100644 index 0000000000..89013ed3d8 --- /dev/null +++ b/docs/api/middleware/recover.md @@ -0,0 +1,67 @@ +--- +id: recover +title: Recover +--- + +Recover middleware for [Fiber](https://github.com/gofiber/fiber) that recovers from panics anywhere in the stack chain and handles the control to the centralized [ErrorHandler](https://docs.gofiber.io/error-handling). + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/recover" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Default middleware config +app.Use(recover.New()) + +// This panic will be catch by the middleware +app.Get("/", func(c *fiber.Ctx) error { + panic("I'm an error") +}) +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // EnableStackTrace enables handling stack trace + // + // Optional. Default: false + EnableStackTrace bool + + // StackTraceHandler defines a function to handle stack trace + // + // Optional. Default: defaultStackTraceHandler + StackTraceHandler func(c *fiber.Ctx, e interface{}) +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + Next: nil, + EnableStackTrace: false, + StackTraceHandler: defaultStackTraceHandler, +} +``` diff --git a/docs/api/middleware/requestid.md b/docs/api/middleware/requestid.md new file mode 100644 index 0000000000..9db86af7b1 --- /dev/null +++ b/docs/api/middleware/requestid.md @@ -0,0 +1,79 @@ +--- +id: requestid +title: RequestID +--- + +RequestID middleware for [Fiber](https://github.com/gofiber/fiber) that adds an indentifier to the response. + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/requestid" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: + +```go +// Default middleware config +app.Use(requestid.New()) + +// Or extend your config for customization +app.Use(requestid.New(requestid.Config{ + Header: "X-Custom-Header", + Generator: func() string { + return "static-id" + }, +})) +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Header is the header key where to get/set the unique request ID + // + // Optional. Default: "X-Request-ID" + Header string + + // Generator defines a function to generate the unique identifier. + // + // Optional. Default: utils.UUID + Generator func() string + + // ContextKey defines the key used when storing the request ID in + // the locals for a specific request. + // + // Optional. Default: requestid + ContextKey string +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + Next: nil, + Header: fiber.HeaderXRequestID, + Generator: func() string { + return utils.UUID() + }, + ContextKey: "requestid" +} +``` diff --git a/docs/api/middleware/session.md b/docs/api/middleware/session.md new file mode 100644 index 0000000000..c637f78219 --- /dev/null +++ b/docs/api/middleware/session.md @@ -0,0 +1,151 @@ +--- +id: session +title: Session +--- + +Session middleware for [Fiber](https://github.com/gofiber/fiber). + +:::note +This middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for this middleware saves data to memory, see the examples below for other databases. +::: + +## Signatures + +```go +func New(config ...Config) *Store +func (s *Store) RegisterType(i interface{}) +func (s *Store) Get(c *fiber.Ctx) (*Session, error) +func (s *Store) Reset() error + +func (s *Session) Get(key string) interface{} +func (s *Session) Set(key string, val interface{}) +func (s *Session) Delete(key string) +func (s *Session) Destroy() error +func (s *Session) Regenerate() error +func (s *Session) Save() error +func (s *Session) Fresh() bool +func (s *Session) ID() string +func (s *Session) Keys() []string +``` + +:::caution +Storing `interface{}` values are limited to built-ins Go types. +::: + +### Examples +Import the middleware package that is part of the Fiber web framework +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/session" +) +``` + +Then create a Fiber app with `app := fiber.New()`. + +### Default Configuration + +```go +// This stores all of your app's sessions +// Default middleware config +store := session.New() + +// This panic will be caught by the middleware +app.Get("/", func(c *fiber.Ctx) error { + // Get session from storage + sess, err := store.Get(c) + if err != nil { + panic(err) + } + + // Get value + name := sess.Get("name") + + // Set key/value + sess.Set("name", "john") + + // Get all Keys + keys := sess.Keys() + + // Delete key + sess.Delete("name") + + // Destroy session + if err := sess.Destroy(); err != nil { + panic(err) + } + + // Save session + if err := sess.Save(); err != nil { + panic(err) + } + + return c.SendString(fmt.Sprintf("Welcome %v", name)) +}) +``` + +### Custom Storage/Database + +You can use any storage from our [storage](https://github.com/gofiber/storage/) package. + +```go +storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3 +store := session.New(session.Config{ + Storage: storage, +}) +``` + +To use the store, see the above example. + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Allowed session duration + // Optional. Default value 24 * time.Hour + Expiration time.Duration + + // Storage interface to store the session data + // Optional. Default value memory.New() + Storage fiber.Storage + + // Name of the session cookie. This cookie will store session key. + // Optional. Default value "session_id". + CookieName string + + // Domain of the cookie. + // Optional. Default value "". + CookieDomain string + + // Path of the cookie. + // Optional. Default value "". + CookiePath string + + // Indicates if cookie is secure. + // Optional. Default value false. + CookieSecure bool + + // Indicates if cookie is HTTP only. + // Optional. Default value false. + CookieHTTPOnly bool + + // Sets the cookie SameSite attribute. + // Optional. Default value "Lax". + CookieSameSite string + + // KeyGenerator generates the session key. + // Optional. Default value utils.UUID + KeyGenerator func() string +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + Expiration: 24 * time.Hour, + CookieName: "session_id", + KeyGenerator: utils.UUID, +} +``` diff --git a/docs/api/middleware/skip.md b/docs/api/middleware/skip.md new file mode 100644 index 0000000000..17e19b7e77 --- /dev/null +++ b/docs/api/middleware/skip.md @@ -0,0 +1,25 @@ +--- +id: skip +title: Skip +--- + +Skip middleware for [Fiber](https://github.com/gofiber/fiber) that skips a wrapped handler if a predicate is true. + +### Signatures +```go +func New(handler fiber.Handler, exclude func(c *fiber.Ctx) bool) fiber.Handler +``` + +### Examples +Import the middleware package that is part of the Fiber web framework +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/skip" +) +``` + +After you initiate your Fiber app, you can use the following possibilities: +```go +app.Use(skip.New(handler, func(ctx *fiber.Ctx) bool { return ctx.Method() == fiber.MethodOptions })) +``` diff --git a/docs/api/middleware/timeout.md b/docs/api/middleware/timeout.md new file mode 100644 index 0000000000..cdd88496bc --- /dev/null +++ b/docs/api/middleware/timeout.md @@ -0,0 +1,128 @@ +--- +id: timeout +title: Timeout +--- + +Timeout middleware for [Fiber](https://github.com/gofiber/fiber). As a `fiber.Handler` wrapper, it creates a context with `context.WithTimeout` and pass it in `UserContext`. If the context passed executions (eg. DB ops, Http calls) takes longer than the given duration to return, the timeout error is set and forwarded to the centralized `ErrorHandler`. + +It does not cancel long running executions. Underlying executions must handle timeout by using `context.Context` parameter. + +## Signatures + +```go +func New(handler fiber.Handler, timeout time.Duration, timeoutErrors ...error) fiber.Handler +``` + +## Examples + +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/timeout" +) +``` + +Sample timeout middleware usage: + +```go +func main() { + app := fiber.New() + h := func(c *fiber.Ctx) error { + sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") + if err := sleepWithContext(c.UserContext(), sleepTime); err != nil { + return fmt.Errorf("%w: execution error", err) + } + return nil + } + + app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second)) + _ = app.Listen(":3000") +} + +func sleepWithContext(ctx context.Context, d time.Duration) error { + timer := time.NewTimer(d) + + select { + case <-ctx.Done(): + if !timer.Stop() { + <-timer.C + } + return context.DeadlineExceeded + case <-timer.C: + } + return nil +} +``` + +Test http 200 with curl: + +```bash +curl --location -I --request GET 'http://localhost:3000/foo/1000' +``` + +Test http 408 with curl: + +```bash +curl --location -I --request GET 'http://localhost:3000/foo/3000' +``` + +Use with custom error: + +```go +var ErrFooTimeOut = errors.New("foo context canceled") + +func main() { + app := fiber.New() + h := func(c *fiber.Ctx) error { + sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") + if err := sleepWithContextWithCustomError(c.UserContext(), sleepTime); err != nil { + return fmt.Errorf("%w: execution error", err) + } + return nil + } + + app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second, ErrFooTimeOut)) + _ = app.Listen(":3000") +} + +func sleepWithContextWithCustomError(ctx context.Context, d time.Duration) error { + timer := time.NewTimer(d) + select { + case <-ctx.Done(): + if !timer.Stop() { + <-timer.C + } + return ErrFooTimeOut + case <-timer.C: + } + return nil +} +``` + +Sample usage with a DB call: + +```go +func main() { + app := fiber.New() + db, _ := gorm.Open(postgres.Open("postgres://localhost/foodb"), &gorm.Config{}) + + handler := func(ctx *fiber.Ctx) error { + tran := db.WithContext(ctx.UserContext()).Begin() + + if tran = tran.Exec("SELECT pg_sleep(50)"); tran.Error != nil { + return tran.Error + } + + if tran = tran.Commit(); tran.Error != nil { + return tran.Error + } + + return nil + } + + app.Get("/foo", timeout.New(handler, 10*time.Second)) + app.Listen(":3000") +} +``` diff --git a/docs/extra/_category_.json b/docs/extra/_category_.json new file mode 100644 index 0000000000..f17f137ab3 --- /dev/null +++ b/docs/extra/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Extra", + "position": 4, + "link": { + "type": "generated-index", + "description": "Extra contents for Fiber." + } +} \ No newline at end of file diff --git a/docs/extra/benchmarks.md b/docs/extra/benchmarks.md new file mode 100644 index 0000000000..3c2a82037d --- /dev/null +++ b/docs/extra/benchmarks.md @@ -0,0 +1,112 @@ +--- +id: benchmarks +title: 📊 Benchmarks +description: >- + These benchmarks aim to compare the performance of Fiber and other web + frameworks. +sidebar_position: 2 +--- + +## TechEmpower + +[TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=composite) provides a performance comparison of many web application frameworks executing fundamental tasks such as JSON serialization, database access, and server-side template composition. + +Each framework is operating in a realistic production configuration. Results are captured on cloud instances and on physical hardware. The test implementations are largely community-contributed and all source is available at the [GitHub repository](https://github.com/TechEmpower/FrameworkBenchmarks). + +* Fiber `v1.10.0` +* 28 HT Cores Intel\(R\) Xeon\(R\) Gold 5120 CPU @ 2.20GHz +* 32GB RAM +* Ubuntu 18.04.3 4.15.0-88-generic +* Dedicated Cisco 10-Gbit Ethernet switch. + +### Plaintext + +The Plaintext test is an exercise of the request-routing fundamentals only, designed to demonstrate the capacity of high-performance platforms in particular. Requests will be sent using HTTP pipelining. The response payload is still small, meaning good performance is still necessary in order to saturate the gigabit Ethernet of the test environment. + +See [Plaintext requirements](https://github.com/TechEmpower/FrameworkBenchmarks/wiki/Project-Information-Framework-Tests-Overview#single-database-query) + +**Fiber** - **6,162,556** responses per second with an average latency of **2.0** ms. +**Express** - **367,069** responses per second with an average latency of **354.1** ms. + +![](/img/plaintext.png) + +![Fiber vs Express](/img/plaintext_express.png) + +### Data Updates + +**Fiber** handled **11,846** responses per second with an average latency of **42.8** ms. +**Express** handled **2,066** responses per second with an average latency of **390.44** ms. + +![](/img/data_updates.png) + +![Fiber vs Express](/img/data_updates_express.png) + +### Multiple Queries + +**Fiber** handled **19,664** responses per second with an average latency of **25.7** ms. +**Express** handled **4,302** responses per second with an average latency of **117.2** ms. + +![](/img/multiple_queries.png) + +![Fiber vs Express](/img/multiple_queries_express.png) + +### Single Query + +**Fiber** handled **368,647** responses per second with an average latency of **0.7** ms. +**Express** handled **57,880** responses per second with an average latency of **4.4** ms. + +![](/img/single_query.png) + +![Fiber vs Express](/img/single_query_express.png) + +### JSON Serialization + +**Fiber** handled **1,146,667** responses per second with an average latency of **0.4** ms. +**Express** handled **244,847** responses per second with an average latency of **1.1** ms. + +![](/img/json.png) + +![Fiber vs Express](/img/json_express.png) + +## Go web framework benchmark + +🔗 [https://github.com/smallnest/go-web-framework-benchmark](https://github.com/smallnest/go-web-framework-benchmark) + +* **CPU** Intel\(R\) Xeon\(R\) Gold 6140 CPU @ 2.30GHz +* **MEM** 4GB +* **GO** go1.13.6 linux/amd64 +* **OS** Linux + +The first test case is to mock **0 ms**, **10 ms**, **100 ms**, **500 ms** processing time in handlers. + +![](/img/benchmark.png) + +The concurrency clients are **5000**. + +![](/img/benchmark_latency.png) + +Latency is the time of real processing time by web servers. _The smaller is the better._ + +![](/img/benchmark_alloc.png) + +Allocs is the heap allocations by web servers when test is running. The unit is MB. _The smaller is the better._ + +If we enable **http pipelining**, test result as below: + +![](/img/benchmark-pipeline.png) + +Concurrency test in **30 ms** processing time, the test result for **100**, **1000**, **5000** clients is: + +![](/img/concurrency.png) + +![](/img/concurrency_latency.png) + +![](/img/concurrency_alloc.png) + +If we enable **http pipelining**, test result as below: + +![](/img/concurrency-pipeline.png) + +Dependency graph for `v1.9.0` + +![](/img/graph.svg) diff --git a/docs/extra/faq.md b/docs/extra/faq.md new file mode 100644 index 0000000000..0f8350543b --- /dev/null +++ b/docs/extra/faq.md @@ -0,0 +1,67 @@ +--- +id: faq +title: 🤔 FAQ +description: >- + List of frequently asked questions. Feel free to open an issue to add your + question to this page. +sidebar_position: 1 +--- + +## How should I structure my application? + +There is no definitive answer to this question. The answer depends on the scale of your application and the team that is involved. To be as flexible as possible, Fiber makes no assumptions in terms of structure. + +Routes and other application-specific logic can live in as many files as you wish, in any directory structure you prefer. View the following examples for inspiration: + +* [gofiber/boilerplate](https://github.com/gofiber/boilerplate) +* [thomasvvugt/fiber-boilerplate](https://github.com/thomasvvugt/fiber-boilerplate) +* [Youtube - Building a REST API using Gorm and Fiber](https://www.youtube.com/watch?v=Iq2qT0fRhAA) +* [embedmode/fiberseed](https://github.com/embedmode/fiberseed) + +## How do I handle custom 404 responses? + +If you're using v2.32.0 or later, all you need to do is to implement a custom error handler. See below, or see a more detailed explanation at [Error Handling](../guide/error-handling.md#custom-error-handler). + +If you're using v2.31.0 or earlier, the error handler will not capture 404 errors. Instead, you need to add a middleware function at the very bottom of the stack \(below all other functions\) to handle a 404 response: + +```go title="Example" +app.Use(func(c *fiber.Ctx) error { + return c.Status(fiber.StatusNotFound).SendString("Sorry can't find that!") +}) +``` + +## How do I set up an error handler? + +To override the default error handler, you can override the default when providing a [Config](../api/fiber.md#config) when initiating a new [Fiber instance](../api/fiber.md#new). + +```go title="Example" +app := fiber.New(fiber.Config{ + ErrorHandler: func(c *fiber.Ctx, err error) error { + return c.Status(fiber.StatusInternalServerError).SendString(err.Error()) + }, +}) +``` + +We have a dedicated page explaining how error handling works in Fiber, see [Error Handling](../guide/error-handling.md). + +## Which template engines does Fiber support? + +Fiber currently supports 8 template engines in our [gofiber/template](https://github.com/gofiber/template) middleware: + +* [Ace](https://github.com/yosssi/ace) +* [Amber](https://github.com/eknkc/amber) +* [Django](https://github.com/flosch/pongo2) +* [Handlebars](https://github.com/aymerick/raymond) +* [HTML](https://pkg.go.dev/html/template/) +* [Jet](https://github.com/CloudyKit/jet) +* [Mustache](https://github.com/cbroglie/mustache) +* [Pug](https://github.com/Joker/jade) + +To learn more about using Templates in Fiber, see [Templates](../guide/templates.md). + +## Does Fiber have a community chat? + +Yes, we have our own [Discord ](https://gofiber.io/discord)server, where we hang out. We have different rooms for every subject. +If you have questions or just want to have a chat, feel free to join us via this **>** [**invite link**](https://gofiber.io/discord) **<**. + +![](/img/support-discord.png) diff --git a/docs/guide/_category_.json b/docs/guide/_category_.json new file mode 100644 index 0000000000..b0e157aa76 --- /dev/null +++ b/docs/guide/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Guide", + "position": 3, + "link": { + "type": "generated-index", + "description": "Guides for Fiber." + } +} diff --git a/docs/guide/error-handling.md b/docs/guide/error-handling.md new file mode 100644 index 0000000000..7d3aa361a9 --- /dev/null +++ b/docs/guide/error-handling.md @@ -0,0 +1,128 @@ +--- +id: error-handling +title: 🐛 Error Handling +description: >- + Fiber supports centralized error handling by returning an error to the handler + which allows you to log errors to external services or send a customized HTTP + response to the client. +sidebar_position: 4 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Catching Errors + +It’s essential to ensure that Fiber catches all errors that occur while running route handlers and middleware. You must return them to the handler function, where Fiber will catch and process them. + + + + +```go +app.Get("/", func(c *fiber.Ctx) error { + // Pass error to Fiber + return c.SendFile("file-does-not-exist") +}) +``` + + + +Fiber does not handle [panics](https://go.dev/blog/defer-panic-and-recover) by default. To recover from a panic thrown by any handler in the stack, you need to include the `Recover` middleware below: + +```go title="Example" +package main + +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/recover" +) + +func main() { + app := fiber.New() + + app.Use(recover.New()) + + app.Get("/", func(c *fiber.Ctx) error { + panic("This panic is caught by fiber") + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +You could use Fiber's custom error struct to pass an additional `status code` using `fiber.NewError()`. It's optional to pass a message; if this is left empty, it will default to the status code message \(`404` equals `Not Found`\). + +```go title="Example" +app.Get("/", func(c *fiber.Ctx) error { + // 503 Service Unavailable + return fiber.ErrServiceUnavailable + + // 503 On vacation! + return fiber.NewError(fiber.StatusServiceUnavailable, "On vacation!") +}) +``` + +## Default Error Handler + +Fiber provides an error handler by default. For a standard error, the response is sent as **500 Internal Server Error**. If the error is of type [fiber.Error](https://godoc.org/github.com/gofiber/fiber#Error), the response is sent with the provided status code and message. + +```go title="Example" +// Default error handler +var DefaultErrorHandler = func(c *fiber.Ctx, err error) error { + // Status code defaults to 500 + code := fiber.StatusInternalServerError + + // Retrieve the custom status code if it's a *fiber.Error + var e *fiber.Error + if errors.As(err, &e) { + code = e.Code + } + + // Set Content-Type: text/plain; charset=utf-8 + c.Set(fiber.HeaderContentType, fiber.MIMETextPlainCharsetUTF8) + + // Return status code with error message + return c.Status(code).SendString(err.Error()) +} +``` + +## Custom Error Handler + +A custom error handler can be set using a [Config ](../api/fiber.md#config)when initializing a [Fiber instance](../api/fiber.md#new). + +In most cases, the default error handler should be sufficient. However, a custom error handler can come in handy if you want to capture different types of errors and take action accordingly e.g., send a notification email or log an error to the centralized system. You can also send customized responses to the client e.g., error page or just a JSON response. + +The following example shows how to display error pages for different types of errors. + +```go title="Example" +// Create a new fiber instance with custom config +app := fiber.New(fiber.Config{ + // Override default error handler + ErrorHandler: func(ctx *fiber.Ctx, err error) error { + // Status code defaults to 500 + code := fiber.StatusInternalServerError + + // Retrieve the custom status code if it's a *fiber.Error + var e *fiber.Error + if errors.As(err, &e) { + code = e.Code + } + + // Send custom error page + err = ctx.Status(code).SendFile(fmt.Sprintf("./%d.html", code)) + if err != nil { + // In case the SendFile fails + return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error") + } + + // Return from handler + return nil + }, +}) + +// ... +``` + +> Special thanks to the [Echo](https://echo.labstack.com/) & [Express](https://expressjs.com/) framework for inspiration regarding error handling. diff --git a/docs/guide/faster-fiber.md b/docs/guide/faster-fiber.md new file mode 100644 index 0000000000..b0b7bcb3e5 --- /dev/null +++ b/docs/guide/faster-fiber.md @@ -0,0 +1,36 @@ +--- +id: faster-fiber +title: ⚡ Make Fiber Faster +sidebar_position: 7 +--- + +## Custom JSON Encoder/Decoder +Since Fiber v2.32.0, we use **encoding/json** as default json library due to stability and producibility. However, the standard library is a bit slow compared to 3rd party libraries. If you're not happy with the performance of **encoding/json**, we recommend you to use these libraries: +- [goccy/go-json](https://github.com/goccy/go-json) +- [bytedance/sonic](https://github.com/bytedance/sonic) +- [segmentio/encoding](https://github.com/segmentio/encoding) +- [mailru/easyjson](https://github.com/mailru/easyjson) +- [minio/simdjson-go](https://github.com/minio/simdjson-go) +- [wI2L/jettison](https://github.com/wI2L/jettison) + +```go title="Example" +package main + +import "github.com/gofiber/fiber/v2" +import "github.com/goccy/go-json" + +func main() { + app := fiber.New(fiber.Config{ + JSONEncoder: json.Marshal, + JSONDecoder: json.Unmarshal, + }) + + # ... +} +``` + +### References +- [Set custom JSON encoder for client](../api/client.md#jsonencoder) +- [Set custom JSON decoder for client](../api/client.md#jsondecoder) +- [Set custom JSON encoder for application](../api/fiber.md#config) +- [Set custom JSON decoder for application](../api/fiber.md#config) \ No newline at end of file diff --git a/docs/guide/grouping.md b/docs/guide/grouping.md new file mode 100644 index 0000000000..b48b9dcd65 --- /dev/null +++ b/docs/guide/grouping.md @@ -0,0 +1,75 @@ +--- +id: grouping +title: 🎭 Grouping +sidebar_position: 2 +--- + +## Paths + +Like **Routing**, groups can also have paths that belong to a cluster. + +```go +func main() { + app := fiber.New() + + api := app.Group("/api", middleware) // /api + + v1 := api.Group("/v1", middleware) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + v2 := api.Group("/v2", middleware) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + log.Fatal(app.Listen(":3000")) +} +``` + +A **Group** of paths can have an optional handler. + +```go +func main() { + app := fiber.New() + + api := app.Group("/api") // /api + + v1 := api.Group("/v1") // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + v2 := api.Group("/v2") // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + log.Fatal(app.Listen(":3000")) +} +``` + +:::caution +Running **/api**, **/v1** or **/v2** will result in **404** error, make sure you have the errors set. +::: + +## Group Handlers + +Group handlers can also be used as a routing path but they must have **Next** added to them so that the flow can continue. + +```go +func main() { + app := fiber.New() + + handler := func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + } + api := app.Group("/api") // /api + + v1 := api.Group("/v1", func(c *fiber.Ctx) error { // middleware for /api/v1 + c.Set("Version", "v1") + return c.Next() + }) + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + log.Fatal(app.Listen(":3000")) +} +``` diff --git a/docs/guide/hooks.md b/docs/guide/hooks.md new file mode 100644 index 0000000000..c0470e7bd7 --- /dev/null +++ b/docs/guide/hooks.md @@ -0,0 +1,192 @@ +--- +id: hooks +title: 🪝 Hooks +sidebar_position: 6 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +With Fiber v2.30.0, you can execute custom user functions when to run some methods. Here is a list of this hooks: +- [OnRoute](#onroute) +- [OnName](#onname) +- [OnGroup](#ongroup) +- [OnGroupName](#ongroupname) +- [OnListen](#onlisten) +- [OnFork](#onfork) +- [OnShutdown](#onshutdown) +- [OnMount](#onmount) + +## Constants +```go +// Handlers define a function to create hooks for Fiber. +type OnRouteHandler = func(Route) error +type OnNameHandler = OnRouteHandler +type OnGroupHandler = func(Group) error +type OnGroupNameHandler = OnGroupHandler +type OnListenHandler = func() error +type OnForkHandler = func(int) error +type OnShutdownHandler = OnListenHandler +type OnMountHandler = func(*App) error +``` + +## OnRoute + +OnRoute is a hook to execute user functions on each route registeration. Also you can get route properties by **route** parameter. + +```go title="Signature" +func (app *App) OnRoute(handler ...OnRouteHandler) +``` + +## OnName + +OnName is a hook to execute user functions on each route naming. Also you can get route properties by **route** parameter. + +:::caution +OnName only works with naming routes, not groups. +::: + +```go title="Signature" +func (app *App) OnName(handler ...OnNameHandler) +``` + + + + +```go +package main + +import ( + "fmt" + + "github.com/gofiber/fiber/v2" +) + +func main() { + app := fiber.New() + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString(c.Route().Name) + }).Name("index") + + app.Hooks().OnName(func(r fiber.Route) error { + fmt.Print("Name: " + r.Name + ", ") + + return nil + }) + + app.Hooks().OnName(func(r fiber.Route) error { + fmt.Print("Method: " + r.Method + "\n") + + return nil + }) + + app.Get("/add/user", func(c *fiber.Ctx) error { + return c.SendString(c.Route().Name) + }).Name("addUser") + + app.Delete("/destroy/user", func(c *fiber.Ctx) error { + return c.SendString(c.Route().Name) + }).Name("destroyUser") + + app.Listen(":5000") +} + +// Results: +// Name: addUser, Method: GET +// Name: destroyUser, Method: DELETE +``` + + + +## OnGroup + +OnGroup is a hook to execute user functions on each group registeration. Also you can get group properties by **group** parameter. + +```go title="Signature" +func (app *App) OnGroup(handler ...OnGroupHandler) +``` + +## OnGroupName + +OnGroupName is a hook to execute user functions on each group naming. Also you can get group properties by **group** parameter. + +:::caution +OnGroupName only works with naming groups, not routes. +::: + +```go title="Signature" +func (app *App) OnGroupName(handler ...OnGroupNameHandler) +``` + +## OnListen + +OnListen is a hook to execute user functions on Listen, ListenTLS, Listener. + +```go title="Signature" +func (app *App) OnListen(handler ...OnListenHandler) +``` + +## OnFork + +OnFork is a hook to execute user functions on Fork. + +```go title="Signature" +func (app *App) OnFork(handler ...OnForkHandler) +``` + +## OnShutdown + +OnShutdown is a hook to execute user functions after Shutdown. + +```go title="Signature" +func (app *App) OnShutdown(handler ...OnShutdownHandler) +``` + +## OnMount + +OnMount is a hook to execute user function after mounting process. The mount event is fired when sub-app is mounted on a parent app. The parent app is passed as a parameter. It works for app and group mounting. + +```go title="Signature" +func (h *Hooks) OnMount(handler ...OnMountHandler) +``` + + + + +```go +package main + +import ( + "fmt" + + "github.com/gofiber/fiber/v2" +) + +func main() { + app := New() + app.Get("/", testSimpleHandler).Name("x") + + subApp := New() + subApp.Get("/test", testSimpleHandler) + + subApp.Hooks().OnMount(func(parent *fiber.App) error { + fmt.Print("Mount path of parent app: "+parent.MountPath()) + // ... + + return nil + }) + + app.Mount("/sub", subApp) +} + +// Result: +// Mount path of parent app: +``` + + + + + +:::caution +OnName/OnRoute/OnGroup/OnGroupName hooks are mount-sensitive. If you use one of these routes on sub app and you mount it; paths of routes and groups will start with mount prefix. diff --git a/docs/guide/routing.md b/docs/guide/routing.md new file mode 100644 index 0000000000..c110fc6d68 --- /dev/null +++ b/docs/guide/routing.md @@ -0,0 +1,285 @@ +--- +id: routing +title: 🔌 Routing +description: >- + Routing refers to how an application's endpoints (URIs) respond to client + requests. +sidebar_position: 1 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import RoutingHandler from './../partials/routing/handler.md'; + +## Handlers + + + +## Paths + +Route paths, combined with a request method, define the endpoints at which requests can be made. Route paths can be **strings** or **string patterns**. + +**Examples of route paths based on strings** + +```go +// This route path will match requests to the root route, "/": +app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("root") +}) + +// This route path will match requests to "/about": +app.Get("/about", func(c *fiber.Ctx) error { + return c.SendString("about") +}) + +// This route path will match requests to "/random.txt": +app.Get("/random.txt", func(c *fiber.Ctx) error { + return c.SendString("random.txt") +}) +``` + +As with the expressJs framework, the order of the route declaration plays a role. +When a request is received, the routes are checked in the order in which they are declared. + +:::info +So please be careful to write routes with variable parameters after the routes that contain fixed parts, so that these variable parts do not match instead and unexpected behavior occurs. +::: + +## Parameters + +Route parameters are dynamic elements in the route, which are **named** or **not named segments**. This segments that are used to capture the values specified at their position in the URL. The obtained values can be retrieved using the [Params](https://fiber.wiki/context#params) function, with the name of the route parameter specified in the path as their respective keys or for unnamed parameters the character\(\*, +\) and the counter of this. + +The characters :, +, and \* are characters that introduce a parameter. + +Greedy parameters are indicated by wildcard\(\*\) or plus\(+\) signs. + +The routing also offers the possibility to use optional parameters, for the named parameters these are marked with a final "?", unlike the plus sign which is not optional, you can use the wildcard character for a parameter range which is optional and greedy. + +**Example of define routes with route parameters** + +```go +// Parameters +app.Get("/user/:name/books/:title", func(c *fiber.Ctx) error { + fmt.Fprintf(c, "%s\n", c.Params("name")) + fmt.Fprintf(c, "%s\n", c.Params("title")) + return nil +}) +// Plus - greedy - not optional +app.Get("/user/+", func(c *fiber.Ctx) error { + return c.SendString(c.Params("+")) +}) + +// Optional parameter +app.Get("/user/:name?", func(c *fiber.Ctx) error { + return c.SendString(c.Params("name")) +}) + +// Wildcard - greedy - optional +app.Get("/user/*", func(c *fiber.Ctx) error { + return c.SendString(c.Params("*")) +}) + +// This route path will match requests to "/v1/some/resource/name:customVerb", since the parameter character is escaped +app.Get("/v1/some/resource/name\\:customVerb", func(c *fiber.Ctx) error { + return c.SendString("Hello, Community") +}) +``` + +:::info +Since the hyphen \(`-`\) and the dot \(`.`\) are interpreted literally, they can be used along with route parameters for useful purposes. +::: + +:::info +All special parameter characters can also be escaped with `"\\"` and lose their value, so you can use them in the route if you want, like in the custom methods of the [google api design guide](https://cloud.google.com/apis/design/custom_methods). +::: + +```go +// http://localhost:3000/plantae/prunus.persica +app.Get("/plantae/:genus.:species", func(c *fiber.Ctx) error { + fmt.Fprintf(c, "%s.%s\n", c.Params("genus"), c.Params("species")) + return nil // prunus.persica +}) +``` + +```go +// http://localhost:3000/flights/LAX-SFO +app.Get("/flights/:from-:to", func(c *fiber.Ctx) error { + fmt.Fprintf(c, "%s-%s\n", c.Params("from"), c.Params("to")) + return nil // LAX-SFO +}) +``` + +Our intelligent router recognizes that the introductory parameter characters should be part of the request route in this case and can process them as such. + +```go +// http://localhost:3000/shop/product/color:blue/size:xs +app.Get("/shop/product/color::color/size::size", func(c *fiber.Ctx) error { + fmt.Fprintf(c, "%s:%s\n", c.Params("color"), c.Params("size")) + return nil // blue:xs +}) +``` + +In addition, several parameters in a row and several unnamed parameter characters in the route, such as the wildcard or plus character, are possible, which greatly expands the possibilities of the router for the user. + +```go +// GET /@v1 +// Params: "sign" -> "@", "param" -> "v1" +app.Get("/:sign:param", handler) + +// GET /api-v1 +// Params: "name" -> "v1" +app.Get("/api-:name", handler) + +// GET /customer/v1/cart/proxy +// Params: "*1" -> "customer/", "*2" -> "/cart" +app.Get("/*v1*/proxy", handler) + +// GET /v1/brand/4/shop/blue/xs +// Params: "*1" -> "brand/4", "*2" -> "blue/xs" +app.Get("/v1/*/shop/*", handler) +``` + +We have adapted the routing strongly to the express routing, but currently without the possibility of the regular expressions, because they are quite slow. The possibilities can be tested with version 0.1.7 \(express 4\) in the online [Express route tester](http://forbeslindesay.github.io/express-route-tester/). + +### Constraints +Route constraints execute when a match has occurred to the incoming URL and the URL path is tokenized into route values by parameters. The feature was intorduced in `v2.37.0` and inspired by [.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-6.0#route-constraints). + +:::caution +Constraints aren't validation for parameters. If constraint aren't valid for parameter value, Fiber returns **404 handler**. +::: + +| Constraint | Example | Example matches | +| ----------------- | ------------------------------------ | ------------------------------------------------------------------------------------------- | +| int | :id | 123456789, -123456789 | +| bool | :active | true,false | +| guid | :id | CD2C1638-1638-72D5-1638-DEADBEEF1638 | +| float | :weight | 1.234, -1,001.01e8 | +| minLen(value) | :username | Test (must be at least 4 characters) | +| maxLen(value) | :filename | MyFile (must be no more than 8 characters | +| len(length) | :filename | somefile.txt (exactly 12 characters) | +| min(value) | :age | 19 (Integer value must be at least 18) | +| max(value) | :age | 91 (Integer value must be no more than 120) | +| range(min,max) | :age | 91 (Integer value must be at least 18 but no more than 120) | +| alpha | :name | Rick (String must consist of one or more alphabetical characters, a-z and case-insensitive) | +| datetime | :dob | 2005-11-01 | +| regex(expression) | :date | 2022-08-27 (Must match regular expression) | + +**Examples** + + + + +```go +app.Get("/:test", func(c *fiber.Ctx) error { + return c.SendString(c.Params("test")) +}) + +// curl -X GET http://localhost:3000/12 +// 12 + +// curl -X GET http://localhost:3000/1 +// Cannot GET /1 +``` + + + +You can use `;` for multiple constraints. +```go +app.Get("/:test", func(c *fiber.Ctx) error { + return c.SendString(c.Params("test")) +}) + +// curl -X GET http://localhost:3000/120000 +// Cannot GET /120000 + +// curl -X GET http://localhost:3000/1 +// Cannot GET /1 + +// curl -X GET http://localhost:3000/250 +// 250 +``` + + + +Fiber precompiles regex query when to register routes. So there're no performance overhead for regex constraint. +```go +app.Get("/:date", func(c *fiber.Ctx) error { + return c.SendString(c.Params("date")) +}) + +// curl -X GET http://localhost:3000/125 +// Cannot GET /125 + +// curl -X GET http://localhost:3000/test +// Cannot GET /test + +// curl -X GET http://localhost:3000/2022-08-27 +// 2022-08-27 +``` + + + + +:::caution +You should use `\\` before routing-specific characters when to use datetime constraint (`*`, `+`, `?`, `:`, `/`, `<`, `>`, `;`, `(`, `)`), to avoid wrong parsing. +::: + +**Optional Parameter Example** + +You can impose constraints on optional parameters as well. + +```go +app.Get("/:test?", func(c *fiber.Ctx) error { + return c.SendString(c.Params("test")) +}) +// curl -X GET http://localhost:3000/42 +// 42 +// curl -X GET http://localhost:3000/ +// +// curl -X GET http://localhost:3000/7.0 +// Cannot GET /7.0 +``` + +## Middleware + +Functions that are designed to make changes to the request or response are called **middleware functions**. The [Next](../api/ctx.md#next) is a **Fiber** router function, when called, executes the **next** function that **matches** the current route. + +**Example of a middleware function** + +```go +app.Use(func(c *fiber.Ctx) error { + // Set a custom header on all responses: + c.Set("X-Custom-Header", "Hello, World") + + // Go to next middleware: + return c.Next() +}) + +app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") +}) +``` + +`Use` method path is a **mount**, or **prefix** path, and limits middleware to only apply to any paths requested that begin with it. + +## Grouping + +If you have many endpoints, you can organize your routes using `Group`. + +```go +func main() { + app := fiber.New() + + api := app.Group("/api", middleware) // /api + + v1 := api.Group("/v1", middleware) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + v2 := api.Group("/v2", middleware) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + log.Fatal(app.Listen(":3000")) +} +``` diff --git a/docs/guide/templates.md b/docs/guide/templates.md new file mode 100644 index 0000000000..2e2178d090 --- /dev/null +++ b/docs/guide/templates.md @@ -0,0 +1,105 @@ +--- +id: templates +title: 📝 Templates +description: Fiber supports server-side template engines. +sidebar_position: 3 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Template interfaces + +Fiber provides a Views interface to provide your own template engine: + + + + +```go +type Views interface { + Load() error + Render(io.Writer, string, interface{}, ...string) error +} +``` + + + +`Views` interface contains a `Load` and `Render` method, `Load` is executed by Fiber on app initialization to load/parse the templates. + +```go +// Pass engine to Fiber's Views Engine +app := fiber.New(fiber.Config{ + Views: engine, + // Views Layout is the global layout for all template render until override on Render function. + ViewsLayout: "layouts/main" +}) +``` + +The `Render` method is linked to the [**ctx.Render\(\)**](../api/ctx.md#render) function that accepts a template name and binding data. It will use global layout if layout is not being defined in `Render` function. +If the Fiber config option `PassLocalsToViews` is enabled, then all locals set using `ctx.Locals(key, value)` will be passed to the template. + +```go +app.Get("/", func(c *fiber.Ctx) error { + return c.Render("index", fiber.Map{ + "hello": "world", + }); +}) +``` + +## Engines + +Fiber team maintains [templates](https://github.com/gofiber/template) package that provides wrappers for multiple template engines: + +* [html](https://github.com/gofiber/template/tree/master/html) +* [ace](https://github.com/gofiber/template/tree/master/ace) +* [amber](https://github.com/gofiber/template/tree/master/amber) +* [django](https://github.com/gofiber/template/tree/master/django) +* [handlebars](https://github.com/gofiber/template/tree/master/handlebars) +* [jet](https://github.com/gofiber/template/tree/master/jet) +* [mustache](https://github.com/gofiber/template/tree/master/mustache) +* [pug](https://github.com/gofiber/template/tree/master/pug) + + + + +```go +package main + +import ( + "log" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/template/html" +) + +func main() { + // Initialize standard Go html template engine + engine := html.New("./views", ".html") + // If you want other engine, just replace with following + // Create a new engine with django + // engine := django.New("./views", ".django") + + app := fiber.New(fiber.Config{ + Views: engine, + }) + app.Get("/", func(c *fiber.Ctx) error { + // Render index template + return c.Render("index", fiber.Map{ + "Title": "Hello, World!", + }) + }) + + log.Fatal(app.Listen(":3000")) +} +``` + + + +```markup + + +

{{.Title}}

+ + +``` +
+
diff --git a/docs/guide/validation.md b/docs/guide/validation.md new file mode 100644 index 0000000000..ea7980727d --- /dev/null +++ b/docs/guide/validation.md @@ -0,0 +1,83 @@ +--- +id: validation +title: 🔎 Validation +sidebar_position: 5 +--- + +## Validator package + +Fiber can make _great_ use of the validator package to ensure correct validation of data to store. + +* [Official validator Github page \(Installation, use, examples..\).](https://github.com/go-playground/validator) + +You can find the detailed descriptions of the _validations_ used in the fields contained on the structs below: + +* [Detailed docs](https://pkg.go.dev/github.com/go-playground/validator?tab=doc) + +```go title="Validation Example" +type Job struct{ + Type string `validate:"required,min=3,max=32"` + Salary int `validate:"required,number"` +} + +type User struct{ + Name string `validate:"required,min=3,max=32"` + // use `*bool` here otherwise the validation will fail for `false` values + // Ref: https://github.com/go-playground/validator/issues/319#issuecomment-339222389 + IsActive *bool `validate:"required"` + Email string `validate:"required,email,min=6,max=32"` + Job Job `validate:"dive"` +} + +type ErrorResponse struct { + FailedField string + Tag string + Value string +} + +var validate = validator.New() +func ValidateStruct(user User) []*ErrorResponse { + var errors []*ErrorResponse + err := validate.Struct(user) + if err != nil { + for _, err := range err.(validator.ValidationErrors) { + var element ErrorResponse + element.FailedField = err.StructNamespace() + element.Tag = err.Tag() + element.Value = err.Param() + errors = append(errors, &element) + } + } + return errors +} + +func AddUser(c *fiber.Ctx) error { + //Connect to database + + user := new(User) + + if err := c.BodyParser(user); err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "message": err.Error(), + }) + + } + + errors := ValidateStruct(*user) + if errors != nil { + return c.Status(fiber.StatusBadRequest).JSON(errors) + + } + + //Do something else here + + //Return user + return c.JSON(user) +} + +// Running a test with the following curl commands +// curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"isactive\":\"True\"}" http://localhost:8080/register/user + +// Results in +// [{"FailedField":"User.Email","Tag":"required","Value":""},{"FailedField":"User.Job.Salary","Tag":"required","Value":""},{"FailedField":"User.Job.Type","Tag":"required","Value":""}]⏎ +``` diff --git a/docs/intro.md b/docs/intro.md new file mode 100644 index 0000000000..7880a126f2 --- /dev/null +++ b/docs/intro.md @@ -0,0 +1,196 @@ +--- +slug: / +id: welcome +title: 👋 Welcome +sidebar_position: 1 +--- + +An online API documentation with examples so you can start building web apps with Fiber right away! + +**Fiber** is an [Express](https://github.com/expressjs/express) inspired **web framework** built on top of [Fasthttp](https://github.com/valyala/fasthttp), the **fastest** HTTP engine for [Go](https://go.dev/doc/). Designed to **ease** things up for **fast** development with **zero memory allocation** and **performance** in mind. + +These docs are for **Fiber v2**, which was released on **September 15th, 2020**. + +### Installation + +First of all, [download](https://go.dev/dl/) and install Go. `1.16` or higher is required. + +Installation is done using the [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) command: + +```bash +go get github.com/gofiber/fiber/v2 +``` + +### Zero Allocation +Some values returned from \***fiber.Ctx** are **not** immutable by default. + +Because fiber is optimized for **high-performance**, values returned from **fiber.Ctx** are **not** immutable by default and **will** be re-used across requests. As a rule of thumb, you **must** only use context values within the handler, and you **must not** keep any references. As soon as you return from the handler, any values you have obtained from the context will be re-used in future requests and will change below your feet. Here is an example: + +```go +func handler(c *fiber.Ctx) error { + // Variable is only valid within this handler + result := c.Params("foo") + + // ... +} +``` + +If you need to persist such values outside the handler, make copies of their **underlying buffer** using the [copy](https://pkg.go.dev/builtin/#copy) builtin. Here is an example for persisting a string: + +```go +func handler(c *fiber.Ctx) error { + // Variable is only valid within this handler + result := c.Params("foo") + + // Make a copy + buffer := make([]byte, len(result)) + copy(buffer, result) + resultCopy := string(buffer) + // Variable is now valid forever + + // ... +} +``` + +We created a custom `CopyString` function that does the above and is available under [gofiber/utils](https://github.com/gofiber/fiber/tree/master/utils). + +```go +app.Get("/:foo", func(c *fiber.Ctx) error { + // Variable is now immutable + result := utils.CopyString(c.Params("foo")) + + // ... +}) +``` + +Alternatively, you can also use the `Immutable` setting. It will make all values returned from the context immutable, allowing you to persist them anywhere. Of course, this comes at the cost of performance. + +```go +app := fiber.New(fiber.Config{ + Immutable: true, +}) +``` + +For more information, please check [**\#426**](https://github.com/gofiber/fiber/issues/426) and [**\#185**](https://github.com/gofiber/fiber/issues/185). + +### Hello, World! + +Embedded below is essentially the most straightforward **Fiber** app you can create: + +```go +package main + +import "github.com/gofiber/fiber/v2" + +func main() { + app := fiber.New() + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + app.Listen(":3000") +} +``` + +```text +go run server.go +``` + +Browse to `http://localhost:3000` and you should see `Hello, World!` on the page. + +### Basic routing + +Routing refers to determining how an application responds to a client request to a particular endpoint, which is a URI (or path) and a specific HTTP request method (`GET`, `PUT`, `POST`, etc.). + +Each route can have **multiple handler functions** that are executed when the route is matched. + +Route definition takes the following structures: + +```go +// Function signature +app.Method(path string, ...func(*fiber.Ctx) error) +``` + +- `app` is an instance of **Fiber** +- `Method` is an [HTTP request method](https://docs.gofiber.io/api/app#route-handlers): `GET`, `PUT`, `POST`, etc. +- `path` is a virtual path on the server +- `func(*fiber.Ctx) error` is a callback function containing the [Context](https://docs.gofiber.io/api/ctx) executed when the route is matched + +**Simple route** + +```go +// Respond with "Hello, World!" on root path, "/" +app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") +}) +``` + +**Parameters** + +```go +// GET http://localhost:8080/hello%20world + +app.Get("/:value", func(c *fiber.Ctx) error { + return c.SendString("value: " + c.Params("value")) + // => Get request with value: hello world +}) +``` + +**Optional parameter** + +```go +// GET http://localhost:3000/john + +app.Get("/:name?", func(c *fiber.Ctx) error { + if c.Params("name") != "" { + return c.SendString("Hello " + c.Params("name")) + // => Hello john + } + return c.SendString("Where is john?") +}) +``` + +**Wildcards** + +```go +// GET http://localhost:3000/api/user/john + +app.Get("/api/*", func(c *fiber.Ctx) error { + return c.SendString("API path: " + c.Params("*")) + // => API path: user/john +}) +``` + +### Static files + +To serve static files such as **images**, **CSS**, and **JavaScript** files, replace your function handler with a file or directory string. + +Function signature: + +```go +app.Static(prefix, root string, config ...Static) +``` + +Use the following code to serve files in a directory named `./public`: + +```go +app := fiber.New() + +app.Static("/", "./public") + +app.Listen(":3000") +``` + +Now, you can load the files that are in the `./public` directory: + +```bash +http://localhost:8080/hello.html +http://localhost:8080/js/jquery.js +http://localhost:8080/css/style.css +``` + +### Note + +For more information on how to build APIs in Go with Fiber, please check out this excellent article +[on building an express-style API in Go with Fiber](https://blog.logrocket.com/express-style-api-go-fiber/). diff --git a/docs/partials/routing/handler.md b/docs/partials/routing/handler.md new file mode 100644 index 0000000000..e0d675e896 --- /dev/null +++ b/docs/partials/routing/handler.md @@ -0,0 +1,69 @@ +--- +id: route-handlers +title: Route Handlers +--- + +Registers a route bound to a specific [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods). + +```go title="Signatures" +// HTTP methods +func (app *App) Get(path string, handlers ...Handler) Router +func (app *App) Head(path string, handlers ...Handler) Router +func (app *App) Post(path string, handlers ...Handler) Router +func (app *App) Put(path string, handlers ...Handler) Router +func (app *App) Delete(path string, handlers ...Handler) Router +func (app *App) Connect(path string, handlers ...Handler) Router +func (app *App) Options(path string, handlers ...Handler) Router +func (app *App) Trace(path string, handlers ...Handler) Router +func (app *App) Patch(path string, handlers ...Handler) Router + +// Add allows you to specifiy a method as value +func (app *App) Add(method, path string, handlers ...Handler) Router + +// All will register the route on all HTTP methods +// Almost the same as app.Use but not bound to prefixes +func (app *App) All(path string, handlers ...Handler) Router +``` + +```go title="Examples" +// Simple GET handler +app.Get("/api/list", func(c *fiber.Ctx)error{ + return c.SendString("I'm a GET request!") +}) + +// Simple POST handler +app.Post("/api/register", func(c *fiber.Ctx) error { + return c.SendString("I'm a POST request!") +}) +``` + +**Use** can be used for middleware packages and prefix catchers. These routes will only match the beginning of each path i.e. `/john` will match `/john/doe`, `/johnnnnn` etc + +```go title="Signature" +func (app *App) Use(args ...interface{}) Router +``` + +```go title="Examples" +// Match any request +app.Use(func(c *fiber.Ctx) error { + return c.Next() +}) + +// Match request starting with /api +app.Use("/api", func(c *fiber.Ctx) error { + return c.Next() +}) + +// Match requests starting with /api or /home (multiple-prefix support) +app.Use([]string{"/api", "/home"}, func(c *fiber.Ctx) error { + return c.Next() +}) + +// Attach multiple handlers +app.Use("/api",func(c *fiber.Ctx) error { + c.Set("X-Custom-Header", random.String(32)) + return c.Next() +}, func(c *fiber.Ctx) error { + return c.Next() +}) +``` From d034115dec2cc80e5e2201cc0e9fa6adc56407b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sat, 25 Feb 2023 10:43:53 +0100 Subject: [PATCH 008/162] optimize workflows and test docs sync --- .github/workflows/benchmark.yml | 13 ++++++++++++- .github/workflows/codeql-analysis.yml | 12 +++++++++--- .github/workflows/linter.yml | 2 -- .github/workflows/sync-docs.yml | 8 ++++++-- .github/workflows/test.yml | 7 +++++++ .github/workflows/vulncheck.yml | 6 ++++++ docs/partials/routing/handler.md | 2 +- 7 files changed, 41 insertions(+), 9 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index b960d9cc47..a9bb10d87a 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -1,4 +1,15 @@ -on: [push] +on: + push: + branches: + - master + - main + paths: + - '**' + - '!docs/**' + pull_request: + paths: + - '**' + - '!docs/**' name: Benchmark jobs: Compare: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 80937bbe46..4bd6a92e09 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -2,10 +2,16 @@ name: "CodeQL" on: push: - branches: [master, ] + branches: + - master + - main + paths: + - '**' + - '!docs/**' pull_request: - # The branches below must be a subset of the branches above - branches: [master] + paths: + - '**' + - '!docs/**' schedule: - cron: '0 3 * * 6' diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index ee4b1f3f8b..bb78911c0d 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -3,8 +3,6 @@ name: golangci-lint on: push: - tags: - - v* branches: - master - main diff --git a/.github/workflows/sync-docs.yml b/.github/workflows/sync-docs.yml index 25ad5a2cdb..4bf02cbbfb 100644 --- a/.github/workflows/sync-docs.yml +++ b/.github/workflows/sync-docs.yml @@ -5,9 +5,13 @@ on: branches: - master - main + paths: + - 'docs/**' release: types: [published] - + paths: + - 'docs/**' + jobs: sync-docs: runs-on: ubuntu-latest @@ -22,4 +26,4 @@ jobs: run: ./.github/scripts/sync_docs.sh env: EVENT: ${{ github.event_name }} - TOKEN: ${{ secrets.TOKEN }} \ No newline at end of file + TOKEN: ${{ secrets.TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4c28b27c9f..ca3bd2536b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,14 @@ on: push: branches: - master + - main + paths: + - '**' + - '!docs/**' pull_request: + paths: + - '**' + - '!docs/**' name: Test jobs: Build: diff --git a/.github/workflows/vulncheck.yml b/.github/workflows/vulncheck.yml index 06477dccbb..78b2c76aa9 100644 --- a/.github/workflows/vulncheck.yml +++ b/.github/workflows/vulncheck.yml @@ -3,7 +3,13 @@ on: branches: - master - main + paths: + - '**' + - '!docs/**' pull_request: + paths: + - '**' + - '!docs/**' name: Vulnerability Check jobs: Security: diff --git a/docs/partials/routing/handler.md b/docs/partials/routing/handler.md index e0d675e896..fc8ca23d79 100644 --- a/docs/partials/routing/handler.md +++ b/docs/partials/routing/handler.md @@ -27,7 +27,7 @@ func (app *App) All(path string, handlers ...Handler) Router ```go title="Examples" // Simple GET handler -app.Get("/api/list", func(c *fiber.Ctx)error{ +app.Get("/api/list", func(c *fiber.Ctx) error { return c.SendString("I'm a GET request!") }) From a1eb0e69a6f1d380e9d2515f5c42ad01ce73fda4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Sat, 25 Feb 2023 12:43:57 +0300 Subject: [PATCH 009/162] :memo: docs: fix example on envvar middleware --- docs/api/middleware/envvar.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/middleware/envvar.md b/docs/api/middleware/envvar.md index 8cb8dd0f48..e72c403ea6 100644 --- a/docs/api/middleware/envvar.md +++ b/docs/api/middleware/envvar.md @@ -37,7 +37,7 @@ app.Use("/expose/envvars", envvar.New()) ```go app.Use("/expose/envvars", envvar.New(envvar.Config{ ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, - ExcludeVars: map[string]string{"excludeKey": ""}} + ExcludeVars: map[string]string{"excludeKey": ""}, })) ``` From 35e6825614ab48634977aa245702add2755be17a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sat, 25 Feb 2023 10:55:27 +0100 Subject: [PATCH 010/162] update proxy docs --- docs/api/middleware/proxy.md | 59 ++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/docs/api/middleware/proxy.md b/docs/api/middleware/proxy.md index f1f159f0cc..6a9e45fbec 100644 --- a/docs/api/middleware/proxy.md +++ b/docs/api/middleware/proxy.md @@ -14,6 +14,12 @@ func Balancer(config Config) fiber.Handler func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler // Do performs the given http request and fills the given http response. func Do(c *fiber.Ctx, addr string, clients ...*fasthttp.Client) error +// DoRedirects performs the given http request and fills the given http response while following up to maxRedirectsCount redirects. +func DoRedirects(c *fiber.Ctx, addr string, maxRedirectsCount int, clients ...*fasthttp.Client) error +// DoDeadline performs the given request and waits for response until the given deadline. +func DoDeadline(c *fiber.Ctx, addr string, deadline time.Time, clients ...*fasthttp.Client) error +// DoTimeout performs the given request and waits for response during the given timeout duration. +func DoTimeout(c *fiber.Ctx, addr string, timeout time.Duration, clients ...*fasthttp.Client) error // DomainForward the given http request based on the given domain and fills the given http response func DomainForward(hostname string, addr string, clients ...*fasthttp.Client) fiber.Handler // BalancerForward performs the given http request based round robin balancer and fills the given http response @@ -68,6 +74,36 @@ app.Get("/:id", func(c *fiber.Ctx) error { return nil }) +// Make proxy requests while following redirects +app.Get("/proxy", func(c *fiber.Ctx) error { + if err := proxy.DoRedirects(c, "http://google.com", 3); err != nil { + return err + } + // Remove Server header from response + c.Response().Header.Del(fiber.HeaderServer) + return nil +}) + +// Make proxy requests and wait up to 5 seconds before timing out +app.Get("/proxy", func(c *fiber.Ctx) error { + if err := proxy.DoTimeout(c, "http://localhost:3000", time.Second * 5); err != nil { + return err + } + // Remove Server header from response + c.Response().Header.Del(fiber.HeaderServer) + return nil +}) + +// Make proxy requests, timeout a minute from now +app.Get("/proxy", func(c *fiber.Ctx) error { + if err := DoDeadline(c, "http://localhost", time.Now().Add(time.Minute)); err != nil { + return err + } + // Remove Server header from response + c.Response().Header.Del(fiber.HeaderServer) + return nil +}) + // Minimal round robin balancer app.Use(proxy.Balancer(proxy.Config{ Servers: []string{ @@ -105,7 +141,7 @@ app.Use(proxy.BalancerForward([]string{ ## Config ```go -// Config defines the config for middleware. +// Config defines the confi^g for middleware. type Config struct { // Next defines a function to skip this middleware when returned true. // @@ -129,9 +165,23 @@ type Config struct { // // Optional. Default: nil ModifyResponse fiber.Handler + + // Timeout is the request timeout used when calling the proxy client + // + // Optional. Default: 1 second + Timeout time.Duration + + // Per-connection buffer size for requests' reading. + // This also limits the maximum header size. + // Increase this buffer if your clients send multi-KB RequestURIs + // and/or multi-KB headers (for example, BIG cookies). + ReadBufferSize int + + // Per-connection buffer size for responses' writing. + WriteBufferSize int // tls config for the http client. - TlsConfig *tls.Config + TlsConfig *tls.Config // Client is custom client when client config is complex. // Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig @@ -145,6 +195,9 @@ type Config struct { ```go // ConfigDefault is the default config var ConfigDefault = Config{ - Next: nil, + Next: nil, + ModifyRequest: nil, + ModifyResponse: nil, + Timeout: fasthttp.DefaultLBClientTimeout, } ``` From 3992cab83b45adf80eac0156d64dac8d7131008a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sat, 25 Feb 2023 10:56:10 +0100 Subject: [PATCH 011/162] update proxy docs --- .github/workflows/sync-docs.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/sync-docs.yml b/.github/workflows/sync-docs.yml index 4bf02cbbfb..f556fbeed0 100644 --- a/.github/workflows/sync-docs.yml +++ b/.github/workflows/sync-docs.yml @@ -9,8 +9,6 @@ on: - 'docs/**' release: types: [published] - paths: - - 'docs/**' jobs: sync-docs: From f6b5ed6d268efcd59a452140aa0a6cdd73dbc949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sat, 25 Feb 2023 10:57:12 +0100 Subject: [PATCH 012/162] update proxy docs --- docs/api/middleware/proxy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/middleware/proxy.md b/docs/api/middleware/proxy.md index 6a9e45fbec..3ca54dad48 100644 --- a/docs/api/middleware/proxy.md +++ b/docs/api/middleware/proxy.md @@ -141,7 +141,7 @@ app.Use(proxy.BalancerForward([]string{ ## Config ```go -// Config defines the confi^g for middleware. +// Config defines the config for middleware. type Config struct { // Next defines a function to skip this middleware when returned true. // From d124757c7809c406fd2ba78f02be355cc6c457cb Mon Sep 17 00:00:00 2001 From: RW Date: Sat, 25 Feb 2023 11:47:42 +0100 Subject: [PATCH 013/162] Update pull_request_template.md --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 5c34c3748c..8d259296da 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -19,7 +19,7 @@ Please delete options that are not relevant. - [ ] For new functionalities I follow the inspiration of the express js framework and built them similar in usage - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas -- [ ] I have made corresponding changes to the documentation - https://github.com/gofiber/docs for https://docs.gofiber.io/ +- [ ] I have made corresponding changes to the documentation - /docs/ directory for https://docs.gofiber.io/ - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] If new dependencies exist, I have checked that they are really necessary and agreed with the maintainers/community (we want to have as few dependencies as possible) From 0e87b260a1af910789940dddb5b184f10f3a4727 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Feb 2023 14:21:16 +0100 Subject: [PATCH 014/162] Bump fuxingloh/multi-labeler from 1 to 2 (#2348) Bumps [fuxingloh/multi-labeler](https://github.com/fuxingloh/multi-labeler) from 1 to 2. - [Release notes](https://github.com/fuxingloh/multi-labeler/releases) - [Commits](https://github.com/fuxingloh/multi-labeler/compare/v1...v2) --- updated-dependencies: - dependency-name: fuxingloh/multi-labeler dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/auto-labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-labeler.yml b/.github/workflows/auto-labeler.yml index c86bd68a48..ef7299117e 100644 --- a/.github/workflows/auto-labeler.yml +++ b/.github/workflows/auto-labeler.yml @@ -16,6 +16,6 @@ jobs: steps: - name: Check Labels id: labeler - uses: fuxingloh/multi-labeler@v1 + uses: fuxingloh/multi-labeler@v2 with: github-token: ${{secrets.GITHUB_TOKEN}} From 19aa55da38fb6afc9da856192a651ec72c276f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Tue, 28 Feb 2023 18:10:05 +0100 Subject: [PATCH 015/162] =?UTF-8?q?improve=20workflows=20-=20exclude=20mar?= =?UTF-8?q?kdown=20changes=20-=20don=C2=B4t=20push=20the=20benchmark=20res?= =?UTF-8?q?ults=20in=20a=20pull=20request?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/benchmark.yml | 4 +++- .github/workflows/codeql-analysis.yml | 2 ++ .github/workflows/test.yml | 2 ++ .github/workflows/vulncheck.yml | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index a9bb10d87a..fcba117b4b 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -6,10 +6,12 @@ on: paths: - '**' - '!docs/**' + - '!**.md' pull_request: paths: - '**' - '!docs/**' + - '!**.md' name: Benchmark jobs: Compare: @@ -37,4 +39,4 @@ jobs: benchmark-data-dir-path: 'benchmarks' fail-on-alert: true comment-on-alert: true - auto-push: true + auto-push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4bd6a92e09..b2a35a1ac6 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -8,10 +8,12 @@ on: paths: - '**' - '!docs/**' + - '!**.md' pull_request: paths: - '**' - '!docs/**' + - '!**.md' schedule: - cron: '0 3 * * 6' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ca3bd2536b..06809a07f1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,10 +6,12 @@ on: paths: - '**' - '!docs/**' + - '!**.md' pull_request: paths: - '**' - '!docs/**' + - '!**.md' name: Test jobs: Build: diff --git a/.github/workflows/vulncheck.yml b/.github/workflows/vulncheck.yml index 78b2c76aa9..b4eb346171 100644 --- a/.github/workflows/vulncheck.yml +++ b/.github/workflows/vulncheck.yml @@ -6,10 +6,12 @@ on: paths: - '**' - '!docs/**' + - '!**.md' pull_request: paths: - '**' - '!docs/**' + - '!**.md' name: Vulnerability Check jobs: Security: From a70b8612b180de0cd0187673833998d3983c20fc Mon Sep 17 00:00:00 2001 From: skyenought <70408571+Skyenought@users.noreply.github.com> Date: Wed, 1 Mar 2023 01:14:41 +0800 Subject: [PATCH 016/162] =?UTF-8?q?=F0=9F=90=9B=20[Bug-Fix]=20Fix=20all=20?= =?UTF-8?q?inaccessible=20links=20in=20docs=20(#2349)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix all inaccessible links in docs --- .github/README.md | 6 +++--- .github/README_ckb.md | 6 +++--- .github/README_de.md | 6 +++--- .github/README_es.md | 4 ++-- .github/README_fa.md | 6 +++--- .github/README_fr.md | 4 ++-- .github/README_he.md | 8 ++++---- .github/README_id.md | 4 ++-- .github/README_it.md | 4 ++-- .github/README_ja.md | 4 ++-- .github/README_ko.md | 4 ++-- .github/README_nl.md | 8 ++++---- .github/README_pt.md | 4 ++-- .github/README_ru.md | 6 +++--- .github/README_sa.md | 6 +++--- .github/README_tr.md | 4 ++-- .github/README_zh-CN.md | 8 ++++---- .github/README_zh-TW.md | 13 ++++++++++--- 18 files changed, 56 insertions(+), 49 deletions(-) diff --git a/.github/README.md b/.github/README.md index 6c26f34c75..0a08ed96e9 100644 --- a/.github/README.md +++ b/.github/README.md @@ -128,12 +128,12 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 Features -- Robust [routing](https://docs.gofiber.io/routing) +- Robust [routing](https://docs.gofiber.io/guide/routing) - Serve [static files](https://docs.gofiber.io/api/app#static) - Extreme [performance](https://docs.gofiber.io/extra/benchmarks) - [Low memory](https://docs.gofiber.io/extra/benchmarks) footprint - [API endpoints](https://docs.gofiber.io/api/ctx) -- [Middleware](https://docs.gofiber.io/middleware) & [Next](https://docs.gofiber.io/api/ctx#next) support +- [Middleware](https://docs.gofiber.io/category/-middleware) & [Next](https://docs.gofiber.io/api/ctx#next) support - [Rapid](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) server-side programming - [Template engines](https://github.com/gofiber/template) - [WebSocket support](https://github.com/gofiber/websocket) @@ -440,7 +440,7 @@ func main() { ### JSON Response -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_ckb.md b/.github/README_ckb.md index 72196ddc0a..7f35be6e80 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -124,12 +124,12 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 تایبەتمەندییەکان -- [ناونیشانی ئاڵۆز](https://docs.gofiber.io/routing) +- [ناونیشانی ئاڵۆز](https://docs.gofiber.io/guide/routing) - [فایلی جێگیر](https://docs.gofiber.io/api/app#static) - [خێراییەکی](https://docs.gofiber.io/extra/benchmarks) بێوێنە - بەکارهێنانی [میمۆریی کەم](https://docs.gofiber.io/extra/benchmarks) - توانای هەبوونی لقی [API](https://docs.gofiber.io/api/ctx) -- پشتگیریی [Middleware](https://docs.gofiber.io/middleware) و [Next](https://docs.gofiber.io/api/ctx#next) وەک Express +- پشتگیریی [Middleware](https://docs.gofiber.io/category/-middleware) و [Next](https://docs.gofiber.io/api/ctx#next) وەک Express - پڕۆگرامکردنی [خێرا](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497)ی ڕاژە - [داڕێژە](https://github.com/gofiber/template) - پشتگیریی [WebSocket](https://github.com/gofiber/websocket) @@ -439,7 +439,7 @@ func main() { ### وەڵامی JSON -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_de.md b/.github/README_de.md index e80ece3571..cbe89fbabb 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -124,12 +124,12 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 Eigenschaften -- Robustes [Routing](https://docs.gofiber.io/routing) +- Robustes [Routing](https://docs.gofiber.io/guide/routing) - Bereitstellen von [statischen Dateien](https://docs.gofiber.io/api/app#static) - Extreme [Performance](https://docs.gofiber.io/extra/benchmarks) - [Geringe Arbeitsspeichernutzung](https://docs.gofiber.io/extra/benchmarks) - Express [API Endpunkte](https://docs.gofiber.io/api/ctx) -- [Middleware](https://docs.gofiber.io/middleware) & [Next](https://docs.gofiber.io/api/ctx#next) Support +- [Middleware](https://docs.gofiber.io/category/-middleware) & [Next](https://docs.gofiber.io/api/ctx#next) Support - [Schnelle](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) serverseitige Programmierung - [Template engines](https://github.com/gofiber/template) - [WebSocket support](https://github.com/gofiber/websocket) @@ -434,7 +434,7 @@ func main() { ### JSON Response -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_es.md b/.github/README_es.md index a584448d90..07646116b1 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -124,7 +124,7 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 Características -- [Enrutamiento](https://docs.gofiber.io/routing) robusto +- [Enrutamiento](https://docs.gofiber.io/guide/routing) robusto - Servir [archivos estáticos](https://docs.gofiber.io/api/app#static) - [Rendimiento](https://docs.gofiber.io/extra/benchmarks) extremo - [Bajo](https://docs.gofiber.io/extra/benchmarks) uso de [memoria](https://docs.gofiber.io/extra/benchmarks) @@ -434,7 +434,7 @@ func main() { ### Respuesta JSON -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_fa.md b/.github/README_fa.md index 558922ec59..7ec3d19ee4 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -153,12 +153,12 @@ go get -u github.com/gofiber/fiber/v2
-- [مسیریابی](https://docs.gofiber.io/routing) قدرتمند +- [مسیریابی](https://docs.gofiber.io/guide/routing) قدرتمند - Serve [پرونده های ثابت](https://docs.gofiber.io/api/app#static) - حداکثر [عملکرد](https://docs.gofiber.io/extra/benchmarks) - مصرف [حافظه کم](https://docs.gofiber.io/extra/benchmarks) - قابلیت [API endpoints](https://docs.gofiber.io/api/ctx) -- پشتیبانی از [Middleware](https://docs.gofiber.io/middleware) & [Next](https://docs.gofiber.io/api/ctx#next) +- پشتیبانی از [Middleware](https://docs.gofiber.io/category/-middleware) & [Next](https://docs.gofiber.io/api/ctx#next) - برنامه نویسی سمت سرور [سریع](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) - دارای [Template engines](https://github.com/gofiber/template) اختصاصی - [پشتیبانی از وب سوکت](https://github.com/gofiber/websocket) @@ -530,7 +530,7 @@ func main() { ### JSON Response -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json)
diff --git a/.github/README_fr.md b/.github/README_fr.md index 7bffea6b75..590dbedb37 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -124,7 +124,7 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 Features -- [Routing](https://docs.gofiber.io/routing) robuste +- [Routing](https://docs.gofiber.io/guide/routing) robuste - Serve [static files](https://docs.gofiber.io/api/app#static) - [Performances](https://docs.gofiber.io/extra/benchmarks) extrêmes - [Faible empreinte mémoire](https://docs.gofiber.io/extra/benchmarks) @@ -436,7 +436,7 @@ func main() { ### JSON Response -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_he.md b/.github/README_he.md index 5e54b71b20..452cae8fa4 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -147,14 +147,14 @@ go get -u github.com/gofiber/fiber/v2
-- [ניתוב](https://docs.gofiber.io/routing) רובסטי +- [ניתוב](https://docs.gofiber.io/guide/routing) רובסטי - הנגשת [קבצים סטטיים](https://docs.gofiber.io/api/app#static) - [ביצועים](https://docs.gofiber.io/extra/benchmarks) גבוהים במיוחד - צורך כמות [זכרון קטנה](https://docs.gofiber.io/extra/benchmarks) - [נקודות קצה עבור API](https://docs.gofiber.io/api/ctx) -- תמיכה ב-[Middleware](https://docs.gofiber.io/middleware) & [Next](https://docs.gofiber.io/api/ctx#next) +- תמיכה ב-[Middleware](https://docs.gofiber.io/category/-middleware) & [Next](https://docs.gofiber.io/api/ctx#next) - תכנות [מהיר](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) של צד שרת -- [מנועי תבניות](https://docs.gofiber.io/middleware#template) +- [מנועי תבניות](https://docs.gofiber.io/category/-middleware#template) - [תמיכה ב-WebSocket](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [הגבלת קצבים ובקשות](https://docs.gofiber.io/api/middleware/limiter) @@ -526,7 +526,7 @@ func main() { ### תגובת JSON -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json)
diff --git a/.github/README_id.md b/.github/README_id.md index b297c36c8a..d8ff8e83c7 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -124,7 +124,7 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 Fitur -- Sistem [Routing](https://docs.gofiber.io/routing) yang padu +- Sistem [Routing](https://docs.gofiber.io/guide/routing) yang padu - Menyajikan [file statis](https://docs.gofiber.io/api/app#static) - [Kinerja](https://docs.gofiber.io/extra/benchmarks) ekstrim - [Penggunaan memori](https://docs.gofiber.io/extra/benchmarks) yang kecil @@ -437,7 +437,7 @@ func main() { ### JSON Response -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_it.md b/.github/README_it.md index bb13792972..249f52681d 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -124,7 +124,7 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 Caratteristiche -- [Routing](https://docs.gofiber.io/routing) solido +- [Routing](https://docs.gofiber.io/guide/routing) solido - Serve [file statici](https://docs.gofiber.io/api/app#static) - [Perfomance](https://docs.gofiber.io/extra/benchmarks) estreme - [Basso](https://docs.gofiber.io/extra/benchmarks) utilizzo di [memoria](https://docs.gofiber.io/extra/benchmarks) @@ -435,7 +435,7 @@ func main() { ### Risposte JSON -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_ja.md b/.github/README_ja.md index ca0f97f770..2fcb6ea8ac 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -125,7 +125,7 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 機能 -- 堅牢な[ルーティング](https://docs.gofiber.io/routing) +- 堅牢な[ルーティング](https://docs.gofiber.io/guide/routing) - [静的ファイル](https://docs.gofiber.io/api/app#static)のサポート - 究極の[パフォーマンス](https://docs.gofiber.io/extra/benchmarks) - [低メモリ](https://docs.gofiber.io/extra/benchmarks)フットプリント @@ -439,7 +439,7 @@ func main() { ### JSON Response -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_ko.md b/.github/README_ko.md index 1d8b9ec222..0a360ce661 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -124,7 +124,7 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 특징 -- 견고한 [라우팅](https://docs.gofiber.io/routing) +- 견고한 [라우팅](https://docs.gofiber.io/guide/routing) - [정적 파일](https://docs.gofiber.io/api/app#static) 제공 - 뛰어난 [성능](https://docs.gofiber.io/extra/benchmarks) - [적은 메모리](https://docs.gofiber.io/extra/benchmarks) 공간 @@ -440,7 +440,7 @@ func main() { ### JSON Response -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_nl.md b/.github/README_nl.md index 250021d202..3a27edcc79 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -124,17 +124,17 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 Features -- Robuuste [routing](https://docs.gofiber.io/routing) +- Robuuste [routing](https://docs.gofiber.io/guide/routing) - Serveer [statische bestanden](https://docs.gofiber.io/api/app#static) - Extreme [prestaties](https://docs.gofiber.io/extra/benchmarks) - [Weinig geheugenruimte](https://docs.gofiber.io/extra/benchmarks) - [API endpoints](https://docs.gofiber.io/api/ctx) -- [Middleware](https://docs.gofiber.io/middleware) & [Next](https://docs.gofiber.io/api/ctx#next) ondersteuning +- [Middleware](https://docs.gofiber.io/category/-middleware) & [Next](https://docs.gofiber.io/api/ctx#next) ondersteuning - [Snelle](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) server-side programmering - [Template engines](https://github.com/gofiber/template) - [WebSocket ondersteuning](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) -- [Rate Limiter](https://docs.gofiber.io/middleware/limiter) +- [Rate Limiter](https://docs.gofiber.io/category/-middleware/limiter) - Vertaald in [18 talen](https://docs.gofiber.io/) - En nog veel meer, [ontdek Fiber](https://docs.gofiber.io/) @@ -440,7 +440,7 @@ func main() { ### JSON Response -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_pt.md b/.github/README_pt.md index a08b6c1e55..97cdac5bbf 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -124,7 +124,7 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 Recursos -- [Roteamento](https://docs.gofiber.io/routing) robusto +- [Roteamento](https://docs.gofiber.io/guide/routing) robusto - Servir [arquivos estáticos](https://docs.gofiber.io/api/app#static) - [Desempenho](https://docs.gofiber.io/extra/benchmarks) extremo - [Baixo consumo de memória](https://docs.gofiber.io/extra/benchmarks) @@ -434,7 +434,7 @@ func main() { ### Resposta JSON -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_ru.md b/.github/README_ru.md index 3afeeef752..84563b9e68 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -124,12 +124,12 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 Особенности -- Надежная [маршрутизация](https://docs.gofiber.io/routing) +- Надежная [маршрутизация](https://docs.gofiber.io/guide/routing) - Доступ к [статичным файлам](https://docs.gofiber.io/api/app#static) - Экстремальная [производительность](https://docs.gofiber.io/extra/benchmarks) - [Низкий объем потребления памяти](https://docs.gofiber.io/extra/benchmarks) - [Эндпоинты](https://docs.gofiber.io/context), как в [API](https://docs.gofiber.io/api/ctx) Express -- [Middleware](https://docs.gofiber.io/middleware) и поддержка [Next](https://docs.gofiber.io/api/ctx#next) +- [Middleware](https://docs.gofiber.io/category/-middleware) и поддержка [Next](https://docs.gofiber.io/api/ctx#next) - [Быстрое](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) программирование на стороне сервера - [Template engines](https://github.com/gofiber/template) - [Поддержка WebSocket](https://github.com/gofiber/websocket) @@ -436,7 +436,7 @@ func main() { ### JSON Response -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_sa.md b/.github/README_sa.md index 2ebfee3938..b059c301fd 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -137,12 +137,12 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 الميزات -- قوي [routing](https://docs.gofiber.io/routing) +- قوي [routing](https://docs.gofiber.io/guide/routing) - يقدم خدمة [static files](https://docs.gofiber.io/api/app#static) - أقصى [أداء](https://docs.gofiber.io/extra/benchmarks) - [ذاكرة منخفضة](https://docs.gofiber.io/extra/benchmarks) - [API endpoints](https://docs.gofiber.io/api/ctx) -- [Middleware](https://docs.gofiber.io/middleware) & [Next](https://docs.gofiber.io/api/ctx#next) مدعوم +- [Middleware](https://docs.gofiber.io/category/-middleware) & [Next](https://docs.gofiber.io/api/ctx#next) مدعوم - [سريع](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) server-side programming - [Template engines](https://github.com/gofiber/template) - [WebSocket دعم](https://github.com/gofiber/websocket) @@ -490,7 +490,7 @@ func main() { ### JSON Response -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json)
diff --git a/.github/README_tr.md b/.github/README_tr.md index 6bc5aacf54..a00a9d1751 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -124,7 +124,7 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 Özellikler -- Güçlü [routing](https://docs.gofiber.io/routing) +- Güçlü [routing](https://docs.gofiber.io/guide/routing) - [Statik dosya](https://docs.gofiber.io/api/app#static) sunumu - Olağanüstü [performans](https://docs.gofiber.io/extra/benchmarks) - [Düşük bellek](https://docs.gofiber.io/extra/benchmarks) kullanımı @@ -434,7 +434,7 @@ func main() { ### JSON Yanıtları -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index b8aeb07e8e..d07f5d342d 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -126,12 +126,12 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 特点 -- 强大的[路由](https://docs.gofiber.io/routing) +- 强大的[路由](https://docs.gofiber.io/guide/routing) - [静态文件](https://docs.gofiber.io/api/app#static)服务 - 极致[性能](https://docs.gofiber.io/extra/benchmarks) - [低内存占用](https://docs.gofiber.io/extra/benchmarks) - [API 接口](https://docs.gofiber.io/api/ctx) -- 支持[中间件](https://docs.gofiber.io/middleware)和 [Next](https://docs.gofiber.io/api/ctx#next) +- 支持[中间件](https://docs.gofiber.io/category/-middleware)和 [Next](https://docs.gofiber.io/api/ctx#next) - [快速上手](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) - [模版引擎](https://github.com/gofiber/template) - [支持 WebSocket](https://github.com/gofiber/websocket) @@ -252,7 +252,7 @@ func main() { ``` -#### 📖 [**中间件**](https://docs.gofiber.io/middleware)和 [**Next**](https://docs.gofiber.io/api/ctx#next) +#### 📖 [**中间件**](https://docs.gofiber.io/category/-middleware)和 [**Next**](https://docs.gofiber.io/api/ctx#next) ```go func main() { @@ -442,7 +442,7 @@ func main() { ### JSON 响应 -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index 2188f81834..21d03bc577 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -128,13 +128,20 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 特色 -- 強固的[路由系統](https://docs.gofiber.io/routing) +- 強固的[路由系統](https://docs.gofiber.io/guide/routing) - 可以寄存[靜態檔案](https://docs.gofiber.io/api/app#static) - 疾速[效能](https://docs.gofiber.io/extra/benchmarks) - 相當低的[記憶體使用量](https://docs.gofiber.io/extra/benchmarks) - [API 端點](https://docs.gofiber.io/api/ctx) -- 支援 [中介模組](https://docs.gofiber.io/middleware) 和 [接續函式 (Next)](https://docs.gofiber.io/api/ctx#next) +- 支援 [中介模組](https://docs.gofiber.io/category/-middleware) 和 [接續函式 (Next)](https://docs.gofiber.io/api/ctx#next) - [迅速開發](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) 伺服器端服務 +- 強大的[路由](https://docs.gofiber.io/guide/routing) +- [靜態檔案](https://docs.gofiber.io/api/app#static)服務 +- [超快速](https://docs.gofiber.io/extra/benchmarks) +- [佔用很少記憶體](https://docs.gofiber.io/extra/benchmarks) +- 支援 Express 的[API](https://docs.gofiber.io/api/ctx) +- 支援中介器和[下一步](https://docs.gofiber.io/api/ctx#next) +- [立即上手](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) - [樣板引擎](https://github.com/gofiber/template) - [支援 WebSocket](https://github.com/gofiber/websocket) - [Server-Sent Events](https://github.com/gofiber/recipes/tree/master/sse) @@ -441,7 +448,7 @@ func main() { ### JSON 回應 -📖 [JSON](https://docs.gofiber.io/ctx#json) +📖 [JSON](https://docs.gofiber.io/api/ctx#json) ```go type User struct { From 44fd1976e75c51d04f36ca87cdb4ffc556400f54 Mon Sep 17 00:00:00 2001 From: Caio Augusto Date: Sun, 5 Mar 2023 17:26:06 -0300 Subject: [PATCH 017/162] fix(docs): add missing comma (#2353) fix: add missing comma nothing too much but it's a fix :p --- middleware/limiter/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/middleware/limiter/README.md b/middleware/limiter/README.md index 068b623f0f..d259580081 100644 --- a/middleware/limiter/README.md +++ b/middleware/limiter/README.md @@ -56,11 +56,11 @@ app.Use(limiter.New(limiter.Config{ Expiration: 30 * time.Second, KeyGenerator: func(c *fiber.Ctx) string{ return "key" - } + }, LimitReached: func(c *fiber.Ctx) error { return c.SendFile("./toofast.html") }, - Storage: myCustomStore{} + Storage: myCustomStore{}, })) ``` From e2da8540be01d30eb51175c4c0fab8ee175331ee Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 6 Mar 2023 07:55:19 +0100 Subject: [PATCH 018/162] Update auto-labeler.yml downgrade autolabeler --- .github/workflows/auto-labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-labeler.yml b/.github/workflows/auto-labeler.yml index ef7299117e..c86bd68a48 100644 --- a/.github/workflows/auto-labeler.yml +++ b/.github/workflows/auto-labeler.yml @@ -16,6 +16,6 @@ jobs: steps: - name: Check Labels id: labeler - uses: fuxingloh/multi-labeler@v2 + uses: fuxingloh/multi-labeler@v1 with: github-token: ${{secrets.GITHUB_TOKEN}} From 2e7e879d6f69902d06b4759a5f31a083968faa40 Mon Sep 17 00:00:00 2001 From: lublak <44057030+lublak@users.noreply.github.com> Date: Mon, 6 Mar 2023 12:03:41 +0100 Subject: [PATCH 019/162] feature: allow preloaded certs with prefork (#2351) * allow preloaded certs with prefork * add to documentation * add comments for ListenMutualTLSWithCertificate * add test for WithCertificate * Update benchmark.yml * Update benchmark.yml * Update benchmark.yml * Update benchmark.yml * Update benchmark.yml * Update benchmark.yml --------- Co-authored-by: RW --- .github/workflows/benchmark.yml | 4 +- docs/api/app.md | 46 ++++++++++++++++ listen.go | 16 ++++++ listen_test.go | 94 +++++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index fcba117b4b..6150c2c8c6 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -38,5 +38,7 @@ jobs: github-token: ${{ secrets.BENCHMARK_TOKEN }} benchmark-data-dir-path: 'benchmarks' fail-on-alert: true - comment-on-alert: true + comment-on-alert: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} + # Enable Job Summary for PRs + #summary-always: ${{ github.event_name != 'push' && github.event_name != 'workflow_dispatch' }} auto-push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} diff --git a/docs/api/app.md b/docs/api/app.md index 314ea83a5c..5ab89922e7 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -525,6 +525,27 @@ Using `ListenTLS` defaults to the following config \( use `Listener` to provide } ``` +## ListenTLSWithCertificate + +```go title="Signature" +func (app *App) ListenTLS(addr string, cert tls.Certificate) error +``` + +```go title="Examples" +app.ListenTLSWithCertificate(":443", cert); +``` + +Using `ListenTLSWithCertificate` defaults to the following config \( use `Listener` to provide your own config \) + +```go title="Default \*tls.Config" +&tls.Config{ + MinVersion: tls.VersionTLS12, + Certificates: []tls.Certificate{ + cert, + }, +} +``` + ## ListenMutualTLS ListenMutualTLS serves HTTPs requests from the given address using certFile, keyFile and clientCertFile are the paths to TLS certificate and key file @@ -550,6 +571,31 @@ Using `ListenMutualTLS` defaults to the following config \( use `Listener` to pr } ``` +## ListenMutualTLSWithCertificate + +ListenMutualTLSWithCertificate serves HTTPs requests from the given address using certFile, keyFile and clientCertFile are the paths to TLS certificate and key file + +```go title="Signature" +func (app *App) ListenMutualTLSWithCertificate(addr string, cert tls.Certificate, clientCertPool *x509.CertPool) error +``` + +```go title="Examples" +app.ListenMutualTLSWithCertificate(":443", cert, clientCertPool); +``` + +Using `ListenMutualTLSWithCertificate` defaults to the following config \( use `Listener` to provide your own config \) + +```go title="Default \*tls.Config" +&tls.Config{ + MinVersion: tls.VersionTLS12, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: clientCertPool, + Certificates: []tls.Certificate{ + cert, + }, +} +``` + ## Listener You can pass your own [`net.Listener`](https://pkg.go.dev/net/#Listener) using the `Listener` method. This method can be used to enable **TLS/HTTPS** with a custom tls.Config. diff --git a/listen.go b/listen.go index 80acd1edd2..d212855cbb 100644 --- a/listen.go +++ b/listen.go @@ -98,6 +98,14 @@ func (app *App) ListenTLS(addr, certFile, keyFile string) error { return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %w", certFile, keyFile, err) } + return app.ListenTLSWithCertificate(addr, cert) +} + +// ListenTLS serves HTTPS requests from the given addr. +// cert is a tls.Certificate +// +// app.ListenTLSWithCertificate(":8080", cert) +func (app *App) ListenTLSWithCertificate(addr string, cert tls.Certificate) error { tlsHandler := &TLSHandler{} config := &tls.Config{ MinVersion: tls.VersionTLS12, @@ -161,6 +169,14 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) clientCertPool := x509.NewCertPool() clientCertPool.AppendCertsFromPEM(clientCACert) + return app.ListenMutualTLSWithCertificate(addr, cert, clientCertPool) +} + +// ListenMutualTLSWithCertificate serves HTTPS requests from the given addr. +// cert is a tls.Certificate and clientCertPool is a *x509.CertPool: +// +// app.ListenMutualTLS(":8080", cert, clientCertPool) +func (app *App) ListenMutualTLSWithCertificate(addr string, cert tls.Certificate, clientCertPool *x509.CertPool) error { tlsHandler := &TLSHandler{} config := &tls.Config{ MinVersion: tls.VersionTLS12, diff --git a/listen_test.go b/listen_test.go index 63283d3485..bbd89315d7 100644 --- a/listen_test.go +++ b/listen_test.go @@ -7,9 +7,11 @@ package fiber import ( "bytes" "crypto/tls" + "crypto/x509" "io" "log" "os" + "path/filepath" "strings" "sync" "testing" @@ -142,6 +144,98 @@ func Test_App_Listener_TLS_Listener(t *testing.T) { utils.AssertEqual(t, nil, app.Listener(ln)) } +// go test -run Test_App_ListenTLSWithCertificate +func Test_App_ListenTLSWithCertificate(t *testing.T) { + t.Parallel() + + // Create tls certificate + cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") + if err != nil { + utils.AssertEqual(t, nil, err) + } + + app := New() + + // invalid port + utils.AssertEqual(t, false, app.ListenTLSWithCertificate(":99999", cer) == nil) + + go func() { + time.Sleep(1000 * time.Millisecond) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + utils.AssertEqual(t, nil, app.ListenTLSWithCertificate(":0", cer)) +} + +// go test -run Test_App_ListenTLSWithCertificate_Prefork +func Test_App_ListenTLSWithCertificate_Prefork(t *testing.T) { + testPreforkMaster = true + + // Create tls certificate + cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") + if err != nil { + utils.AssertEqual(t, nil, err) + } + + app := New(Config{DisableStartupMessage: true, Prefork: true}) + + utils.AssertEqual(t, nil, app.ListenTLSWithCertificate(":99999", cer)) +} + +// go test -run Test_App_ListenMutualTLSWithCertificate +func Test_App_ListenMutualTLSWithCertificate(t *testing.T) { + t.Parallel() + + // Create tls certificate + cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") + if err != nil { + utils.AssertEqual(t, nil, err) + } + + // Create pool + clientCACert, err := os.ReadFile(filepath.Clean("./.github/testdata/ca-chain.cert.pem")) + if err != nil { + utils.AssertEqual(t, nil, err) + } + clientCertPool := x509.NewCertPool() + clientCertPool.AppendCertsFromPEM(clientCACert) + + app := New() + + // invalid port + utils.AssertEqual(t, false, app.ListenMutualTLSWithCertificate(":99999", cer, clientCertPool) == nil) + + go func() { + time.Sleep(1000 * time.Millisecond) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + utils.AssertEqual(t, nil, app.ListenMutualTLSWithCertificate(":0", cer, clientCertPool)) +} + +// go test -run Test_App_ListenMutualTLS_Prefork +func Test_App_ListenMutualTLSWithCertificate_Prefork(t *testing.T) { + testPreforkMaster = true + + // Create tls certificate + cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") + if err != nil { + utils.AssertEqual(t, nil, err) + } + + // Create pool + clientCACert, err := os.ReadFile(filepath.Clean("./.github/testdata/ca-chain.cert.pem")) + if err != nil { + utils.AssertEqual(t, nil, err) + } + clientCertPool := x509.NewCertPool() + clientCertPool.AppendCertsFromPEM(clientCACert) + + app := New(Config{DisableStartupMessage: true, Prefork: true}) + + utils.AssertEqual(t, nil, app.ListenMutualTLSWithCertificate(":99999", cer, clientCertPool)) +} + func captureOutput(f func()) string { reader, writer, err := os.Pipe() if err != nil { From 15e92353830c2f9303eea3900eba33d77f34d518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Mon, 6 Mar 2023 16:42:35 +0300 Subject: [PATCH 020/162] :memo: docs: remove README.mds from middleware dirs --- docs/api/middleware/limiter.md | 4 +- middleware/basicauth/README.md | 130 ------------- middleware/cache/README.md | 159 --------------- middleware/compress/README.md | 96 ---------- middleware/cors/README.md | 111 ----------- middleware/csrf/README.md | 161 ---------------- middleware/earlydata/README.md | 101 ---------- middleware/encryptcookie/README.md | 113 ----------- middleware/envvar/README.md | 83 -------- middleware/etag/README.md | 88 --------- middleware/expvar/README.md | 85 -------- middleware/favicon/README.md | 91 --------- middleware/filesystem/README.md | 298 ----------------------------- middleware/idempotency/README.md | 118 ------------ middleware/limiter/README.md | 146 -------------- middleware/logger/README.md | 212 -------------------- middleware/monitor/README.md | 98 ---------- middleware/pprof/README.md | 62 ------ middleware/proxy/README.md | 208 -------------------- middleware/recover/README.md | 64 ------- middleware/requestid/README.md | 78 -------- middleware/session/README.md | 170 ---------------- middleware/skip/README.md | 26 --- middleware/timeout/README.md | 98 ---------- 24 files changed, 2 insertions(+), 2798 deletions(-) delete mode 100644 middleware/basicauth/README.md delete mode 100644 middleware/cache/README.md delete mode 100644 middleware/compress/README.md delete mode 100644 middleware/cors/README.md delete mode 100644 middleware/csrf/README.md delete mode 100644 middleware/earlydata/README.md delete mode 100644 middleware/encryptcookie/README.md delete mode 100644 middleware/envvar/README.md delete mode 100644 middleware/etag/README.md delete mode 100644 middleware/expvar/README.md delete mode 100644 middleware/favicon/README.md delete mode 100644 middleware/filesystem/README.md delete mode 100644 middleware/idempotency/README.md delete mode 100644 middleware/limiter/README.md delete mode 100644 middleware/logger/README.md delete mode 100644 middleware/monitor/README.md delete mode 100644 middleware/pprof/README.md delete mode 100644 middleware/proxy/README.md delete mode 100644 middleware/recover/README.md delete mode 100644 middleware/requestid/README.md delete mode 100644 middleware/session/README.md delete mode 100644 middleware/skip/README.md delete mode 100644 middleware/timeout/README.md diff --git a/docs/api/middleware/limiter.md b/docs/api/middleware/limiter.md index 8680f57014..d54cf40329 100644 --- a/docs/api/middleware/limiter.md +++ b/docs/api/middleware/limiter.md @@ -45,7 +45,7 @@ app.Use(limiter.New(limiter.Config{ LimitReached: func(c *fiber.Ctx) error { return c.SendFile("./toofast.html") }, - Storage: myCustomStorage{} + Storage: myCustomStorage{}, })) ``` @@ -59,7 +59,7 @@ A example of such configuration is: app.Use(limiter.New(limiter.Config{ Max: 20, Expiration: 30 * time.Second, - LimiterMiddleware: limiter.SlidingWindow{} + LimiterMiddleware: limiter.SlidingWindow{}, })) ``` diff --git a/middleware/basicauth/README.md b/middleware/basicauth/README.md deleted file mode 100644 index fd623298f4..0000000000 --- a/middleware/basicauth/README.md +++ /dev/null @@ -1,130 +0,0 @@ -# Basic Authentication Middleware - -Basic Authentication middleware for [Fiber](https://github.com/gofiber/fiber) that provides an HTTP basic authentication. It calls the next handler for valid credentials and [401 Unauthorized](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) or a custom response for missing or invalid credentials. - -## Table of Contents - -- [Basic Authentication Middleware](#basic-authentication-middleware) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Custom Config](#custom-config) - - [Config](#config) - - [Default Config](#default-config) - -## Signatures - -```go -func New(config Config) fiber.Handler -``` - -## Examples - -First import the middleware from Fiber, - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/basicauth" -) -``` - -Then create a Fiber app with `app := fiber.New()`. - -### Custom Config - -```go -// Provide a minimal config -app.Use(basicauth.New(basicauth.Config{ - Users: map[string]string{ - "john": "doe", - "admin": "123456", - }, -})) - -// Or extend your config for customization -app.Use(basicauth.New(basicauth.Config{ - Users: map[string]string{ - "john": "doe", - "admin": "123456", - }, - Realm: "Forbidden", - Authorizer: func(user, pass string) bool { - if user == "john" && pass == "doe" { - return true - } - if user == "admin" && pass == "123456" { - return true - } - return false - }, - Unauthorized: func(c *fiber.Ctx) error { - return c.SendFile("./unauthorized.html") - }, - ContextUsername: "_user", - ContextPassword: "_pass", -})) -``` - -## Config - -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Users defines the allowed credentials - // - // Required. Default: map[string]string{} - Users map[string]string - - // Realm is a string to define realm attribute of BasicAuth. - // the realm identifies the system to authenticate against - // and can be used by clients to save credentials - // - // Optional. Default: "Restricted". - Realm string - - // Authorizer defines a function you can pass - // to check the credentials however you want. - // It will be called with a username and password - // and is expected to return true or false to indicate - // that the credentials were approved or not. - // - // Optional. Default: nil. - Authorizer func(string, string) bool - - // Unauthorized defines the response body for unauthorized responses. - // By default it will return with a 401 Unauthorized and the correct WWW-Auth header - // - // Optional. Default: nil - Unauthorized fiber.Handler - - // ContextUser is the key to store the username in Locals - // - // Optional. Default: "username" - ContextUsername string - - // ContextPass is the key to store the password in Locals - // - // Optional. Default: "password" - ContextPassword string -} -``` - -## Default Config - -```go -var ConfigDefault = Config{ - Next: nil, - Users: map[string]string{}, - Realm: "Restricted", - Authorizer: nil, - Unauthorized: nil, - ContextUsername: "username", - ContextPassword: "password", -} -``` diff --git a/middleware/cache/README.md b/middleware/cache/README.md deleted file mode 100644 index 638d3d525d..0000000000 --- a/middleware/cache/README.md +++ /dev/null @@ -1,159 +0,0 @@ -# Cache Middleware - -Cache middleware for [Fiber](https://github.com/gofiber/fiber) designed to intercept responses and cache them. This middleware will cache the `Body`, `Content-Type` and `StatusCode` using the `c.Path()` (or a string returned by the Key function) as unique identifier. Special thanks to [@codemicro](https://github.com/codemicro/fiber-cache) for creating this middleware for Fiber core! - -Request Directives
-`Cache-Control: no-cache` will return the up-to-date response but still caches it. You will always get a `miss` cache status.
-`Cache-Control: no-store` will refrain from caching. You will always get the up-to-date response. - -## Table of Contents - -- [Cache Middleware](#cache-middleware) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Default Config](#default-config) - - [Custom Config](#custom-config) - - [Custom Cache Key Or Expiration](#custom-cache-key-or-expiration) - - [Config](#config) - - [Default Config](#default-config-1) - -## Signatures - -```go -func New(config ...Config) fiber.Handler -``` - -## Examples - -First import the middleware from Fiber, - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/cache" -) -``` - -Then create a Fiber app with `app := fiber.New()`. - -### Default Config - -```go -app.Use(cache.New()) -``` - -### Custom Config - -```go -app.Use(cache.New(cache.Config{ - Next: func(c *fiber.Ctx) bool { - return c.Query("refresh") == "true" - }, - Expiration: 30 * time.Minute, - CacheControl: true, -})) -``` - -### Custom Cache Key Or Expiration - -```go -app.Use(New(Config{ - ExpirationGenerator: func(c *fiber.Ctx, cfg *Config) time.Duration { - newCacheTime, _ := strconv.Atoi(c.GetRespHeader("Cache-Time", "600")) - return time.Second * time.Duration(newCacheTime) - }, - KeyGenerator: func(c *fiber.Ctx) string { - return utils.CopyString(c.Path()) - } -})) - -app.Get("/", func(c *fiber.Ctx) error { - c.Response().Header.Add("Cache-Time", "6000") - return c.SendString("hi") -}) -``` - -### Config - -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Expiration is the time that an cached response will live - // - // Optional. Default: 1 * time.Minute - Expiration time.Duration - - // CacheHeader header on response header, indicate cache status, with the following possible return value - // - // hit, miss, unreachable - // - // Optional. Default: X-Cache - CacheHeader string - - // CacheControl enables client side caching if set to true - // - // Optional. Default: false - CacheControl bool - - // Key allows you to generate custom keys, by default c.Path() is used - // - // Default: func(c *fiber.Ctx) string { - // return utils.CopyString(c.Path()) - // } - KeyGenerator func(*fiber.Ctx) string - - // allows you to generate custom Expiration Key By Key, default is Expiration (Optional) - // - // Default: nil - ExpirationGenerator func(*fiber.Ctx, *Config) time.Duration - - // Store is used to store the state of the middleware - // - // Default: an in memory store for this process only - Storage fiber.Storage - - // allows you to store additional headers generated by next middlewares & handler - // - // Default: false - StoreResponseHeaders bool - - // Max number of bytes of response bodies simultaneously stored in cache. When limit is reached, - // entries with the nearest expiration are deleted to make room for new. - // 0 means no limit - // - // Default: 0 - MaxBytes uint - - // You can specify HTTP methods to cache. - // The middleware just caches the routes of its methods in this slice. - // - // Default: []string{fiber.MethodGet, fiber.MethodHead} - Methods []string -} -``` - -### Default Config - -```go -// ConfigDefault is the default config -var ConfigDefault = Config{ - Next: nil, - Expiration: 1 * time.Minute, - CacheHeader: "X-Cache", - CacheControl: false, - KeyGenerator: func(c *fiber.Ctx) string { - return utils.CopyString(c.Path()) - }, - ExpirationGenerator: nil, - StoreResponseHeaders: false, - Storage: nil, - MaxBytes: 0, - Methods: []string{fiber.MethodGet, fiber.MethodHead}, -} -``` diff --git a/middleware/compress/README.md b/middleware/compress/README.md deleted file mode 100644 index d64e958d7e..0000000000 --- a/middleware/compress/README.md +++ /dev/null @@ -1,96 +0,0 @@ -# Compress Middleware - -Compression middleware for [Fiber](https://github.com/gofiber/fiber) that will compress the response using `gzip`, `deflate` and `brotli` compression depending on the [Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header. - -- [Compress Middleware](#compress-middleware) - - [Signatures](#signatures) - - [Examples](#examples) - - [Default Config](#default-config) - - [Custom Config](#custom-config) - - [Config](#config) - - [Default Config](#default-config-1) - - [Constants](#constants) - -## Signatures - -```go -func New(config ...Config) fiber.Handler -``` - -## Examples - -First import the middleware from Fiber, - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/compress" -) -``` - -Then create a Fiber app with `app := fiber.New()`. - -### Default Config - -```go -app.Use(compress.New()) -``` - -### Custom Config - -```go -// Provide a custom compression level -app.Use(compress.New(compress.Config{ - Level: compress.LevelBestSpeed, // 1 -})) - -// Skip middleware for specific routes -app.Use(compress.New(compress.Config{ - Next: func(c *fiber.Ctx) bool { - return c.Path() == "/dont_compress" - }, - Level: compress.LevelBestSpeed, // 1 -})) -``` - -## Config - -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // CompressLevel determines the compression algoritm - // - // Optional. Default: LevelDefault - // LevelDisabled: -1 - // LevelDefault: 0 - // LevelBestSpeed: 1 - // LevelBestCompression: 2 - Level int -} -``` - -## Default Config - -```go -var ConfigDefault = Config{ - Next: nil, - Level: LevelDefault, -} -``` - -## Constants - -```go -// Compression levels -const ( - LevelDisabled = -1 - LevelDefault = 0 - LevelBestSpeed = 1 - LevelBestCompression = 2 -) -``` diff --git a/middleware/cors/README.md b/middleware/cors/README.md deleted file mode 100644 index 8c0dcf731b..0000000000 --- a/middleware/cors/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# Cross-Origin Resource Sharing (CORS) Middleware - -CORS middleware for [Fiber](https://github.com/gofiber/fiber) that that can be used to enable [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) with various options. - -## Table of Contents - -- [Cross-Origin Resource Sharing (CORS) Middleware](#cross-origin-resource-sharing-cors-middleware) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Default Config](#default-config) - - [Custom Config](#custom-config) - - [Config](#config) - - [Default Config](#default-config-1) - -## Signatures - -```go -func New(config ...Config) fiber.Handler -``` - -## Examples - -First import the middleware from Fiber, - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/cors" -) -``` - -Then create a Fiber app with `app := fiber.New()`. - -### Default Config - -```go -app.Use(cors.New()) -``` - -### Custom Config - -```go -app.Use(cors.New(cors.Config{ - AllowOrigins: "https://gofiber.io, https://gofiber.net", - AllowHeaders: "Origin, Content-Type, Accept", -})) -``` - -## Config - -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // AllowOrigin defines a list of origins that may access the resource. - // - // Optional. Default value "*" - AllowOrigins string - - // AllowMethods defines a list methods allowed when accessing the resource. - // This is used in response to a preflight request. - // - // Optional. Default value "GET,POST,HEAD,PUT,DELETE,PATCH" - AllowMethods string - - // AllowHeaders defines a list of request headers that can be used when - // making the actual request. This is in response to a preflight request. - // - // Optional. Default value "". - AllowHeaders string - - // AllowCredentials indicates whether or not the response to the request - // can be exposed when the credentials flag is true. When used as part of - // a response to a preflight request, this indicates whether or not the - // actual request can be made using credentials. - // - // Optional. Default value false. - AllowCredentials bool - - // ExposeHeaders defines a whitelist headers that clients are allowed to - // access. - // - // Optional. Default value "". - ExposeHeaders string - - // MaxAge indicates how long (in seconds) the results of a preflight request - // can be cached. - // - // Optional. Default value 0. - MaxAge int -} -``` - -## Default Config - -```go -var ConfigDefault = Config{ - Next: nil, - AllowOrigins: "*", - AllowMethods: "GET,POST,HEAD,PUT,DELETE,PATCH", - AllowHeaders: "", - AllowCredentials: false, - ExposeHeaders: "", - MaxAge: 0, -} -``` diff --git a/middleware/csrf/README.md b/middleware/csrf/README.md deleted file mode 100644 index ac57428875..0000000000 --- a/middleware/csrf/README.md +++ /dev/null @@ -1,161 +0,0 @@ -# CSRF Middleware - -CSRF middleware for [Fiber](https://github.com/gofiber/fiber) that provides [Cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) protection by passing a csrf token via cookies. This cookie value will be used to compare against the client csrf token in POST requests. When the csrf token is invalid, this middleware will delete the `csrf_` cookie and return the `fiber.ErrForbidden` error. -CSRF Tokens are generated on GET requests. You can retrieve the CSRF token with `c.Locals(contextKey)`, where `contextKey` is the string you set in the config (see Custom Config below). - -_NOTE: This middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for this middleware saves data to memory, see the examples below for other databases._ - -## Table of Contents - -- [CSRF Middleware](#csrf-middleware) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Default Config](#default-config) - - [Custom Config](#custom-config) - - [Custom Storage/Database](#custom-storagedatabase) - - [Config](#config) - - [Default Config](#default-config-1) - -## Signatures - -```go -func New(config ...Config) fiber.Handler -``` - -### Examples - -Import the middleware package that is part of the Fiber web framework - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/crsf" -) -``` - -After you initiate your Fiber app, you can use the following possibilities: - -```go -app.Use(csrf.New()) // Default config -``` - -### Custom Config - -```go -app.Use(csrf.New(csrf.Config{ - KeyLookup: "header:X-Csrf-Token", - CookieName: "csrf_", - CookieSameSite: "Lax", - Expiration: 1 * time.Hour, - KeyGenerator: utils.UUID, - Extractor: func(c *fiber.Ctx) (string, error) { ... }, -})) -``` - -Note: KeyLookup will be ignored if Extractor is explicitly set. - -### Custom Storage/Database - -You can use any storage from our [storage](https://github.com/gofiber/storage/) package. - -```go -storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3 -app.Use(csrf.New(csrf.Config{ - Storage: storage, -})) -``` - -### Config - -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // KeyLookup is a string in the form of ":" that is used - // to create an Extractor that extracts the token from the request. - // Possible values: - // - "header:" - // - "query:" - // - "param:" - // - "form:" - // - "cookie:" - // - // Ignored if an Extractor is explicitly set. - // - // Optional. Default: "header:X-CSRF-Token" - KeyLookup string - - // Name of the session cookie. This cookie will store session key. - // Optional. Default value "csrf_". - CookieName string - - // Domain of the CSRF cookie. - // Optional. Default value "". - CookieDomain string - - // Path of the CSRF cookie. - // Optional. Default value "". - CookiePath string - - // Indicates if CSRF cookie is secure. - // Optional. Default value false. - CookieSecure bool - - // Indicates if CSRF cookie is HTTP only. - // Optional. Default value false. - CookieHTTPOnly bool - - // Indicates if CSRF cookie is requested by SameSite. - // Optional. Default value "Lax". - CookieSameSite string - - // Decides whether cookie should last for only the browser sesison. - // Ignores Expiration if set to true - CookieSessionOnly bool - - // Expiration is the duration before csrf token will expire - // - // Optional. Default: 1 * time.Hour - Expiration time.Duration - - // Store is used to store the state of the middleware - // - // Optional. Default: memory.New() - Storage fiber.Storage - - // Context key to store generated CSRF token into context. - // If left empty, token will not be stored in context. - // - // Optional. Default: "" - ContextKey string - - // KeyGenerator creates a new CSRF token - // - // Optional. Default: utils.UUID - KeyGenerator func() string - - // Extractor returns the csrf token - // - // If set this will be used in place of an Extractor based on KeyLookup. - // - // Optional. Default will create an Extractor based on KeyLookup. - Extractor func(c *fiber.Ctx) (string, error) -} -``` - -### Default Config - -```go -var ConfigDefault = Config{ - KeyLookup: "header:X-Csrf-Token", - CookieName: "csrf_", - CookieSameSite: "Lax", - Expiration: 1 * time.Hour, - KeyGenerator: utils.UUID, -} -``` diff --git a/middleware/earlydata/README.md b/middleware/earlydata/README.md deleted file mode 100644 index 862e78b496..0000000000 --- a/middleware/earlydata/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# Early Data Middleware - -The Early Data middleware for [Fiber](https://github.com/gofiber/fiber) adds support for TLS 1.3's early data ("0-RTT") feature. -Citing [RFC 8446](https://datatracker.ietf.org/doc/html/rfc8446#section-2-3), when a client and server share a PSK, TLS 1.3 allows clients to send data on the first flight ("early data") to speed up the request, effectively reducing the regular 1-RTT request to a 0-RTT request. - -Make sure to enable fiber's `EnableTrustedProxyCheck` config option before using this middleware in order to not trust bogus HTTP request headers of the client. - -Also be aware that enabling support for early data in your reverse proxy (e.g. nginx, as done with a simple `ssl_early_data on;`) makes requests replayable. Refer to the following documents before continuing: - -- https://datatracker.ietf.org/doc/html/rfc8446#section-8 -- https://blog.trailofbits.com/2019/03/25/what-application-developers-need-to-know-about-tls-early-data-0rtt/ - -By default, this middleware allows early data requests on safe HTTP request methods only and rejects the request otherwise, i.e. aborts the request before executing your handler. This behavior can be controlled by the `AllowEarlyData` config option. -Safe HTTP methods — `GET`, `HEAD`, `OPTIONS` and `TRACE` — should not modify a state on the server. - -## Table of Contents - -- [Early Data Middleware](#early-data-middleware) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Default Config](#default-config) - - [Custom Config](#custom-config) - - [Config](#config) - - [Default Config](#default-config-1) - -## Signatures - -```go -func New(config ...Config) fiber.Handler -``` - -## Examples - -First import the middleware from Fiber, - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/earlydata" -) -``` - -Then create a Fiber app with `app := fiber.New()`. - -### Default Config - -```go -app.Use(earlydata.New()) -``` - -### Custom Config - -```go -app.Use(earlydata.New(earlydata.Config{ - Error: fiber.ErrTooEarly, - // ... -})) -``` - -### Config - -```go -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // IsEarlyData returns whether the request is an early-data request. - // - // Optional. Default: a function which checks if the "Early-Data" request header equals "1". - IsEarlyData func(c *fiber.Ctx) bool - - // AllowEarlyData returns whether the early-data request should be allowed or rejected. - // - // Optional. Default: a function which rejects the request on unsafe and allows the request on safe HTTP request methods. - AllowEarlyData func(c *fiber.Ctx) bool - - // Error is returned in case an early-data request is rejected. - // - // Optional. Default: fiber.ErrTooEarly. - Error error -} -``` - -### Default Config - -```go -var ConfigDefault = Config{ - IsEarlyData: func(c *fiber.Ctx) bool { - return c.Get("Early-Data") == "1" - }, - - AllowEarlyData: func(c *fiber.Ctx) bool { - return fiber.IsMethodSafe(c.Method()) - }, - - Error: fiber.ErrTooEarly, -} -``` diff --git a/middleware/encryptcookie/README.md b/middleware/encryptcookie/README.md deleted file mode 100644 index 94cb7fc836..0000000000 --- a/middleware/encryptcookie/README.md +++ /dev/null @@ -1,113 +0,0 @@ -# Encrypt Cookie Middleware - -Encrypt middleware for [Fiber](https://github.com/gofiber/fiber) which encrypts cookie values. Note: this middleware does not encrypt cookie names. - -## Table of Contents - -* [Signatures](encryptcookie.md#signatures) -* [Setup](encryptcookie.md#setup) -* [Config](encryptcookie.md#config) -* [Default Config](encryptcookie.md#default-config) - -## Signatures - -```go -// Intitializes the middleware -func New(config ...Config) fiber.Handler - -// Returns a random 32 character long string -func GenerateKey() string -``` - -## Examples - -Import the middleware package that is part of the Fiber web framework - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/encryptcookie" -) -``` - -After you initiate your Fiber app, you can use the following possibilities: - -```go -// Default middleware config -app.Use(encryptcookie.New(encryptcookie.Config{ - Key: "secret-thirty-2-character-string", -})) - -// Get / reading out the encrypted cookie -app.Get("/", func(c *fiber.Ctx) error { - return c.SendString("value=" + c.Cookies("test")) -}) - -// Post / create the encrypted cookie -app.Post("/", func(c *fiber.Ctx) error { - c.Cookie(&fiber.Cookie{ - Name: "test", - Value: "SomeThing", - }) - return nil -}) -``` - -## Config - -```go -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Array of cookie keys that should not be encrypted. - // - // Optional. Default: ["csrf_"] - Except []string - - // Base64 encoded unique key to encode & decode cookies. - // - // Required. The key should be 32 bytes of random data in base64-encoded form. - // You may run `openssl rand -base64 32` or use `encryptcookie.GenerateKey()` to generate a new key. - Key string - - // Custom function to encrypt cookies. - // - // Optional. Default: EncryptCookie - Encryptor func(decryptedString, key string) (string, error) - - // Custom function to decrypt cookies. - // - // Optional. Default: DecryptCookie - Decryptor func(encryptedString, key string) (string, error) -} -``` - -## Default Config - -```go -// `Key` must be a 32 character string. It's used to encrpyt the values, so make sure it is random and keep it secret. -// You can run `openssl rand -base64 32` or call `encryptcookie.GenerateKey()` to create a random key for you. -// Make sure not to set `Key` to `encryptcookie.GenerateKey()` because that will create a new key every run. -app.Use(encryptcookie.New(encryptcookie.Config{ - Key: "secret-thirty-2-character-string", -})) -``` - -## Usage of CSRF and Encryptcookie Middlewares with Custom Cookie Names -Normally, encryptcookie middleware skips `csrf_` cookies. However, it won't work when you use custom cookie names for CSRF. You should update `Except` config to avoid this problem. For example: - -```go -app.Use(encryptcookie.New(encryptcookie.Config{ - Key: "secret-thirty-2-character-string", - Except: []string{"csrf_1"}, // exclude CSRF cookie -})) - -app.Use(csrf.New(csrf.Config{ - KeyLookup: "form:test", - CookieName: "csrf_1", - CookieHTTPOnly: true, -})) -``` diff --git a/middleware/envvar/README.md b/middleware/envvar/README.md deleted file mode 100644 index a16923d048..0000000000 --- a/middleware/envvar/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# Exposing Environment Variables Middleware - -EnvVar middleware for [Fiber](https://github.com/gofiber/fiber) that can be used to expose environment variables with various options. - -## Table of Contents - -- [Environment Variables (EnvVar) Middleware](#environment-variables-envvar-middleware) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Default Config](#default-config) - - [Custom Config](#custom-config) - - [Response](#response) - - [Config](#config) - - [Default Config](#default-config-1) - -## Signatures - -```go -func New(config ...Config) fiber.Handler -``` - -## Examples - -First import the middleware from Fiber, - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/envvar" -) -``` - -Then create a Fiber app with `app := fiber.New()`. - -**Note**: You need to provide a path to use envvar middleware. - -### Default Config - -```go -app.Use("/expose/envvars", envvar.New()) -``` - -### Custom Config - -```go -app.Use("/expose/envvars", envvar.New( - envvar.Config{ - ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, - ExcludeVars: map[string]string{"excludeKey": ""}, - }), -) -``` - -### Response - -Http response contract: -``` -{ - "vars": { - "someEnvVariable": "someValue", - "anotherEnvVariable": "anotherValue" - } -} -``` - -## Config - -```go -// Config defines the config for middleware. -type Config struct { - // ExportVars specifies the environment variables that should export - ExportVars map[string]string - // ExcludeVars specifies the environment variables that should not export - ExcludeVars map[string]string -} -``` - -## Default Config - -```go -Config{} -``` diff --git a/middleware/etag/README.md b/middleware/etag/README.md deleted file mode 100644 index db381a6f64..0000000000 --- a/middleware/etag/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# ETag Middleware - -ETag middleware for [Fiber](https://github.com/gofiber/fiber) that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. - -## Table of Contents - -- [ETag Middleware](#etag-middleware) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Default Config](#default-config) - - [Custom Config](#custom-config) - - [Config](#config) - - [Default Config](#default-config-2) - -## Signatures - -```go -func New(config ...Config) fiber.Handler -``` - -## Examples - -Import the middleware package that is part of the Fiber web framework - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/etag" -) -``` - -After you initiate your Fiber app, you can use the following possibilities: - -### Default Config - -```go -app.Use(etag.New()) - -// Get / receives Etag: "13-1831710635" in response header -app.Get("/", func(c *fiber.Ctx) error { - return c.SendString("Hello, World!") -}) -``` - -### Custom Config - -```go -app.Use(etag.New(etag.Config{ - Weak: true, -})) - -// Get / receives Etag: "W/"13-1831710635" in response header -app.Get("/", func(c *fiber.Ctx) error { - return c.SendString("Hello, World!") -}) -``` - -## Config - -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Weak indicates that a weak validator is used. Weak etags are easy - // to generate, but are far less useful for comparisons. Strong - // validators are ideal for comparisons but can be very difficult - // to generate efficiently. Weak ETag values of two representations - // of the same resources might be semantically equivalent, but not - // byte-for-byte identical. This means weak etags prevent caching - // when byte range requests are used, but strong etags mean range - // requests can still be cached. - Weak bool -} -``` - -## Default Config - -```go -var ConfigDefault = Config{ - Next: nil, - Weak: false, -} -``` diff --git a/middleware/expvar/README.md b/middleware/expvar/README.md deleted file mode 100644 index 4abe1b5bf8..0000000000 --- a/middleware/expvar/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# Expvar Middleware - -Expvar middleware for [Fiber](https://github.com/gofiber/fiber) that serves via its HTTP server runtime exposed variants in the JSON format. The package is typically only imported for the side effect of registering its HTTP handlers. The handled path is `/debug/vars`. - -- [Expvar Middleware](#expvar-middleware) - - [Signatures](#signatures) - - [Example](#example) - -## Signatures - -```go -func New() fiber.Handler -``` - -## Example - -Import the expvar package that is part of the Fiber web framework - -```go -package main - -import ( - "expvar" - "fmt" - - "github.com/gofiber/fiber/v2" - expvarmw "github.com/gofiber/fiber/v2/middleware/expvar" -) - -var count = expvar.NewInt("count") - -func main() { - app := fiber.New() - app.Use(expvarmw.New()) - app.Get("/", func(c *fiber.Ctx) error { - count.Add(1) - - return c.SendString(fmt.Sprintf("hello expvar count %d", count.Value())) - }) - - fmt.Println(app.Listen(":3000")) -} -``` - -Visit path `/debug/vars` to see all vars and use query `r=key` to filter exposed variables. - -```bash -curl 127.0.0.1:3000 -hello expvar count 1 - -curl 127.0.0.1:3000/debug/vars -{ - "cmdline": ["xxx"], - "count": 1, - "expvarHandlerCalls": 33, - "expvarRegexpErrors": 0, - "memstats": {...} -} - -curl 127.0.0.1:3000/debug/vars?r=c -{ - "cmdline": ["xxx"], - "count": 1 -} -``` - -## Config - -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool -} -``` - -## Default Config - -```go -var ConfigDefault = Config{ - Next: nil, -} -``` \ No newline at end of file diff --git a/middleware/favicon/README.md b/middleware/favicon/README.md deleted file mode 100644 index 521d021d42..0000000000 --- a/middleware/favicon/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# Favicon Middleware - -Favicon middleware for [Fiber](https://github.com/gofiber/fiber) that ignores favicon requests or caches a provided icon in memory to improve performance by skipping disk access. User agents request favicon.ico frequently and indiscriminately, so you may wish to exclude these requests from your logs by using this middleware before your logger middleware. - -**Note** This middleware is exclusively for serving the default, implicit favicon, which is GET /favicon.ico or [custom favicon URL](#config). - -## Table of Contents -- [Favicon Middleware](#favicon-middleware) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Default Config](#default-config) - - [Custom Config](#custom-config) - - [Config](#config) - - [Default Config](#default-config-1) - -## Signatures - -```go -func New(config ...Config) fiber.Handler -``` - -## Examples - -First import the middleware from Fiber, - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/favicon" -) -``` - -Then create a Fiber app with `app := fiber.New()`. - -### Default Config - -```go -app.Use(favicon.New()) -``` - -### Custom Config -```go -app.Use(favicon.New(favicon.Config{ - File: "./favicon.ico", - URL: "/favicon.ico", -})) -``` - -### Config - -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // File holds the path to an actual favicon that will be cached - // - // Optional. Default: "" - File string - - // URL for favicon handler - // - // Optional. Default: "/favicon.ico" - URL string - - // FileSystem is an optional alternate filesystem to search for the favicon in. - // An example of this could be an embedded or network filesystem - // - // Optional. Default: nil - FileSystem http.FileSystem - - // CacheControl defines how the Cache-Control header in the response should be set - // - // Optional. Default: "public, max-age=31536000" - CacheControl string -} -``` - -### Default Config - -```go -var ConfigDefault = Config{ - Next: nil, - File: "", - URL: "/favicon.ico", -} -``` diff --git a/middleware/filesystem/README.md b/middleware/filesystem/README.md deleted file mode 100644 index 80a76c6cc7..0000000000 --- a/middleware/filesystem/README.md +++ /dev/null @@ -1,298 +0,0 @@ -# Filesystem Middleware - -Filesystem middleware for [Fiber](https://github.com/gofiber/fiber) that enables you to serve files from a directory. - -⚠️ **`:params` & `:optionals?` within the prefix path are not supported!** -⚠️ **To handle paths with spaces (or other url encoded values) make sure to set `fiber.Config{ UnescapePath: true}`** - -## Table of Contents - -- [Filesystem Middleware](#filesystem-middleware) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Config](#config) - - [embed](#embed) - - [pkger](#pkger) - - [packr](#packr) - - [go.rice](#gorice) - - [fileb0x](#fileb0x) - - [statik](#statik) - - [Config](#config-1) - - [Default Config](#default-config) - -## Signatures - -```go -func New(config Config) fiber.Handler -``` - -## Examples - -First import the middleware from Fiber, - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" -) -``` - -Then create a Fiber app with `app := fiber.New()`. - -### Config - -```go -// Provide a minimal config -app.Use(filesystem.New(filesystem.Config{ - Root: http.Dir("./assets"), -})) - -// Or extend your config for customization -app.Use(filesystem.New(filesystem.Config{ - Root: http.Dir("./assets"), - Browse: true, - Index: "index.html", - NotFoundFile: "404.html", - MaxAge: 3600, -})) -``` - -> If your environment (Go 1.16+) supports it, we recommend using Go Embed instead of the other solutions listed as this one is native to Go and the easiest to use. - -### embed - -[Embed](https://golang.org/pkg/embed/) is the native method to embed files in a Golang excecutable. Introduced in Go 1.16. - -```go -package main - -import ( - "embed" - "io/fs" - "log" - "net/http" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" -) - -// Embed a single file -//go:embed index.html -var f embed.FS - -// Embed a directory -//go:embed static/* -var embedDirStatic embed.FS - -func main() { - app := fiber.New() - - app.Use("/", filesystem.New(filesystem.Config{ - Root: http.FS(f), - })) - - // Access file "image.png" under `static/` directory via URL: `http:///static/image.png`. - // Without `PathPrefix`, you have to access it via URL: - // `http:///static/static/image.png`. - app.Use("/static", filesystem.New(filesystem.Config{ - Root: http.FS(embedDirStatic), - PathPrefix: "static", - Browse: true, - })) - - log.Fatal(app.Listen(":3000")) -} -``` - -### pkger - -[Pkger](https://github.com/markbates/pkger) can be used to embed files in a Golang excecutable. - -```go -package main - -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" - - "github.com/markbates/pkger" -) - -func main() { - app := fiber.New() - - app.Use("/assets", filesystem.New(filesystem.Config{ - Root: pkger.Dir("/assets"), - })) - - log.Fatal(app.Listen(":3000")) -} -``` - -### packr - -[Packr](https://github.com/gobuffalo/packr) can be used to embed files in a Golang excecutable. - -```go -package main - -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" - - "github.com/gobuffalo/packr/v2" -) - -func main() { - app := fiber.New() - - app.Use("/assets", filesystem.New(filesystem.Config{ - Root: packr.New("Assets Box", "/assets"), - })) - - log.Fatal(app.Listen(":3000")) -} -``` - -### go.rice - -https://github.com/GeertJohan/go.rice - -```go -package main - -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" - - "github.com/GeertJohan/go.rice" -) - -func main() { - app := fiber.New() - - app.Use("/assets", filesystem.New(filesystem.Config{ - Root: rice.MustFindBox("assets").HTTPBox(), - })) - - log.Fatal(app.Listen(":3000")) -} -``` - -### fileb0x - -[Fileb0x](https://github.com/UnnoTed/fileb0x) can be used to embed files in a Golang excecutable. - -```go -package main - -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" - - "/myEmbeddedFiles" -) - -func main() { - app := fiber.New() - - app.Use("/assets", filesystem.New(filesystem.Config{ - Root: myEmbeddedFiles.HTTP, - })) - - log.Fatal(app.Listen(":3000")) -} -``` - -### statik - -[Statik](https://github.com/rakyll/statik) can be used to embed files in a Golang excecutable. - -```go -package main - -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/filesystem" - - // Use blank to invoke init function and register data to statik - _ "/statik" - "github.com/rakyll/statik/fs" -) - -func main() { - statikFS, err := fs.New() - if err != nil { - panic(err) - } - - app := fiber.New() - - app.Use("/", filesystem.New(filesystem.Config{ - Root: statikFS, - })) - - log.Fatal(app.Listen(":3000")) -} -``` - -## Config - -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Root is a FileSystem that provides access - // to a collection of files and directories. - // - // Required. Default: nil - Root http.FileSystem `json:"-"` - - // PathPrefix defines a prefix to be added to a filepath when - // reading a file from the FileSystem. - // - // Use when using Go 1.16 embed.FS - // - // Optional. Default "" - PathPrefix string `json:"path_prefix"` - - // Enable directory browsing. - // - // Optional. Default: false - Browse bool `json:"browse"` - - // Index file for serving a directory. - // - // Optional. Default: "index.html" - Index string `json:"index"` - - // The value for the Cache-Control HTTP-header - // that is set on the file response. MaxAge is defined in seconds. - // - // Optional. Default value 0. - MaxAge int `json:"max_age"` - - // File to return if path is not found. Useful for SPA's. - // - // Optional. Default: "" - NotFoundFile string `json:"not_found_file"` -} -``` - -### Default Config - -```go -var ConfigDefault = Config{ - Next: nil, - Root: nil, - PathPrefix: "", - Browse: false, - Index: "/index.html", - MaxAge: 0, -} -``` diff --git a/middleware/idempotency/README.md b/middleware/idempotency/README.md deleted file mode 100644 index f95295fd07..0000000000 --- a/middleware/idempotency/README.md +++ /dev/null @@ -1,118 +0,0 @@ -# Idempotency Middleware - -Idempotency middleware for [Fiber](https://github.com/gofiber/fiber) allows for fault-tolerant APIs where duplicate requests — for example due to networking issues on the client-side — do not erroneously cause the same action performed multiple times on the server-side. - -Refer to https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-02 for a better understanding. - -## Table of Contents - -- [Idempotency Middleware](#idempotency-middleware) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Default Config](#default-config) - - [Custom Config](#custom-config) - - [Config](#config) - - [Default Config](#default-config-1) - -## Signatures - -```go -func New(config ...Config) fiber.Handler -``` - -## Examples - -First import the middleware from Fiber, - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/idempotency" -) -``` - -Then create a Fiber app with `app := fiber.New()`. - -### Default Config - -```go -app.Use(idempotency.New()) -``` - -### Custom Config - -```go -app.Use(idempotency.New(idempotency.Config{ - Lifetime: 42 * time.Minute, - // ... -})) -``` - -### Config - -```go -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: a function which skips the middleware on safe HTTP request method. - Next func(c *fiber.Ctx) bool - - // Lifetime is the maximum lifetime of an idempotency key. - // - // Optional. Default: 30 * time.Minute - Lifetime time.Duration - - // KeyHeader is the name of the header that contains the idempotency key. - // - // Optional. Default: X-Idempotency-Key - KeyHeader string - // KeyHeaderValidate defines a function to validate the syntax of the idempotency header. - // - // Optional. Default: a function which ensures the header is 36 characters long (the size of an UUID). - KeyHeaderValidate func(string) error - - // KeepResponseHeaders is a list of headers that should be kept from the original response. - // - // Optional. Default: nil (to keep all headers) - KeepResponseHeaders []string - - // Lock locks an idempotency key. - // - // Optional. Default: an in-memory locker for this process only. - Lock Locker - - // Storage stores response data by idempotency key. - // - // Optional. Default: an in-memory storage for this process only. - Storage fiber.Storage -} -``` - -### Default Config - -```go -var ConfigDefault = Config{ - Next: func(c *fiber.Ctx) bool { - // Skip middleware if the request was done using a safe HTTP method - return fiber.IsMethodSafe(c.Method()) - }, - - Lifetime: 30 * time.Minute, - - KeyHeader: "X-Idempotency-Key", - KeyHeaderValidate: func(k string) error { - if l, wl := len(k), 36; l != wl { // UUID length is 36 chars - return fmt.Errorf("%w: invalid length: %d != %d", ErrInvalidIdempotencyKey, l, wl) - } - - return nil - }, - - KeepResponseHeaders: nil, - - Lock: nil, // Set in configDefault so we don't allocate data here. - - Storage: nil, // Set in configDefault so we don't allocate data here. -} -``` diff --git a/middleware/limiter/README.md b/middleware/limiter/README.md deleted file mode 100644 index d259580081..0000000000 --- a/middleware/limiter/README.md +++ /dev/null @@ -1,146 +0,0 @@ -# Limiter Middleware - -Limiter middleware for [Fiber](https://github.com/gofiber/fiber) that is used to limit repeat requests to public APIs and/or endpoints such as password reset. It is also useful for API clients, web crawling, or other tasks that need to be throttled. - -_NOTE: This middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for this middleware saves data to memory, see the examples below for other databases._ - -**NOTE: this module does not share state with other processes/servers by default.** - -## Table of Contents - -- [Limiter Middleware](#limiter-middleware) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Default Config](#default-config) - - [Custom Config](#custom-config) - - [Custom Storage/Database](#custom-storagedatabase) - - [Config](#config) - - [Default Config](#default-config-1) - -## Signatures - -```go -func New(config ...Config) fiber.Handler -``` - -## Examples - -First import the middleware from Fiber, - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/limiter" -) -``` - -Then create a Fiber app with `app := fiber.New()`. - -### Default Config - -```go -// Default middleware config -app.Use(limiter.New()) -``` - -### Custom Config - -```go -// Or extend your config for customization -app.Use(limiter.New(limiter.Config{ - Next: func(c *fiber.Ctx) bool { - return c.IP() == "127.0.0.1" - }, - Max: 20, - Expiration: 30 * time.Second, - KeyGenerator: func(c *fiber.Ctx) string{ - return "key" - }, - LimitReached: func(c *fiber.Ctx) error { - return c.SendFile("./toofast.html") - }, - Storage: myCustomStore{}, -})) -``` - -### Custom Storage/Database - -You can use any storage from our [storage](https://github.com/gofiber/storage/) package. - -```go -storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3 -app.Use(limiter.New(limiter.Config{ - Storage: storage, -})) -``` - -## Config - -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Max number of recent connections during `Duration` seconds before sending a 429 response - // - // Default: 5 - Max int - - // KeyGenerator allows you to generate custom keys, by default c.IP() is used - // - // Default: func(c *fiber.Ctx) string { - // return c.IP() - // } - KeyGenerator func(*fiber.Ctx) string - - // Expiration is the time on how long to keep records of requests in memory - // - // Default: 1 * time.Minute - Expiration time.Duration - - // LimitReached is called when a request hits the limit - // - // Default: func(c *fiber.Ctx) error { - // return c.SendStatus(fiber.StatusTooManyRequests) - // } - LimitReached fiber.Handler - - // When set to true, requests with StatusCode >= 400 won't be counted. - // - // Default: false - SkipFailedRequests bool - - // When set to true, requests with StatusCode < 400 won't be counted. - // - // Default: false - SkipSuccessfulRequests bool - - // Store is used to store the state of the middleware - // - // Default: an in memory store for this process only - Storage fiber.Storage -} -``` - -A custom store can be used if it implements the `Storage` interface - more details and an example can be found in `store.go`. - -### Default Config - -```go -var ConfigDefault = Config{ - Max: 5, - Expiration: 1 * time.Minute, - KeyGenerator: func(c *fiber.Ctx) string { - return c.IP() - }, - LimitReached: func(c *fiber.Ctx) error { - return c.SendStatus(fiber.StatusTooManyRequests) - }, - SkipFailedRequests: false, - SkipSuccessfulRequests: false, -} -``` diff --git a/middleware/logger/README.md b/middleware/logger/README.md deleted file mode 100644 index 652f375fe5..0000000000 --- a/middleware/logger/README.md +++ /dev/null @@ -1,212 +0,0 @@ -# Logger Middleware -Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP request/response details. - -## Table of Contents -- [Logger Middleware](#logger-middleware) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Default Config](#default-config) - - [Logging remote IP and Port](#logging-remote-ip-and-port) - - [Logging Request ID](#logging-request-id) - - [Changing TimeZone & TimeFormat](#changing-timezone--timeformat) - - [Custom File Writer](#custom-file-writer) - - [Add Custom Tags](#add-custom-tags) - - [Config](#config) - - [Default Config](#default-config-1) - - [Constants](#constants) - -## Signatures -```go -func New(config ...Config) fiber.Handler -``` - -## Examples -First ensure the appropriate packages are imported -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/logger" -) -``` - -### Default Config -```go -// Default middleware config -app.Use(logger.New()) -``` - -### Logging remote IP and Port - -```go -app.Use(logger.New(logger.Config{ - Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", -})) -``` - -### Logging Request ID -```go -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", -})) -``` - -### Changing TimeZone & TimeFormat - -```go -app.Use(logger.New(logger.Config{ - Format: "${pid} ${status} - ${method} ${path}\n", - TimeFormat: "02-Jan-2006", - TimeZone: "America/New_York", -})) -``` - -### Custom File Writer -```go -file, err := os.OpenFile("./123.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) -if err != nil { - log.Fatalf("error opening file: %v", err) -} -defer file.Close() - -app.Use(logger.New(logger.Config{ - Output: file, -})) -``` -### Add Custom Tags -```go -app.Use(logger.New(logger.Config{ - CustomTags: map[string]logger.LogFunc{ - "custom_tag": func(output logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) { - return output.WriteString("it is a custom tag") - }, - }, -})) -``` - -### Callback after log is written - -```go -app.Use(logger.New(logger.Config{ - TimeFormat: time.RFC3339Nano, - TimeZone: "Asia/Shanghai", - Done: func(c *fiber.Ctx, logString []byte) { - if c.Response().StatusCode() != fiber.StatusOK { - reporter.SendToSlack(logString) - } - }, -})) -``` - -## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Done is a function that is called after the log string for a request is written to Output, - // and pass the log string as parameter. - // - // Optional. Default: nil - Done func(c *fiber.Ctx, logString []byte) - - // tagFunctions defines the custom tag action - // - // Optional. Default: map[string]LogFunc - CustomTags map[string]LogFunc - - // Format defines the logging tags - // - // Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n - Format string - - // TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html - // - // Optional. Default: 15:04:05 - TimeFormat string - - // TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc - // - // Optional. Default: "Local" - TimeZone string - - // TimeInterval is the delay before the timestamp is updated - // - // Optional. Default: 500 * time.Millisecond - TimeInterval time.Duration - - // Output is a writer where logs are written - // - // Default: os.Stdout - Output io.Writer -} - -type LogFunc func(buf logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) -``` - -## Default Config -```go -var ConfigDefault = Config{ - Next: nil, - Done: nil, - Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", - TimeFormat: "15:04:05", - TimeZone: "Local", - TimeInterval: 500 * time.Millisecond, - Output: os.Stdout, -} -``` - -## Constants -```go -// Logger variables -const ( - TagPid = "pid" - TagTime = "time" - TagReferer = "referer" - TagProtocol = "protocol" - TagPort = "port" - TagIP = "ip" - TagIPs = "ips" - TagHost = "host" - TagMethod = "method" - TagPath = "path" - TagURL = "url" - TagUA = "ua" - TagLatency = "latency" - TagStatus = "status" // response status - TagResBody = "resBody" // response body - TagReqHeaders = "reqHeaders" - TagQueryStringParams = "queryParams" // request query parameters - TagBody = "body" // request body - TagBytesSent = "bytesSent" - TagBytesReceived = "bytesReceived" - TagRoute = "route" - TagError = "error" - // Deprecated: Use TagReqHeader instead - TagHeader = "header:" // request header - TagReqHeader = "reqHeader:" // request header - TagRespHeader = "respHeader:" // response header - TagQuery = "query:" // request query - TagForm = "form:" // request form - TagCookie = "cookie:" // request cookie - TagLocals = "locals:" - - // colors - TagBlack = "black" - TagRed = "red" - TagGreen = "green" - TagYellow = "yellow" - TagBlue = "blue" - TagMagenta = "magenta" - TagCyan = "cyan" - TagWhite = "white" - TagReset = "reset" -) -``` diff --git a/middleware/monitor/README.md b/middleware/monitor/README.md deleted file mode 100644 index 5987aec00b..0000000000 --- a/middleware/monitor/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# Monitor -Monitor middleware for [Fiber](https://github.com/gofiber/fiber) that reports server metrics, inspired by [express-status-monitor](https://github.com/RafalWilinski/express-status-monitor) - -:warning: **Warning:** Monitor is still in beta, API might change in the future! - -![](https://i.imgur.com/nHAtBpJ.gif) - -### Signatures -```go -func New() fiber.Handler -``` - -### Examples -Import the middleware package and assign it to a route. -```go -package main - -import ( - "log" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/monitor" -) - -func main() { - app := fiber.New() - - app.Get("/metrics", monitor.New(monitor.Config{Title: "MyService Metrics Page"})) - - log.Fatal(app.Listen(":3000")) -} -``` -You can also access the API endpoint with -`curl -X GET -H "Accept: application/json" http://localhost:3000/metrics` which returns: -```json -{"pid":{ "cpu":0.4568381746582226, "ram":20516864, "conns":3 }, - "os": { "cpu":8.759124087593099, "ram":3997155328, "conns":44, - "total_ram":8245489664, "load_avg":0.51 }} -``` - -## Config - -```go -// Config defines the config for middleware. -type Config struct { - // Metrics page title - // - // Optional. Default: "Fiber Monitor" - Title string - - // Refresh period - // - // Optional. Default: 3 seconds - Refresh time.Duration - - // Whether the service should expose only the monitoring API. - // - // Optional. Default: false - APIOnly bool - - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Custom HTML Code to Head Section(Before End) - // - // Optional. Default: empty - CustomHead string - - // FontURL for specify font resource path or URL . also you can use relative path - // - // Optional. Default: https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap - - FontURL string - // ChartJsURL for specify ChartJS library path or URL . also you can use relative path - // - // Optional. Default: https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js - - ChartJsURL string - -} -``` - -## Default Config - -```go -var ConfigDefault = Config{ - Title: "Fiber Monitor", - Refresh: 3 * time.Second, - APIOnly: false, - Next: nil, - CustomHead:"", - FontURL:"https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap", - ChartJsURL:"https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js" - -} -``` diff --git a/middleware/pprof/README.md b/middleware/pprof/README.md deleted file mode 100644 index 2a5bbc2f41..0000000000 --- a/middleware/pprof/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Pprof -Pprof middleware for [Fiber](https://github.com/gofiber/fiber) that serves via its HTTP server runtime profiling data in the format expected by the pprof visualization tool. The package is typically only imported for the side effect of registering its HTTP handlers. The handled paths all begin with /debug/pprof/. - -- [Signatures](#signatures) -- [Examples](#examples) - -### Signatures -```go -func New() fiber.Handler -``` - -### Examples -Import the middleware package that is part of the Fiber web framework -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/pprof" -) -``` - -After you initiate your Fiber app, you can use the following possibilities: -```go -// Default middleware -app.Use(pprof.New()) -``` - -In systems where you have multiple ingress endpoints, it is common to add a URL prefix, like so: - -```go -// Default middleware -app.Use(pprof.New(pprof.Config{Prefix: "/endpoint-prefix"})) -``` - -This prefix will be added to the default path of "/debug/pprof/", for a resulting URL of: -"/endpoint-prefix/debug/pprof/". - -## Config - -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Prefix defines a URL prefix added before "/debug/pprof". - // Note that it should start with (but not end with) a slash. - // Example: "/federated-fiber" - // - // Optional. Default: "" - Prefix string -} -``` - -## Default Config - -```go -var ConfigDefault = Config{ - Next: nil, -} -``` diff --git a/middleware/proxy/README.md b/middleware/proxy/README.md deleted file mode 100644 index e8663878fc..0000000000 --- a/middleware/proxy/README.md +++ /dev/null @@ -1,208 +0,0 @@ -# Proxy - -Proxy middleware for [Fiber](https://github.com/gofiber/fiber) that allows you to proxy requests to multiple servers. - -### Table of Contents - -- [Signatures](#signatures) -- [Examples](#examples) -- [Config](#config) -- [Default Config](#default-config) - -### Signatures - -```go -// Balancer create a load balancer among multiple upstrem servers. -func Balancer(config Config) fiber.Handler -// Forward performs the given http request and fills the given http response. -func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler -// Do performs the given http request and fills the given http response. -func Do(c *fiber.Ctx, addr string, clients ...*fasthttp.Client) error -// DoRedirects performs the given http request and fills the given http response while following up to maxRedirectsCount redirects. -func DoRedirects(c *fiber.Ctx, addr string, maxRedirectsCount int, clients ...*fasthttp.Client) error -// DoDeadline performs the given request and waits for response until the given deadline. -func DoDeadline(c *fiber.Ctx, addr string, deadline time.Time, clients ...*fasthttp.Client) error -// DoTimeout performs the given request and waits for response during the given timeout duration. -func DoTimeout(c *fiber.Ctx, addr string, timeout time.Duration, clients ...*fasthttp.Client) error -// DomainForward the given http request based on the given domain and fills the given http response -func DomainForward(hostname string, addr string, clients ...*fasthttp.Client) fiber.Handler -// BalancerForward performs the given http request based round robin balancer and fills the given http response -func BalancerForward(servers []string, clients ...*fasthttp.Client) fiber.Handler -``` - -### Examples - -Import the middleware package that is part of the Fiber web framework - -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/proxy" -) -``` - -After you initiate your Fiber app, you can use the following possibilities: - -```go -// if target https site uses a self-signed certificate, you should -// call WithTlsConfig before Do and Forward -proxy.WithTlsConfig(&tls.Config{ - InsecureSkipVerify: true, -}) - -// if you need to use global self-custom client, you should use proxy.WithClient. -proxy.WithClient(&fasthttp.Client{ - NoDefaultUserAgentHeader: true, - DisablePathNormalizing: true, -}) - -// Forward to url -app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif")) - -// If you want to forward with a specific domain. You have to use proxy.DomainForward. -app.Get("/payments", proxy.DomainForward("docs.gofiber.io", "http://localhost:8000")) - -// Forward to url with local custom client -app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif", &fasthttp.Client{ - NoDefaultUserAgentHeader: true, - DisablePathNormalizing: true, -})) - -// Make request within handler -app.Get("/:id", func(c *fiber.Ctx) error { - url := "https://i.imgur.com/"+c.Params("id")+".gif" - if err := proxy.Do(c, url); err != nil { - return err - } - // Remove Server header from response - c.Response().Header.Del(fiber.HeaderServer) - return nil -}) - -// Make proxy requests while following redirects -app.Get("/proxy", func(c *fiber.Ctx) error { - if err := proxy.DoRedirects(c, "http://google.com", 3); err != nil { - return err - } - // Remove Server header from response - c.Response().Header.Del(fiber.HeaderServer) - return nil -}) - -// Make proxy requests and wait up to 5 seconds before timing out -app.Get("/proxy", func(c *fiber.Ctx) error { - if err := proxy.DoTimeout(c, "http://localhost:3000", time.Second * 5); err != nil { - return err - } - // Remove Server header from response - c.Response().Header.Del(fiber.HeaderServer) - return nil -}) - -// Make proxy requests, timeout a minute from now -app.Get("/proxy", func(c *fiber.Ctx) error { - if err := DoDeadline(c, "http://localhost", time.Now().Add(time.Minute)); err != nil { - return err - } - // Remove Server header from response - c.Response().Header.Del(fiber.HeaderServer) - return nil -}) - -// Minimal round robin balancer -app.Use(proxy.Balancer(proxy.Config{ - Servers: []string{ - "http://localhost:3001", - "http://localhost:3002", - "http://localhost:3003", - }, -})) - -// Or extend your balancer for customization -app.Use(proxy.Balancer(proxy.Config{ - Servers: []string{ - "http://localhost:3001", - "http://localhost:3002", - "http://localhost:3003", - }, - ModifyRequest: func(c *fiber.Ctx) error { - c.Request().Header.Add("X-Real-IP", c.IP()) - return nil - }, - ModifyResponse: func(c *fiber.Ctx) error { - c.Response().Header.Del(fiber.HeaderServer) - return nil - }, -})) - -// Or this way if the balancer is using https and the destination server is only using http. -app.Use(proxy.BalancerForward([]string{ - "http://localhost:3001", - "http://localhost:3002", - "http://localhost:3003", -})) -``` - -### Config - -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Servers defines a list of :// HTTP servers, - // - // which are used in a round-robin manner. - // i.e.: "https://foobar.com, http://www.foobar.com" - // - // Required - Servers []string - - // ModifyRequest allows you to alter the request - // - // Optional. Default: nil - ModifyRequest fiber.Handler - - // ModifyResponse allows you to alter the response - // - // Optional. Default: nil - ModifyResponse fiber.Handler - - // Timeout is the request timeout used when calling the proxy client - // - // Optional. Default: 1 second - Timeout time.Duration - - // Per-connection buffer size for requests' reading. - // This also limits the maximum header size. - // Increase this buffer if your clients send multi-KB RequestURIs - // and/or multi-KB headers (for example, BIG cookies). - ReadBufferSize int - - // Per-connection buffer size for responses' writing. - WriteBufferSize int - - // tls config for the http client. - TlsConfig *tls.Config - - // Client is custom client when client config is complex. - // Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig - // will not be used if the client are set. - Client *fasthttp.LBClient -} -``` - -### Default Config - -```go -// ConfigDefault is the default config -var ConfigDefault = Config{ - Next: nil, - ModifyRequest: nil, - ModifyResponse: nil, - Timeout: fasthttp.DefaultLBClientTimeout, -} -``` diff --git a/middleware/recover/README.md b/middleware/recover/README.md deleted file mode 100644 index dc0ad6f96f..0000000000 --- a/middleware/recover/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# Recover -Recover middleware for [Fiber](https://github.com/gofiber/fiber) that recovers from panics anywhere in the stack chain and handles the control to the centralized [ErrorHandler](https://docs.gofiber.io/error-handling). - -### Table of Contents -- [Signatures](#signatures) -- [Examples](#examples) -- [Config](#config) -- [Default Config](#default-config) - - -### Signatures -```go -func New(config ...Config) fiber.Handler -``` - -### Examples -Import the middleware package that is part of the Fiber web framework -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/recover" -) -``` - -After you initiate your Fiber app, you can use the following possibilities: -```go -// Default middleware config -app.Use(recover.New()) - -// This panic will be caught by the middleware -app.Get("/", func(c *fiber.Ctx) error { - panic("I'm an error") -}) -``` - -### Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // EnableStackTrace enables handling stack trace - // - // Optional. Default: false - EnableStackTrace bool - - // StackTraceHandler defines a function to handle stack trace - // - // Optional. Default: defaultStackTraceHandler - StackTraceHandler func(c *fiber.Ctx, e interface{}) -} -``` - -### Default Config -```go -var ConfigDefault = Config{ - Next: nil, - EnableStackTrace: false, - StackTraceHandler: defaultStackTraceHandler, -} -``` diff --git a/middleware/requestid/README.md b/middleware/requestid/README.md deleted file mode 100644 index ffba06629b..0000000000 --- a/middleware/requestid/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# RequestID -RequestID middleware for [Fiber](https://github.com/gofiber/fiber) that adds an identifier to the response. - -### Table of Contents -- [Signatures](#signatures) -- [Examples](#examples) -- [Config](#config) -- [Default Config](#default-config) - - -### Signatures -```go -func New(config ...Config) fiber.Handler -``` - -### Examples -Import the middleware package that is part of the Fiber web framework -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/requestid" -) -``` - -After you initiate your Fiber app, you can use the following possibilities: -```go -// Default middleware config -app.Use(requestid.New()) - -// Or extend your config for customization -app.Use(requestid.New(requestid.Config{ - Header: "X-Custom-Header", - Generator: func() string { - return "static-id" - }, -})) -``` - -### Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Header is the header key where to get/set the unique request ID - // - // Optional. Default: "X-Request-ID" - Header string - - // Generator defines a function to generate the unique identifier. - // - // Optional. Default: utils.UUID - Generator func() string - - // ContextKey defines the key used when storing the request ID in - // the locals for a specific request. - // - // Optional. Default: requestid - ContextKey string -} -``` - -### Default Config -The default config uses a fast UUID generator which will expose the number of -requests made to the server. To conceal this value for better privacy, use the -`utils.UUIDv4` generator. - -```go -var ConfigDefault = Config{ - Next: nil, - Header: fiber.HeaderXRequestID, - Generator: utils.UUID, - ContextKey: "requestid", -} -``` diff --git a/middleware/session/README.md b/middleware/session/README.md deleted file mode 100644 index 5e46cb1a6f..0000000000 --- a/middleware/session/README.md +++ /dev/null @@ -1,170 +0,0 @@ -# Session - -Session middleware for [Fiber](https://github.com/gofiber/fiber). - -_NOTE: This middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for this middleware saves data to memory, see the examples below for other databases._ - -## Table of Contents - -- [Session](#session) - - [Table of Contents](#table-of-contents) - - [Signatures](#signatures) - - [Examples](#examples) - - [Default Configuration](#default-configuration) - - [Custom Storage/Database](#custom-storagedatabase) - - [Config](#config) - - [Default Config](#default-config) - -## Signatures - -```go -func New(config ...Config) *Store -func (s *Store) RegisterType(i interface{}) -func (s *Store) Get(c *fiber.Ctx) (*Session, error) -func (s *Store) Reset() error - -func (s *Session) Get(key string) interface{} -func (s *Session) Set(key string, val interface{}) -func (s *Session) Delete(key string) -func (s *Session) Destroy() error -func (s *Session) Regenerate() error -func (s *Session) Save() error -func (s *Session) Fresh() bool -func (s *Session) ID() string -func (s *Session) Keys() []string -func (s *Session) SetExpiry(time.Duration) -``` - -**⚠ _Storing `interface{}` values are limited to built-ins Go types_** - -### Examples -Import the middleware package that is part of the Fiber web framework -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/session" -) -``` - -Then create a Fiber app with `app := fiber.New()`. - -### Default Configuration - -```go -// This stores all of your app's sessions -// Default middleware config -store := session.New() - -// This panic will be catch by the middleware -app.Get("/", func(c *fiber.Ctx) error { - // Get session from storage - sess, err := store.Get(c) - if err != nil { - panic(err) - } - - // Get value - name := sess.Get("name") - - // Set key/value - sess.Set("name", "john") - - // Get all Keys - keys := sess.Keys() - - // Delete key - sess.Delete("name") - - // Destroy session - if err := sess.Destroy(); err != nil { - panic(err) - } - - // Sets a specific expiration for this session - sess.SetExpiry(time.Second * 2) - - // Save session - if err := sess.Save(); err != nil { - panic(err) - } - - return c.SendString(fmt.Sprintf("Welcome %v", name)) -}) -``` - -### Custom Storage/Database - -You can use any storage from our [storage](https://github.com/gofiber/storage/) package. - -```go -storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3 -store := session.New(session.Config{ - Storage: storage, -}) -``` - -To use the the store, see the above example. - -## Config - -```go -// Config defines the config for middleware. -type Config struct { - // Allowed session duration - // Optional. Default value 24 * time.Hour - Expiration time.Duration - - // Storage interface to store the session data - // Optional. Default value memory.New() - Storage fiber.Storage - - // KeyLookup is a string in the form of ":" that is used - // to extract session id from the request. - // Possible values: "header:", "query:" or "cookie:" - // Optional. Default value "cookie:session_id". - KeyLookup string - - // Domain of the cookie. - // Optional. Default value "". - CookieDomain string - - // Path of the cookie. - // Optional. Default value "". - CookiePath string - - // Indicates if cookie is secure. - // Optional. Default value false. - CookieSecure bool - - // Indicates if cookie is HTTP only. - // Optional. Default value false. - CookieHTTPOnly bool - - // Sets the cookie SameSite attribute. - // Optional. Default value "Lax". - CookieSameSite string - - // KeyGenerator generates the session key. - // Optional. Default value utils.UUID - KeyGenerator func() string - - // Deprecated: Please use KeyLookup - CookieName string - - // Source defines where to obtain the session id - source Source - - // The session name - sessionName string -} -``` - -## Default Config - -```go -var ConfigDefault = Config{ - Expiration: 24 * time.Hour, - KeyLookup: "cookie:session_id", - KeyGenerator: utils.UUID, -} -``` diff --git a/middleware/skip/README.md b/middleware/skip/README.md deleted file mode 100644 index fb66c7bedf..0000000000 --- a/middleware/skip/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Skip -Skip middleware for [Fiber](https://github.com/gofiber/fiber) that skips a wrapped handler is a predicate is true. - -### Table of Contents -- [Signatures](#signatures) -- [Examples](#examples) - - -### Signatures -```go -func New(handler fiber.Handler, exclude func(c *fiber.Ctx) bool) fiber.Handler -``` - -### Examples -Import the middleware package that is part of the Fiber web framework -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/skip" -) -``` - -After you initiate your Fiber app, you can use the following possibilities: -```go -app.Use(skip.New(handler, func(ctx *fiber.Ctx) bool { return ctx.Method() == fiber.MethodOptions })) -``` diff --git a/middleware/timeout/README.md b/middleware/timeout/README.md deleted file mode 100644 index 55ada503dd..0000000000 --- a/middleware/timeout/README.md +++ /dev/null @@ -1,98 +0,0 @@ -# Timeout -Timeout middleware for Fiber. As a `fiber.Handler` wrapper, it creates a context with `context.WithTimeout` and pass it in `UserContext`. - -If the context passed executions (eg. DB ops, Http calls) takes longer than the given duration to return, the timeout error is set and forwarded to the centralized `ErrorHandler`. - -It has no race conditions, ready to use on production. - -### Table of Contents -- [Signatures](#signatures) -- [Examples](#examples) - - -### Signatures -```go -func New(handler fiber.Handler, timeout time.Duration, timeoutErrors ...error) fiber.Handler -``` - -### Examples -Import the middleware package that is part of the Fiber web framework -```go -import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/timeout" -) -``` - -Sample timeout middleware usage -```go -func main() { - app := fiber.New() - h := func(c *fiber.Ctx) error { - sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") - if err := sleepWithContext(c.UserContext(), sleepTime); err != nil { - return fmt.Errorf("%w: execution error", err) - } - return nil - } - - app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second)) - _ = app.Listen(":3000") -} - -func sleepWithContext(ctx context.Context, d time.Duration) error { - timer := time.NewTimer(d) - select { - case <-ctx.Done(): - if !timer.Stop() { - <-timer.C - } - return context.DeadlineExceeded - case <-timer.C: - } - return nil -} -``` - -Test http 200 with curl: -```bash -curl --location -I --request GET 'http://localhost:3000/foo/1000' -``` - -Test http 408 with curl: -```bash -curl --location -I --request GET 'http://localhost:3000/foo/3000' -``` - - -When using with custom error: -```go -var ErrFooTimeOut = errors.New("foo context canceled") - -func main() { - app := fiber.New() - h := func(c *fiber.Ctx) error { - sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") - if err := sleepWithContextWithCustomError(c.UserContext(), sleepTime); err != nil { - return fmt.Errorf("%w: execution error", err) - } - return nil - } - - app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second, ErrFooTimeOut)) - _ = app.Listen(":3000") -} - -func sleepWithContextWithCustomError(ctx context.Context, d time.Duration) error { - timer := time.NewTimer(d) - select { - case <-ctx.Done(): - if !timer.Stop() { - <-timer.C - } - return ErrFooTimeOut - case <-timer.C: - } - return nil -} -``` From c1c119337f2f645863956307ca859be51f565aae Mon Sep 17 00:00:00 2001 From: Hong Seungwoo <34399997+HHongSeungWoo@users.noreply.github.com> Date: Wed, 8 Mar 2023 23:59:36 +0900 Subject: [PATCH 021/162] Fix typo in docs (#2357) Co-authored-by: Seungwoo Hong --- docs/partials/routing/handler.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/partials/routing/handler.md b/docs/partials/routing/handler.md index fc8ca23d79..2d198f02f8 100644 --- a/docs/partials/routing/handler.md +++ b/docs/partials/routing/handler.md @@ -60,7 +60,7 @@ app.Use([]string{"/api", "/home"}, func(c *fiber.Ctx) error { }) // Attach multiple handlers -app.Use("/api",func(c *fiber.Ctx) error { +app.Use("/api", func(c *fiber.Ctx) error { c.Set("X-Custom-Header", random.String(32)) return c.Next() }, func(c *fiber.Ctx) error { From 9e440635f08a6c7f2b0d2e20546c34ea28380f94 Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 10 Mar 2023 10:30:52 +0100 Subject: [PATCH 022/162] enable benchmark summary for pull requests/ fix auto labeler (#2365) * enable benchmark summary for pull requests * enable benchmark summary for pull requests * correct auto labeler --- .github/labeler.yml | 17 +++++++++++++++++ .github/workflows/auto-labeler.yml | 2 +- .github/workflows/benchmark.yml | 5 +++-- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 .github/labeler.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 0000000000..4f215ac905 --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,17 @@ +version: v1 +labels: + - label: '📒 Documentation' + matcher: + title: '\b(docs|doc:|\[doc\]|README|typos|comment|documentation)\b|[📝📒📚]' + - label: '☢️ Bug' + matcher: + title: '\b(fix|race|bug|missing|correct)\b|[🐛☢🩹🚨]' + - label: '🧹 Updates' + matcher: + title: '\b(improve|update|refactor|deprecated|remove|unused|test)\b|[⚡👷🚧♻️🎨🧪🧹]' + - label: '🤖 Dependencies' + matcher: + title: '\b(bumb|bdependencies)/b|[📦🤖]' + - label: '✏️ Feature' + matcher: + title: '\b(feature|create|implement|add)\b|[🚀✨🔥]' diff --git a/.github/workflows/auto-labeler.yml b/.github/workflows/auto-labeler.yml index c86bd68a48..ef7299117e 100644 --- a/.github/workflows/auto-labeler.yml +++ b/.github/workflows/auto-labeler.yml @@ -16,6 +16,6 @@ jobs: steps: - name: Check Labels id: labeler - uses: fuxingloh/multi-labeler@v1 + uses: fuxingloh/multi-labeler@v2 with: github-token: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 6150c2c8c6..4c0eed42c0 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -31,7 +31,7 @@ jobs: path: ./cache key: ${{ runner.os }}-benchmark - name: Save Benchmark Results - uses: rhysd/github-action-benchmark@v1 + uses: benchmark-action/github-action-benchmark@v1 with: tool: 'go' output-file-path: output.txt @@ -40,5 +40,6 @@ jobs: fail-on-alert: true comment-on-alert: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} # Enable Job Summary for PRs - #summary-always: ${{ github.event_name != 'push' && github.event_name != 'workflow_dispatch' }} + summary-always: ${{ github.event_name != 'push' && github.event_name != 'workflow_dispatch' }} auto-push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} + save-data-file: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} From 01fcdedcef5a1a834cc9ff6e606fc882cbf44c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sun, 12 Mar 2023 09:31:49 +0100 Subject: [PATCH 023/162] improve labeler config --- .github/labeler.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 4f215ac905..1ecaf74253 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -2,16 +2,19 @@ version: v1 labels: - label: '📒 Documentation' matcher: - title: '\b(docs|doc:|\[doc\]|README|typos|comment|documentation)\b|[📝📒📚]' + title: '\b(docs|doc:|\[doc\]|README|typos|comment|documentation)\b' - label: '☢️ Bug' matcher: - title: '\b(fix|race|bug|missing|correct)\b|[🐛☢🩹🚨]' + title: '\b(fix|race|bug|missing|correct)\b' - label: '🧹 Updates' matcher: - title: '\b(improve|update|refactor|deprecated|remove|unused|test)\b|[⚡👷🚧♻️🎨🧪🧹]' + title: '\b(improve|update|refactor|deprecated|remove|unused|test)\b' - label: '🤖 Dependencies' matcher: - title: '\b(bumb|bdependencies)/b|[📦🤖]' + title: '\b(bumb|bdependencies)\b' - label: '✏️ Feature' matcher: - title: '\b(feature|create|implement|add)\b|[🚀✨🔥]' + title: '\b(feature|create|implement|add)\b' + - label: '🤔 Question' + matcher: + title: '\b(question|how)\b' From 634f163e3f6292e658e61d0dd9e3c475d87b5d54 Mon Sep 17 00:00:00 2001 From: Tumushimire Yves Date: Sun, 12 Mar 2023 20:14:22 +0200 Subject: [PATCH 024/162] =?UTF-8?q?=F0=9F=9A=80=20[Feature]:=20SessionOnly?= =?UTF-8?q?=20when=20cookie.Expires=20is=200=20(#2152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feature: session only for zero expire cookie #2145 * refactor condition to set MaxAge and Expire on cookie * move checking zero maxage and expire in session middleware Signed-off-by: Yves Tumushimire * feature: session only for zero expire cookie #2145 * refactor condition to set MaxAge and Expire on cookie * move checking zero maxage and expire in session middleware Signed-off-by: Yves Tumushimire * CR changes * some updates --------- Signed-off-by: Yves Tumushimire Co-authored-by: Muhammed Efe Çetin Co-authored-by: René Werner --- ctx_test.go | 8 ++++++++ docs/api/middleware/session.md | 5 +++++ middleware/session/config.go | 5 +++++ middleware/session/session.go | 8 ++++++-- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/ctx_test.go b/ctx_test.go index cecef6ab9f..0bbd8351d0 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -721,6 +721,14 @@ func Test_Ctx_Cookie(t *testing.T) { cookie.MaxAge = 10000 c.Cookie(cookie) utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) + + expect = "username=john; path=/; secure; SameSite=None" + // should remove expires and max-age headers when no expire and no MaxAge (default time) + cookie.SessionOnly = false + cookie.Expires = time.Time{} + cookie.MaxAge = 0 + c.Cookie(cookie) + utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) } // go test -v -run=^$ -bench=Benchmark_Ctx_Cookie -benchmem -count=4 diff --git a/docs/api/middleware/session.md b/docs/api/middleware/session.md index c637f78219..6c746ceb49 100644 --- a/docs/api/middleware/session.md +++ b/docs/api/middleware/session.md @@ -134,6 +134,11 @@ type Config struct { // Optional. Default value "Lax". CookieSameSite string + // Decides whether cookie should last for only the browser sesison. + // Ignores Expiration if set to true + // Optional. Default value false. + CookieSessionOnly bool + // KeyGenerator generates the session key. // Optional. Default value utils.UUID KeyGenerator func() string diff --git a/middleware/session/config.go b/middleware/session/config.go index bee6c2d384..758db5538a 100644 --- a/middleware/session/config.go +++ b/middleware/session/config.go @@ -46,6 +46,11 @@ type Config struct { // Optional. Default value "Lax". CookieSameSite string + // Decides whether cookie should last for only the browser sesison. + // Ignores Expiration if set to true + // Optional. Default value false. + CookieSessionOnly bool + // KeyGenerator generates the session key. // Optional. Default value utils.UUIDv4 KeyGenerator func() string diff --git a/middleware/session/session.go b/middleware/session/session.go index 34ff67da98..fab7e4867b 100644 --- a/middleware/session/session.go +++ b/middleware/session/session.go @@ -197,8 +197,12 @@ func (s *Session) setSession() { fcookie.SetValue(s.id) fcookie.SetPath(s.config.CookiePath) fcookie.SetDomain(s.config.CookieDomain) - fcookie.SetMaxAge(int(s.exp.Seconds())) - fcookie.SetExpire(time.Now().Add(s.exp)) + // Cookies are also session cookies if they do not specify the Expires or Max-Age attribute. + // refer: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie + if !s.config.CookieSessionOnly { + fcookie.SetMaxAge(int(s.exp.Seconds())) + fcookie.SetExpire(time.Now().Add(s.exp)) + } fcookie.SetSecure(s.config.CookieSecure) fcookie.SetHTTPOnly(s.config.CookieHTTPOnly) From 0dee42a57cd76d7922a753d437894fa214819a63 Mon Sep 17 00:00:00 2001 From: RW Date: Tue, 14 Mar 2023 11:24:11 +0100 Subject: [PATCH 025/162] Update ctx.md ip documentation --- docs/api/ctx.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 4ba2c4cd71..56a15520bd 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -614,6 +614,14 @@ app.Get("/", func(c *fiber.Ctx) error { }) ``` +When registering the proxy request header in the fiber app, the ip address of the header is returned [(Fiber configuration)](fiber.md#config) + +```go +app := fiber.New(fiber.Config{ + ProxyHeader: fiber.HeaderXForwardedFor, +}) +``` + ## IPs Returns an array of IP addresses specified in the [X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) request header. From 01d39dbb81f48812b0be807641fb56249d97535a Mon Sep 17 00:00:00 2001 From: RW Date: Tue, 14 Mar 2023 11:41:39 +0100 Subject: [PATCH 026/162] Update benchmark.yml --- .github/workflows/benchmark.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 4c0eed42c0..017c680efd 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -39,7 +39,7 @@ jobs: benchmark-data-dir-path: 'benchmarks' fail-on-alert: true comment-on-alert: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} - # Enable Job Summary for PRs - summary-always: ${{ github.event_name != 'push' && github.event_name != 'workflow_dispatch' }} + # Enable Job Summary for PRs - deactivated because of issues + #summary-always: ${{ github.event_name != 'push' && github.event_name != 'workflow_dispatch' }} auto-push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} save-data-file: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} From 678728de6d38a60667b3390b3e0c28f99ff041d9 Mon Sep 17 00:00:00 2001 From: UtopiaGitHub <50892490+UtopiaGitHub@users.noreply.github.com> Date: Tue, 14 Mar 2023 18:41:48 +0800 Subject: [PATCH 027/162] =?UTF-8?q?=F0=9F=90=9B=20[Bug-Fix]:=20add=20lock?= =?UTF-8?q?=20to=20avoid=20data=20race=20#2360=20(#2368)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update memory.go The fix is to protect the access to s.db and save the result to a local variable. --- internal/storage/memory/memory.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/storage/memory/memory.go b/internal/storage/memory/memory.go index 1a56106106..b3f70e6c09 100644 --- a/internal/storage/memory/memory.go +++ b/internal/storage/memory/memory.go @@ -139,5 +139,7 @@ func (s *Storage) gc() { // Return database client func (s *Storage) Conn() map[string]entry { + s.mux.RLock() + defer s.mux.RUnlock() return s.db } From d7b36cde54a0a5f96f0fb0fe0e9030bd506f1305 Mon Sep 17 00:00:00 2001 From: Benjamin Grosse Date: Tue, 14 Mar 2023 18:37:10 +0000 Subject: [PATCH 028/162] :bug: requestid.Config.ContextKey is interface{} (#2369) requestid.Config.ContextKey is interface{} Consistent with c.Locals(key inteface{}, ...). Fixes #2356 --- docs/api/middleware/requestid.md | 2 +- middleware/requestid/config.go | 2 +- middleware/requestid/requestid_test.go | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/api/middleware/requestid.md b/docs/api/middleware/requestid.md index 9db86af7b1..7617f76ae9 100644 --- a/docs/api/middleware/requestid.md +++ b/docs/api/middleware/requestid.md @@ -61,7 +61,7 @@ type Config struct { // the locals for a specific request. // // Optional. Default: requestid - ContextKey string + ContextKey interface{} } ``` diff --git a/middleware/requestid/config.go b/middleware/requestid/config.go index b3b605e590..cc69907822 100644 --- a/middleware/requestid/config.go +++ b/middleware/requestid/config.go @@ -26,7 +26,7 @@ type Config struct { // the locals for a specific request. // // Optional. Default: requestid - ContextKey string + ContextKey interface{} } // ConfigDefault is the default config diff --git a/middleware/requestid/requestid_test.go b/middleware/requestid/requestid_test.go index 451e96b4b2..ee3d33d629 100644 --- a/middleware/requestid/requestid_test.go +++ b/middleware/requestid/requestid_test.go @@ -55,20 +55,21 @@ func Test_RequestID_Next(t *testing.T) { func Test_RequestID_Locals(t *testing.T) { t.Parallel() reqID := "ThisIsARequestId" - ctxKey := "ThisIsAContextKey" + type ContextKey int + const requestContextKey ContextKey = iota app := fiber.New() app.Use(New(Config{ Generator: func() string { return reqID }, - ContextKey: ctxKey, + ContextKey: requestContextKey, })) var ctxVal string app.Use(func(c *fiber.Ctx) error { - ctxVal = c.Locals(ctxKey).(string) //nolint:forcetypeassert,errcheck // We always store a string in here + ctxVal = c.Locals(requestContextKey).(string) //nolint:forcetypeassert,errcheck // We always store a string in here return c.Next() }) From 56839b433ea67b0c2159f30ee34a34bb9c8e45a2 Mon Sep 17 00:00:00 2001 From: Rorke76753 <36150718+Rorke76753@users.noreply.github.com> Date: Wed, 15 Mar 2023 16:45:42 +0800 Subject: [PATCH 029/162] =?UTF-8?q?=F0=9F=9A=80=20[Feature]:=20middleware/?= =?UTF-8?q?requestid:=20don't=20call=20"Generator"=20func=20on=20existing?= =?UTF-8?q?=20request=20ID=20header=20(#2371)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit call uuid generator only if rid is empty --- middleware/requestid/requestid.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/middleware/requestid/requestid.go b/middleware/requestid/requestid.go index cc77803815..6e076282f1 100644 --- a/middleware/requestid/requestid.go +++ b/middleware/requestid/requestid.go @@ -16,7 +16,10 @@ func New(config ...Config) fiber.Handler { return c.Next() } // Get id from request, else we generate one - rid := c.Get(cfg.Header, cfg.Generator()) + rid := c.Get(cfg.Header) + if rid == "" { + rid = cfg.Generator() + } // Set new id to response header c.Set(cfg.Header, rid) From 9064eb64692bc5adb058b2f8d302397c55010b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Mon, 20 Mar 2023 10:22:31 +0300 Subject: [PATCH 030/162] :broom: chore: drop go 1.16 support & update dependencies (#2374) * :broom: chore: drop go 1.16 support * downgrade uniseg * fix tests * fix tests * fix tests --- .github/README.md | 4 ++-- .github/README_ckb.md | 2 +- .github/README_de.md | 4 ++-- .github/README_es.md | 4 ++-- .github/README_fa.md | 4 ++-- .github/README_fr.md | 4 ++-- .github/README_he.md | 4 ++-- .github/README_id.md | 4 ++-- .github/README_it.md | 4 ++-- .github/README_ja.md | 4 ++-- .github/README_ko.md | 4 ++-- .github/README_nl.md | 4 ++-- .github/README_pt.md | 4 ++-- .github/README_ru.md | 4 ++-- .github/README_sa.md | 4 ++-- .github/README_tr.md | 6 ++--- .github/README_uk.md | 4 ++-- .github/README_zh-CN.md | 4 ++-- .github/README_zh-TW.md | 4 ++-- .github/workflows/test.yml | 2 +- docs/intro.md | 2 +- go.mod | 14 ++++++------ go.sum | 46 +++++++++++++++++++++++++------------- listen_test.go | 3 --- prefork_test.go | 1 - 25 files changed, 77 insertions(+), 67 deletions(-) diff --git a/.github/README.md b/.github/README.md index 0a08ed96e9..38cb97615f 100644 --- a/.github/README.md +++ b/.github/README.md @@ -118,7 +118,7 @@ These tests are performed by [TechEmpower](https://www.techempower.com/benchmark ## ⚙️ Installation -Make sure you have Go installed ([download](https://go.dev/dl/)). Version `1.16` or higher is required. +Make sure you have Go installed ([download](https://go.dev/dl/)). Version `1.17` or higher is required. Initialize your project by creating a folder and then running `go mod init github.com/your/repo` ([learn more](https://go.dev/blog/using-go-modules)) inside the folder. Then install Fiber with the [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) command: @@ -151,7 +151,7 @@ Fiber is **inspired** by Express, the most popular web framework on the Internet We **listen** to our users in [issues](https://github.com/gofiber/fiber/issues), Discord [channel](https://gofiber.io/discord) _and all over the Internet_ to create a **fast**, **flexible** and **friendly** Go web framework for **any** task, **deadline** and developer **skill**! Just like Express does in the JavaScript world. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.20. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Examples diff --git a/.github/README_ckb.md b/.github/README_ckb.md index 7f35be6e80..df7f3db582 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -114,7 +114,7 @@ func main() { ## ⚙️ دامەزراندن -دڵنیا بە لەوەی کە لەناو ئامێرەکەت Go دامەزراوە ([دای بگرە](https://go.dev/dl/)). دەبێت وەشانەکەشی `1.16` یان سەرووتر بێت. +دڵنیا بە لەوەی کە لەناو ئامێرەکەت Go دامەزراوە ([دای بگرە](https://go.dev/dl/)). دەبێت وەشانەکەشی `1.17` یان سەرووتر بێت. پڕۆژەکەت دەست پێ بکە بە دروستکردنی بوخچەیەک و کار پێ کردنی فەرمانی `go mod init github.com/your/repo` ([زیاتر](https://go.dev/blog/using-go-modules)) لەناو بوخچەکە. دواتریش بەم فەرمانەی خوارەوە فایبەر دامەزرێنە: diff --git a/.github/README_de.md b/.github/README_de.md index cbe89fbabb..be6b164858 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -114,7 +114,7 @@ Diese Tests wurden von [TechEmpower](https://www.techempower.com/benchmarks/#sec ## ⚙️ Installation -Stelle sicher, dass du Go installiert hast ([Download hier](https://go.dev/dl/)). Version `1.16` oder neuer wird zu der Nutzung Fibers benötigt. +Stelle sicher, dass du Go installiert hast ([Download hier](https://go.dev/dl/)). Version `1.17` oder neuer wird zu der Nutzung Fibers benötigt. Erstelle ein neues Project, indem du zunächst einen neuen Ordner erstellst und dort in diesem Ordner `go mod init github.com/dein/repo` ausführst ([hier mehr dazu](https://go.dev/blog/using-go-modules)). Daraufhin kannst du Fiber mit dem [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) Kommandozeilenbefehl installieren: @@ -145,7 +145,7 @@ Neue Gopher, welche von [Node.js](https://nodejs.org/en/about/) zu [Go](https:// Fiber ist **inspiriert** von Express.js, dem beliebtesten Web-Framework im Internet. Wir haben die **Leichtigkeit** von Express und die **Rohleistung** von Go kombiniert. Wenn du jemals eine Webanwendung mit Node.js implementiert hast (_mit Express.js oder ähnlichem_), werden dir viele Methoden und Prinzipien **sehr vertraut** vorkommen. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.20. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Beispiele diff --git a/.github/README_es.md b/.github/README_es.md index 07646116b1..a907003310 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -114,7 +114,7 @@ Estas pruebas son realizadas por [TechEmpower](https://www.techempower.com/bench ## ⚙️ Instalación -Asegúrese de tener instalado Go ([descargar](https://go.dev/dl/)). Versión `1.16` o superior. +Asegúrese de tener instalado Go ([descargar](https://go.dev/dl/)). Versión `1.17` o superior. Arranque su proyecto creando una nueva carpeta y ejecutando `go mod init github.com/your/repo` ([mas información](https://go.dev/blog/using-go-modules)) dentro del mismo directorio. Después instale Fiber mediante el comando [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them): @@ -145,7 +145,7 @@ Los nuevos gophers que hacen el cambio de [Node.js](https://nodejs.org/en/about/ Fiber está **inspirado** en Expressjs, el framework web más popular en Internet. Combinamos la **facilidad** de Express y **el rendimiento bruto** de Go. Si alguna vez ha implementado una aplicación web en Node.js ( _utilizando Express.js o similar_ ), muchos métodos y principios le parecerán **muy comunes** . ## ⚠️ Limitantes -* Debido a que Fiber utiliza unsafe, la biblioteca no siempre será compatible con la última versión de Go. Fiber 2.40.0 ha sido probado con las versiones de Go 1.16 a 1.20. +* Debido a que Fiber utiliza unsafe, la biblioteca no siempre será compatible con la última versión de Go. Fiber 2.40.0 ha sido probado con las versiones de Go 1.17 a 1.20. * Fiber no es compatible con interfaces net/http. Esto significa que no lo podrá usar en proyectos como qglgen, go-swagger, u otros que son parte del ecosistema net/http. ## 👀 Ejemplos diff --git a/.github/README_fa.md b/.github/README_fa.md index 7ec3d19ee4..2dc4c38a0d 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -133,7 +133,7 @@ func main() {

-مطمئن شوید Go را نصب (دانلود) کرده اید. نسخه 1.16 یا بیشتر مورد نیاز است.
+مطمئن شوید Go را نصب (دانلود) کرده اید. نسخه 1.17 یا بیشتر مورد نیاز است.
پروژه خود را با ساختن یک پوشه و سپس اجرای go mod init github.com/your/repo داخل پوشه (یادگیری بیشتر) راه اندازی کنید. سپس Fiber را با دستور go get نصب کنید :

@@ -185,7 +185,7 @@ Fiber از Express الهام گرفته, که محبوب ترین فری

## ⚠️ محدودیت ها -* به دلیل استفاده ناامن از Fiber, ممکن است کتابخانه همیشه با آخرین نسخه Go سازگار نباشد. Fiber 2.40.0 با زبان گو نسخه 1.16 تا 1.20 تست شده است. +* به دلیل استفاده ناامن از Fiber, ممکن است کتابخانه همیشه با آخرین نسخه Go سازگار نباشد. Fiber 2.40.0 با زبان گو نسخه 1.17 تا 1.20 تست شده است. * فریمورک Fiber با پکیج net/http سازگار نیست. این بدان معناست شما نمی توانید از پکیج های مانند go-swagger, gqlgen یا سایر پروژه هایی که بخشی از اکوسیستم net/http هستند استفاده کنید.
diff --git a/.github/README_fr.md b/.github/README_fr.md index 590dbedb37..5d53a4e7db 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -114,7 +114,7 @@ Ces tests sont effectués par [TechEmpower](https://www.techempower.com/benchmar ## ⚙️ Installation -Make sure you have Go installed ([download](https://go.dev/dl/)). Version `1.16` or higher is required. +Make sure you have Go installed ([download](https://go.dev/dl/)). Version `1.17` or higher is required. Initialize your project by creating a folder and then running `go mod init github.com/your/repo` ([learn more](https://go.dev/blog/using-go-modules)) inside the folder. Then install Fiber with the [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) command: @@ -145,7 +145,7 @@ Les nouveaux gophers qui passent de [Node.js](https://nodejs.org/en/about/) à [ Fiber est **inspiré** par Express, le framework web le plus populaire d'Internet. Nous avons combiné la **facilité** d'Express, et la **performance brute** de Go. Si vous avez déja développé une application web en Node.js (_en utilisant Express ou équivalent_), alors de nombreuses méthodes et principes vous sembleront **familiers**. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.20. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Exemples diff --git a/.github/README_he.md b/.github/README_he.md index 452cae8fa4..0e0684fe21 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -133,7 +133,7 @@ func main() { ## ⚙️ התקנה -Make sure you have Go installed ([download](https://go.dev/dl/)). Version `1.16` or higher is required. +Make sure you have Go installed ([download](https://go.dev/dl/)). Version `1.17` or higher is required. Initialize your project by creating a folder and then running `go mod init github.com/your/repo` ([learn more](https://go.dev/blog/using-go-modules)) inside the folder. Then install Fiber with the [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) command: @@ -190,7 +190,7 @@ Fiber נוצרה **בהשראת** Express, ה-web framework הפופולרית
## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.20. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 דוגמאות diff --git a/.github/README_id.md b/.github/README_id.md index d8ff8e83c7..7232786b21 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -114,7 +114,7 @@ Pengukuran ini dilakukan oleh [TechEmpower](https://www.techempower.com/benchmar ## ⚙️ Instalasi -Pastikan kamu sudah menginstalasi Golang ([unduh](https://go.dev/dl/)). Dengan versi `1.16` atau lebih tinggi [ Direkomendasikan ]. +Pastikan kamu sudah menginstalasi Golang ([unduh](https://go.dev/dl/)). Dengan versi `1.17` atau lebih tinggi [ Direkomendasikan ]. Inisialisasi proyek kamu dengan membuat folder lalu jalankan `go mod init github.com/nama-kamu/repo` ([belajar lebih banyak](https://go.dev/blog/using-go-modules)) di dalam folder. Kemudian instal Fiber dengan perintah [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them): @@ -148,7 +148,7 @@ Kami **mendengarkan** para pengguna di [GitHub Issues](https://github.com/gofibe ## ⚠️ Limitasi -* Karena penggunaan Fiber yang tidak aman, perpustakaan mungkin tidak selalu kompatibel dengan versi Go terbaru. Fiber 2.40.0 telah diuji dengan Go versi 1.16 hingga 1.20. +* Karena penggunaan Fiber yang tidak aman, perpustakaan mungkin tidak selalu kompatibel dengan versi Go terbaru. Fiber 2.40.0 telah diuji dengan Go versi 1.17 hingga 1.20. * Fiber tidak kompatibel dengan antarmuka net/http. Ini berarti kamu tidak akan dapat menggunakan proyek seperti gqlgen, go-swagger, atau lainnya yang merupakan bagian dari ekosistem net/http. ## 👀 Contoh diff --git a/.github/README_it.md b/.github/README_it.md index 249f52681d..b8155ce10e 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -114,7 +114,7 @@ Questi test sono stati eseguiti da [TechEmpower](https://www.techempower.com/ben ## ⚙️ Installazione -Assicurati di avere Go ([per scaricalro](https://go.dev/dl/)) installato. Devi avere la versione `1.16` o superiore. +Assicurati di avere Go ([per scaricalro](https://go.dev/dl/)) installato. Devi avere la versione `1.17` o superiore. Inizializza il tuo progetto creando una cartella e successivamente usando il comando `go mod init github.com/la-tua/repo` ([per maggiori informazioni](https://go.dev/blog/using-go-modules)) dentro la cartella. Dopodiche installa Fiber con il comando [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them): @@ -146,7 +146,7 @@ Fiber è **ispirato** da Express, il web framework più popolare su internet. Ab ## ⚠️ Limitazioni -* Dato che Fiber utilizza unsafe, la libreria non sempre potrebbe essere compatibile con l'ultima versione di Go. Fiber 2.40.0 è stato testato con la versioni 1.16 alla 1.20 di Go. +* Dato che Fiber utilizza unsafe, la libreria non sempre potrebbe essere compatibile con l'ultima versione di Go. Fiber 2.40.0 è stato testato con la versioni 1.17 alla 1.20 di Go. * Fiber non è compatibile con le interfacce net/http. Questo significa che non è possibile utilizzare progetti come qglgen, go-swagger, o altri che fanno parte dell'ecosistema net/http. ## 👀 Esempi diff --git a/.github/README_ja.md b/.github/README_ja.md index 2fcb6ea8ac..17673f88dd 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -115,7 +115,7 @@ func main() { ## ⚙️ インストール -Go がインストールされていることを確認してください ([ダウンロード](https://go.dev/dl/)). バージョン `1.16` またはそれ以上であることが必要です。 +Go がインストールされていることを確認してください ([ダウンロード](https://go.dev/dl/)). バージョン `1.17` またはそれ以上であることが必要です。 フォルダを作成し、フォルダ内で `go mod init github.com/your/repo` ([learn more](https://go.dev/blog/using-go-modules)) を実行してプロジェクトを初期化してください。その後、 Fiber を以下の [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) コマンドでインストールしてください。 @@ -150,7 +150,7 @@ Fiber は人気の高い Web フレームワークである Expressjs に**イ ## ⚠️ 制限事項 -- Fiber は unsafe パッケージを使用しているため、最新の Go バージョンと互換性がない場合があります。Fiber 2.40.0 は、Go のバージョン 1.16 から 1.20 でテストされています。 +- Fiber は unsafe パッケージを使用しているため、最新の Go バージョンと互換性がない場合があります。Fiber 2.40.0 は、Go のバージョン 1.17 から 1.20 でテストされています。 - Fiber は net/http インターフェースと互換性がありません。つまり、gqlgen や go-swagger など、net/http のエコシステムの一部であるプロジェクトを使用することができません。 ## 👀 例 diff --git a/.github/README_ko.md b/.github/README_ko.md index 0a360ce661..c1b390401d 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -114,7 +114,7 @@ func main() { ## ⚙️ 설치 -Go가 설치되어 있는 것을 확인해 주세요 ([download](https://go.dev/dl/)). 버전 1.16 또는 그 이상이어야 합니다. +Go가 설치되어 있는 것을 확인해 주세요 ([download](https://go.dev/dl/)). 버전 1.17 또는 그 이상이어야 합니다. 폴더를 생성하여 당신의 프로젝트를 초기화하고, 폴더 안에서 `go mod init github.com/your/repo` ([learn more](https://go.dev/blog/using-go-modules)) 를 실행하세요. 그리고 [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) 명령어로 Fiber를 설치하세요: @@ -147,7 +147,7 @@ Fiber는 인터넷에서 가장 인기있는 웹 프레임워크인 Express에 우리는 **어떤한** 작업, **마감일정**, 개발자의 **기술**이던간에 **빠르고**, **유연하고**, **익숙한** Go 웹 프레임워크를 만들기 위해 사용자들의 [이슈들](https://github.com/gofiber/fiber/issues)을(그리고 모든 인터넷을 통해) **듣고 있습니다**! Express가 자바스크립트 세계에서 하는 것 처럼요. ## ⚠️ 한계점 -* Fiber는 unsafe 패키지를 사용하기 때문에 최신 Go버전과 호환되지 않을 수 있습니다.Fiber 2.40.0은 Go 버전 1.16에서 1.20로 테스트되고 있습니다. +* Fiber는 unsafe 패키지를 사용하기 때문에 최신 Go버전과 호환되지 않을 수 있습니다.Fiber 2.40.0은 Go 버전 1.17에서 1.20로 테스트되고 있습니다. * Fiber는 net/http 인터페이스와 호환되지 않습니다.즉, gqlgen이나 go-swagger 등 net/http 생태계의 일부인 프로젝트를 사용할 수 없습니다. ## 👀 예제 diff --git a/.github/README_nl.md b/.github/README_nl.md index 3a27edcc79..0cbdb2d6a1 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -114,7 +114,7 @@ Deze tests zijn uitgevoerd door [TechEmpower](https://www.techempower.com/benchm ## ⚙️ Installatie -Make sure you have Go installed ([download](https://go.dev/dl/)). Version `1.16` or higher is required. +Make sure you have Go installed ([download](https://go.dev/dl/)). Version `1.17` or higher is required. Initialize your project by creating a folder and then running `go mod init github.com/your/repo` ([learn more](https://go.dev/blog/using-go-modules)) inside the folder. Then install Fiber with the [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) command: @@ -147,7 +147,7 @@ Fiber is **geïnspireerd** door Express, het populairste webframework op interne We **luisteren** naar onze gebruikers in [issues](https://github.com/gofiber/fiber/issues) (_en overal op het internet_) om een **snelle**, **flexibele** en **vriendelijk** Go web framework te maken voor **elke** taak, **deadline** en ontwikkelaar **vaardigheid**! Net zoals Express dat doet in de JavaScript-wereld. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.20. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Voorbeelden diff --git a/.github/README_pt.md b/.github/README_pt.md index 97cdac5bbf..26379d313a 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -114,7 +114,7 @@ Esses testes são realizados pelo [TechEmpower](https://www.techempower.com/benc ## ⚙️ Instalação -Certifique-se de ter o Go instalado ([download](https://go.dev/dl/)). Versão `1.16` ou superior é obrigatória. +Certifique-se de ter o Go instalado ([download](https://go.dev/dl/)). Versão `1.17` ou superior é obrigatória. Inicie seu projeto criando um diretório e então execute `go mod init github.com/your/repo` ([saiba mais](https://go.dev/blog/using-go-modules)) dentro dele. Então, instale o Fiber com o comando [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them): @@ -145,7 +145,7 @@ Os novos gophers que mudaram do [Node.js](https://nodejs.org/en/about/) para o [ O Fiber é **inspirado** no Express, o framework web mais popular da Internet. Combinamos a **facilidade** do Express e com o **desempenho bruto** do Go. Se você já implementou um aplicativo web com Node.js ( _usando Express.js ou similar_ ), então muitos métodos e princípios parecerão **muito familiares** para você. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.20. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Exemplos diff --git a/.github/README_ru.md b/.github/README_ru.md index 84563b9e68..267dbe6c53 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -114,7 +114,7 @@ func main() { ## ⚙️ Установка -Убедитесь, что Go установлен ([скачать](https://go.dev/dl/)). Требуется версия `1.16` или выше. +Убедитесь, что Go установлен ([скачать](https://go.dev/dl/)). Требуется версия `1.17` или выше. Инициализируйте проект, создав папку, а затем запустив `go mod init github.com/your/repo` ([подробнее](https://go.dev/blog/using-go-modules)) внутри этой папки. Далее, установите Fiber с помощью команды [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them): @@ -147,7 +147,7 @@ Fiber **вдохновлен** Express, самым популярным веб Мы **прислушиваемся** к нашим пользователям в [issues](https://github.com/gofiber/fiber/issues), Discord [канале](https://gofiber.io/discord) _и в остальном Интернете_, чтобы создать **быстрый**, **гибкий** и **дружелюбный** веб фреймворк на Go для **любых** задач, **дедлайнов** и **уровней** разработчиков! Как это делает Express в мире JavaScript. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.20. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Примеры diff --git a/.github/README_sa.md b/.github/README_sa.md index b059c301fd..9521f89753 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -120,7 +120,7 @@ func main() { ## ⚙️ تثبيت -تأكد من تثبيت Go ([تحميل](https://go.dev/dl/)). الإصدار `1.16` أو أعلى مطلوب. +تأكد من تثبيت Go ([تحميل](https://go.dev/dl/)). الإصدار `1.17` أو أعلى مطلوب. ابدأ مشروعك بإنشاء مجلد ثم تشغيله `go mod init github.com/your/repo` ([أعرف أكثر](https://go.dev/blog/using-go-modules)) داخل المجلد. ثم قم بتثبيت Fiber باستخدام ملف [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) أمر: @@ -161,7 +161,7 @@ Fiber هو **مستوحى** من Express, إطار الويب الأكثر شع ** و تطوير **مهارات**! فقط مثل Express تفعل لـ JavaScript عالم. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.20. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 أمثلة diff --git a/.github/README_tr.md b/.github/README_tr.md index a00a9d1751..41561e0eaa 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -114,7 +114,7 @@ Bu testler [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r1 ## ⚙️ Kurulum -Go'nun `1.16` sürümü ([indir](https://go.dev/dl/)) veya daha yüksek bir sürüm gerekli. +Go'nun `1.17` sürümü ([indir](https://go.dev/dl/)) veya daha yüksek bir sürüm gerekli. Bir dizin oluşturup dizinin içinde `go mod init github.com/your/repo` komutunu yazarak projenizi geliştirmeye başlayın ([daha fazla öğren](https://go.dev/blog/using-go-modules)). Ardından Fiber'ı kurmak için [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) komutunu çalıştırın: @@ -146,7 +146,7 @@ Fiber, internet üzerinde en popüler web framework'ü olan Express'ten **esinle ## ⚠️ Sınırlamalar -- Fiber unsafe kullanımı sebebiyle Go'nun son sürümüyle her zaman uyumlu olmayabilir. Fiber 2.40.0, Go 1.16 ile 1.20 sürümleriyle test edildi. +- Fiber unsafe kullanımı sebebiyle Go'nun son sürümüyle her zaman uyumlu olmayabilir. Fiber 2.40.0, Go 1.17 ile 1.20 sürümleriyle test edildi. - Fiber net/http arabirimiyle uyumlu değildir. Yani gqlgen veya go-swagger gibi net/http ekosisteminin parçası olan projeleri kullanamazsınız. ## 👀 Örnekler @@ -603,7 +603,7 @@ Harici olarak barındırılan middlewareların modüllerinin listesi. Bu middlew | [redirect](https://github.com/gofiber/redirect) | Yönlendirme middleware 'ı. | | [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware, sağlanan kurallara göre URL yolunu yeniden yazar. Geriye dönük uyumluluk için veya yalnızca daha temiz ve daha açıklayıcı bağlantılar oluşturmak için yardımcı olabilir. | | [storage](https://github.com/gofiber/storage) | Fiber'in Storage yapısını destekleyen birçok storage driver'ı verir. Bu sayede depolama gerektiren Fiber middlewarelarında kolaylıkla kullanılabilir. | -| [template](https://github.com/gofiber/template) | Bu paket, Fiber `v2.x.x`, Go sürüm 1.16 veya üzeri gerekli olduğunda kullanılabilecek 9 template motoru içerir. | +| [template](https://github.com/gofiber/template) | Bu paket, Fiber `v2.x.x`, Go sürüm 1.17 veya üzeri gerekli olduğunda kullanılabilecek 9 template motoru içerir. | | [websocket](https://github.com/gofiber/websocket) | Yereller desteğiyle Fiber için Fasthttp WebSocket'a dayalıdır! | ## 🕶️ Awesome Listesi diff --git a/.github/README_uk.md b/.github/README_uk.md index 82fcb23525..85f105829b 100644 --- a/.github/README_uk.md +++ b/.github/README_uk.md @@ -124,7 +124,7 @@ func main() { ## ⚙️ Встановлення -Переконайтеся, що Go встановлено ([завантажити](https://go.dev/dl/)). Потрібна версія `1.16` або вища. +Переконайтеся, що Go встановлено ([завантажити](https://go.dev/dl/)). Потрібна версія `1.17` або вища. Ініціалізуйте проект, створивши папку, а потім запустивши `go mod init github.com/your/repo` ([детальніше](https://go.dev/blog/using-go-modules)) всередині цієї папки. Далі встановіть Fiber за допомогою @@ -160,7 +160,7 @@ Fiber **натхненний** Express, найпопулярнішим веб-ф ## ⚠️ Обмеження -- Через те, що Fiber використовує unsafe, бібліотека не завжди може бути сумісною з останньою версією Go. Fiber 2.40.0 було протестовано з Go версій 1.16 до 1.20. +- Через те, що Fiber використовує unsafe, бібліотека не завжди може бути сумісною з останньою версією Go. Fiber 2.40.0 було протестовано з Go версій 1.17 до 1.20. - Fiber не сумісний з інтерфейсами net/http. Це означає, що ви не зможете використовувати такі проекти, як gqlgen, go-swagger або будь-які інші, які є частиною екосистеми net/http. ## 👀 Приклади diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index d07f5d342d..bda48509cd 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -116,7 +116,7 @@ func main() { ## ⚙️ 安装 -确保已安装 `1.16` 或更高版本的 Go ([下载](https://go.dev/dl/))。 +确保已安装 `1.17` 或更高版本的 Go ([下载](https://go.dev/dl/))。 通过创建文件夹并在文件夹内运行 `go mod init github.com/your/repo` ([了解更多](https://go.dev/blog/using-go-modules)) 来初始化项目,然后使用 [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) 命令安装 Fiber: @@ -152,7 +152,7 @@ go get -u github.com/gofiber/fiber/v2 以及在互联网上的所有诉求,为了创建一个能让有着任何技术栈的开发者都能在 deadline 前完成任务的**迅速**,**灵活**以及**友好**的 `Go web` 框架,就像 `Express` 在 `JavaScript` 世界中一样。 ## ⚠️ 限制 -* 由于 Fiber 使用了 unsafe 特性,导致其可能与最新的 Go 版本不兼容。Fiber 2.40.0 已经在 Go 1.16 到 1.20 上测试过。 +* 由于 Fiber 使用了 unsafe 特性,导致其可能与最新的 Go 版本不兼容。Fiber 2.40.0 已经在 Go 1.17 到 1.20 上测试过。 * Fiber 与 net/http 接口不兼容。也就是说你无法直接使用例如 gqlen,go-swagger 或者任何其他属于 net/http 生态的项目。 ## 👀 示例 diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index 21d03bc577..b42173a53d 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -118,7 +118,7 @@ func main() { ## ⚙️ 安裝 -先確定您已經安裝 `1.16` 或更新版本的 Go([點此下載](https://go.dev/dl/))。 +先確定您已經安裝 `1.17` 或更新版本的 Go([點此下載](https://go.dev/dl/))。 要初始化專案,首先建立檔案夾,然後在檔案夾中執行 `go mod init github.com/名稱/儲存庫`([深入了解](https://go.dev/blog/using-go-modules))。接著,使用 [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) 命令安裝 Fiber: @@ -159,7 +159,7 @@ Fiber **啟發自** Express——網際網路上最知名的 Web 框架,我們 ## ⚠️ 限制 -- 由於 Fiber 有用到 Unsafe,本函式庫有時可能無法相容最新版的 Go 語言。Fiber 2.40.0 已在 Go 1.16 至 1.20 的版本測試過。 +- 由於 Fiber 有用到 Unsafe,本函式庫有時可能無法相容最新版的 Go 語言。Fiber 2.40.0 已在 Go 1.17 至 1.20 的版本測試過。 - Fiber 不相容 net/http 的介面,意味著您無法使用像是 gqlgen、go-swagger 或其他任何屬於 net/http 生態系統的專案。 ## 👀 範例 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 06809a07f1..1b638290f8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: Build: strategy: matrix: - go-version: [1.16.x, 1.18.x, 1.20.x] + go-version: [1.17.x, 1.18.x, 1.20.x] platform: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: diff --git a/docs/intro.md b/docs/intro.md index 7880a126f2..6655b98392 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -13,7 +13,7 @@ These docs are for **Fiber v2**, which was released on **September 15th, 2020**. ### Installation -First of all, [download](https://go.dev/dl/) and install Go. `1.16` or higher is required. +First of all, [download](https://go.dev/dl/) and install Go. `1.17` or higher is required. Installation is done using the [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) command: diff --git a/go.mod b/go.mod index 92bd65d4b0..1d7903a09f 100644 --- a/go.mod +++ b/go.mod @@ -8,17 +8,17 @@ require ( github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-runewidth v0.0.14 github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 - github.com/tinylib/msgp v1.1.6 + github.com/tinylib/msgp v1.1.8 github.com/valyala/bytebufferpool v1.0.0 - github.com/valyala/fasthttp v1.44.0 - golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab + github.com/valyala/fasthttp v1.45.0 + golang.org/x/sys v0.6.0 ) require ( - github.com/andybalholm/brotli v1.0.4 // indirect - github.com/klauspost/compress v1.15.9 // indirect - github.com/philhofer/fwd v1.1.1 // indirect + github.com/andybalholm/brotli v1.0.5 // indirect + github.com/klauspost/compress v1.16.3 // indirect + github.com/philhofer/fwd v1.1.2 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect + github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect github.com/valyala/tcplisten v1.0.0 // indirect ) diff --git a/go.sum b/go.sum index 279ea15b4f..68de41fa9f 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,9 @@ -github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= -github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= -github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY= +github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -11,53 +11,67 @@ github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPn github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= +github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4= github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8= -github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d h1:Q+gqLBOPkFGHyCJxXMRqtUgUbTjI8/Ze8vu8GGyNFwo= github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= -github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= +github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= +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/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.44.0 h1:R+gLUhldIsfg1HokMuQjdQ5bh9nuXHPIfvkYUu9eR5Q= -github.com/valyala/fasthttp v1.44.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= +github.com/valyala/fasthttp v1.45.0 h1:zPkkzpIn8tdHZUrVa6PzYd0i5verqiPSkgTd3bSUcpA= +github.com/valyala/fasthttp v1.45.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= 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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= +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/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 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.6/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/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.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +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/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/listen_test.go b/listen_test.go index bbd89315d7..80f6441301 100644 --- a/listen_test.go +++ b/listen_test.go @@ -273,7 +273,6 @@ func captureOutput(f func()) string { } func Test_App_Master_Process_Show_Startup_Message(t *testing.T) { - t.Parallel() startupMessage := captureOutput(func() { New(Config{Prefork: true}). startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) @@ -286,7 +285,6 @@ func Test_App_Master_Process_Show_Startup_Message(t *testing.T) { } func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { - t.Parallel() app := New(Config{Prefork: true, AppName: "Test App v1.0.1"}) startupMessage := captureOutput(func() { app.startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) @@ -296,7 +294,6 @@ func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { } func Test_App_Master_Process_Show_Startup_MessageWithAppNameNonAscii(t *testing.T) { - t.Parallel() appName := "Serveur de vérification des données" app := New(Config{Prefork: true, AppName: appName}) startupMessage := captureOutput(func() { diff --git a/prefork_test.go b/prefork_test.go index 506244a263..ef24d88fb7 100644 --- a/prefork_test.go +++ b/prefork_test.go @@ -71,7 +71,6 @@ func Test_App_Prefork_Master_Process(t *testing.T) { } func Test_App_Prefork_Child_Process_Never_Show_Startup_Message(t *testing.T) { - t.Parallel() setupIsChild(t) defer teardownIsChild(t) From 7536ce8b1abfa84967a3e77f97b1e48c085975e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Mar 2023 08:27:53 +0100 Subject: [PATCH 031/162] Bump actions/setup-go from 3 to 4 (#2376) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/benchmark.yml | 2 +- .github/workflows/linter.yml | 2 +- .github/workflows/test.yml | 2 +- .github/workflows/vulncheck.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 017c680efd..d7db1b84aa 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: 1.20.x - name: Fetch Repository diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index bb78911c0d..7599009bc6 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -14,7 +14,7 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 with: # NOTE: Keep this in sync with the version from go.mod go-version: 1.20.x diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1b638290f8..9c7ee0c345 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: runs-on: ${{ matrix.platform }} steps: - name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: ${{ matrix.go-version }} - name: Setup Golang caches diff --git a/.github/workflows/vulncheck.yml b/.github/workflows/vulncheck.yml index b4eb346171..c725128892 100644 --- a/.github/workflows/vulncheck.yml +++ b/.github/workflows/vulncheck.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: go-version: 1.20.x check-latest: true From 32c39617268b95d9123f6e317b54ab37d3399be9 Mon Sep 17 00:00:00 2001 From: Kyle Manning Date: Wed, 22 Mar 2023 15:44:36 +0900 Subject: [PATCH 032/162] corrected coding typos in MountPath docs section (#2379) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * corrected coding typos in MountPath docs section * Update app.md --------- Co-authored-by: Kyle Manning Co-authored-by: M. Efe Çetin --- docs/api/app.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/api/app.md b/docs/api/app.md index 5ab89922e7..21e631a48d 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -145,10 +145,10 @@ func (app *App) MountPath() string ```go title="Examples" func main() { - app := New() - one := New() - two := New() - three := New() + app := fiber.New() + one := fiber.New() + two := fiber.New() + three := fiber.New() two.Mount("/three", three) one.Mount("/two", two) From f0582a59efe3b72c0455fba3184ebb3345a4f3a5 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Thu, 23 Mar 2023 17:16:52 +0100 Subject: [PATCH 033/162] github: use proper discord invitation link (#2382) --- .github/SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 30d08a5cf0..9d4826fe02 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -20,7 +20,7 @@ The table below shows the supported versions for Fiber which include security up **DO NOT CREATE AN ISSUE** to report a security problem. Instead, please send us an e-mail at `team@gofiber.io` or join our discord server via -[this invite link](https://discord.gg/bSnH7db) and send a private message +[this invite link](https://gofiber.io/discord) and send a private message to Fenny or any of the maintainers. From 1f527996862af97a47f18a8611069e7f1fcfd0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Fri, 24 Mar 2023 13:23:52 +0100 Subject: [PATCH 034/162] Refresh middleware documentation --- docs/api/middleware/cache.md | 10 +++- docs/api/middleware/csrf.md | 27 +++++++++-- docs/api/middleware/encryptcookie.md | 6 +-- docs/api/middleware/envvar.md | 10 ++-- docs/api/middleware/filesystem.md | 69 +++++++++++++++++++++++++--- docs/api/middleware/limiter.md | 23 +++++++++- docs/api/middleware/monitor.md | 2 + docs/api/middleware/requestid.md | 9 ++-- docs/api/middleware/session.md | 24 ++++++++-- docs/api/middleware/timeout.md | 4 +- 10 files changed, 156 insertions(+), 28 deletions(-) diff --git a/docs/api/middleware/cache.md b/docs/api/middleware/cache.md index c4d5375982..5ffe20ba2d 100644 --- a/docs/api/middleware/cache.md +++ b/docs/api/middleware/cache.md @@ -51,7 +51,7 @@ app.Use(New(Config{ return time.Second * time.Duration(newCacheTime) }, KeyGenerator: func(c *fiber.Ctx) string { - return c.Path() + return utils.CopyString(c.Path()) } })) @@ -76,6 +76,13 @@ type Config struct { // Optional. Default: 1 * time.Minute Expiration time.Duration + // CacheHeader header on response header, indicate cache status, with the following possible return value + // + // hit, miss, unreachable + // + // Optional. Default: X-Cache + CacheHeader string + // CacheControl enables client side caching if set to true // // Optional. Default: false @@ -125,6 +132,7 @@ type Config struct { var ConfigDefault = Config{ Next: nil, Expiration: 1 * time.Minute, + CacheHeader: "X-Cache", CacheControl: false, KeyGenerator: func(c *fiber.Ctx) string { return utils.CopyString(c.Path()) diff --git a/docs/api/middleware/csrf.md b/docs/api/middleware/csrf.md index 0ccce01ed1..b5982e3b28 100644 --- a/docs/api/middleware/csrf.md +++ b/docs/api/middleware/csrf.md @@ -3,7 +3,17 @@ id: csrf title: CSRF --- -CSRF middleware for [Fiber](https://github.com/gofiber/fiber) that provides [Cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) protection by passing a csrf token via cookies. This cookie value will be used to compare against the client csrf token on requests, other than those defined as "safe" by RFC7231 \(GET, HEAD, OPTIONS, or TRACE\). When the csrf token is invalid, this middleware will return the `fiber.ErrForbidden` error. When no `_csrf` cookie is set, or the token has expired, a new token will be generated and `_csrf` cookie set. +CSRF middleware for [Fiber](https://github.com/gofiber/fiber) that provides [Cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) protection by passing a csrf token via cookies. This cookie value will be used to compare against the client csrf token on requests, other than those defined as "safe" by RFC7231 \(GET, HEAD, OPTIONS, or TRACE\). When the csrf token is invalid, this middleware will return the `fiber.ErrForbidden` error. + +CSRF Tokens are generated on GET requests. You can retrieve the CSRF token with `c.Locals(contextKey)`, where `contextKey` is the string you set in the config (see Custom Config below). + +When no `csrf_` cookie is set, or the token has expired, a new token will be generated and `csrf_` cookie set. + +:::note + +This middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for this middleware saves data to memory, see the examples below for other databases._ + +::: ## Signatures @@ -32,7 +42,7 @@ app.Use(csrf.New()) app.Use(csrf.New(csrf.Config{ KeyLookup: "header:X-Csrf-Token", CookieName: "csrf_", - CookieSameSite: "Strict", + CookieSameSite: "Lax", Expiration: 1 * time.Hour, KeyGenerator: utils.UUID, Extractor: func(c *fiber.Ctx) (string, error) { ... }, @@ -66,7 +76,7 @@ type Config struct { KeyLookup string // Name of the session cookie. This cookie will store session key. - // Optional. Default value "_csrf". + // Optional. Default value "csrf_". CookieName string // Domain of the CSRF cookie. @@ -134,3 +144,14 @@ var ConfigDefault = Config{ KeyGenerator: utils.UUID, } ``` + +### Custom Storage/Database + +You can use any storage from our [storage](https://github.com/gofiber/storage/) package. + +```go +storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3 +app.Use(csrf.New(csrf.Config{ + Storage: storage, +})) +``` diff --git a/docs/api/middleware/encryptcookie.md b/docs/api/middleware/encryptcookie.md index 9a8f3141f6..5b2d34e9a5 100644 --- a/docs/api/middleware/encryptcookie.md +++ b/docs/api/middleware/encryptcookie.md @@ -65,8 +65,8 @@ type Config struct { // Base64 encoded unique key to encode & decode cookies. // - // Required. Key length should be 32 characters. - // You may use `encryptcookie.GenerateKey()` to generate a new key. + // Required. The key should be 32 bytes of random data in base64-encoded form. + // You may run `openssl rand -base64 32` or use `encryptcookie.GenerateKey()` to generate a new key. Key string // Custom function to encrypt cookies. @@ -85,7 +85,7 @@ type Config struct { ```go // `Key` must be a 32 character string. It's used to encrpyt the values, so make sure it is random and keep it secret. -// You can call `encryptcookie.GenerateKey()` to create a random key for you. +// You can run `openssl rand -base64 32` or call `encryptcookie.GenerateKey()` to create a random key for you. // Make sure not to set `Key` to `encryptcookie.GenerateKey()` because that will create a new key every run. app.Use(encryptcookie.New(encryptcookie.Config{ Key: "secret-thirty-2-character-string", diff --git a/docs/api/middleware/envvar.md b/docs/api/middleware/envvar.md index e72c403ea6..c2eb6794ad 100644 --- a/docs/api/middleware/envvar.md +++ b/docs/api/middleware/envvar.md @@ -35,10 +35,12 @@ app.Use("/expose/envvars", envvar.New()) ### Custom Config ```go -app.Use("/expose/envvars", envvar.New(envvar.Config{ - ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, - ExcludeVars: map[string]string{"excludeKey": ""}, -})) +app.Use("/expose/envvars", envvar.New( + envvar.Config{ + ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, + ExcludeVars: map[string]string{"excludeKey": ""}, + }), +) ``` ### Response diff --git a/docs/api/middleware/filesystem.md b/docs/api/middleware/filesystem.md index 71bdc92972..d0a41a7166 100644 --- a/docs/api/middleware/filesystem.md +++ b/docs/api/middleware/filesystem.md @@ -8,7 +8,7 @@ Filesystem middleware for [Fiber](https://github.com/gofiber/fiber) that enables :::caution **`:params` & `:optionals?` within the prefix path are not supported!** -**To handle paths with spaces (or other url encoded values) make sure to set `fiber.Config{ UnescapePath: true}`** +**To handle paths with spaces (or other url encoded values) make sure to set `fiber.Config{ UnescapePath: true }`** ::: ### Table of Contents @@ -40,7 +40,7 @@ After you initiate your Fiber app, you can use the following possibilities: ```go // Provide a minimal config app.Use(filesystem.New(filesystem.Config{ - Root: http.Dir("./assets") + Root: http.Dir("./assets"), })) // Or extend your config for customization @@ -53,6 +53,54 @@ app.Use(filesystem.New(filesystem.Config{ })) ``` + +> If your environment (Go 1.16+) supports it, we recommend using Go Embed instead of the other solutions listed as this one is native to Go and the easiest to use. + +### embed + +[Embed](https://golang.org/pkg/embed/) is the native method to embed files in a Golang excecutable. Introduced in Go 1.16. + +```go +package main + +import ( + "embed" + "io/fs" + "log" + "net/http" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/filesystem" +) + +// Embed a single file +//go:embed index.html +var f embed.FS + +// Embed a directory +//go:embed static/* +var embedDirStatic embed.FS + +func main() { + app := fiber.New() + + app.Use("/", filesystem.New(filesystem.Config{ + Root: http.FS(f), + })) + + // Access file "image.png" under `static/` directory via URL: `http:///static/image.png`. + // Without `PathPrefix`, you have to access it via URL: + // `http:///static/static/image.png`. + app.Use("/static", filesystem.New(filesystem.Config{ + Root: http.FS(embedDirStatic), + PathPrefix: "static", + Browse: true, + })) + + log.Fatal(app.Listen(":3000")) +} +``` + ## pkger [https://github.com/markbates/pkger](https://github.com/markbates/pkger) @@ -72,7 +120,7 @@ func main() { app.Use("/assets", filesystem.New(filesystem.Config{ Root: pkger.Dir("/assets"), - }) + })) log.Fatal(app.Listen(":3000")) } @@ -97,7 +145,7 @@ func main() { app.Use("/assets", filesystem.New(filesystem.Config{ Root: packr.New("Assets Box", "/assets"), - }) + })) log.Fatal(app.Listen(":3000")) } @@ -122,7 +170,7 @@ func main() { app.Use("/assets", filesystem.New(filesystem.Config{ Root: rice.MustFindBox("assets").HTTPBox(), - }) + })) log.Fatal(app.Listen(":3000")) } @@ -147,7 +195,7 @@ func main() { app.Use("/assets", filesystem.New(filesystem.Config{ Root: myEmbeddedFiles.HTTP, - }) + })) log.Fatal(app.Listen(":3000")) } @@ -201,6 +249,14 @@ type Config struct { // Required. Default: nil Root http.FileSystem `json:"-"` + // PathPrefix defines a prefix to be added to a filepath when + // reading a file from the FileSystem. + // + // Use when using Go 1.16 embed.FS + // + // Optional. Default "" + PathPrefix string `json:"path_prefix"` + // Enable directory browsing. // // Optional. Default: false @@ -230,6 +286,7 @@ type Config struct { var ConfigDefault = Config{ Next: nil, Root: nil, + PathPrefix: "", Browse: false, Index: "/index.html", MaxAge: 0, diff --git a/docs/api/middleware/limiter.md b/docs/api/middleware/limiter.md index d54cf40329..cc5c23666f 100644 --- a/docs/api/middleware/limiter.md +++ b/docs/api/middleware/limiter.md @@ -3,10 +3,18 @@ id: limiter title: Limiter --- -Limiter middleware for [Fiber](https://github.com/gofiber/fiber) used to limit repeated requests to public APIs and/or endpoints such as password reset etc. Also useful for API clients, web crawling, or other tasks that need to be throttled. +Limiter middleware for [Fiber](https://github.com/gofiber/fiber) that is used to limit repeat requests to public APIs and/or endpoints such as password reset. It is also useful for API clients, web crawling, or other tasks that need to be throttled. :::note + +This middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for this middleware saves data to memory, see the examples below for other databases. + +::: + +:::note + This module does not share state with other processes/servers by default. + ::: ## Signatures @@ -69,6 +77,17 @@ weightOfPreviousWindpw = previous window's amount request * (whenNewWindow / Exp rate = weightOfPreviousWindpw + current window's amount request. ``` +## Custom Storage/Database + +You can use any storage from our [storage](https://github.com/gofiber/storage/) package. + +```go +storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3 +app.Use(limiter.New(limiter.Config{ + Storage: storage, +})) +``` + ## Config ```go @@ -79,7 +98,7 @@ type Config struct { // Optional. Default: nil Next func(c *fiber.Ctx) bool - // Max number of recent connections during `Expiration` seconds before sending a 429 response + // Max number of recent connections during `Duration` seconds before sending a 429 response // // Default: 5 Max int diff --git a/docs/api/middleware/monitor.md b/docs/api/middleware/monitor.md index 66353ea49e..459b47d1e9 100644 --- a/docs/api/middleware/monitor.md +++ b/docs/api/middleware/monitor.md @@ -6,7 +6,9 @@ title: Monitor Monitor middleware for [Fiber](https://github.com/gofiber/fiber) that reports server metrics, inspired by [express-status-monitor](https://github.com/RafalWilinski/express-status-monitor) :::caution + Monitor is still in beta, API might change in the future! + ::: ![](https://i.imgur.com/nHAtBpJ.gif) diff --git a/docs/api/middleware/requestid.md b/docs/api/middleware/requestid.md index 7617f76ae9..b1510ca76a 100644 --- a/docs/api/middleware/requestid.md +++ b/docs/api/middleware/requestid.md @@ -66,14 +66,15 @@ type Config struct { ``` ## Default Config +The default config uses a fast UUID generator which will expose the number of +requests made to the server. To conceal this value for better privacy, use the +`utils.UUIDv4` generator. ```go var ConfigDefault = Config{ Next: nil, Header: fiber.HeaderXRequestID, - Generator: func() string { - return utils.UUID() - }, - ContextKey: "requestid" + Generator: utils.UUID, + ContextKey: "requestid", } ``` diff --git a/docs/api/middleware/session.md b/docs/api/middleware/session.md index 6c746ceb49..be121326ea 100644 --- a/docs/api/middleware/session.md +++ b/docs/api/middleware/session.md @@ -29,7 +29,9 @@ func (s *Session) Keys() []string ``` :::caution + Storing `interface{}` values are limited to built-ins Go types. + ::: ### Examples @@ -75,6 +77,9 @@ app.Get("/", func(c *fiber.Ctx) error { panic(err) } + // Sets a specific expiration for this session + sess.SetExpiry(time.Second * 2) + // Save session if err := sess.Save(); err != nil { panic(err) @@ -110,9 +115,11 @@ type Config struct { // Optional. Default value memory.New() Storage fiber.Storage - // Name of the session cookie. This cookie will store session key. - // Optional. Default value "session_id". - CookieName string + // KeyLookup is a string in the form of ":" that is used + // to extract session id from the request. + // Possible values: "header:", "query:" or "cookie:" + // Optional. Default value "cookie:session_id". + KeyLookup string // Domain of the cookie. // Optional. Default value "". @@ -142,6 +149,15 @@ type Config struct { // KeyGenerator generates the session key. // Optional. Default value utils.UUID KeyGenerator func() string + + // Deprecated: Please use KeyLookup + CookieName string + + // Source defines where to obtain the session id + source Source + + // The session name + sessionName string } ``` @@ -150,7 +166,7 @@ type Config struct { ```go var ConfigDefault = Config{ Expiration: 24 * time.Hour, - CookieName: "session_id", + KeyLookup: "cookie:session_id", KeyGenerator: utils.UUID, } ``` diff --git a/docs/api/middleware/timeout.md b/docs/api/middleware/timeout.md index cdd88496bc..aa0f2eb3c4 100644 --- a/docs/api/middleware/timeout.md +++ b/docs/api/middleware/timeout.md @@ -3,7 +3,9 @@ id: timeout title: Timeout --- -Timeout middleware for [Fiber](https://github.com/gofiber/fiber). As a `fiber.Handler` wrapper, it creates a context with `context.WithTimeout` and pass it in `UserContext`. If the context passed executions (eg. DB ops, Http calls) takes longer than the given duration to return, the timeout error is set and forwarded to the centralized `ErrorHandler`. +Timeout middleware for [Fiber](https://github.com/gofiber/fiber). As a `fiber.Handler` wrapper, it creates a context with `context.WithTimeout` and pass it in `UserContext`. + +If the context passed executions (eg. DB ops, Http calls) takes longer than the given duration to return, the timeout error is set and forwarded to the centralized `ErrorHandler`. It does not cancel long running executions. Underlying executions must handle timeout by using `context.Context` parameter. From 547db83cdd91cfa7f7f75879d5fe9dffb1b6e563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erkan=20Durmu=C5=9F?= Date: Fri, 24 Mar 2023 16:29:42 +0300 Subject: [PATCH 035/162] Get mime fallback (#2340) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added fallback to go's mime detection * added test for getting mime * added err check * added err check * removing import alias for builtin mime and aserting error for adding mime type. * removing import alias for builtin mime and aserting error for adding mime type. * added fallback to go's mime detection * added test for getting mime * added err check * added err check * removing import alias for builtin mime and aserting error for adding mime type. * removing import alias for builtin mime and aserting error for adding mime type. --------- Co-authored-by: René Werner --- utils/http.go | 22 ++++++++++++++++------ utils/http_test.go | 8 ++++++++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/utils/http.go b/utils/http.go index 02498aede3..fe394f518a 100644 --- a/utils/http.go +++ b/utils/http.go @@ -5,6 +5,7 @@ package utils import ( + "mime" "strings" ) @@ -15,16 +16,25 @@ func GetMIME(extension string) string { if len(extension) == 0 { return "" } - var mime string + var foundMime string if extension[0] == '.' { - mime = mimeExtensions[extension[1:]] + foundMime = mimeExtensions[extension[1:]] } else { - mime = mimeExtensions[extension] + foundMime = mimeExtensions[extension] } - if len(mime) == 0 { - return MIMEOctetStream + + if len(foundMime) == 0 { + if extension[0] != '.' { + foundMime = mime.TypeByExtension("." + extension) + } else { + foundMime = mime.TypeByExtension(extension) + } + + if foundMime == "" { + return MIMEOctetStream + } } - return mime + return foundMime } // ParseVendorSpecificContentType check if content type is vendor specific and diff --git a/utils/http_test.go b/utils/http_test.go index 2abe6c51a4..56a6029639 100644 --- a/utils/http_test.go +++ b/utils/http_test.go @@ -23,6 +23,14 @@ func Test_GetMIME(t *testing.T) { res = GetMIME("unknown") AssertEqual(t, MIMEOctetStream, res) + + err := mime.AddExtensionType(".mjs", "application/javascript") + if err == nil { + res = GetMIME(".mjs") + AssertEqual(t, "application/javascript", res) + } + AssertEqual(t, nil, err) + // empty case res = GetMIME("") AssertEqual(t, "", res) From 152d59aeb7ad4d5ac138553433c9d0a2d38c9971 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 24 Mar 2023 13:30:56 +0000 Subject: [PATCH 036/162] Bump github.com/mattn/go-isatty from 0.0.17 to 0.0.18 Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.17 to 0.0.18. - [Release notes](https://github.com/mattn/go-isatty/releases) - [Commits](https://github.com/mattn/go-isatty/compare/v0.0.17...v0.0.18) --- updated-dependencies: - dependency-name: github.com/mattn/go-isatty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1d7903a09f..9961966cc3 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/google/uuid v1.3.0 github.com/mattn/go-colorable v0.1.13 - github.com/mattn/go-isatty v0.0.17 + github.com/mattn/go-isatty v0.0.18 github.com/mattn/go-runewidth v0.0.14 github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 github.com/tinylib/msgp v1.1.8 diff --git a/go.sum b/go.sum index 68de41fa9f..de8d1651d6 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= From 69884117c294933c6a2b46f8eb204a1cfdfeb93f Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 24 Mar 2023 18:23:38 +0100 Subject: [PATCH 037/162] prepare release v2.43.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index b4dd071e2d..7857da349d 100644 --- a/app.go +++ b/app.go @@ -30,7 +30,7 @@ import ( ) // Version of current fiber package -const Version = "2.42.0" +const Version = "2.43.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From cc5c793d834373597ddb21d84b7d5973850b9e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Fri, 24 Mar 2023 18:54:09 +0100 Subject: [PATCH 038/162] Add documentation for ctx.QueryFloat and ctx.QueryBool --- docs/api/ctx.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 56a15520bd..ba6e330be8 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1082,6 +1082,68 @@ app.Get("/", func(c *fiber.Ctx) error { > _Returned value is only valid within the handler. Do not store any references. > Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) +## QueryBool + +This property is an object containing a property for each query boolean parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. + + +:::caution +Please note if that parameter is not in the request, true will be returned. +If the parameter is not a boolean, it is still tried to be converted and usually returned as true. +::: + +```go title="Signature" +func (c *Ctx) QueryBool(key string, defaultValue ...bool) bool +``` + +```go title="Example" +// GET http://example.com/?name=alex&want_pizza=false&id= + +app.Get("/", func(c *fiber.Ctx) error { + c.QueryBool("want_pizza") // false + c.QueryBool("want_pizza", true) // false + c.QueryBool("alex") // true + c.QueryBool("alex", false) // false + c.QueryBool("id") // true + c.QueryBool("id", false) // false + + // ... +}) +``` + + +## QueryFloat + +This property is an object containing a property for each query float64 parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. + +:::caution +Please note if that parameter is not in the request, zero will be returned. +If the parameter is not a number, it is still tried to be converted and usually returned as 1. +::: + +:::info +Defaults to the float64 zero \(`0`\), if the param **doesn't** exist. +::: + +```go title="Signature" +func (c *Ctx) QueryFloat(key string, defaultValue ...float64) float64 +``` + +```go title="Example" +// GET http://example.com/?name=alex&amount=32.23&id= + +app.Get("/", func(c *fiber.Ctx) error { + c.QueryFloat("amount") // 32.23 + c.QueryFloat("amount", 3) // 32.23 + c.QueryFloat("name", 1) // 1 + c.QueryFloat("name") // 0 + c.QueryFloat("id", 3) // 3 + + // ... +}) +``` + + ## QueryInt This property is an object containing a property for each query integer parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. @@ -1104,10 +1166,10 @@ func (c *Ctx) QueryInt(key string, defaultValue ...int) int // GET http://example.com/?name=alex&wanna_cake=2&id= app.Get("/", func(c *fiber.Ctx) error { - QueryInt("wanna_cake", 1) // 2 - QueryInt("name", 1) // 1 - QueryInt("id", 1) // 1 - QueryInt("id") // 0 + c.QueryInt("wanna_cake", 1) // 2 + c.QueryInt("name", 1) // 1 + c.QueryInt("id", 1) // 1 + c.QueryInt("id") // 0 // ... }) From c9121189a9a3ec40ed3ec9f4e0f4a3eaaada7c44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Fri, 24 Mar 2023 18:58:15 +0100 Subject: [PATCH 039/162] Fix docu --- docs/api/ctx.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index ba6e330be8..52a2d3c6a3 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1111,7 +1111,6 @@ app.Get("/", func(c *fiber.Ctx) error { }) ``` - ## QueryFloat This property is an object containing a property for each query float64 parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. @@ -1175,9 +1174,6 @@ app.Get("/", func(c *fiber.Ctx) error { }) ``` -> _Returned value is only valid within the handler. Do not store any references. -> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) - ## QueryParser This method is similar to [BodyParser](ctx.md#bodyparser), but for query parameters. From c6e86ac9064fa436c6de4933a849b68a27a79127 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Sat, 25 Mar 2023 17:47:38 +0100 Subject: [PATCH 040/162] github/workflows: also run tests with Go 1.19.x (#2384) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9c7ee0c345..08137af2b7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: Build: strategy: matrix: - go-version: [1.17.x, 1.18.x, 1.20.x] + go-version: [1.17.x, 1.18.x, 1.19.x, 1.20.x] platform: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: From 28d9abb71b4bf4d0eb01b3b5914fa9d5bac6d47c Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 27 Mar 2023 15:55:41 +0200 Subject: [PATCH 041/162] Fix #2383, accepts mimeType (#2386) * Fix #2383, accepts mimeType * Fix #2383, accepts mimeType * Fix #2383, accepts mimeType --- ctx.go | 59 +++------------------------------ ctx_test.go | 27 ++++++++++++---- helpers.go | 86 +++++++++++++++++++++++++++++++++++++------------ helpers_test.go | 6 ++-- 4 files changed, 93 insertions(+), 85 deletions(-) diff --git a/ctx.go b/ctx.go index 2a99b8384f..ea7d5bff96 100644 --- a/ctx.go +++ b/ctx.go @@ -197,73 +197,22 @@ func (app *App) ReleaseCtx(c *Ctx) { // Accepts checks if the specified extensions or content types are acceptable. func (c *Ctx) Accepts(offers ...string) string { - if len(offers) == 0 { - return "" - } - header := c.Get(HeaderAccept) - if header == "" { - return offers[0] - } - - spec, commaPos := "", 0 - for len(header) > 0 && commaPos != -1 { - commaPos = strings.IndexByte(header, ',') - if commaPos != -1 { - spec = utils.Trim(header[:commaPos], ' ') - } else { - spec = utils.TrimLeft(header, ' ') - } - if factorSign := strings.IndexByte(spec, ';'); factorSign != -1 { - spec = spec[:factorSign] - } - - var mimetype string - for _, offer := range offers { - if len(offer) == 0 { - continue - // Accept: */* - } else if spec == "*/*" { - return offer - } - - if strings.IndexByte(offer, '/') != -1 { - mimetype = offer // MIME type - } else { - mimetype = utils.GetMIME(offer) // extension - } - - if spec == mimetype { - // Accept: / - return offer - } - - s := strings.IndexByte(mimetype, '/') - // Accept: /* - if strings.HasPrefix(spec, mimetype[:s]) && (spec[s:] == "/*" || mimetype[s:] == "/*") { - return offer - } - } - if commaPos != -1 { - header = header[commaPos+1:] - } - } - - return "" + return getOffer(c.Get(HeaderAccept), acceptsOfferType, offers...) } // AcceptsCharsets checks if the specified charset is acceptable. func (c *Ctx) AcceptsCharsets(offers ...string) string { - return getOffer(c.Get(HeaderAcceptCharset), offers...) + return getOffer(c.Get(HeaderAcceptCharset), acceptsOffer, offers...) } // AcceptsEncodings checks if the specified encoding is acceptable. func (c *Ctx) AcceptsEncodings(offers ...string) string { - return getOffer(c.Get(HeaderAcceptEncoding), offers...) + return getOffer(c.Get(HeaderAcceptEncoding), acceptsOffer, offers...) } // AcceptsLanguages checks if the specified language is acceptable. func (c *Ctx) AcceptsLanguages(offers ...string) string { - return getOffer(c.Get(HeaderAcceptLanguage), offers...) + return getOffer(c.Get(HeaderAcceptLanguage), acceptsOffer, offers...) } // App returns the *App reference to the instance of the Fiber application diff --git a/ctx_test.go b/ctx_test.go index 0bbd8351d0..2a67302f02 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -66,14 +66,27 @@ func Benchmark_Ctx_Accepts(b *testing.B) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Request().Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9") - var res string - b.ReportAllocs() - b.ResetTimer() - for n := 0; n < b.N; n++ { - res = c.Accepts(".xml") + acceptHeader := "text/html,application/xhtml+xml,application/xml;q=0.9" + c.Request().Header.Set("Accept", acceptHeader) + acceptValues := [][]string{ + {".xml"}, + {"json", "xml"}, + {"application/json", "application/xml"}, + } + expectedResults := []string{".xml", "xml", "application/xml"} + + for i := 0; i < len(acceptValues); i++ { + b.Run(fmt.Sprintf("run-%#v", acceptValues[i]), func(bb *testing.B) { + var res string + bb.ReportAllocs() + bb.ResetTimer() + + for n := 0; n < bb.N; n++ { + res = c.Accepts(acceptValues[i]...) + } + utils.AssertEqual(bb, expectedResults[i], res) + }) } - utils.AssertEqual(b, ".xml", res) } // go test -run Test_Ctx_Accepts_EmptyAccept diff --git a/helpers.go b/helpers.go index ec7bd2395b..d792b0dc52 100644 --- a/helpers.go +++ b/helpers.go @@ -215,36 +215,82 @@ func getGroupPath(prefix, path string) string { return utils.TrimRight(prefix, '/') + path } -// return valid offer for header negotiation -func getOffer(header string, offers ...string) string { +// acceptsOffer This function determines if an offer matches a given specification. +// It checks if the specification ends with a '*' or if the offer has the prefix of the specification. +// Returns true if the offer matches the specification, false otherwise. +func acceptsOffer(spec, offer string) bool { + if len(spec) >= 1 && spec[len(spec)-1] == '*' { + return true + } else if strings.HasPrefix(spec, offer) { + return true + } + return false +} + +// acceptsOfferType This function determines if an offer type matches a given specification. +// It checks if the specification is equal to */* (i.e., all types are accepted). +// It gets the MIME type of the offer (either from the offer itself or by its file extension). +// It checks if the offer MIME type matches the specification MIME type or if the specification is of the form /* and the offer MIME type has the same MIME type. +// Returns true if the offer type matches the specification, false otherwise. +func acceptsOfferType(spec, offerType string) bool { + // Accept: */* + if spec == "*/*" { + return true + } + + var mimetype string + if strings.IndexByte(offerType, '/') != -1 { + mimetype = offerType // MIME type + } else { + mimetype = utils.GetMIME(offerType) // extension + } + + if spec == mimetype { + // Accept: / + return true + } + + s := strings.IndexByte(mimetype, '/') + // Accept: /* + if strings.HasPrefix(spec, mimetype[:s]) && (spec[s:] == "/*" || mimetype[s:] == "/*") { + return true + } + + return false +} + +// getOffer return valid offer for header negotiation +func getOffer(header string, isAccepted func(spec, offer string) bool, offers ...string) string { if len(offers) == 0 { return "" } else if header == "" { return offers[0] } - spec, commaPos := "", 0 - for len(header) > 0 && commaPos != -1 { - commaPos = strings.IndexByte(header, ',') - if commaPos != -1 { - spec = utils.Trim(header[:commaPos], ' ') - } else { - spec = header - } - if factorSign := strings.IndexByte(spec, ';'); factorSign != -1 { - spec = spec[:factorSign] + for _, offer := range offers { + if len(offer) == 0 { + continue } + spec, commaPos := "", 0 + for len(header) > 0 && commaPos != -1 { + commaPos = strings.IndexByte(header, ',') + if commaPos != -1 { + spec = utils.Trim(header[:commaPos], ' ') + } else { + spec = utils.TrimLeft(header, ' ') + } + if factorSign := strings.IndexByte(spec, ';'); factorSign != -1 { + spec = spec[:factorSign] + } - for _, offer := range offers { - // has star prefix - if len(spec) >= 1 && spec[len(spec)-1] == '*' { - return offer - } else if strings.HasPrefix(spec, offer) { + // isAccepted if the current offer is accepted + if isAccepted(spec, offer) { return offer } - } - if commaPos != -1 { - header = header[commaPos+1:] + + if commaPos != -1 { + header = header[commaPos+1:] + } } } diff --git a/helpers_test.go b/helpers_test.go index 7bb394e520..389ff416fe 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -223,9 +223,9 @@ func Test_Utils_Parse_Address(t *testing.T) { func Test_Utils_GetOffset(t *testing.T) { t.Parallel() - utils.AssertEqual(t, "", getOffer("hello")) - utils.AssertEqual(t, "1", getOffer("", "1")) - utils.AssertEqual(t, "", getOffer("2", "1")) + utils.AssertEqual(t, "", getOffer("hello", acceptsOffer)) + utils.AssertEqual(t, "1", getOffer("", acceptsOffer, "1")) + utils.AssertEqual(t, "", getOffer("2", acceptsOffer, "1")) } func Test_Utils_TestConn_Deadline(t *testing.T) { From bf31f1f3c6e31a434d6489af3b904921820d7bb4 Mon Sep 17 00:00:00 2001 From: Iliya Date: Thu, 30 Mar 2023 14:56:26 +0330 Subject: [PATCH 042/162] =?UTF-8?q?=F0=9F=92=8A=20Change=20default=20value?= =?UTF-8?q?=20of=20Querybool=20from=20true=20to=20false.=20(#2391)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🩹 Fix QueryBool function: change default value from true to false * 📚 Update QueryBool function document * Update ctx.md --------- Co-authored-by: RW --- ctx.go | 10 +++++----- ctx_test.go | 8 ++++---- docs/api/ctx.md | 14 +++++++------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/ctx.go b/ctx.go index ea7d5bff96..2f9394bd8e 100644 --- a/ctx.go +++ b/ctx.go @@ -1081,17 +1081,17 @@ func (c *Ctx) QueryInt(key string, defaultValue ...int) int { // Get /?name=alex&want_pizza=false&id= // QueryBool("want_pizza") == false // QueryBool("want_pizza", true) == false -// QueryBool("alex") == true -// QueryBool("alex", false) == false -// QueryBool("id") == true -// QueryBool("id", false) == false +// QueryBool("name") == false +// QueryBool("name", true) == true +// QueryBool("id") == false +// QueryBool("id", true) == true func (c *Ctx) QueryBool(key string, defaultValue ...bool) bool { value, err := strconv.ParseBool(c.app.getString(c.fasthttp.QueryArgs().Peek(key))) if err != nil { if len(defaultValue) > 0 { return defaultValue[0] } - return true + return false } return value } diff --git a/ctx_test.go b/ctx_test.go index 2a67302f02..b3192a8f3d 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2179,10 +2179,10 @@ func Test_Ctx_QueryBool(t *testing.T) { utils.AssertEqual(t, false, c.QueryBool("want_pizza")) utils.AssertEqual(t, false, c.QueryBool("want_pizza", true)) - utils.AssertEqual(t, true, c.QueryBool("name")) - utils.AssertEqual(t, false, c.QueryBool("name", false)) - utils.AssertEqual(t, true, c.QueryBool("id")) - utils.AssertEqual(t, false, c.QueryBool("id", false)) + utils.AssertEqual(t, false, c.QueryBool("name")) + utils.AssertEqual(t, true, c.QueryBool("name", true)) + utils.AssertEqual(t, false, c.QueryBool("id")) + utils.AssertEqual(t, true, c.QueryBool("id", true)) } func Test_Ctx_QueryFloat(t *testing.T) { diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 52a2d3c6a3..f102ee374e 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1088,8 +1088,8 @@ This property is an object containing a property for each query boolean paramete :::caution -Please note if that parameter is not in the request, true will be returned. -If the parameter is not a boolean, it is still tried to be converted and usually returned as true. +Please note if that parameter is not in the request, false will be returned. +If the parameter is not a boolean, it is still tried to be converted and usually returned as false. ::: ```go title="Signature" @@ -1100,12 +1100,12 @@ func (c *Ctx) QueryBool(key string, defaultValue ...bool) bool // GET http://example.com/?name=alex&want_pizza=false&id= app.Get("/", func(c *fiber.Ctx) error { - c.QueryBool("want_pizza") // false + c.QueryBool("want_pizza") // false c.QueryBool("want_pizza", true) // false - c.QueryBool("alex") // true - c.QueryBool("alex", false) // false - c.QueryBool("id") // true - c.QueryBool("id", false) // false + c.QueryBool("name") // false + c.QueryBool("name", true) // true + c.QueryBool("id") // false + c.QueryBool("id", true) // true // ... }) From 5eb4d57d88485b060250135e8e731c8f517d00d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Vitor?= <39197618+ancogamer@users.noreply.github.com> Date: Fri, 31 Mar 2023 07:29:14 -0300 Subject: [PATCH 043/162] =?UTF-8?q?feat:=20adding=20to=20fac=20sub=20domai?= =?UTF-8?q?n=20routing=20=F0=9F=93=9D=20(#2393)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: adding to fac sub domain routing * update: docs to include a example * Update faq.md * fix code after tests --------- Co-authored-by: RW --- docs/extra/faq.md | 72 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/docs/extra/faq.md b/docs/extra/faq.md index 0f8350543b..5f0d74c4c5 100644 --- a/docs/extra/faq.md +++ b/docs/extra/faq.md @@ -65,3 +65,75 @@ Yes, we have our own [Discord ](https://gofiber.io/discord)server, where we hang If you have questions or just want to have a chat, feel free to join us via this **>** [**invite link**](https://gofiber.io/discord) **<**. ![](/img/support-discord.png) + +## Does fiber support sub domain routing ? + +Yes we do, here are some examples: +This example works v2 +```go +package main + +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/logger" +) + +type Host struct { + Fiber *fiber.App +} + +func main() { + // Hosts + hosts := map[string]*Host{} + //----- + // API + //----- + api := fiber.New() + api.Use(logger.New(logger.Config{ + Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", + })) + hosts["api.localhost:3000"] = &Host{api} + api.Get("/", func(c *fiber.Ctx) error { + return c.SendString("API") + }) + //------ + // Blog + //------ + blog := fiber.New() + blog.Use(logger.New(logger.Config{ + Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", + })) + hosts["blog.localhost:3000"] = &Host{blog} + blog.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Blog") + }) + //--------- + // Website + //--------- + site := fiber.New() + site.Use(logger.New(logger.Config{ + Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", + })) + + hosts["localhost:3000"] = &Host{site} + site.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Website") + }) + // Server + app := fiber.New() + app.Use(func(c *fiber.Ctx) error { + host := hosts[c.Hostname()] + if host == nil { + return c.SendStatus(fiber.StatusNotFound) + } else { + host.Fiber.Handler()(c.Context()) + return nil + } + }) + log.Fatal(app.Listen(":3000")) +} +``` +If more information is needed, please refer to this issue https://github.com/gofiber/fiber/issues/750 or let us know on our Discord **>** [**invite link**](https://gofiber.io/discord) **<**. +![](/img/support-discord.png) From 856332c16a6a0783022b7a4e3b5e0ddb19f04a0d Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 31 Mar 2023 12:35:20 +0200 Subject: [PATCH 044/162] Update faq.md --- docs/extra/faq.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/extra/faq.md b/docs/extra/faq.md index 5f0d74c4c5..76b17758f3 100644 --- a/docs/extra/faq.md +++ b/docs/extra/faq.md @@ -135,5 +135,4 @@ func main() { log.Fatal(app.Listen(":3000")) } ``` -If more information is needed, please refer to this issue https://github.com/gofiber/fiber/issues/750 or let us know on our Discord **>** [**invite link**](https://gofiber.io/discord) **<**. -![](/img/support-discord.png) +If more information is needed, please refer to this issue https://github.com/gofiber/fiber/issues/750 From e9541945e553268e3d17b3b7276218ae0b9a0eef Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 31 Mar 2023 12:36:30 +0200 Subject: [PATCH 045/162] Update faq.md --- docs/extra/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/extra/faq.md b/docs/extra/faq.md index 76b17758f3..923881c865 100644 --- a/docs/extra/faq.md +++ b/docs/extra/faq.md @@ -135,4 +135,4 @@ func main() { log.Fatal(app.Listen(":3000")) } ``` -If more information is needed, please refer to this issue https://github.com/gofiber/fiber/issues/750 +If more information is needed, please refer to this issue [#750](https://github.com/gofiber/fiber/issues/750) From 243f393434b176e9fa9264e605d7d9c03f96e35b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sun, 2 Apr 2023 14:08:20 +0200 Subject: [PATCH 046/162] Fix #2396, data race logger middleware --- middleware/logger/logger.go | 1 + 1 file changed, 1 insertion(+) diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index daa97063f9..b34c1908bc 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -171,6 +171,7 @@ func New(config ...Config) fiber.Handler { return nil } + var err error // Loop over template parts execute dynamic parts and add fixed parts to the buffer for i, logFunc := range logFunChain { if logFunc == nil { From 035e7d4f4337df3a51772761e5bdf7c8bc6ff894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sun, 2 Apr 2023 14:36:00 +0200 Subject: [PATCH 047/162] Fix #2396, data race logger middleware --- middleware/logger/logger_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index fb81875223..df4045c2ab 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -275,6 +275,9 @@ func Test_Logger_Data_Race(t *testing.T) { defer bytebufferpool.Put(buf) app.Use(New(ConfigDefault)) + app.Use(New(Config{ + Format: "${time} | ${pid} | ${locals:requestid} | ${status} | ${latency} | ${method} | ${path}\n", + })) app.Get("/", func(c *fiber.Ctx) error { return c.SendString("hello") From 74a9fa96f5d74647e28d197f08208cb20c3a94ed Mon Sep 17 00:00:00 2001 From: Shahriar Sohan Date: Tue, 4 Apr 2023 15:12:53 +0600 Subject: [PATCH 048/162] docs: added code link to fiber config fields (#2385) * docs: added code link to fiber config fields * docs: added code link to fiber config fields [reference line link updated] * docs: added code link to fiber config fields [reference line link updated] * removed reference line link from docs * docs/api: fix indentation --------- Co-authored-by: leonklingele --- docs/api/fiber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/fiber.md b/docs/api/fiber.md index 5e0c4deef8..2218b26103 100644 --- a/docs/api/fiber.md +++ b/docs/api/fiber.md @@ -44,7 +44,7 @@ app := fiber.New(fiber.Config{ | AppName | `string` | This allows to setup app name for the app | `""` | | BodyLimit | `int` | Sets the maximum allowed size for a request body, if the size exceeds the configured limit, it sends `413 - Request Entity Too Large` response. | `4 * 1024 * 1024` | | CaseSensitive | `bool` | When enabled, `/Foo` and `/foo` are different routes. When disabled, `/Foo`and `/foo` are treated the same. | `false` | -| ColorScheme | `Colors` | You can define custom color scheme. They'll be used for startup message, route list and some middlewares. | `DefaultColors` | +| ColorScheme | [`Colors`](https://github.com/gofiber/fiber/blob/master/color.go) | You can define custom color scheme. They'll be used for startup message, route list and some middlewares. | [`DefaultColors`](https://github.com/gofiber/fiber/blob/master/color.go) | | CompressedFileSuffix | `string` | Adds a suffix to the original file name and tries saving the resulting compressed file under the new file name. | `".fiber.gz"` | | Concurrency | `int` | Maximum number of concurrent connections. | `256 * 1024` | | DisableDefaultContentType | `bool` | When set to true, causes the default Content-Type header to be excluded from the Response. | `false` | From e7adba48c95825d9a1d9f351fb83ed683f40fd7f Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:10:39 +0200 Subject: [PATCH 049/162] Update README.md --- .github/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index 38cb97615f..9643ba627b 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 336a80260899e05be7ac13fec3170c502aa3c70d Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:12:50 +0200 Subject: [PATCH 050/162] Update README_de.md --- .github/README_de.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_de.md b/.github/README_de.md index be6b164858..78950f3984 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 6dee3c8191f613a9a5d27cbf6345ab20d02e08ee Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:12:53 +0200 Subject: [PATCH 051/162] Update README_es.md --- .github/README_es.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_es.md b/.github/README_es.md index a907003310..9b4ed1e94c 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 79af24ee9a130da335f2752200d7e6903dc62f86 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:12:57 +0200 Subject: [PATCH 052/162] Update README_fa.md --- .github/README_fa.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_fa.md b/.github/README_fa.md index 2dc4c38a0d..6861f70eed 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From e18c211d7dfb4d8d9194ab574105132fde6693a6 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:13:01 +0200 Subject: [PATCH 053/162] Update README_fr.md --- .github/README_fr.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_fr.md b/.github/README_fr.md index 5d53a4e7db..d3b20711e1 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 66fb58cc749ada6c601fd6a2400e2cf0a8fb7be3 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:13:05 +0200 Subject: [PATCH 054/162] Update README_he.md --- .github/README_he.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_he.md b/.github/README_he.md index 0e0684fe21..2b7d4ef008 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 820d86abdbd5b1e0f0896f6e721271bfd72e71ae Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:13:09 +0200 Subject: [PATCH 055/162] Update README_id.md --- .github/README_id.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_id.md b/.github/README_id.md index 7232786b21..c8826096ce 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From ef22e461d3cb549145a75bc0150adc5ae42eedaf Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:13:33 +0200 Subject: [PATCH 056/162] Update README_it.md --- .github/README_it.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_it.md b/.github/README_it.md index b8155ce10e..62a1cc8184 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 8988ac4d11d9c23b0336c11894800bc52476aa1c Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:13:36 +0200 Subject: [PATCH 057/162] Update README_ja.md --- .github/README_ja.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_ja.md b/.github/README_ja.md index 17673f88dd..90de60688e 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 87c8a3e995ffe1e83851e7566b5c6e151f8f0207 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:13:40 +0200 Subject: [PATCH 058/162] Update README_ko.md --- .github/README_ko.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_ko.md b/.github/README_ko.md index c1b390401d..d26be91101 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 700868c98600e46413cc4f966b508e89642612c9 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:13:44 +0200 Subject: [PATCH 059/162] Update README_nl.md --- .github/README_nl.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_nl.md b/.github/README_nl.md index 0cbdb2d6a1..b349e31db3 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From f431f965fbd30a0edc42fd99b51553d03744e4bf Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:13:47 +0200 Subject: [PATCH 060/162] Update README_pt.md --- .github/README_pt.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_pt.md b/.github/README_pt.md index 26379d313a..9d62c55031 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 069617be16f41835b9bf90d85aa616762b76b9fe Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:13:51 +0200 Subject: [PATCH 061/162] Update README_ru.md --- .github/README_ru.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_ru.md b/.github/README_ru.md index 267dbe6c53..844d57c58d 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 7a5e033a4b65f3c131d5ffc7953d7a4575541dee Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:13:54 +0200 Subject: [PATCH 062/162] Update README_sa.md --- .github/README_sa.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_sa.md b/.github/README_sa.md index 9521f89753..3c2a8ca4bd 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 1f5d7ca80c54d2daef5168500755df9044b970ab Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:13:58 +0200 Subject: [PATCH 063/162] Update README_tr.md --- .github/README_tr.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_tr.md b/.github/README_tr.md index 41561e0eaa..5971d56a23 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From f3d67f0b50d0b9e8d7b8235b0ad203a3d41ec3e5 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:14:01 +0200 Subject: [PATCH 064/162] Update README_uk.md --- .github/README_uk.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_uk.md b/.github/README_uk.md index 85f105829b..5d3206a8da 100644 --- a/.github/README_uk.md +++ b/.github/README_uk.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From b1bd06691b8d08615b2acbfff3898268712ba400 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:14:05 +0200 Subject: [PATCH 065/162] Update README_zh-CN.md --- .github/README_zh-CN.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index bda48509cd..d0d1a27dd7 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 00781c3df24940ca0cd435a3d504a6c6bcac51c0 Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:14:09 +0200 Subject: [PATCH 066/162] Update README_zh-TW.md --- .github/README_zh-TW.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index b42173a53d..3d7ce42ba3 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From f0987ec071b49d4b9fbafc51ab8762982e7e921e Mon Sep 17 00:00:00 2001 From: Joey Date: Thu, 6 Apr 2023 14:14:26 +0200 Subject: [PATCH 067/162] Update README_ckb.md --- .github/README_ckb.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/README_ckb.md b/.github/README_ckb.md index df7f3db582..ae8afa6c81 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -1,6 +1,11 @@

- Fiber + + + + Fiber + +
From 1c5eb1846ebaebd10fca0a655a1400e90a96ed1d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 7 Apr 2023 11:22:10 +0300 Subject: [PATCH 068/162] Bump golang.org/x/sys from 0.6.0 to 0.7.0 (#2405) Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.6.0 to 0.7.0. - [Release notes](https://github.com/golang/sys/releases) - [Commits](https://github.com/golang/sys/compare/v0.6.0...v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 9961966cc3..1a20a8fef8 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/tinylib/msgp v1.1.8 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.45.0 - golang.org/x/sys v0.6.0 + golang.org/x/sys v0.7.0 ) require ( diff --git a/go.sum b/go.sum index de8d1651d6..7722ba9450 100644 --- a/go.sum +++ b/go.sum @@ -58,8 +58,9 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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= From 562d15db863248fe4d249b76560843c3a29511d3 Mon Sep 17 00:00:00 2001 From: "Juan C. Yamacho H" Date: Sun, 9 Apr 2023 15:08:03 +0200 Subject: [PATCH 069/162] :rocket: Feature: Public ShutdownWithContext (#2407) * feat: public shutdown with context * docs: add server shutdown option * chore: revert spacing changes * test: app shutdown with context --- app.go | 12 ++++++++---- app_test.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ docs/api/app.md | 3 +++ 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/app.go b/app.go index 7857da349d..ce23ee7a55 100644 --- a/app.go +++ b/app.go @@ -847,7 +847,7 @@ func (app *App) HandlersCount() uint32 { // // Shutdown does not close keepalive connections so its recommended to set ReadTimeout to something else than 0. func (app *App) Shutdown() error { - return app.shutdownWithContext(context.Background()) + return app.ShutdownWithContext(context.Background()) } // ShutdownWithTimeout gracefully shuts down the server without interrupting any active connections. However, if the timeout is exceeded, @@ -860,11 +860,15 @@ func (app *App) Shutdown() error { func (app *App) ShutdownWithTimeout(timeout time.Duration) error { ctx, cancelFunc := context.WithTimeout(context.Background(), timeout) defer cancelFunc() - return app.shutdownWithContext(ctx) + return app.ShutdownWithContext(ctx) } -// shutdownWithContext shuts down the server including by force if the context's deadline is exceeded. -func (app *App) shutdownWithContext(ctx context.Context) error { +// ShutdownWithContext shuts down the server including by force if the context's deadline is exceeded. +// +// Make sure the program doesn't exit and waits instead for ShutdownWithTimeout to return. +// +// ShutdownWithContext does not close keepalive connections so its recommended to set ReadTimeout to something else than 0. +func (app *App) ShutdownWithContext(ctx context.Context) error { if app.hooks != nil { defer app.hooks.executeOnShutdownHooks() } diff --git a/app_test.go b/app_test.go index aeb946b813..3ef57948b5 100644 --- a/app_test.go +++ b/app_test.go @@ -788,6 +788,53 @@ func Test_App_ShutdownWithTimeout(t *testing.T) { } } +func Test_App_ShutdownWithContext(t *testing.T) { + t.Parallel() + + app := New() + app.Get("/", func(ctx *Ctx) error { + time.Sleep(5 * time.Second) + return ctx.SendString("body") + }) + + ln := fasthttputil.NewInmemoryListener() + + go func() { + utils.AssertEqual(t, nil, app.Listener(ln)) + }() + + time.Sleep(1 * time.Second) + + go func() { + conn, err := ln.Dial() + if err != nil { + t.Errorf("unexepcted error: %v", err) + } + + if _, err = conn.Write([]byte("GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")); err != nil { + t.Errorf("unexpected error: %v", err) + } + }() + + time.Sleep(1 * time.Second) + + shutdownErr := make(chan error) + go func() { + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + shutdownErr <- app.ShutdownWithContext(ctx) + }() + + select { + case <-time.After(5 * time.Second): + t.Fatal("idle connections not closed on shutdown") + case err := <-shutdownErr: + if err == nil || !errors.Is(err, context.DeadlineExceeded) { + t.Fatalf("unexpected err %v. Expecting %v", err, context.DeadlineExceeded) + } + } +} + // go test -run Test_App_Static_Index_Default func Test_App_Static_Index_Default(t *testing.T) { t.Parallel() diff --git a/docs/api/app.md b/docs/api/app.md index 21e631a48d..5f788ca356 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -236,9 +236,12 @@ Shutdown gracefully shuts down the server without interrupting any active connec ShutdownWithTimeout will forcefully close any active connections after the timeout expires. +ShutdownWithContext shuts down the server including by force if the context's deadline is exceeded. + ```go func (app *App) Shutdown() error func (app *App) ShutdownWithTimeout(timeout time.Duration) error +func (app *App) ShutdownWithContext(ctx context.Context) error ``` ## HandlersCount From 22b407e2e7838c08939315b67ec982519e8f4679 Mon Sep 17 00:00:00 2001 From: Hakan Kutluay <77051856+hakankutluay@users.noreply.github.com> Date: Sun, 9 Apr 2023 17:05:51 +0300 Subject: [PATCH 070/162] :bug: [Bug-Fix] add original timeout middleware (#2367) * add original timeout middleware * fix linter issues * deprecate original timeout middleware * update timeout middleware documentation --- docs/api/middleware/timeout.md | 19 ++++++++++-- middleware/timeout/timeout.go | 47 ++++++++++++++++++++++++++++-- middleware/timeout/timeout_test.go | 12 ++++---- 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/docs/api/middleware/timeout.md b/docs/api/middleware/timeout.md index aa0f2eb3c4..7f8adf9336 100644 --- a/docs/api/middleware/timeout.md +++ b/docs/api/middleware/timeout.md @@ -3,16 +3,29 @@ id: timeout title: Timeout --- -Timeout middleware for [Fiber](https://github.com/gofiber/fiber). As a `fiber.Handler` wrapper, it creates a context with `context.WithTimeout` and pass it in `UserContext`. +There exist two distinct implementations of timeout middleware [Fiber](https://github.com/gofiber/fiber). +**New** + + Wraps a `fiber.Handler` with a timeout. If the handler takes longer than the given duration to return, the timeout error is set and forwarded to the centralized [ErrorHandler](https://docs.gofiber.io/error-handling). + + Note: This has been depreciated since it raises race conditions. + +**NewWithContext** + + As a `fiber.Handler` wrapper, it creates a context with `context.WithTimeout` and pass it in `UserContext`. + If the context passed executions (eg. DB ops, Http calls) takes longer than the given duration to return, the timeout error is set and forwarded to the centralized `ErrorHandler`. + It does not cancel long running executions. Underlying executions must handle timeout by using `context.Context` parameter. ## Signatures ```go func New(handler fiber.Handler, timeout time.Duration, timeoutErrors ...error) fiber.Handler + +func NewWithContext(handler fiber.Handler, timeout time.Duration, timeoutErrors ...error) fiber.Handler ``` ## Examples @@ -85,7 +98,7 @@ func main() { return nil } - app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second, ErrFooTimeOut)) + app.Get("/foo/:sleepTime", timeout.NewWithContext(h, 2*time.Second, ErrFooTimeOut)) _ = app.Listen(":3000") } @@ -124,7 +137,7 @@ func main() { return nil } - app.Get("/foo", timeout.New(handler, 10*time.Second)) + app.Get("/foo", timeout.NewWithContext(handler, 10*time.Second)) app.Listen(":3000") } ``` diff --git a/middleware/timeout/timeout.go b/middleware/timeout/timeout.go index 9f1fd21997..1485704dff 100644 --- a/middleware/timeout/timeout.go +++ b/middleware/timeout/timeout.go @@ -3,13 +3,56 @@ package timeout import ( "context" "errors" + "log" + "sync" "time" "github.com/gofiber/fiber/v2" ) -// New implementation of timeout middleware. Set custom errors(context.DeadlineExceeded vs) for get fiber.ErrRequestTimeout response. -func New(h fiber.Handler, t time.Duration, tErrs ...error) fiber.Handler { +var once sync.Once + +// New wraps a handler and aborts the process of the handler if the timeout is reached. +// +// Deprecated: This implementation contains data race issues. Use NewWithContext instead. +// Find documentation and sample usage on https://docs.gofiber.io/api/middleware/timeout +func New(handler fiber.Handler, timeout time.Duration) fiber.Handler { + once.Do(func() { + log.Printf("[Warning] timeout contains data race issues, not ready for production!") + }) + + if timeout <= 0 { + return handler + } + + // logic is from fasthttp.TimeoutWithCodeHandler https://github.com/valyala/fasthttp/blob/master/server.go#L418 + return func(ctx *fiber.Ctx) error { + ch := make(chan struct{}, 1) + + go func() { + defer func() { + if err := recover(); err != nil { + log.Printf("[Warning] recover error %v", err) + } + }() + if err := handler(ctx); err != nil { + log.Printf("[Warning] handler error %v", err) + } + ch <- struct{}{} + }() + + select { + case <-ch: + case <-time.After(timeout): + return fiber.ErrRequestTimeout + } + + return nil + } +} + +// NewWithContext implementation of timeout middleware. Set custom errors(context.DeadlineExceeded vs) for get fiber.ErrRequestTimeout response. +func NewWithContext(h fiber.Handler, t time.Duration, tErrs ...error) fiber.Handler { return func(ctx *fiber.Ctx) error { timeoutContext, cancel := context.WithTimeout(ctx.UserContext(), t) defer cancel() diff --git a/middleware/timeout/timeout_test.go b/middleware/timeout/timeout_test.go index 8498cb816d..413ac0da2d 100644 --- a/middleware/timeout/timeout_test.go +++ b/middleware/timeout/timeout_test.go @@ -12,12 +12,12 @@ import ( "github.com/gofiber/fiber/v2/utils" ) -// go test -run Test_Timeout -func Test_Timeout(t *testing.T) { +// go test -run Test_WithContextTimeout +func Test_WithContextTimeout(t *testing.T) { t.Parallel() // fiber instance app := fiber.New() - h := New(func(c *fiber.Ctx) error { + h := NewWithContext(func(c *fiber.Ctx) error { sleepTime, err := time.ParseDuration(c.Params("sleepTime") + "ms") utils.AssertEqual(t, nil, err) if err := sleepWithContext(c.UserContext(), sleepTime, context.DeadlineExceeded); err != nil { @@ -44,12 +44,12 @@ func Test_Timeout(t *testing.T) { var ErrFooTimeOut = errors.New("foo context canceled") -// go test -run Test_TimeoutWithCustomError -func Test_TimeoutWithCustomError(t *testing.T) { +// go test -run Test_WithContextTimeoutWithCustomError +func Test_WithContextTimeoutWithCustomError(t *testing.T) { t.Parallel() // fiber instance app := fiber.New() - h := New(func(c *fiber.Ctx) error { + h := NewWithContext(func(c *fiber.Ctx) error { sleepTime, err := time.ParseDuration(c.Params("sleepTime") + "ms") utils.AssertEqual(t, nil, err) if err := sleepWithContext(c.UserContext(), sleepTime, ErrFooTimeOut); err != nil { From 8b1f9260a398a1c5ca41608432fcef6871f501d9 Mon Sep 17 00:00:00 2001 From: cmd777 <83428931+cmd777@users.noreply.github.com> Date: Mon, 10 Apr 2023 04:48:12 +0200 Subject: [PATCH 071/162] :books: Docs: Fix typos, and make middleware documentation more consistent (#2408) Fix typos, and make docs more consistent This fixes several typos in the ISSUE_TEMPLATES, as well as improve their readability, also makes the documentation markdowns more consistent --- .github/ISSUE_TEMPLATE/bug-report.yaml | 10 +- .github/ISSUE_TEMPLATE/feature-request.yaml | 10 +- .github/ISSUE_TEMPLATE/question.yaml | 10 +- docs/api/middleware/cache.md | 1 - docs/api/middleware/compress.md | 6 +- docs/api/middleware/cors.md | 23 ++-- docs/api/middleware/csrf.md | 28 +++-- docs/api/middleware/earlydata.md | 28 +++-- docs/api/middleware/encryptcookie.md | 19 ++-- docs/api/middleware/envvar.md | 20 ++-- docs/api/middleware/etag.md | 8 +- docs/api/middleware/expvar.md | 31 +++--- docs/api/middleware/favicon.md | 11 +- docs/api/middleware/filesystem.md | 12 +-- docs/api/middleware/idempotency.md | 7 +- docs/api/middleware/limiter.md | 30 +++--- docs/api/middleware/logger.md | 66 ++++++------ docs/api/middleware/monitor.md | 112 +++++++++++--------- docs/api/middleware/pprof.md | 13 +-- docs/api/middleware/proxy.md | 1 - docs/api/middleware/recover.md | 4 +- docs/api/middleware/requestid.md | 2 +- docs/api/middleware/session.md | 65 +++++++----- docs/api/middleware/skip.md | 27 ++++- docs/api/middleware/timeout.md | 16 +-- 25 files changed, 304 insertions(+), 256 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index 0f7e123505..c48df1dec7 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -10,15 +10,15 @@ body: value: | ### Notice **This repository is not related to external or third-part Fiber modules. If you have a problem with them, open an issue under their repos. If you think the problem is related to Fiber, open the issue here.** - - Dont't forget you can ask your questions on our [Discord server](https://gofiber.io/discord). - - If you think Fiber doesn't have a nice feature that you think, open the issue with **✏️ Feature Request** template. + - Don't forget you can ask your questions in our [Discord server](https://gofiber.io/discord). + - If you have a suggestion for a Fiber feature you would like to see, open the issue with the **✏️ Feature Request** template. - Write your issue with clear and understandable English. - type: textarea id: description attributes: label: "Bug Description" description: "A clear and detailed description of what the bug is." - placeholder: "Explain your problem as clear and detailed." + placeholder: "Explain your problem clearly and in detail." validations: required: true - type: textarea @@ -39,7 +39,7 @@ body: id: expected-behavior attributes: label: Expected Behavior - description: "A clear and detailed description of what you think should happens." + description: "A clear and detailed description of what you think should happen." placeholder: "Tell us what Fiber should normally do." validations: required: true @@ -56,7 +56,7 @@ body: attributes: label: "Code Snippet (optional)" description: "For some issues, we need to know some parts of your code." - placeholder: "Share a code you think related to the issue." + placeholder: "Share a code snippet that you think is related to the issue." render: go value: | package main diff --git a/.github/ISSUE_TEMPLATE/feature-request.yaml b/.github/ISSUE_TEMPLATE/feature-request.yaml index 733dcc7f02..fbdb871f62 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yaml +++ b/.github/ISSUE_TEMPLATE/feature-request.yaml @@ -9,15 +9,15 @@ body: attributes: value: | ### Notice - - Dont't forget you can ask your questions on our [Discord server](https://gofiber.io/discord). - - If you think this is just a bug, open the issue with **☢️ Bug Report** template. + - Don't forget you can ask your questions in our [Discord server](https://gofiber.io/discord). + - If you think this is just a bug, open the issue with the **☢️ Bug Report** template. - Write your issue with clear and understandable English. - type: textarea id: description attributes: label: "Feature Description" - description: "A clear and detailed description of the feature we need to do." - placeholder: "Explain your feature as clear and detailed." + description: "A clear and detailed description of the feature you would like to see added." + placeholder: "Explain your feature clearly, and in detail." validations: required: true - type: textarea @@ -31,7 +31,7 @@ body: attributes: label: "Code Snippet (optional)" description: "Code snippet may be really helpful to describe some features." - placeholder: "Share a code to explain the feature better." + placeholder: "Share a code snippet to explain the feature better." render: go value: | package main diff --git a/.github/ISSUE_TEMPLATE/question.yaml b/.github/ISSUE_TEMPLATE/question.yaml index 976c34a6e9..7204183bdb 100644 --- a/.github/ISSUE_TEMPLATE/question.yaml +++ b/.github/ISSUE_TEMPLATE/question.yaml @@ -9,16 +9,16 @@ body: attributes: value: | ### Notice - - Dont't forget you can ask your questions on our [Discord server](https://gofiber.io/discord). - - If you think this is just a bug, open the issue with **☢️ Bug Report** template. - - If you think Fiber doesn't have a nice feature that you think, open the issue with **✏️ Feature Request** template. + - Don't forget you can ask your questions in our [Discord server](https://gofiber.io/discord). + - If you think this is just a bug, open the issue with the **☢️ Bug Report** template. + - If you have a suggestion for a Fiber feature you would like to see, open the issue with the **✏️ Feature Request** template. - Write your issue with clear and understandable English. - type: textarea id: description attributes: label: "Question Description" description: "A clear and detailed description of the question." - placeholder: "Explain your question as clear and detailed." + placeholder: "Explain your question clearly, and in detail." validations: required: true - type: textarea @@ -26,7 +26,7 @@ body: attributes: label: "Code Snippet (optional)" description: "Code snippet may be really helpful to describe some features." - placeholder: "Share a code to explain the feature better." + placeholder: "Share a code snippet to explain the feature better." render: go value: | package main diff --git a/docs/api/middleware/cache.md b/docs/api/middleware/cache.md index 5ffe20ba2d..209865ba3b 100644 --- a/docs/api/middleware/cache.md +++ b/docs/api/middleware/cache.md @@ -128,7 +128,6 @@ type Config struct { ## Default Config ```go -// ConfigDefault is the default config var ConfigDefault = Config{ Next: nil, Expiration: 1 * time.Minute, diff --git a/docs/api/middleware/compress.md b/docs/api/middleware/compress.md index 1ea51f5751..6066ac7246 100644 --- a/docs/api/middleware/compress.md +++ b/docs/api/middleware/compress.md @@ -25,10 +25,10 @@ import ( After you initiate your Fiber app, you can use the following possibilities: ```go -// Default middleware config +// Initialize default config app.Use(compress.New()) -// Provide a custom compression level +// Or extend your config for customization app.Use(compress.New(compress.Config{ Level: compress.LevelBestSpeed, // 1 })) @@ -52,7 +52,7 @@ type Config struct { // Optional. Default: nil Next func(c *fiber.Ctx) bool - // CompressLevel determines the compression algoritm + // Level determines the compression algoritm // // Optional. Default: LevelDefault // LevelDisabled: -1 diff --git a/docs/api/middleware/cors.md b/docs/api/middleware/cors.md index 38783f7ca9..e84f14bfd7 100644 --- a/docs/api/middleware/cors.md +++ b/docs/api/middleware/cors.md @@ -25,7 +25,7 @@ import ( After you initiate your Fiber app, you can use the following possibilities: ```go -// Default config +// Initialize default config app.Use(cors.New()) // Or extend your config for customization @@ -88,12 +88,19 @@ type Config struct { ```go var ConfigDefault = Config{ - Next: nil, - AllowOrigins: "*", - AllowMethods: "GET,POST,HEAD,PUT,DELETE,PATCH", - AllowHeaders: "", - AllowCredentials: false, - ExposeHeaders: "", - MaxAge: 0, + Next: nil, + AllowOrigins: "*", + AllowMethods: strings.Join([]string{ + fiber.MethodGet, + fiber.MethodPost, + fiber.MethodHead, + fiber.MethodPut, + fiber.MethodDelete, + fiber.MethodPatch, + }, ","), + AllowHeaders: "", + AllowCredentials: false, + ExposeHeaders: "", + MaxAge: 0, } ``` diff --git a/docs/api/middleware/csrf.md b/docs/api/middleware/csrf.md index b5982e3b28..d0452a8ece 100644 --- a/docs/api/middleware/csrf.md +++ b/docs/api/middleware/csrf.md @@ -10,9 +10,7 @@ CSRF Tokens are generated on GET requests. You can retrieve the CSRF token with When no `csrf_` cookie is set, or the token has expired, a new token will be generated and `csrf_` cookie set. :::note - -This middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for this middleware saves data to memory, see the examples below for other databases._ - +This middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for this middleware saves data to memory, see the examples below for other databases. ::: ## Signatures @@ -49,7 +47,9 @@ app.Use(csrf.New(csrf.Config{ })) ``` -Note: KeyLookup will be ignored if Extractor is explicitly set. +:::note +KeyLookup will be ignored if Extractor is explicitly set. +::: ## Config @@ -137,14 +137,24 @@ type Config struct { ```go var ConfigDefault = Config{ - KeyLookup: "header:X-Csrf-Token", - CookieName: "csrf_", - CookieSameSite: "Lax", - Expiration: 1 * time.Hour, - KeyGenerator: utils.UUID, + KeyLookup: "header:" + HeaderName, + CookieName: "csrf_", + CookieSameSite: "Lax", + Expiration: 1 * time.Hour, + KeyGenerator: utils.UUID, + ErrorHandler: defaultErrorHandler, + Extractor: CsrfFromHeader(HeaderName), } ``` +## Constants + +```go +const ( + HeaderName = "X-Csrf-Token" +) +``` + ### Custom Storage/Database You can use any storage from our [storage](https://github.com/gofiber/storage/) package. diff --git a/docs/api/middleware/earlydata.md b/docs/api/middleware/earlydata.md index 46da54b573..6b83f1214f 100644 --- a/docs/api/middleware/earlydata.md +++ b/docs/api/middleware/earlydata.md @@ -24,7 +24,7 @@ func New(config ...Config) fiber.Handler ## Examples -First import the middleware from Fiber, +Import the middleware package that is part of the Fiber web framework ```go import ( @@ -33,26 +33,23 @@ import ( ) ``` -Then create a Fiber app with `app := fiber.New()`. - -### Default Config +After you initiate your Fiber app, you can use the following possibilities: ```go +// Initialize default config app.Use(earlydata.New()) -``` -### Custom Config - -```go +// Or extend your config for customization app.Use(earlydata.New(earlydata.Config{ Error: fiber.ErrTooEarly, // ... })) ``` -### Config +## Config ```go +// Config defines the config for middleware. type Config struct { // Next defines a function to skip this middleware when returned true. // @@ -76,12 +73,12 @@ type Config struct { } ``` -### Default Config +## Default Config ```go var ConfigDefault = Config{ IsEarlyData: func(c *fiber.Ctx) bool { - return c.Get("Early-Data") == "1" + return c.Get(DefaultHeaderName) == DefaultHeaderTrueValue }, AllowEarlyData: func(c *fiber.Ctx) bool { @@ -91,3 +88,12 @@ var ConfigDefault = Config{ Error: fiber.ErrTooEarly, } ``` + +## Constants + +```go +const ( + DefaultHeaderName = "Early-Data" + DefaultHeaderTrueValue = "1" +) +``` \ No newline at end of file diff --git a/docs/api/middleware/encryptcookie.md b/docs/api/middleware/encryptcookie.md index 5b2d34e9a5..594df96389 100644 --- a/docs/api/middleware/encryptcookie.md +++ b/docs/api/middleware/encryptcookie.md @@ -29,7 +29,10 @@ import ( After you initiate your Fiber app, you can use the following possibilities: ```go -// Default middleware config +// Provide a minimal config +// `Key` must be a 32 character string. It's used to encrypt the values, so make sure it is random and keep it secret. +// You can run `openssl rand -base64 32` or call `encryptcookie.GenerateKey()` to create a random key for you. +// Make sure not to set `Key` to `encryptcookie.GenerateKey()` because that will create a new key every run. app.Use(encryptcookie.New(encryptcookie.Config{ Key: "secret-thirty-2-character-string", })) @@ -52,6 +55,7 @@ app.Post("/", func(c *fiber.Ctx) error { ## Config ```go +// Config defines the config for middleware. type Config struct { // Next defines a function to skip this middleware when returned true. // @@ -84,12 +88,13 @@ type Config struct { ## Default Config ```go -// `Key` must be a 32 character string. It's used to encrpyt the values, so make sure it is random and keep it secret. -// You can run `openssl rand -base64 32` or call `encryptcookie.GenerateKey()` to create a random key for you. -// Make sure not to set `Key` to `encryptcookie.GenerateKey()` because that will create a new key every run. -app.Use(encryptcookie.New(encryptcookie.Config{ - Key: "secret-thirty-2-character-string", -})) +var ConfigDefault = Config{ + Next: nil, + Except: []string{"csrf_"}, + Key: "", + Encryptor: EncryptCookie, + Decryptor: DecryptCookie, +} ``` ## Usage of CSRF and Encryptcookie Middlewares with Custom Cookie Names diff --git a/docs/api/middleware/envvar.md b/docs/api/middleware/envvar.md index c2eb6794ad..592a502df4 100644 --- a/docs/api/middleware/envvar.md +++ b/docs/api/middleware/envvar.md @@ -13,7 +13,7 @@ func New(config ...Config) fiber.Handler ## Examples -First import the middleware from Fiber, +Import the middleware package that is part of the Fiber web framework ```go import ( @@ -22,19 +22,13 @@ import ( ) ``` -Then create a Fiber app with `app := fiber.New()`. - -**Note**: You need to provide a path to use envvar middleware. - -### Default Config +After you initiate your Fiber app, you can use the following possibilities: ```go +// Initialize default config app.Use("/expose/envvars", envvar.New()) -``` -### Custom Config - -```go +// Or extend your config for customization app.Use("/expose/envvars", envvar.New( envvar.Config{ ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, @@ -43,7 +37,11 @@ app.Use("/expose/envvars", envvar.New( ) ``` -### Response +:::note +You will need to provide a path to use the envvar middleware. +::: + +## Response Http response contract: ``` diff --git a/docs/api/middleware/etag.md b/docs/api/middleware/etag.md index ee2fa4ee28..b9df4e93fe 100644 --- a/docs/api/middleware/etag.md +++ b/docs/api/middleware/etag.md @@ -24,20 +24,16 @@ import ( After you initiate your Fiber app, you can use the following possibilities: -### Default Config - ```go +// Initialize default config app.Use(etag.New()) // Get / receives Etag: "13-1831710635" in response header app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello, World!") }) -``` -### Custom Config - -```go +// Or extend your config for customization app.Use(etag.New(etag.Config{ Weak: true, })) diff --git a/docs/api/middleware/expvar.md b/docs/api/middleware/expvar.md index a4ecb3c421..c2023fbb33 100644 --- a/docs/api/middleware/expvar.md +++ b/docs/api/middleware/expvar.md @@ -11,34 +11,27 @@ Expvar middleware for [Fiber](https://github.com/gofiber/fiber) that serves via func New() fiber.Handler ``` -## Example +## Examples -Import the expvar package that is part of the Fiber web framework +Import the middleware package that is part of the Fiber web framework ```go -package main - import ( - "expvar" - "fmt" - - "github.com/gofiber/fiber/v2" - expvarmw "github.com/gofiber/fiber/v2/middleware/expvar" + "github.com/gofiber/fiber/v2" + expvarmw "github.com/gofiber/fiber/v2/middleware/expvar" ) +``` +After you initiate your Fiber app, you can use the following possibilities: +```go var count = expvar.NewInt("count") -func main() { - app := fiber.New() - app.Use(expvarmw.New()) - app.Get("/", func(c *fiber.Ctx) error { - count.Add(1) - - return c.SendString(fmt.Sprintf("hello expvar count %d", count.Value())) - }) +app.Use(expvarmw.New()) +app.Get("/", func(c *fiber.Ctx) error { + count.Add(1) - fmt.Println(app.Listen(":3000")) -} + return c.SendString(fmt.Sprintf("hello expvar count %d", count.Value())) +}) ``` Visit path `/debug/vars` to see all vars and use query `r=key` to filter exposed variables. diff --git a/docs/api/middleware/favicon.md b/docs/api/middleware/favicon.md index 20fe6682b3..39432d45b9 100644 --- a/docs/api/middleware/favicon.md +++ b/docs/api/middleware/favicon.md @@ -29,7 +29,7 @@ import ( After you initiate your Fiber app, you can use the following possibilities: ```go -// Provide a minimal config +// Initialize default config app.Use(favicon.New()) // Or extend your config for customization @@ -76,10 +76,9 @@ type Config struct { ```go var ConfigDefault = Config{ - Next: nil, - File: "", - URL: "/favicon.ico", - FileSystem: nil, - CacheControl: "public, max-age=31536000", + Next: nil, + File: "", + URL: fPath, + CacheControl: "public, max-age=31536000", } ``` diff --git a/docs/api/middleware/filesystem.md b/docs/api/middleware/filesystem.md index d0a41a7166..a3029f96f1 100644 --- a/docs/api/middleware/filesystem.md +++ b/docs/api/middleware/filesystem.md @@ -11,20 +11,20 @@ Filesystem middleware for [Fiber](https://github.com/gofiber/fiber) that enables **To handle paths with spaces (or other url encoded values) make sure to set `fiber.Config{ UnescapePath: true }`** ::: -### Table of Contents +## Table of Contents * [Signatures](filesystem.md#signatures) * [Examples](filesystem.md#examples) * [Config](filesystem.md#config) * [Default Config](filesystem.md#default-config) -### Signatures +## Signatures ```go func New(config Config) fiber.Handler ``` -### Examples +## Examples Import the middleware package that is part of the Fiber web framework @@ -56,7 +56,7 @@ app.Use(filesystem.New(filesystem.Config{ > If your environment (Go 1.16+) supports it, we recommend using Go Embed instead of the other solutions listed as this one is native to Go and the easiest to use. -### embed +## embed [Embed](https://golang.org/pkg/embed/) is the native method to embed files in a Golang excecutable. Introduced in Go 1.16. @@ -233,7 +233,7 @@ func main() { } ``` -### Config +## Config ```go // Config defines the config for middleware. @@ -280,7 +280,7 @@ type Config struct { } ``` -### Default Config +## Default Config ```go var ConfigDefault = Config{ diff --git a/docs/api/middleware/idempotency.md b/docs/api/middleware/idempotency.md index 72eee4b4bb..dc0cda8a15 100644 --- a/docs/api/middleware/idempotency.md +++ b/docs/api/middleware/idempotency.md @@ -15,7 +15,7 @@ func New(config ...Config) fiber.Handler ## Examples -First import the middleware from Fiber, +Import the middleware package that is part of the Fiber web framework ```go import ( @@ -24,7 +24,7 @@ import ( ) ``` -Then create a Fiber app with `app := fiber.New()`. +After you initiate your Fiber app, you can use the following possibilities: ### Default Config @@ -44,6 +44,7 @@ app.Use(idempotency.New(idempotency.Config{ ### Config ```go +// Config defines the config for middleware. type Config struct { // Next defines a function to skip this middleware when returned true. // @@ -81,7 +82,7 @@ type Config struct { } ``` -### Default Config +## Default Config ```go var ConfigDefault = Config{ diff --git a/docs/api/middleware/limiter.md b/docs/api/middleware/limiter.md index cc5c23666f..5a88d69f4a 100644 --- a/docs/api/middleware/limiter.md +++ b/docs/api/middleware/limiter.md @@ -6,15 +6,11 @@ title: Limiter Limiter middleware for [Fiber](https://github.com/gofiber/fiber) that is used to limit repeat requests to public APIs and/or endpoints such as password reset. It is also useful for API clients, web crawling, or other tasks that need to be throttled. :::note - This middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for this middleware saves data to memory, see the examples below for other databases. - ::: :::note - This module does not share state with other processes/servers by default. - ::: ## Signatures @@ -37,7 +33,7 @@ import ( After you initiate your Fiber app, you can use the following possibilities: ```go -// Default middleware config +// Initialize default config app.Use(limiter.New()) // Or extend your config for customization @@ -77,17 +73,6 @@ weightOfPreviousWindpw = previous window's amount request * (whenNewWindow / Exp rate = weightOfPreviousWindpw + current window's amount request. ``` -## Custom Storage/Database - -You can use any storage from our [storage](https://github.com/gofiber/storage/) package. - -```go -storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3 -app.Use(limiter.New(limiter.Config{ - Storage: storage, -})) -``` - ## Config ```go @@ -144,7 +129,9 @@ type Config struct { } ``` +:::note A custom store can be used if it implements the `Storage` interface - more details and an example can be found in `store.go`. +::: ## Default Config @@ -163,3 +150,14 @@ var ConfigDefault = Config{ LimiterMiddleware: FixedWindow{}, } ``` + +### Custom Storage/Database + +You can use any storage from our [storage](https://github.com/gofiber/storage/) package. + +```go +storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3 +app.Use(limiter.New(limiter.Config{ + Storage: storage, +})) +``` \ No newline at end of file diff --git a/docs/api/middleware/logger.md b/docs/api/middleware/logger.md index 1da3342431..1c0860eb2f 100644 --- a/docs/api/middleware/logger.md +++ b/docs/api/middleware/logger.md @@ -10,7 +10,9 @@ Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP r func New(config ...Config) fiber.Handler ``` ## Examples -First ensure the appropriate packages are imported + +Import the middleware package that is part of the Fiber web framework + ```go import ( "github.com/gofiber/fiber/v2" @@ -19,45 +21,37 @@ import ( ``` :::tip - The order of registration plays a role. Only all routes that are registered after this one will be logged. The middleware should therefore be one of the first to be registered. - ::: -### Default Config +After you initiate your Fiber app, you can use the following possibilities: + ```go -// Default middleware config +// Initialize default config app.Use(logger.New()) -``` -### Logging remote IP and Port -```go + +// Or extend your config for customization +// Logging remote IP and Port app.Use(logger.New(logger.Config{ Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", })) -``` -### Logging Request ID -```go +// Logging Request ID 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", })) -``` - -### Changing TimeZone & TimeFormat -```go +// Changing TimeZone & TimeFormat app.Use(logger.New(logger.Config{ Format: "${pid} ${status} - ${method} ${path}\n", TimeFormat: "02-Jan-2006", TimeZone: "America/New_York", })) -``` -### Custom File Writer -```go +// Custom File Writer file, err := os.OpenFile("./123.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { log.Fatalf("error opening file: %v", err) @@ -66,9 +60,8 @@ defer file.Close() app.Use(logger.New(logger.Config{ Output: file, })) -``` -### Add Custom Tags -```go + +// Add Custom Tags app.Use(logger.New(logger.Config{ CustomTags: map[string]logger.LogFunc{ "custom_tag": func(output logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) { @@ -76,11 +69,8 @@ app.Use(logger.New(logger.Config{ }, }, })) -``` -### Callback after log is written - -```go +// Callback after log is written app.Use(logger.New(logger.Config{ TimeFormat: time.RFC3339Nano, TimeZone: "Asia/Shanghai", @@ -100,48 +90,60 @@ type Config struct { // // Optional. Default: nil Next func(c *fiber.Ctx) bool + // Done is a function that is called after the log string for a request is written to Output, // and pass the log string as parameter. // // Optional. Default: nil Done func(c *fiber.Ctx, logString []byte) + // tagFunctions defines the custom tag action // // Optional. Default: map[string]LogFunc CustomTags map[string]LogFunc + // Format defines the logging tags // // Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n Format string + // TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html // // Optional. Default: 15:04:05 TimeFormat string + // TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc // // Optional. Default: "Local" TimeZone string + // TimeInterval is the delay before the timestamp is updated // // Optional. Default: 500 * time.Millisecond TimeInterval time.Duration + // Output is a writer where logs are written // // Default: os.Stdout Output io.Writer + + enableColors bool + enableLatency bool + timeZoneLocation *time.Location } type LogFunc func(buf logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) ``` ## Default Config ```go var ConfigDefault = Config{ - Next: nil, - Done: nil, - Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", - TimeFormat: "15:04:05", - TimeZone: "Local", - TimeInterval: 500 * time.Millisecond, - Output: os.Stdout, + Next: nil, + Done: nil, + Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", + TimeFormat: "15:04:05", + TimeZone: "Local", + TimeInterval: 500 * time.Millisecond, + Output: os.Stdout, + enableColors: true, } ``` diff --git a/docs/api/middleware/monitor.md b/docs/api/middleware/monitor.md index 459b47d1e9..4ecdca647d 100644 --- a/docs/api/middleware/monitor.md +++ b/docs/api/middleware/monitor.md @@ -19,24 +19,24 @@ func New() fiber.Handler ``` ### Examples -Import the middleware package and assign it to a route. -```go -package main +Import the middleware package that is part of the Fiber web framework +```go import ( - "log" - - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/monitor" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/favicon" ) +``` -func main() { - app := fiber.New() - - app.Get("/metrics", monitor.New(monitor.Config{Title: "MyService Metrics Page"})) +After you initiate your Fiber app, you can use the following possibilities: +```go +// Initialize default config (Assign the middleware to /metrics) +app.Get("/metrics", monitor.New()) - log.Fatal(app.Listen(":3000")) -} +// Or extend your config for customization +// Assign the middleware to /metrics +// and change the Title to `MyService Metrics Page` +app.Get("/metrics", monitor.New(monitor.Config{Title: "MyService Metrics Page"})) ``` You can also access the API endpoint with `curl -X GET -H "Accept: application/json" http://localhost:3000/metrics` which returns: @@ -51,42 +51,42 @@ You can also access the API endpoint with ```go // Config defines the config for middleware. type Config struct { - // Metrics page title - // - // Optional. Default: "Fiber Monitor" - Title string - - // Refresh period - // - // Optional. Default: 3 seconds - Refresh time.Duration - - // Whether the service should expose only the monitoring API. - // - // Optional. Default: false - APIOnly bool - - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Custom HTML Code to Head Section(Before End) - // - // Optional. Default: empty - CustomHead string - - // FontURL for specify font resource path or URL . also you can use relative path - // - // Optional. Default: https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap - - FontURL string - // ChartJsURL for specify ChartJS library path or URL . also you can use relative path + // Metrics page title // - // Optional. Default: https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js + // Optional. Default: "Fiber Monitor" + Title string + // Refresh period + // + // Optional. Default: 3 seconds + Refresh time.Duration + + // Whether the service should expose only the monitoring API. + // + // Optional. Default: false + APIOnly bool + + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Custom HTML Code to Head Section(Before End) + // + // Optional. Default: empty + CustomHead string + + // FontURL for specify font resource path or URL . also you can use relative path + // + // Optional. Default: https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap + FontURL string + + // ChartJsURL for specify ChartJS library path or URL . also you can use relative path + // + // Optional. Default: https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js ChartJsURL string + index string } ``` @@ -94,13 +94,19 @@ type Config struct { ```go var ConfigDefault = Config{ - Title: "Fiber Monitor", - Refresh: 3 * time.Second, - APIOnly: false, - Next: nil, - CustomHead:"", - FontURL:"https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap", - ChartJsURL:"https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js" - + Title: defaultTitle, + Refresh: defaultRefresh, + FontURL: defaultFontURL, + ChartJsURL: defaultChartJSURL, + CustomHead: defaultCustomHead, + APIOnly: false, + Next: nil, + index: newIndex(viewBag{ + defaultTitle, + defaultRefresh, + defaultFontURL, + defaultChartJSURL, + defaultCustomHead, + }), } ``` diff --git a/docs/api/middleware/pprof.md b/docs/api/middleware/pprof.md index fbcb47e6a0..c56971e566 100644 --- a/docs/api/middleware/pprof.md +++ b/docs/api/middleware/pprof.md @@ -28,19 +28,16 @@ import ( After you initiate your Fiber app, you can use the following possibilities: ```go -// Default middleware +// Initialize default config app.Use(pprof.New()) -``` -In systems where you have multiple ingress endpoints, it is common to add a URL prefix, like so: +// Or extend your config for customization -```go -// Default middleware +// For example, in systems where you have multiple ingress endpoints, it is common to add a URL prefix, like so: app.Use(pprof.New(pprof.Config{Prefix: "/endpoint-prefix"})) -``` -This prefix will be added to the default path of "/debug/pprof/", for a resulting URL of: -"/endpoint-prefix/debug/pprof/". +// This prefix will be added to the default path of "/debug/pprof/", for a resulting URL of: "/endpoint-prefix/debug/pprof/". +``` ## Config diff --git a/docs/api/middleware/proxy.md b/docs/api/middleware/proxy.md index 3ca54dad48..3179414036 100644 --- a/docs/api/middleware/proxy.md +++ b/docs/api/middleware/proxy.md @@ -193,7 +193,6 @@ type Config struct { ## Default Config ```go -// ConfigDefault is the default config var ConfigDefault = Config{ Next: nil, ModifyRequest: nil, diff --git a/docs/api/middleware/recover.md b/docs/api/middleware/recover.md index 89013ed3d8..e60703f2de 100644 --- a/docs/api/middleware/recover.md +++ b/docs/api/middleware/recover.md @@ -25,10 +25,10 @@ import ( After you initiate your Fiber app, you can use the following possibilities: ```go -// Default middleware config +// Initialize default config app.Use(recover.New()) -// This panic will be catch by the middleware +// This panic will be caught by the middleware app.Get("/", func(c *fiber.Ctx) error { panic("I'm an error") }) diff --git a/docs/api/middleware/requestid.md b/docs/api/middleware/requestid.md index b1510ca76a..aafbd1799e 100644 --- a/docs/api/middleware/requestid.md +++ b/docs/api/middleware/requestid.md @@ -25,7 +25,7 @@ import ( After you initiate your Fiber app, you can use the following possibilities: ```go -// Default middleware config +// Initialize default config app.Use(requestid.New()) // Or extend your config for customization diff --git a/docs/api/middleware/session.md b/docs/api/middleware/session.md index be121326ea..f27eaf42bc 100644 --- a/docs/api/middleware/session.md +++ b/docs/api/middleware/session.md @@ -29,12 +29,10 @@ func (s *Session) Keys() []string ``` :::caution - Storing `interface{}` values are limited to built-ins Go types. - ::: -### Examples +## Examples Import the middleware package that is part of the Fiber web framework ```go import ( @@ -43,16 +41,13 @@ import ( ) ``` -Then create a Fiber app with `app := fiber.New()`. - -### Default Configuration +After you initiate your Fiber app, you can use the following possibilities: ```go +// Initialize default config // This stores all of your app's sessions -// Default middleware config store := session.New() -// This panic will be caught by the middleware app.Get("/", func(c *fiber.Ctx) error { // Get session from storage sess, err := store.Get(c) @@ -89,19 +84,6 @@ app.Get("/", func(c *fiber.Ctx) error { }) ``` -### Custom Storage/Database - -You can use any storage from our [storage](https://github.com/gofiber/storage/) package. - -```go -storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3 -store := session.New(session.Config{ - Storage: storage, -}) -``` - -To use the store, see the above example. - ## Config ```go @@ -121,23 +103,23 @@ type Config struct { // Optional. Default value "cookie:session_id". KeyLookup string - // Domain of the cookie. + // Domain of the CSRF cookie. // Optional. Default value "". CookieDomain string - // Path of the cookie. + // Path of the CSRF cookie. // Optional. Default value "". CookiePath string - // Indicates if cookie is secure. + // Indicates if CSRF cookie is secure. // Optional. Default value false. CookieSecure bool - // Indicates if cookie is HTTP only. + // Indicates if CSRF cookie is HTTP only. // Optional. Default value false. CookieHTTPOnly bool - // Sets the cookie SameSite attribute. + // Value of SameSite cookie. // Optional. Default value "Lax". CookieSameSite string @@ -147,14 +129,14 @@ type Config struct { CookieSessionOnly bool // KeyGenerator generates the session key. - // Optional. Default value utils.UUID + // Optional. Default value utils.UUIDv4 KeyGenerator func() string // Deprecated: Please use KeyLookup CookieName string // Source defines where to obtain the session id - source Source + source Source // The session name sessionName string @@ -167,6 +149,31 @@ type Config struct { var ConfigDefault = Config{ Expiration: 24 * time.Hour, KeyLookup: "cookie:session_id", - KeyGenerator: utils.UUID, + KeyGenerator: utils.UUIDv4, + source: "cookie", + sessionName: "session_id", } ``` + +## Constants + +```go +const ( + SourceCookie Source = "cookie" + SourceHeader Source = "header" + SourceURLQuery Source = "query" +) +``` + +### Custom Storage/Database + +You can use any storage from our [storage](https://github.com/gofiber/storage/) package. + +```go +storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3 +store := session.New(session.Config{ + Storage: storage, +}) +``` + +To use the store, see the [Examples](#examples). \ No newline at end of file diff --git a/docs/api/middleware/skip.md b/docs/api/middleware/skip.md index 17e19b7e77..1c026715fe 100644 --- a/docs/api/middleware/skip.md +++ b/docs/api/middleware/skip.md @@ -5,12 +5,12 @@ title: Skip Skip middleware for [Fiber](https://github.com/gofiber/fiber) that skips a wrapped handler if a predicate is true. -### Signatures +## Signatures ```go func New(handler fiber.Handler, exclude func(c *fiber.Ctx) bool) fiber.Handler ``` -### Examples +## Examples Import the middleware package that is part of the Fiber web framework ```go import ( @@ -20,6 +20,27 @@ import ( ``` After you initiate your Fiber app, you can use the following possibilities: + ```go -app.Use(skip.New(handler, func(ctx *fiber.Ctx) bool { return ctx.Method() == fiber.MethodOptions })) +func main() { + app := fiber.New() + + app.Use(skip.New(BasicHandler, func(ctx *fiber.Ctx) bool { + return ctx.Method() == fiber.MethodGet + })) + + app.Get("/", func(ctx *fiber.Ctx) error { + return ctx.SendString("It was a GET request!") + }) + + app.Listen(":3000") +} + +func BasicHandler(ctx *fiber.Ctx) error { + return ctx.SendString("It was not a GET request!") +} ``` + +:::tip +app.Use will handle requests from any route, and any method. In the example above, it will only skip if the method is GET. +::: \ No newline at end of file diff --git a/docs/api/middleware/timeout.md b/docs/api/middleware/timeout.md index 7f8adf9336..3caa2c2f26 100644 --- a/docs/api/middleware/timeout.md +++ b/docs/api/middleware/timeout.md @@ -7,13 +7,15 @@ There exist two distinct implementations of timeout middleware [Fiber](https://g **New** - Wraps a `fiber.Handler` with a timeout. If the handler takes longer than the given duration to return, the timeout error is set and forwarded to the centralized [ErrorHandler](https://docs.gofiber.io/error-handling). +Wraps a `fiber.Handler` with a timeout. If the handler takes longer than the given duration to return, the timeout error is set and forwarded to the centralized [ErrorHandler](https://docs.gofiber.io/error-handling). - Note: This has been depreciated since it raises race conditions. +:::caution +This has been deprecated since it raises race conditions. +::: **NewWithContext** - As a `fiber.Handler` wrapper, it creates a context with `context.WithTimeout` and pass it in `UserContext`. +As a `fiber.Handler` wrapper, it creates a context with `context.WithTimeout` and pass it in `UserContext`. If the context passed executions (eg. DB ops, Http calls) takes longer than the given duration to return, the timeout error is set and forwarded to the centralized `ErrorHandler`. @@ -39,11 +41,12 @@ import ( ) ``` -Sample timeout middleware usage: +After you initiate your Fiber app, you can use the following possibilities: ```go func main() { app := fiber.New() + h := func(c *fiber.Ctx) error { sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") if err := sleepWithContext(c.UserContext(), sleepTime); err != nil { @@ -53,7 +56,8 @@ func main() { } app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second)) - _ = app.Listen(":3000") + + app.Listen(":3000") } func sleepWithContext(ctx context.Context, d time.Duration) error { @@ -99,7 +103,7 @@ func main() { } app.Get("/foo/:sleepTime", timeout.NewWithContext(h, 2*time.Second, ErrFooTimeOut)) - _ = app.Listen(":3000") + app.Listen(":3000") } func sleepWithContextWithCustomError(ctx context.Context, d time.Duration) error { From c396d2aa45966dc4c0b249405aea9d2f69ac10a0 Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 10 Apr 2023 12:28:57 +0200 Subject: [PATCH 072/162] Update grouping.md --- docs/guide/grouping.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/guide/grouping.md b/docs/guide/grouping.md index b48b9dcd65..429e170229 100644 --- a/docs/guide/grouping.md +++ b/docs/guide/grouping.md @@ -4,6 +4,10 @@ title: 🎭 Grouping sidebar_position: 2 --- +:::info +In general, the Group functionality in Fiber behaves similarly to ExpressJS. Groups are declared virtually and all routes declared within the group are flattened into a single list with a prefix, which is then checked by the framework in the order it was declared. This means that the behavior of Group in Fiber is identical to that of ExpressJS. +::: + ## Paths Like **Routing**, groups can also have paths that belong to a cluster. From fcf708dfc2edb5dfcb5d8c527eff9f5b30755d89 Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 10 Apr 2023 12:31:49 +0200 Subject: [PATCH 073/162] Update routing.md --- docs/guide/routing.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/guide/routing.md b/docs/guide/routing.md index c110fc6d68..615d1aa2f2 100644 --- a/docs/guide/routing.md +++ b/docs/guide/routing.md @@ -283,3 +283,5 @@ func main() { log.Fatal(app.Listen(":3000")) } ``` + +More information about this in our [Grouping Guide](./grouping.md) From 866d5b7628efcc38f25fb5479fe7aeb720de95d3 Mon Sep 17 00:00:00 2001 From: James Lucas Date: Tue, 11 Apr 2023 09:24:29 +0100 Subject: [PATCH 074/162] =?UTF-8?q?=E2=9C=A8=20feat(cors):=20Added=20new?= =?UTF-8?q?=20'AllowOriginsFunc'=20function.=20(#2394)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ feat(cors): Added new 'AllowOriginsFunc' function. * feat(cors): Added warning log for when both 'AllowOrigins' and 'AllowOriginsFunc' are set. * feat(docs): Updated docs to include note about discouraging the use of this function in production workloads. --------- Co-authored-by: RW --- docs/api/middleware/cors.md | 108 ++++++++++++++++++++++------------- middleware/cors/cors.go | 26 ++++++++- middleware/cors/cors_test.go | 52 +++++++++++++++++ 3 files changed, 143 insertions(+), 43 deletions(-) diff --git a/docs/api/middleware/cors.md b/docs/api/middleware/cors.md index e84f14bfd7..8a76def1eb 100644 --- a/docs/api/middleware/cors.md +++ b/docs/api/middleware/cors.md @@ -35,52 +35,77 @@ app.Use(cors.New(cors.Config{ })) ``` +Using the `AllowOriginsFunc` function. In this example any origin will be allowed via CORS. + +For example, if a browser running on `http://localhost:3000` sends a request, this will be accepted and the `access-control-allow-origin` response header will be set to `http://localhost:3000`. + +**Note: Using this feature is discouraged in production and it's best practice to explicitly set CORS origins via `AllowOrigins`.** + +```go +app.Use(cors.New()) + +app.Use(cors.New(cors.Config{ + AllowOriginsFunc: func(origin string) bool { + return os.Getenv("ENVIRONMENT") == "development" + }, +})) +``` + ## Config ```go // Config defines the config for middleware. type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // AllowOrigin defines a list of origins that may access the resource. - // - // Optional. Default value "*" - AllowOrigins string - - // AllowMethods defines a list of methods allowed when accessing the resource. - // This is used in response to a preflight request. - // - // Optional. Default value "GET,POST,HEAD,PUT,DELETE,PATCH" - AllowMethods string - - // AllowHeaders defines a list of request headers that can be used when - // making the actual request. This is in response to a preflight request. - // - // Optional. Default value "". - AllowHeaders string - - // AllowCredentials indicates whether or not the response to the request - // can be exposed when the credentials flag is true. When used as part of - // a response to a preflight request, this indicates whether or not the - // actual request can be made using credentials. - // - // Optional. Default value false. - AllowCredentials bool - - // ExposeHeaders defines a whitelist headers that clients are allowed to - // access. - // - // Optional. Default value "". - ExposeHeaders string - - // MaxAge indicates how long (in seconds) the results of a preflight request - // can be cached. - // - // Optional. Default value 0. - MaxAge int + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // AllowOriginsFunc defines a function that will set the 'access-control-allow-origin' + // response header to the 'origin' request header when returned true. + // + // Note: Using this feature is discouraged in production and it's best practice to explicitly + // set CORS origins via 'AllowOrigins' + // + // Optional. Default: nil + AllowOriginsFunc func(origin string) bool + + // AllowOrigin defines a list of origins that may access the resource. + // + // Optional. Default value "*" + AllowOrigins string + + // AllowMethods defines a list methods allowed when accessing the resource. + // This is used in response to a preflight request. + // + // Optional. Default value "GET,POST,HEAD,PUT,DELETE,PATCH" + AllowMethods string + + // AllowHeaders defines a list of request headers that can be used when + // making the actual request. This is in response to a preflight request. + // + // Optional. Default value "". + AllowHeaders string + + // AllowCredentials indicates whether or not the response to the request + // can be exposed when the credentials flag is true. When used as part of + // a response to a preflight request, this indicates whether or not the + // actual request can be made using credentials. + // + // Optional. Default value false. + AllowCredentials bool + + // ExposeHeaders defines a whitelist headers that clients are allowed to + // access. + // + // Optional. Default value "". + ExposeHeaders string + + // MaxAge indicates how long (in seconds) the results of a preflight request + // can be cached. + // + // Optional. Default value 0. + MaxAge int } ``` @@ -89,6 +114,7 @@ type Config struct { ```go var ConfigDefault = Config{ Next: nil, + AllowOriginsFunc: nil, AllowOrigins: "*", AllowMethods: strings.Join([]string{ fiber.MethodGet, diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index 011ca25459..cf90aee25c 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -1,6 +1,7 @@ package cors import ( + "log" "strconv" "strings" @@ -14,6 +15,12 @@ type Config struct { // Optional. Default: nil Next func(c *fiber.Ctx) bool + // AllowOriginsFunc defines a function that will set the 'access-control-allow-origin' + // response header to the 'origin' request header when returned true. + // + // Optional. Default: nil + AllowOriginsFunc func(origin string) bool + // AllowOrigin defines a list of origins that may access the resource. // // Optional. Default value "*" @@ -54,8 +61,9 @@ type Config struct { // ConfigDefault is the default config var ConfigDefault = Config{ - Next: nil, - AllowOrigins: "*", + Next: nil, + AllowOriginsFunc: nil, + AllowOrigins: "*", AllowMethods: strings.Join([]string{ fiber.MethodGet, fiber.MethodPost, @@ -88,6 +96,11 @@ func New(config ...Config) fiber.Handler { } } + // Warning logs if both AllowOrigins and AllowOriginsFunc are set + if cfg.AllowOrigins != "" && cfg.AllowOriginsFunc != nil { + log.Printf("[CORS] - [Warning] Both 'AllowOrigins' and 'AllowOriginsFunc' have been defined.\n") + } + // Convert string to slice allowOrigins := strings.Split(strings.ReplaceAll(cfg.AllowOrigins, " ", ""), ",") @@ -126,6 +139,15 @@ func New(config ...Config) fiber.Handler { } } + // Run AllowOriginsFunc if the logic for + // handling the value in 'AllowOrigins' does + // not result in allowOrigin being set. + if allowOrigin == "" && cfg.AllowOriginsFunc != nil { + if cfg.AllowOriginsFunc(origin) { + allowOrigin = origin + } + } + // Simple request if c.Method() != fiber.MethodOptions { c.Vary(fiber.HeaderOrigin) diff --git a/middleware/cors/cors_test.go b/middleware/cors/cors_test.go index 4343cc2313..1f4a3c91d6 100644 --- a/middleware/cors/cors_test.go +++ b/middleware/cors/cors_test.go @@ -2,6 +2,7 @@ package cors import ( "net/http/httptest" + "strings" "testing" "github.com/gofiber/fiber/v2" @@ -242,3 +243,54 @@ func Test_CORS_Next(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } + +func Test_CORS_AllowOriginsFunc(t *testing.T) { + t.Parallel() + // New fiber instance + app := fiber.New() + app.Use("/", New(Config{ + AllowOrigins: "http://example-1.com", + AllowOriginsFunc: func(origin string) bool { + return strings.Contains(origin, "example-2") + }, + })) + + // Get handler pointer + handler := app.Handler() + + // Make request with disallowed origin + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/") + ctx.Request.Header.SetMethod(fiber.MethodOptions) + ctx.Request.Header.Set(fiber.HeaderOrigin, "http://google.com") + + // Perform request + handler(ctx) + + // Allow-Origin header should be "" because http://google.com does not satisfy http://*.example.com + utils.AssertEqual(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + + ctx.Request.Reset() + ctx.Response.Reset() + + // Make request with allowed origin + ctx.Request.SetRequestURI("/") + ctx.Request.Header.SetMethod(fiber.MethodOptions) + ctx.Request.Header.Set(fiber.HeaderOrigin, "http://example-1.com") + + handler(ctx) + + utils.AssertEqual(t, "http://example-1.com", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + + ctx.Request.Reset() + ctx.Response.Reset() + + // Make request with allowed origin + ctx.Request.SetRequestURI("/") + ctx.Request.Header.SetMethod(fiber.MethodOptions) + ctx.Request.Header.Set(fiber.HeaderOrigin, "http://example-2.com") + + handler(ctx) + + utils.AssertEqual(t, "http://example-2.com", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) +} From 3b7a7d491b6cba6141dd67b9670e2d9e603c807b Mon Sep 17 00:00:00 2001 From: cmd777 <83428931+cmd777@users.noreply.github.com> Date: Thu, 13 Apr 2023 06:32:39 +0200 Subject: [PATCH 075/162] :books: Docs: Fix import and comma issues (#2410) Fix import and comma issues --- docs/api/middleware/cache.md | 6 +++--- docs/api/middleware/monitor.md | 2 +- docs/api/middleware/proxy.md | 2 +- docs/api/middleware/skip.md | 2 +- docs/api/middleware/timeout.md | 7 +++---- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/api/middleware/cache.md b/docs/api/middleware/cache.md index 209865ba3b..99dd833c5f 100644 --- a/docs/api/middleware/cache.md +++ b/docs/api/middleware/cache.md @@ -45,14 +45,14 @@ app.Use(cache.New(cache.Config{ Or you can custom key and expire time like this: ```go -app.Use(New(Config{ - ExpirationGenerator: func(c *fiber.Ctx, cfg *Config) time.Duration { +app.Use(cache.New(cache.Config{ + ExpirationGenerator: func(c *fiber.Ctx, cfg *cache.Config) time.Duration { newCacheTime, _ := strconv.Atoi(c.GetRespHeader("Cache-Time", "600")) return time.Second * time.Duration(newCacheTime) }, KeyGenerator: func(c *fiber.Ctx) string { return utils.CopyString(c.Path()) - } + }, })) app.Get("/", func(c *fiber.Ctx) error { diff --git a/docs/api/middleware/monitor.md b/docs/api/middleware/monitor.md index 4ecdca647d..0c13509a29 100644 --- a/docs/api/middleware/monitor.md +++ b/docs/api/middleware/monitor.md @@ -24,7 +24,7 @@ Import the middleware package that is part of the Fiber web framework ```go import ( "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/favicon" + "github.com/gofiber/fiber/v2/middleware/monitor" ) ``` diff --git a/docs/api/middleware/proxy.md b/docs/api/middleware/proxy.md index 3179414036..4395a2554b 100644 --- a/docs/api/middleware/proxy.md +++ b/docs/api/middleware/proxy.md @@ -96,7 +96,7 @@ app.Get("/proxy", func(c *fiber.Ctx) error { // Make proxy requests, timeout a minute from now app.Get("/proxy", func(c *fiber.Ctx) error { - if err := DoDeadline(c, "http://localhost", time.Now().Add(time.Minute)); err != nil { + if err := proxy.DoDeadline(c, "http://localhost", time.Now().Add(time.Minute)); err != nil { return err } // Remove Server header from response diff --git a/docs/api/middleware/skip.md b/docs/api/middleware/skip.md index 1c026715fe..820c7b2c38 100644 --- a/docs/api/middleware/skip.md +++ b/docs/api/middleware/skip.md @@ -33,7 +33,7 @@ func main() { return ctx.SendString("It was a GET request!") }) - app.Listen(":3000") + log.Fatal(app.Listen(":3000")) } func BasicHandler(ctx *fiber.Ctx) error { diff --git a/docs/api/middleware/timeout.md b/docs/api/middleware/timeout.md index 3caa2c2f26..67769e2f9f 100644 --- a/docs/api/middleware/timeout.md +++ b/docs/api/middleware/timeout.md @@ -56,8 +56,7 @@ func main() { } app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second)) - - app.Listen(":3000") + log.Fatal(app.Listen(":3000")) } func sleepWithContext(ctx context.Context, d time.Duration) error { @@ -103,7 +102,7 @@ func main() { } app.Get("/foo/:sleepTime", timeout.NewWithContext(h, 2*time.Second, ErrFooTimeOut)) - app.Listen(":3000") + log.Fatal(app.Listen(":3000")) } func sleepWithContextWithCustomError(ctx context.Context, d time.Duration) error { @@ -142,6 +141,6 @@ func main() { } app.Get("/foo", timeout.NewWithContext(handler, 10*time.Second)) - app.Listen(":3000") + log.Fatal(app.Listen(":3000")) } ``` From 2237e9c5115e6ade2bfedf6a85d04a36957cd635 Mon Sep 17 00:00:00 2001 From: eldaniz Date: Thu, 13 Apr 2023 06:54:07 +0100 Subject: [PATCH 076/162] =?UTF-8?q?=F0=9F=93=9A=20Docs:=20Added=20Azerbaij?= =?UTF-8?q?ani=20README=20translation=20(#2411)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit azerbaijani translation completed --- .github/README.md | 3 + .github/README_az.md | 710 ++++++++++++++++++++++++++++++++++++++++ .github/README_ckb.md | 3 + .github/README_de.md | 3 + .github/README_es.md | 3 + .github/README_fa.md | 3 + .github/README_fr.md | 3 + .github/README_he.md | 3 + .github/README_id.md | 3 + .github/README_it.md | 3 + .github/README_ja.md | 3 + .github/README_ko.md | 3 + .github/README_nl.md | 3 + .github/README_pt.md | 3 + .github/README_ru.md | 3 + .github/README_sa.md | 3 + .github/README_tr.md | 3 + .github/README_uk.md | 3 + .github/README_zh-CN.md | 3 + .github/README_zh-TW.md | 3 + 20 files changed, 767 insertions(+) create mode 100644 .github/README_az.md diff --git a/.github/README.md b/.github/README.md index 9643ba627b..92dd13b1dd 100644 --- a/.github/README.md +++ b/.github/README.md @@ -66,6 +66,9 @@ + + +
diff --git a/.github/README_az.md b/.github/README_az.md new file mode 100644 index 0000000000..d4f78cc13f --- /dev/null +++ b/.github/README_az.md @@ -0,0 +1,710 @@ +

+ + + + + Fiber + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +

+

+ Fiber Go dili üçün ən sürətli HTTP aparıcısı FasthttpExpress kitabxanasına bənzər arxitektura üzərində qurulmuş web framework'dür. Sıfır yaddaş ayrılması(zero-memory allocation) və performans nəzərə alınmaqla birlikdə development prosesini sürətləndirməkasanlaşdırmaq üçün qurulmuşdur. +

+ +## ⚡️ Sürətli Başlanğıc + +```go +package main + +import "github.com/gofiber/fiber/v2" + +func main() { + app := fiber.New() + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World 👋!") + }) + + app.Listen(":3000") +} +``` + +## 🤖 Performans Dəyərləri + +Bu testlər [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) və [Go Web](https://github.com/smallnest/go-web-framework-benchmark) tərəfindən gerçəkləşdirilmişdir. Bütün nəticələri görmək üçün [Wiki](https://docs.gofiber.io/extra/benchmarks) səhifəsinə göz ata bilərsiniz. + +

+ + +

+ +## ⚙️ Qurulum Prosesi + +Go dilinin `1.17` və ya daha yuxarı versiyasının ([yükləmək üçün](https://go.dev/dl/)) yükləndiyindən əmin olun. + + +Bir qovluq yaratdıqdan sonra, `go mod init github.com/your/repo` ([go modulları haqqında əlavə bilgilər](https://go.dev/blog/using-go-modules)) komandasını eyni qovluğun daxilində işə salaraq proyektinizi başladın. Növbəti addım olaraq Fiber'i [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) komandasını işlədərək yükləyin: + +```bash +go get -u github.com/gofiber/fiber/v2 +``` + +## 🎯 Özəlliklər + +- Güclü [routing](https://docs.gofiber.io/guide/routing) +- [static faylların](https://docs.gofiber.io/api/app#static) təqdimatı +- Yüksək [performans](https://docs.gofiber.io/extra/benchmarks) +- [Daha az yaddaş istifadəsi](https://docs.gofiber.io/extra/benchmarks) +- [API son nöqtələri (endpoint)](https://docs.gofiber.io/api/ctx) +- [Middleware](https://docs.gofiber.io/category/-middleware) & [Next](https://docs.gofiber.io/api/ctx#next) dəstəyi +- [Rapid](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) server tərəfli proqramlaşdırma +- [Template mühərrikləri](https://github.com/gofiber/template) +- [WebSocket dəstəyi](https://github.com/gofiber/websocket) +- [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) +- [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) +- [18 dildə](https://docs.gofiber.io/) mövcuddur +- Və daha daha necələri, [Fiber'i kəşf et](https://docs.gofiber.io/) + +## 💡 Fəlsəfə + +[Node.js](https://nodejs.org/en/about/)'dən [Go](https://go.dev/doc/)'ya yeni keçən gopher'lar veb aplikasiyalar və mikroservislər yazmağa başlamadan öncə dilin özünəməxsus sintaksisini öyrənməklə məşğul olurlar. Fiber, bir veb framework olaraq MinimalizmUNIX'in yoluna əsasən və onun qanunlarına riyaət edərək qurulmuşdur. Bu sayədə sahədə yeni olan gopher'lər özlərini isti və güvənilir bir xoş gəldin ilə Go dünyasında qarşılana bilərlər. + +Fiber, internet üzərində olan ən məşhur veb framework'lərdən biri olan Express'dən əsinlənmişdir. Biz Express'in rahatlıq və asanlığını, Go'nun çiy performansı ilə birləşdirdik. Əgər daha öncədən Node.js üzərində (Express və ya bənzərləri) veb aplikasiyası yaratdıysanız, əksər metodlar və prinsipləri sizə tanış gələcəkdir. + +Biz istifadəçilərdən gələn [issue'lara](https://github.com/gofiber/fiber/issues), Discord [kanalımıza](https://gofiber.io/discord) və bütün internetə sürətli, rahat, hər tapşırığa, hər səviyyədən olan mühəndisə uyğun, və dostcasına bir Go web framework'ü olmağı hədəfləmişik. Express'in JavaScript dünyasında etdiyi kimi. + +## ⚠️ Limitlər +* Fiberin unsafe istifadəsi səbəbilə, hər zaman Go'nun son versiyası ile uyğun olmaya bilər. Fiber 2.40.0, Go 1.17 və 1.20 versiyaları ilə test edildi. +* Fiber net/http interfeysləri ilə uyğun deyildir. Yəni gqlgen, go-swagger kimi net/http ekosisteminin parçası olan proyektləri istifadə edə bilməzsiniz. + +## 👀 Misallar + +Aşağıda geniş istifadə olunan misallardan bəziləri list halında verilmişdir. Əgər daha çox kod misalları görmək istəyirsinizsə [Əlavə misallardan ibarət github repository'sini](https://github.com/gofiber/recipes) və ya [API dokumentasiyasını](https://docs.gofiber.io) ziyarət edin. + +#### 📖 [**Sadə Routing**](https://docs.gofiber.io/#basic-routing) + +```go +func main() { + app := fiber.New() + + // GET /api/register + app.Get("/api/*", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("✋ %s", c.Params("*")) + return c.SendString(msg) // => ✋ register + }) + + // GET /flights/LAX-SFO + app.Get("/flights/:from-:to", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("💸 From: %s, To: %s", c.Params("from"), c.Params("to")) + return c.SendString(msg) // => 💸 From: LAX, To: SFO + }) + + // GET /dictionary.txt + app.Get("/:file.:ext", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("📃 %s.%s", c.Params("file"), c.Params("ext")) + return c.SendString(msg) // => 📃 dictionary.txt + }) + + // GET /john/75 + app.Get("/:name/:age/:gender?", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("👴 %s is %s years old", c.Params("name"), c.Params("age")) + return c.SendString(msg) // => 👴 john is 75 years old + }) + + // GET /john + app.Get("/:name", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("Hello, %s 👋!", c.Params("name")) + return c.SendString(msg) // => Hello john 👋! + }) + + log.Fatal(app.Listen(":3000")) +} + +``` + +#### 📖 [**Route Adlandırılması**](https://docs.gofiber.io/api/app#name) + +```go +func main() { + app := fiber.New() + + // GET /api/register + app.Get("/api/*", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("✋ %s", c.Params("*")) + return c.SendString(msg) // => ✋ register + }).Name("api") + + data, _ := json.MarshalIndent(app.GetRoute("api"), "", " ") + fmt.Print(string(data)) + // Prints: + // { + // "method": "GET", + // "name": "api", + // "path": "/api/*", + // "params": [ + // "*1" + // ] + // } + + + log.Fatal(app.Listen(":3000")) +} + +``` + +#### 📖 [**Static Fayl Təqdimatı**](https://docs.gofiber.io/api/app#static) + +```go +func main() { + app := fiber.New() + + app.Static("/", "./public") + // => http://localhost:3000/js/script.js + // => http://localhost:3000/css/style.css + + app.Static("/prefix", "./public") + // => http://localhost:3000/prefix/js/script.js + // => http://localhost:3000/prefix/css/style.css + + app.Static("*", "./public/index.html") + // => http://localhost:3000/any/path/shows/index/html + + log.Fatal(app.Listen(":3000")) +} + +``` + +#### 📖 [**Middleware & Next**](https://docs.gofiber.io/api/ctx#next) + +```go +func main() { + app := fiber.New() + + // Match any route + app.Use(func(c *fiber.Ctx) error { + fmt.Println("🥇 First handler") + return c.Next() + }) + + // Match all routes starting with /api + app.Use("/api", func(c *fiber.Ctx) error { + fmt.Println("🥈 Second handler") + return c.Next() + }) + + // GET /api/list + app.Get("/api/list", func(c *fiber.Ctx) error { + fmt.Println("🥉 Last handler") + return c.SendString("Hello, World 👋!") + }) + + log.Fatal(app.Listen(":3000")) +} + +``` + +
+ 📚 Daha çox misalllar + +### Views engines + +📖 [Config](https://docs.gofiber.io/api/fiber#config) +📖 [Engines](https://github.com/gofiber/template) +📖 [Render](https://docs.gofiber.io/api/ctx#render) + +Fiber defaults to the [html/template](https://pkg.go.dev/html/template/) when no view engine is set. + +If you want to execute partials or use a different engine like [amber](https://github.com/eknkc/amber), [handlebars](https://github.com/aymerick/raymond), [mustache](https://github.com/cbroglie/mustache) or [pug](https://github.com/Joker/jade) etc.. + +Checkout our [Template](https://github.com/gofiber/template) package that support multiple view engines. + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/template/pug" +) + +func main() { + // You can setup Views engine before initiation app: + app := fiber.New(fiber.Config{ + Views: pug.New("./views", ".pug"), + }) + + // And now, you can call template `./views/home.pug` like this: + app.Get("/", func(c *fiber.Ctx) error { + return c.Render("home", fiber.Map{ + "title": "Homepage", + "year": 1999, + }) + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### Grouping routes into chains + +📖 [Group](https://docs.gofiber.io/api/app#group) + +```go +func middleware(c *fiber.Ctx) error { + fmt.Println("Don't mind me!") + return c.Next() +} + +func handler(c *fiber.Ctx) error { + return c.SendString(c.Path()) +} + +func main() { + app := fiber.New() + + // Root API route + api := app.Group("/api", middleware) // /api + + // API v1 routes + v1 := api.Group("/v1", middleware) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + // API v2 routes + v2 := api.Group("/v2", middleware) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + // ... +} + +``` + +### Middleware logger + +📖 [Logger](https://docs.gofiber.io/api/middleware/logger) + +```go +package main + +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/logger" +) + +func main() { + app := fiber.New() + + app.Use(logger.New()) + + // ... + + log.Fatal(app.Listen(":3000")) +} +``` + +### Cross-Origin Resource Sharing (CORS) + +📖 [CORS](https://docs.gofiber.io/api/middleware/cors) + +```go +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cors" +) + +func main() { + app := fiber.New() + + app.Use(cors.New()) + + // ... + + log.Fatal(app.Listen(":3000")) +} +``` + +Check CORS by passing any domain in `Origin` header: + +```bash +curl -H "Origin: http://example.com" --verbose http://localhost:3000 +``` + +### Custom 404 response + +📖 [HTTP Methods](https://docs.gofiber.io/api/ctx#status) + +```go +func main() { + app := fiber.New() + + app.Static("/", "./public") + + app.Get("/demo", func(c *fiber.Ctx) error { + return c.SendString("This is a demo!") + }) + + app.Post("/register", func(c *fiber.Ctx) error { + return c.SendString("Welcome!") + }) + + // Last middleware to match anything + app.Use(func(c *fiber.Ctx) error { + return c.SendStatus(404) + // => 404 "Not Found" + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### JSON Response + +📖 [JSON](https://docs.gofiber.io/api/ctx#json) + +```go +type User struct { + Name string `json:"name"` + Age int `json:"age"` +} + +func main() { + app := fiber.New() + + app.Get("/user", func(c *fiber.Ctx) error { + return c.JSON(&User{"John", 20}) + // => {"name":"John", "age":20} + }) + + app.Get("/json", func(c *fiber.Ctx) error { + return c.JSON(fiber.Map{ + "success": true, + "message": "Hi John!", + }) + // => {"success":true, "message":"Hi John!"} + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### WebSocket Upgrade + +📖 [Websocket](https://github.com/gofiber/websocket) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/websocket" +) + +func main() { + app := fiber.New() + + app.Get("/ws", websocket.New(func(c *websocket.Conn) { + for { + mt, msg, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + log.Printf("recv: %s", msg) + err = c.WriteMessage(mt, msg) + if err != nil { + log.Println("write:", err) + break + } + } + })) + + log.Fatal(app.Listen(":3000")) + // ws://localhost:3000/ws +} +``` + +### Server-Sent Events + +📖 [More Info](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp" +) + +func main() { + app := fiber.New() + + app.Get("/sse", func(c *fiber.Ctx) error { + c.Set("Content-Type", "text/event-stream") + c.Set("Cache-Control", "no-cache") + c.Set("Connection", "keep-alive") + c.Set("Transfer-Encoding", "chunked") + + c.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) { + fmt.Println("WRITER") + var i int + + for { + i++ + msg := fmt.Sprintf("%d - the time is %v", i, time.Now()) + fmt.Fprintf(w, "data: Message: %s\n\n", msg) + fmt.Println(msg) + + w.Flush() + time.Sleep(5 * time.Second) + } + })) + + return nil + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### Recover middleware + +📖 [Recover](https://docs.gofiber.io/api/middleware/recover) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/recover" +) + +func main() { + app := fiber.New() + + app.Use(recover.New()) + + app.Get("/", func(c *fiber.Ctx) error { + panic("normally this would crash your app") + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +
+ +### Güvənilir Proxy İstifadəsi + +📖 [Config](https://docs.gofiber.io/api/fiber#config) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/recover" +) + +func main() { + app := fiber.New(fiber.Config{ + EnableTrustedProxyCheck: true, + TrustedProxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range + ProxyHeader: fiber.HeaderXForwardedFor}, + }) + + // ... + + log.Fatal(app.Listen(":3000")) +} +``` + + + +## 🧬 Daxili Middleware + +Aşağıda Fiber'in daxilində olan middleware'lər siyahı şəklində göstərilmişdir. + +| Middleware | Açıqlama | +|:---------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Sadə bir auth middleware'idir və HTTP Basic Auth yaratmaq üçün istifadə olunur. Keçərli vəsiqə(credentials) bilgiləri üçün sonrakı handler'i, əksik və ya keçərsiz vəsiqə bilgiləri üçün 401 qaytarır. | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Response'ları dayandır və keşə yerləşdir. | +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Fiber üçün sıxışdırma(compression) middleware'idir. Default olaraq `deflate`, `gzip` və `brotli` dəstəkləyir. | +| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Çeşidli seçimlərlə başlanğıclar arası mənbə paylaşımı (CORS) aktivləşdirin. | +| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | CSRF exploit'lərindən qorunun. | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware'i cookie dəyərlərini şifrələyər. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Environment dəyərlərini göstərilən konfigə görə çölə açar. | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Keşlərin daha səmərəli istifadəsinə və bant genişliyinə qənaət etməyə imkan verən ETag middleware'i, məzmun dəyişməyibsə veb serverin response'u yenidən göndərməməsinə şərait yaradır. | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware, HTTP serverlərinin bəzi runtime dəyərlərini JSON formatında göstərər. | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Əgər faylın yolu(path) göstərilmişsə loglarda olan favicon'u yox sayar və ya saxlama deposundan götürər | +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Fiber üçün fayl sistem middleware'i. Alireza Salary'ə xüsusi təşəkkürlər. | +| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Fiber üçün rate limitləyən middleware. Açıq API'lara vəya şifrə yeniləmə kimi endpoint'lərə yönəlik təkrarlanan requestlərin qarşısını alır. | +| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP istək/cavab (request/response) logger'i. | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Monitor middleware'i serverin metriklərini report edər. Express-status-monitor'dan əsinləndi. | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Matthew Lee'yə xüsusi təşəkkürlər \(@mthli\) | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Birdən çox server'ə proxy istəyi göndərməyiniz üçündür. | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Recover middleware'i stack chain'ni hər hansı bir yerindəki paniklərdən qurtulmasına kömək edir və kontrolu mərkəzləşdirilmiş [ErrorHandler'ə](https://docs.gofiber.io/guide/error-handling) ötürür.| +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Hər request üçün ayrı request id yaradır. | +| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session üçün middleware. NOT: Bu middleware Fiber'in öz Storage struktrunu istifadə edir. | +| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware'i verilən şərt true olduğu halda handler'i görməz və keçər. | +| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Bir request üçün maksimum zaman əlavə edər və zaman aşımı olarsa ErrorHandler'ə göndərilir. | + +## 🧬 Xarici Middleware + +[Fiber komandası](https://github.com/orgs/gofiber/people) tərəfindən dəstəklənən və inkişaf etdirilən middleware'lərin siyahısı. + +| Middleware | Description | +| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [adaptor](https://github.com/gofiber/adaptor) | Fiber request handlerindən net/http handler'inə çeviricisi. @arsmn'ə xüsusi təşəkkürlər! | +| [helmet](https://github.com/gofiber/helmet) | Fərqli-fərqli HTTP header'lər istifadə edərək aplikasiyanızı daha təhlükəsiz saxlamağa kömək edir. | +| [jwt](https://github.com/gofiber/jwt) | JWT, JSON Web Token(JWT) girişi qaytaran bir middleware'dir. | +| [keyauth](https://github.com/gofiber/keyauth) | Key giriş middleware'i, key əsaslı bir authentication metodudur. | +| [redirect](https://github.com/gofiber/redirect) | Yönləndirmə üçün middleware. | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware'i verilən qanunlara əsasən URL yolunu(path) yenidən yazar. Geriyə dönülü uyğunluq üçün və ya yalnızca təmiz və açıqlayıcı linklər yaratmaq üçün yaradılmışdır. | +| [storage](https://github.com/gofiber/storage) | Fiber'in Storage arxitekturasını dəstəkləyən bir çox storage driver verir. Bu sayədə saxlamaya (storage) ehtiyac duyan Fiber middleware'lərində rahatlıqla istifadə oluna bilər | +| [template](https://github.com/gofiber/template) | Bu paket, Fiber `v1.10.x`, Go versiyası 1.13 və ya daha yuxarı olduqda istifadə oluna bilər və 8 template mühərriki var. | +| [websocket](https://github.com/gofiber/websocket) | Yerlilərin dəstəyi ilə Fiber üçün Fasthttp, WebSocket'ə əsaslıdır. | + +## 🕶️ Möhtəşəm Siyahı + +Əlavə yazılar, middleware'lər, misallar, və alətlər üçün bizim [möhtəşəm siyahımıza](https://github.com/gofiber/awesome-fiber) göz atın. + +## 👍 Dəstək + +Əgər `Fiber`'ə dəstək olmaq və ya **təşəkkür etmək** istəyirsinizsə: + +1. Proyektə [GitHub Ulduzu](https://github.com/gofiber/fiber/stargazers) əlavə edin. +2. Proyekt haqqında [şəxsi twitter hesabınızda](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber) tweet atın. +3. [Medium](https://medium.com/), [Dev.to](https://dev.to/) və ya şəxsi blogunuz üzərindən bir incələmə və ya tədris verici bir yazı yaza bilərsiniz +4. Bir [stekan kofe alaraq](https://buymeacoff.ee/fenny) bizə daha çox dəstək ola bilərsiniz. + +## ☕ Dəstəkçilər + +Fiber açıq qaynaqlı bir proyekt olduğu üçün, gəlirlərini ianələr əsasında idarə edir və bu ianələri domeyn adı, gitbook, netlify, serverless hosting xərcləri üçün istifadə edilir. Əgər Fiber'ə daha çox dəstək olmaq istəyirsinizsə ☕ [**burdan bir stekan kofe alın**](https://buymeacoff.ee/fenny) + +| | İstifadəçi | İanə | +| :--------------------------------------------------------- | :----------------------------------------------- | :------- | +| ![](https://avatars.githubusercontent.com/u/204341?s=25) | [@destari](https://github.com/destari) | ☕ x 10 | +| ![](https://avatars.githubusercontent.com/u/63164982?s=25) | [@dembygenesis](https://github.com/dembygenesis) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/56607882?s=25) | [@thomasvvugt](https://github.com/thomasvvugt) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/27820675?s=25) | [@hendratommy](https://github.com/hendratommy) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/1094221?s=25) | [@ekaputra07](https://github.com/ekaputra07) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/194590?s=25) | [@jorgefuertes](https://github.com/jorgefuertes) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/186637?s=25) | [@candidosales](https://github.com/candidosales) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/29659953?s=25) | [@l0nax](https://github.com/l0nax) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/635852?s=25) | [@bihe](https://github.com/bihe) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/307334?s=25) | [@justdave](https://github.com/justdave) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/11155743?s=25) | [@koddr](https://github.com/koddr) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/29042462?s=25) | [@lapolinar](https://github.com/lapolinar) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/2978730?s=25) | [@diegowifi](https://github.com/diegowifi) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/44171355?s=25) | [@ssimk0](https://github.com/ssimk0) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/5638101?s=25) | [@raymayemir](https://github.com/raymayemir) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/619996?s=25) | [@melkorm](https://github.com/melkorm) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/31022056?s=25) | [@marvinjwendt](https://github.com/marvinjwendt) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/31921460?s=25) | [@toishy](https://github.com/toishy) | ☕ x 1 | + +## ‎‍💻 Koda Dəstək Göstərənlər + +Code Contributors + +## ⭐️ Proyekti Ulduzlayanlar + +Stargazers over time + +## ⚠️ Lisenziya + +Müəllif Hüququ (c) 2019-bugün [Fenny](https://github.com/fenny) və [Contributors](https://github.com/gofiber/fiber/graphs/contributors). `Fiber` pulsuz və açıq qaynaqlı proqram təminatıdır və [MIT License](https://github.com/gofiber/fiber/blob/master/LICENSE) altında lisenziyalaşmışdır. Rəsmi loqo [Vic Shóstak](https://github.com/koddr) tərəfindən yaradılmışdır və [Creative Commons](https://creativecommons.org/licenses/by-sa/4.0/) lisenziyası altında paylanmışdır (CC BY-SA 4.0 International). + +**Üçüncü Parti Kitabxana Lisenziyaları** + +- [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) +- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) +- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) +- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) +- [msgp](https://github.com/tinylib/msgp/blob/master/LICENSE) +- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) +- [uuid](https://github.com/google/uuid/blob/master/LICENSE) +- [wmi](https://github.com/StackExchange/wmi/blob/master/LICENSE) diff --git a/.github/README_ckb.md b/.github/README_ckb.md index ae8afa6c81..04583f8e2c 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_de.md b/.github/README_de.md index 78950f3984..94f88aa46a 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_es.md b/.github/README_es.md index 9b4ed1e94c..3f16fe8bd9 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_fa.md b/.github/README_fa.md index 6861f70eed..0442e2af05 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_fr.md b/.github/README_fr.md index d3b20711e1..6e19a47115 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_he.md b/.github/README_he.md index 2b7d4ef008..dfd43cc3c3 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_id.md b/.github/README_id.md index c8826096ce..f0968fe207 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_it.md b/.github/README_it.md index 62a1cc8184..311e324066 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_ja.md b/.github/README_ja.md index 90de60688e..637c20d5f7 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_ko.md b/.github/README_ko.md index d26be91101..3a9fb4256b 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_nl.md b/.github/README_nl.md index b349e31db3..405bcb871a 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_pt.md b/.github/README_pt.md index 9d62c55031..783489b965 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_ru.md b/.github/README_ru.md index 844d57c58d..17c98d4321 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_sa.md b/.github/README_sa.md index 3c2a8ca4bd..af7519e2dd 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_tr.md b/.github/README_tr.md index 5971d56a23..f229c8de66 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -60,6 +60,9 @@ + + +
diff --git a/.github/README_uk.md b/.github/README_uk.md index 5d3206a8da..d68ff24c08 100644 --- a/.github/README_uk.md +++ b/.github/README_uk.md @@ -66,6 +66,9 @@ + + +
diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index d0d1a27dd7..2520ca4d2a 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -63,6 +63,9 @@ + + +
diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index 3d7ce42ba3..cb5cffc941 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -66,6 +66,9 @@ + + +
From 65e0ce285a374fed0a89260db37bf41576975510 Mon Sep 17 00:00:00 2001 From: RW Date: Thu, 13 Apr 2023 14:19:04 +0200 Subject: [PATCH 077/162] =?UTF-8?q?=F0=9F=90=9B=20[Bug-Fix]:=20Mounted=20s?= =?UTF-8?q?ubapps=20don't=20work=20correctly=20if=20parent=20app=20attache?= =?UTF-8?q?d=20=E2=80=A6=20(#2331)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 [Bug]: Mounted subapps don't work correctly if parent app attached additional middlewares after mounting (v2.40.1 bug) #2233 --- app.go | 7 +-- helpers.go | 8 ++-- mount.go | 128 +++++++++++++++++++++++++++++++++++++++++--------- mount_test.go | 50 ++++++++++++++++++++ router.go | 46 ++++++++++++------ 5 files changed, 193 insertions(+), 46 deletions(-) diff --git a/app.go b/app.go index ce23ee7a55..a4ab95ccc6 100644 --- a/app.go +++ b/app.go @@ -1085,12 +1085,7 @@ func (app *App) startupProcess() *App { app.mutex.Lock() defer app.mutex.Unlock() - // add routes of sub-apps - app.mountFields.subAppsRoutesAdded.Do(func() { - app.appendSubAppLists(app.mountFields.appList) - app.addSubAppsRoutes(app.mountFields.appList) - app.generateAppListKeys() - }) + app.mountStartupProcess() // build route tree stack app.buildTree() diff --git a/helpers.go b/helpers.go index d792b0dc52..a211d787b2 100644 --- a/helpers.go +++ b/helpers.go @@ -93,7 +93,7 @@ func (app *App) methodExist(ctx *Ctx) bool { continue } // Reset stack index - ctx.indexRoute = -1 + indexRoute := -1 tree, ok := ctx.app.treeStack[i][ctx.treePath] if !ok { tree = ctx.app.treeStack[i][""] @@ -101,11 +101,11 @@ func (app *App) methodExist(ctx *Ctx) bool { // Get stack length lenr := len(tree) - 1 // Loop over the route stack starting from previous index - for ctx.indexRoute < lenr { + for indexRoute < lenr { // Increment route index - ctx.indexRoute++ + indexRoute++ // Get *Route - route := tree[ctx.indexRoute] + route := tree[indexRoute] // Skip use routes if route.use { continue diff --git a/mount.go b/mount.go index 2becfef6bd..71a7e61dc1 100644 --- a/mount.go +++ b/mount.go @@ -19,6 +19,8 @@ type mountFields struct { appListKeys []string // check added routes of sub-apps subAppsRoutesAdded sync.Once + // check mounted sub-apps + subAppsProcessed sync.Once // Prefix of app if it was mounted mountPath string } @@ -36,22 +38,26 @@ func newMountFields(app *App) *mountFields { // compose them as a single service using Mount. The fiber's error handler and // any of the fiber's sub apps are added to the application's error handlers // to be invoked on errors that happen within the prefix route. -func (app *App) Mount(prefix string, fiber *App) Router { +func (app *App) Mount(prefix string, subApp *App) Router { prefix = strings.TrimRight(prefix, "/") if prefix == "" { prefix = "/" } // Support for configs of mounted-apps and sub-mounted-apps - for mountedPrefixes, subApp := range fiber.mountFields.appList { + for mountedPrefixes, subApp := range subApp.mountFields.appList { path := getGroupPath(prefix, mountedPrefixes) subApp.mountFields.mountPath = path app.mountFields.appList[path] = subApp } + // register mounted group + mountGroup := &Group{Prefix: prefix, app: subApp} + app.register(methodUse, prefix, mountGroup) + // Execute onMount hooks - if err := fiber.hooks.executeOnMountHooks(app); err != nil { + if err := subApp.hooks.executeOnMountHooks(app); err != nil { panic(err) } @@ -61,7 +67,7 @@ func (app *App) Mount(prefix string, fiber *App) Router { // Mount attaches another app instance as a sub-router along a routing path. // It's very useful to split up a large API as many independent routers and // compose them as a single service using Mount. -func (grp *Group) Mount(prefix string, fiber *App) Router { +func (grp *Group) Mount(prefix string, subApp *App) Router { groupPath := getGroupPath(grp.Prefix, prefix) groupPath = strings.TrimRight(groupPath, "/") if groupPath == "" { @@ -69,15 +75,19 @@ func (grp *Group) Mount(prefix string, fiber *App) Router { } // Support for configs of mounted-apps and sub-mounted-apps - for mountedPrefixes, subApp := range fiber.mountFields.appList { + for mountedPrefixes, subApp := range subApp.mountFields.appList { path := getGroupPath(groupPath, mountedPrefixes) subApp.mountFields.mountPath = path grp.app.mountFields.appList[path] = subApp } + // register mounted group + mountGroup := &Group{Prefix: groupPath, app: subApp} + grp.app.register(methodUse, groupPath, mountGroup) + // Execute onMount hooks - if err := fiber.hooks.executeOnMountHooks(grp.app); err != nil { + if err := subApp.hooks.executeOnMountHooks(grp.app); err != nil { panic(err) } @@ -89,6 +99,26 @@ func (app *App) MountPath() string { return app.mountFields.mountPath } +// hasMountedApps Checks if there are any mounted apps in the current application. +func (app *App) hasMountedApps() bool { + return len(app.mountFields.appList) > 1 +} + +// mountStartupProcess Handles the startup process of mounted apps by appending sub-app routes, generating app list keys, and processing sub-app routes. +func (app *App) mountStartupProcess() { + if app.hasMountedApps() { + // add routes of sub-apps + app.mountFields.subAppsProcessed.Do(func() { + app.appendSubAppLists(app.mountFields.appList) + app.generateAppListKeys() + }) + // adds the routes of the sub-apps to the current application. + app.mountFields.subAppsRoutesAdded.Do(func() { + app.processSubAppsRoutes() + }) + } +} + // generateAppListKeys generates app list keys for Render, should work after appendSubAppLists func (app *App) generateAppListKeys() { for key := range app.mountFields.appList { @@ -102,14 +132,20 @@ func (app *App) generateAppListKeys() { // appendSubAppLists supports nested for sub apps func (app *App) appendSubAppLists(appList map[string]*App, parent ...string) { + // Optimize: Cache parent prefix + parentPrefix := "" + if len(parent) > 0 { + parentPrefix = parent[0] + } + for prefix, subApp := range appList { // skip real app if prefix == "" { continue } - if len(parent) > 0 { - prefix = getGroupPath(parent[0], prefix) + if parentPrefix != "" { + prefix = getGroupPath(parentPrefix, prefix) } if _, ok := app.mountFields.appList[prefix]; !ok { @@ -123,27 +159,75 @@ func (app *App) appendSubAppLists(appList map[string]*App, parent ...string) { } } -// addSubAppsRoutes adds routes of sub apps nestedly when to start the server -func (app *App) addSubAppsRoutes(appList map[string]*App, parent ...string) { - for prefix, subApp := range appList { +// processSubAppsRoutes adds routes of sub-apps recursively when the server is started +func (app *App) processSubAppsRoutes() { + for prefix, subApp := range app.mountFields.appList { // skip real app if prefix == "" { continue } - - if len(parent) > 0 { - prefix = getGroupPath(parent[0], prefix) + // process the inner routes + if subApp.hasMountedApps() { + subApp.mountFields.subAppsRoutesAdded.Do(func() { + subApp.processSubAppsRoutes() + }) } + } + var handlersCount uint32 + // Iterate over the stack of the parent app + for m := range app.stack { + // Keep track of the position shift caused by adding routes for mounted apps + var positionShift uint32 + // Iterate over each route in the stack + stackLen := len(app.stack[m]) + for i := 0; i < stackLen; i++ { + route := app.stack[m][i] + // Check if the route has a mounted app + if !route.mount { + // If not, update the route's position and continue + route.pos += positionShift + if !route.use || (route.use && m == 0) { + handlersCount += uint32(len(route.Handlers)) + } + continue + } + + // Update the position shift to account for the mounted app's routes + positionShift = route.pos + + // Create a slice to hold the sub-app's routes + subRoutes := make([]*Route, len(route.group.app.stack[m])) - // add routes - stack := subApp.stack - for m := range stack { - for r := range stack[m] { - route := app.copyRoute(stack[m][r]) - app.addRoute(route.Method, app.addPrefixToRoute(prefix, route), true) + // Iterate over the sub-app's routes + for j, subAppRoute := range route.group.app.stack[m] { + // Clone the sub-app's route + subAppRouteClone := app.copyRoute(subAppRoute) + + // Add the parent route's path as a prefix to the sub-app's route + app.addPrefixToRoute(route.path, subAppRouteClone) + + // Add the cloned sub-app's route to the slice of sub-app routes + subRoutes[j] = subAppRouteClone } - } - atomic.AddUint32(&app.handlersCount, subApp.handlersCount) + // Insert the sub-app's routes into the parent app's stack + newStack := make([]*Route, len(app.stack[m])+len(subRoutes)-1) + copy(newStack[:i], app.stack[m][:i]) + copy(newStack[i:i+len(subRoutes)], subRoutes) + copy(newStack[i+len(subRoutes):], app.stack[m][i+1:]) + app.stack[m] = newStack + + // Decrease the parent app's route count to account for the mounted app's original route + atomic.AddUint32(&app.routesCount, ^uint32(0)) + i-- + // Increase the parent app's route count to account for the sub-app's routes + atomic.AddUint32(&app.routesCount, uint32(len(subRoutes))) + + // Mark the parent app's routes as refreshed + app.routesRefreshed = true + // update stackLen after appending subRoutes to app.stack[m] + stackLen = len(app.stack[m]) + } } + atomic.StoreUint32(&app.handlersCount, handlersCount) } diff --git a/mount_test.go b/mount_test.go index b70be3085c..8481cf54e5 100644 --- a/mount_test.go +++ b/mount_test.go @@ -88,6 +88,56 @@ func Test_App_Mount_Nested(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode, "Status code") utils.AssertEqual(t, uint32(6), app.handlersCount) + utils.AssertEqual(t, uint32(6), app.routesCount) +} + +// go test -run Test_App_Mount_Express_Behavior +func Test_App_Mount_Express_Behavior(t *testing.T) { + t.Parallel() + createTestHandler := func(body string) func(c *Ctx) error { + return func(c *Ctx) error { + return c.SendString(body) + } + } + testEndpoint := func(app *App, route, expectedBody string) { + resp, err := app.Test(httptest.NewRequest(MethodGet, route, nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, expectedBody, string(body), "Response body") + } + + app := New() + subApp := New() + // app setup + { + subApp.Get("/hello", createTestHandler("subapp hello!")) + subApp.Get("/world", createTestHandler("subapp world!")) // <- wins + + app.Get("/hello", createTestHandler("app hello!")) // <- wins + app.Mount("/", subApp) // <- subApp registration + app.Get("/world", createTestHandler("app world!")) + + app.Get("/bar", createTestHandler("app bar!")) + subApp.Get("/bar", createTestHandler("subapp bar!")) // <- wins + + subApp.Get("/foo", createTestHandler("subapp foo!")) // <- wins + app.Get("/foo", createTestHandler("app foo!")) + + // 404 Handler + app.Use(func(c *Ctx) error { + return c.SendStatus(StatusNotFound) + }) + } + // expectation check + testEndpoint(app, "/world", "subapp world!") + testEndpoint(app, "/hello", "app hello!") + testEndpoint(app, "/bar", "subapp bar!") + testEndpoint(app, "/foo", "subapp foo!") + testEndpoint(app, "/unknown", ErrNotFound.Message) + + utils.AssertEqual(t, uint32(17), app.handlersCount) + utils.AssertEqual(t, uint32(16+9), app.routesCount) } // go test -run Test_App_MountPath diff --git a/router.go b/router.go index ce27583955..a5aac7f6f5 100644 --- a/router.go +++ b/router.go @@ -46,9 +46,11 @@ type Router interface { // Route is a struct that holds all metadata for each registered handler. type Route struct { + // always keep in sync with the copy method "app.copyRoute" // Data for routing pos uint32 // Position in stack -> important for the sort of the matched routes use bool // USE matches path prefixes + mount bool // Indicated a mounted app on a specific route star bool // Path equals '*' root bool // Path equals '/' path string // Prettified path @@ -105,18 +107,25 @@ func (app *App) next(c *Ctx) (bool, error) { if !ok { tree = app.treeStack[c.methodINT][""] } - lenr := len(tree) - 1 + lenTree := len(tree) - 1 // Loop over the route stack starting from previous index - for c.indexRoute < lenr { + for c.indexRoute < lenTree { // Increment route index c.indexRoute++ // Get *Route route := tree[c.indexRoute] + var match bool + var err error + // skip for mounted apps + if route.mount { + continue + } + // Check if it matches the request path - match := route.match(c.detectionPath, c.path, &c.values) + match = route.match(c.detectionPath, c.path, &c.values) if !match { // No match, next route continue @@ -131,7 +140,9 @@ func (app *App) next(c *Ctx) (bool, error) { // Execute first handler of route c.indexHandler = 0 - err := route.Handlers[0](c) + if len(route.Handlers) > 0 { + err = route.Handlers[0](c) + } return match, err // Stop scanning the stack } @@ -194,15 +205,19 @@ func (app *App) addPrefixToRoute(prefix string, route *Route) *Route { func (*App) copyRoute(route *Route) *Route { return &Route{ // Router booleans - use: route.use, - star: route.star, - root: route.root, + use: route.use, + mount: route.mount, + star: route.star, + root: route.root, // Path data path: route.path, routeParser: route.routeParser, Params: route.Params, + // misc + pos: route.pos, + // Public data Path: route.Path, Method: route.Method, @@ -217,8 +232,10 @@ func (app *App) register(method, pathRaw string, group *Group, handlers ...Handl if method != methodUse && app.methodInt(method) == -1 { panic(fmt.Sprintf("add: invalid http method %s\n", method)) } + // is mounted app + isMount := group != nil && group.app != app // A route requires atleast one ctx handler - if len(handlers) == 0 { + if len(handlers) == 0 && !isMount { panic(fmt.Sprintf("missing handler in route: %s\n", pathRaw)) } // Cannot have an empty path @@ -252,9 +269,10 @@ func (app *App) register(method, pathRaw string, group *Group, handlers ...Handl // Create route metadata without pointer route := Route{ // Router booleans - use: isUse, - star: isStar, - root: isRoot, + use: isUse, + mount: isMount, + star: isStar, + root: isRoot, // Path data path: RemoveEscapeChar(pathPretty), @@ -278,11 +296,11 @@ func (app *App) register(method, pathRaw string, group *Group, handlers ...Handl for _, m := range app.config.RequestMethods { // Create a route copy to avoid duplicates during compression r := route - app.addRoute(m, &r) + app.addRoute(m, &r, isMount) } } else { // Add route to stack - app.addRoute(method, &route) + app.addRoute(method, &route, isMount) } return app } @@ -438,7 +456,7 @@ func (app *App) addRoute(method string, route *Route, isMounted ...bool) { // prevent identically route registration l := len(app.stack[m]) - if l > 0 && app.stack[m][l-1].Path == route.Path && route.use == app.stack[m][l-1].use { + if l > 0 && app.stack[m][l-1].Path == route.Path && route.use == app.stack[m][l-1].use && !route.mount && !app.stack[m][l-1].mount { preRoute := app.stack[m][l-1] preRoute.Handlers = append(preRoute.Handlers, route.Handlers...) } else { From 41b08b6cb01875d3876c83470b24b94ee7f6661e Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 14 Apr 2023 11:36:57 +0200 Subject: [PATCH 078/162] Update faq.md --- docs/extra/faq.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/extra/faq.md b/docs/extra/faq.md index 923881c865..efac14b45f 100644 --- a/docs/extra/faq.md +++ b/docs/extra/faq.md @@ -30,6 +30,36 @@ app.Use(func(c *fiber.Ctx) error { }) ``` +## How can i use live reload ? + +[Air](https://github.com/cosmtrek/air) is a handy tool that automatically restarts your Go applications whenever the source code changes, making your development process faster and more efficient. + +To use Air in a Fiber project, follow these steps: + +1. Install Air by downloading the appropriate binary for your operating system from the GitHub release page or by building the tool directly from source. +2. Create a configuration file for Air in your project directory. This file can be named, for example, .air.toml or air.conf. Here's a sample configuration file that works with Fiber: +```toml +# .air.toml +root = "." +tmp_dir = "tmp" +[build] + cmd = "go build -o ./tmp/main ." + bin = "./tmp/main" + delay = 1000 # ms + exclude_dir = ["assets", "tmp", "vendor"] + include_ext = ["go", "tpl", "tmpl", "html"] + exclude_regex = ["_test\\.go"] +``` +3. Start your Fiber application using Air by running the following command in the terminal: +```sh +air +``` + +As you make changes to your source code, Air will detect them and automatically restart the application. + +A complete example demonstrating the use of Air with Fiber can be found in the [Fiber Recipes repository](https://github.com/gofiber/recipes/tree/master/air). This example shows how to configure and use Air in a Fiber project to create an efficient development environment. + + ## How do I set up an error handler? To override the default error handler, you can override the default when providing a [Config](../api/fiber.md#config) when initiating a new [Fiber instance](../api/fiber.md#new). From ff390b5bb8796d683ee8d9308a32b7fdb6436c79 Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 14 Apr 2023 11:49:07 +0200 Subject: [PATCH 079/162] prepare release v2.44.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index a4ab95ccc6..956ff56ce8 100644 --- a/app.go +++ b/app.go @@ -30,7 +30,7 @@ import ( ) // Version of current fiber package -const Version = "2.43.0" +const Version = "2.44.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From ee2b13c8c0c6eec1b0955c10b67c4067de8e0ab0 Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 14 Apr 2023 12:18:00 +0200 Subject: [PATCH 080/162] Update timeout.md --- docs/api/middleware/timeout.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/api/middleware/timeout.md b/docs/api/middleware/timeout.md index 67769e2f9f..36db36f3bb 100644 --- a/docs/api/middleware/timeout.md +++ b/docs/api/middleware/timeout.md @@ -26,7 +26,6 @@ It does not cancel long running executions. Underlying executions must handle ti ```go func New(handler fiber.Handler, timeout time.Duration, timeoutErrors ...error) fiber.Handler - func NewWithContext(handler fiber.Handler, timeout time.Duration, timeoutErrors ...error) fiber.Handler ``` From dbc8c42040123be970431fdebbc8de246cba2830 Mon Sep 17 00:00:00 2001 From: Kanan N <99095303+kanansnote@users.noreply.github.com> Date: Sat, 15 Apr 2023 16:39:49 +0400 Subject: [PATCH 081/162] Correct grammar errors in Azerbaijani translation. (#2413) * Update README_az.md * Update README_az.md * Update README_az.md --- .github/README_az.md | 151 ++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 75 deletions(-) diff --git a/.github/README_az.md b/.github/README_az.md index d4f78cc13f..85f0826c83 100644 --- a/.github/README_az.md +++ b/.github/README_az.md @@ -91,7 +91,7 @@

- Fiber Go dili üçün ən sürətli HTTP aparıcısı FasthttpExpress kitabxanasına bənzər arxitektura üzərində qurulmuş web framework'dür. Sıfır yaddaş ayrılması(zero-memory allocation) və performans nəzərə alınmaqla birlikdə development prosesini sürətləndirməkasanlaşdırmaq üçün qurulmuşdur. + Fiber Go dili üçün ən sürətli HTTP mühərriki FasthttpExpress kitabxanasına bənzər arxitektura üzərində qurulmuş bir web framework-dür. Sıfır yaddaş ayrılması (zero-memory allocation) və performans səbəbilə development prosesini sürətləndirməkasanlaşdırmaq üçün tərtib edilmişdir.

## ⚡️ Sürətli Başlanğıc @@ -114,55 +114,56 @@ func main() { ## 🤖 Performans Dəyərləri -Bu testlər [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) və [Go Web](https://github.com/smallnest/go-web-framework-benchmark) tərəfindən gerçəkləşdirilmişdir. Bütün nəticələri görmək üçün [Wiki](https://docs.gofiber.io/extra/benchmarks) səhifəsinə göz ata bilərsiniz. +Bu testlər [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) və [Go Web](https://github.com/smallnest/go-web-framework-benchmark) tərəfindən aparılıb. Bütün nəticələri görmək üçün [Wiki](https://docs.gofiber.io/extra/benchmarks) səhifəsinə keçid edə bilərsiniz.

-## ⚙️ Qurulum Prosesi +## ⚙️ Quraşdırılması -Go dilinin `1.17` və ya daha yuxarı versiyasının ([yükləmək üçün](https://go.dev/dl/)) yükləndiyindən əmin olun. +Go dilinin `1.17` və ya daha yuxarı versiyanın [yükləndiyindən](https://go.dev/dl/) əmin olun. -Bir qovluq yaratdıqdan sonra, `go mod init github.com/your/repo` ([go modulları haqqında əlavə bilgilər](https://go.dev/blog/using-go-modules)) komandasını eyni qovluğun daxilində işə salaraq proyektinizi başladın. Növbəti addım olaraq Fiber'i [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) komandasını işlədərək yükləyin: +Bir qovluq yaratdıqdan sonra, `go mod init github.com/your/repo` komandasını eyni qovluğun daxilində işə salaraq layihənizi başladın ([go modulları haqqında əlavə bilgilər](https://go.dev/blog/using-go-modules)). Növbəti addım olaraq Fiber-i [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) komandasını işlədərək yükləyin: ```bash go get -u github.com/gofiber/fiber/v2 ``` -## 🎯 Özəlliklər +## 🎯 Özəllikləri - Güclü [routing](https://docs.gofiber.io/guide/routing) -- [static faylların](https://docs.gofiber.io/api/app#static) təqdimatı +- [Static faylların](https://docs.gofiber.io/api/app#static) təqdimatı - Yüksək [performans](https://docs.gofiber.io/extra/benchmarks) - [Daha az yaddaş istifadəsi](https://docs.gofiber.io/extra/benchmarks) - [API son nöqtələri (endpoint)](https://docs.gofiber.io/api/ctx) - [Middleware](https://docs.gofiber.io/category/-middleware) & [Next](https://docs.gofiber.io/api/ctx#next) dəstəyi -- [Rapid](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) server tərəfli proqramlaşdırma +- [Rapid](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) server yönümlü proqramlaşdırma - [Template mühərrikləri](https://github.com/gofiber/template) - [WebSocket dəstəyi](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- [18 dildə](https://docs.gofiber.io/) mövcuddur -- Və daha daha necələri, [Fiber'i kəşf et](https://docs.gofiber.io/) +- [18 dildə](https://docs.gofiber.io/) mövcudluğu + +Daha ətraflı məlumat üçün [rəsmi sənədləşməyə](https://docs.gofiber.io/) baxış keçirə bilərsiniz. ## 💡 Fəlsəfə -[Node.js](https://nodejs.org/en/about/)'dən [Go](https://go.dev/doc/)'ya yeni keçən gopher'lar veb aplikasiyalar və mikroservislər yazmağa başlamadan öncə dilin özünəməxsus sintaksisini öyrənməklə məşğul olurlar. Fiber, bir veb framework olaraq MinimalizmUNIX'in yoluna əsasən və onun qanunlarına riyaət edərək qurulmuşdur. Bu sayədə sahədə yeni olan gopher'lər özlərini isti və güvənilir bir xoş gəldin ilə Go dünyasında qarşılana bilərlər. +[Node.js](https://nodejs.org/en/about/)-dən [Go](https://go.dev/doc/)-ya yeni keçən gopher-lər veb tətbiqlər və mikroservislər yazmadan öncə dilin özünəməxsus sintaksisini öyrənməklə məşğul olurlar. Fiber MinimalizmUNIX-in yaradılış prinsiplərinə uyğun şəkildə qurulmuş bir web framework-dür. Bu sahədə yeni olan gopher-lər Go dünyasında özlərini doğma və güvənli hiss edə biləcək şəkildə bir ab-hava ilə rastlaşa bilərlər. -Fiber, internet üzərində olan ən məşhur veb framework'lərdən biri olan Express'dən əsinlənmişdir. Biz Express'in rahatlıq və asanlığını, Go'nun çiy performansı ilə birləşdirdik. Əgər daha öncədən Node.js üzərində (Express və ya bənzərləri) veb aplikasiyası yaratdıysanız, əksər metodlar və prinsipləri sizə tanış gələcəkdir. +Fiber internet üzərində olan ən məşhur web framework-lərdən biri olan Express-dən ilhamlanaraq ərsəyə gəlmişdir. Biz Express-in rahatlıq və asanlıq xüsusiyyətlərini, Go-nun çiy performansı ilə birləşdirmişik; əgər əvvəldən Node.js üzərində (Express və ya bənzərləri) veb tətbiqi yaratmısınızsa, onda əksər metodlar və prinsiplər sizə tanış gələcəkdir. -Biz istifadəçilərdən gələn [issue'lara](https://github.com/gofiber/fiber/issues), Discord [kanalımıza](https://gofiber.io/discord) və bütün internetə sürətli, rahat, hər tapşırığa, hər səviyyədən olan mühəndisə uyğun, və dostcasına bir Go web framework'ü olmağı hədəfləmişik. Express'in JavaScript dünyasında etdiyi kimi. +Biz istifadəçilərdən gələn [issue-a](https://github.com/gofiber/fiber/issues), Discord [kanalımıza](https://gofiber.io/discord) və bütün interneti əhatə edən vasitələrdən gələn rəyləri nəzərə alırıq. Bunun nəzdində, biz sürətli və rahat şəkildə hər bir tapşırığın səviyyəsinə uyğun olan — dostcasına bir Go web framework-ü olmağı hədəfləmişik (Express-in JavaScript dünyasında etdiyi kimi). ## ⚠️ Limitlər -* Fiberin unsafe istifadəsi səbəbilə, hər zaman Go'nun son versiyası ile uyğun olmaya bilər. Fiber 2.40.0, Go 1.17 və 1.20 versiyaları ilə test edildi. -* Fiber net/http interfeysləri ilə uyğun deyildir. Yəni gqlgen, go-swagger kimi net/http ekosisteminin parçası olan proyektləri istifadə edə bilməzsiniz. +* Fiber unsafe prinsiplərə əsaslanaraq çalışdığından, o hər zaman Go-nun son versiyası ilə uyğunlaşmaya bilər. Buna görə də, Fiber 2.40.0 — Go 1.17 və 1.20 versiyaları ilə test edilərək saz vəziyyətə gətirilmişdir. +* Fiber net/http interfeysləri ilə uyğun deyil. Yəni gqlgen, go-swagger kimi net/http ekosisteminin parçası olan layihələri istifadə edə bilməzsiniz. ## 👀 Misallar -Aşağıda geniş istifadə olunan misallardan bəziləri list halında verilmişdir. Əgər daha çox kod misalları görmək istəyirsinizsə [Əlavə misallardan ibarət github repository'sini](https://github.com/gofiber/recipes) və ya [API dokumentasiyasını](https://docs.gofiber.io) ziyarət edin. +Aşağıda geniş istifadə olunan misallardan bəziləri siyahı şəklində qeyd olunub. Əgər daha çox koda dair misalları görmək istəyirsinizsə, onda [Əlavə misallardan ibarət github deposunu](https://github.com/gofiber/recipes) və ya [API sənədləşməni](https://docs.gofiber.io) nəzərdən keçirin. #### 📖 [**Sadə Routing**](https://docs.gofiber.io/#basic-routing) @@ -205,7 +206,7 @@ func main() { ``` -#### 📖 [**Route Adlandırılması**](https://docs.gofiber.io/api/app#name) +#### 📖 [**Route-un Adlandırılması**](https://docs.gofiber.io/api/app#name) ```go func main() { @@ -289,17 +290,17 @@ func main() {
📚 Daha çox misalllar -### Views engines +### Baxış mühərriki (View Engine) 📖 [Config](https://docs.gofiber.io/api/fiber#config) -📖 [Engines](https://github.com/gofiber/template) +📖 [Mühərriklər](https://github.com/gofiber/template) 📖 [Render](https://docs.gofiber.io/api/ctx#render) -Fiber defaults to the [html/template](https://pkg.go.dev/html/template/) when no view engine is set. +Fiber baxış mühərriki təyin edilmədikdə [html/template-in](https://pkg.go.dev/html/template/) default formasını alır. -If you want to execute partials or use a different engine like [amber](https://github.com/eknkc/amber), [handlebars](https://github.com/aymerick/raymond), [mustache](https://github.com/cbroglie/mustache) or [pug](https://github.com/Joker/jade) etc.. +Əgər siz partial-ı və ya müxtəlif tipdə olan mühərrikləri istifadə etmək istəyirsinizsə, o zaman [amber](https://github.com/eknkc/amber), [handlebars](https://github.com/aymerick/raymond), [mustache](https://github.com/cbroglie/mustache), [pug](https://github.com/Joker/jade) və s. kimi misallara baxa bilərsiniz. -Checkout our [Template](https://github.com/gofiber/template) package that support multiple view engines. +Çoxsaylı baxış mühərriklərini dəstəkləyən [template](https://github.com/gofiber/template) package-ə göstərilən link vasitəsilə nəzərdən keçirə bilərsiniz. ```go package main @@ -310,12 +311,12 @@ import ( ) func main() { - // You can setup Views engine before initiation app: + // Baxış mühərrikini tətbiqi başlatzmadan əvvəl quraşdıra bilərsiniz: app := fiber.New(fiber.Config{ Views: pug.New("./views", ".pug"), }) - // And now, you can call template `./views/home.pug` like this: + // Və indi `./views/home.pug` template-i bu şəkildə çağıra bilərsiniz: app.Get("/", func(c *fiber.Ctx) error { return c.Render("home", fiber.Map{ "title": "Homepage", @@ -327,7 +328,7 @@ func main() { } ``` -### Grouping routes into chains +### Route-ın zəncirlərdə qruplaşdırılması 📖 [Group](https://docs.gofiber.io/api/app#group) @@ -362,7 +363,7 @@ func main() { ``` -### Middleware logger +### Middleware Logger 📖 [Logger](https://docs.gofiber.io/api/middleware/logger) @@ -410,7 +411,7 @@ func main() { } ``` -Check CORS by passing any domain in `Origin` header: +"Origin" başlığında istənilən domeni keçməklə CORS-un yoxlanması: ```bash curl -H "Origin: http://example.com" --verbose http://localhost:3000 @@ -434,7 +435,7 @@ func main() { return c.SendString("Welcome!") }) - // Last middleware to match anything + // Sonuncu middleware-in hər şeyə uyğunlaşdırılması app.Use(func(c *fiber.Ctx) error { return c.SendStatus(404) // => 404 "Not Found" @@ -474,7 +475,7 @@ func main() { } ``` -### WebSocket Upgrade +### WebSocket-in təkminləşdirilməsi (upgrade) 📖 [Websocket](https://github.com/gofiber/websocket) @@ -549,7 +550,7 @@ func main() { } ``` -### Recover middleware +### Middleware-in Bərpası 📖 [Recover](https://docs.gofiber.io/api/middleware/recover) @@ -574,7 +575,7 @@ func main() {
-### Güvənilir Proxy İstifadəsi +### Etibarlı Proxy İstifadəsi 📖 [Config](https://docs.gofiber.io/api/fiber#config) @@ -601,64 +602,64 @@ func main() { ## 🧬 Daxili Middleware -Aşağıda Fiber'in daxilində olan middleware'lər siyahı şəklində göstərilmişdir. +Aşağıda Fiber-in daxilində olan middleware-lər siyahı şəklində göstərilmişdir. | Middleware | Açıqlama | |:---------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Sadə bir auth middleware'idir və HTTP Basic Auth yaratmaq üçün istifadə olunur. Keçərli vəsiqə(credentials) bilgiləri üçün sonrakı handler'i, əksik və ya keçərsiz vəsiqə bilgiləri üçün 401 qaytarır. | -| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Response'ları dayandır və keşə yerləşdir. | -| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Fiber üçün sıxışdırma(compression) middleware'idir. Default olaraq `deflate`, `gzip` və `brotli` dəstəkləyir. | -| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Çeşidli seçimlərlə başlanğıclar arası mənbə paylaşımı (CORS) aktivləşdirin. | -| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | CSRF exploit'lərindən qorunun. | -| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware'i cookie dəyərlərini şifrələyər. | -| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Environment dəyərlərini göstərilən konfigə görə çölə açar. | -| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Keşlərin daha səmərəli istifadəsinə və bant genişliyinə qənaət etməyə imkan verən ETag middleware'i, məzmun dəyişməyibsə veb serverin response'u yenidən göndərməməsinə şərait yaradır. | -| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware, HTTP serverlərinin bəzi runtime dəyərlərini JSON formatında göstərər. | -| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Əgər faylın yolu(path) göstərilmişsə loglarda olan favicon'u yox sayar və ya saxlama deposundan götürər | -| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Fiber üçün fayl sistem middleware'i. Alireza Salary'ə xüsusi təşəkkürlər. | -| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Fiber üçün rate limitləyən middleware. Açıq API'lara vəya şifrə yeniləmə kimi endpoint'lərə yönəlik təkrarlanan requestlərin qarşısını alır. | -| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP istək/cavab (request/response) logger'i. | -| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Monitor middleware'i serverin metriklərini report edər. Express-status-monitor'dan əsinləndi. | -| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Matthew Lee'yə xüsusi təşəkkürlər \(@mthli\) | -| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Birdən çox server'ə proxy istəyi göndərməyiniz üçündür. | -| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Recover middleware'i stack chain'ni hər hansı bir yerindəki paniklərdən qurtulmasına kömək edir və kontrolu mərkəzləşdirilmiş [ErrorHandler'ə](https://docs.gofiber.io/guide/error-handling) ötürür.| +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Sadə bir auth middleware-dir və HTTP Basic Auth yaratmaq üçün istifadə olunur. Keçərli vəsiqə (credentials) bilgiləri üçün sonrakı handler-i, əksik və ya keçərsiz vəsiqə bilgiləri üçün 401 qaytarır. | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Response-ı dayandırır və keşə yerləşdirir. | +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Fiber üçün sıxışdırma (compression) middleware-dir. Default olaraq `deflate`, `gzip` və `brotli` dəstəkləyir. | +| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Çeşidli seçimlərlə başlanğıclar arası mənbə paylaşımı (CORS) aktivləşdirir. | +| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | CSRF exploit-dən qorunmasını təmin edir. | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware-i cookie dəyərlərini şifrələyir. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Environment dəyərlərini göstərilən config-ə görə təyin edir. | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Keşlərin daha səmərəli istifadəsinə və bant genişliyinə qənaət etməyə imkan verən ETag middleware-i; məzmun dəyişməyibsə veb serverin response-nı təkrar göndərməsinin qarşısını alır. | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware, HTTP serverlərinin bəzi runtime dəyərlərini JSON formatında göstərir. | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Əgər faylın yolu (path) göstərilmişdirsə, artıq loglarda olan favicon-u yox sayıb onu saxlanan depodan götürür. | +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Fiber üçün fayl sistem middleware-i. Alireza Salary-ə xüsusi təşəkkürlər. | +| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Fiber üçün rate limitləyən middleware. Açıq API-ə və ya şifrə yeniləmə kimi endpoint-ə yönəlik təkrarlanan request-in qarşısını alır. | +| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP istək/cavab (request/response) logger-i. | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Monitor middleware-i serverin metriklərini report edər ("Express-status-monitor"-dan qaynaqlanıb). | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Matthew Lee-yə xüsusi təşəkkürlər \(@mthli\). | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Birdən çox server-ə proxy istəyi göndərməyiniz üçündür. | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Recover middleware-i stack chain-ni hər hansı bir yerindəki paniklərdən qurtulmasına kömək edir və kontrolu mərkəzləşdirilmiş [ErrorHandler-ə](https://docs.gofiber.io/guide/error-handling) ötürür.| | [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Hər request üçün ayrı request id yaradır. | -| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session üçün middleware. NOT: Bu middleware Fiber'in öz Storage struktrunu istifadə edir. | -| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware'i verilən şərt true olduğu halda handler'i görməz və keçər. | -| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Bir request üçün maksimum zaman əlavə edər və zaman aşımı olarsa ErrorHandler'ə göndərilir. | +| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session üçün middleware. Qeyd: Bu middleware Fiber-in öz storage struktrunu istifadə edir. | +| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware-i verilən şərt true olduğu halda handler-i görməyərək üstündən ötüb keçir. | +| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Bir request üçün maksimum vaxt əlavə edir. Əgər arada fasilə yaranarsa, onda proses məhz ErrorHandler-ə göndərilərək icra edilir. | ## 🧬 Xarici Middleware -[Fiber komandası](https://github.com/orgs/gofiber/people) tərəfindən dəstəklənən və inkişaf etdirilən middleware'lərin siyahısı. +[Fiber komandası](https://github.com/orgs/gofiber/people) tərəfindən dəstəklənən və inkişaf etdirilən middleware-in siyahısı. | Middleware | Description | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Fiber request handlerindən net/http handler'inə çeviricisi. @arsmn'ə xüsusi təşəkkürlər! | -| [helmet](https://github.com/gofiber/helmet) | Fərqli-fərqli HTTP header'lər istifadə edərək aplikasiyanızı daha təhlükəsiz saxlamağa kömək edir. | -| [jwt](https://github.com/gofiber/jwt) | JWT, JSON Web Token(JWT) girişi qaytaran bir middleware'dir. | -| [keyauth](https://github.com/gofiber/keyauth) | Key giriş middleware'i, key əsaslı bir authentication metodudur. | +| [adaptor](https://github.com/gofiber/adaptor) | Fiber request handler-dən net/http handler-ə çevirici. @arsmn-ə xüsusi təşəkkürlər! | +| [helmet](https://github.com/gofiber/helmet) | Fərqli HTTP header istifadə edərək tətbiqi daha təhlükəsiz saxlamağa kömək edir. | +| [jwt](https://github.com/gofiber/jwt) | JWT, JSON Web Token(JWT) girişi qaytaran bir middleware-dir. | +| [keyauth](https://github.com/gofiber/keyauth) | Key giriş middleware-i, key əsaslı bir authentication metodudur. | | [redirect](https://github.com/gofiber/redirect) | Yönləndirmə üçün middleware. | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware'i verilən qanunlara əsasən URL yolunu(path) yenidən yazar. Geriyə dönülü uyğunluq üçün və ya yalnızca təmiz və açıqlayıcı linklər yaratmaq üçün yaradılmışdır. | -| [storage](https://github.com/gofiber/storage) | Fiber'in Storage arxitekturasını dəstəkləyən bir çox storage driver verir. Bu sayədə saxlamaya (storage) ehtiyac duyan Fiber middleware'lərində rahatlıqla istifadə oluna bilər | -| [template](https://github.com/gofiber/template) | Bu paket, Fiber `v1.10.x`, Go versiyası 1.13 və ya daha yuxarı olduqda istifadə oluna bilər və 8 template mühərriki var. | -| [websocket](https://github.com/gofiber/websocket) | Yerlilərin dəstəyi ilə Fiber üçün Fasthttp, WebSocket'ə əsaslıdır. | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware-i verilən qanunlara əsasən URL yolunu (path) yenidən yazır. Geri dönüşün icrası üçün uyğunluq təşkil edən təsviri linklərin yaradılması üçün nəzərdə tutulmuşdur. | +| [storage](https://github.com/gofiber/storage) | Fiber-in Storage arxitekturasını dəstəkləyən bir sıra storage driver verir. Bu sayədə storage-ə ehtiyac duyan Fiber middleware-də rahatlıqla istifadə oluna bilər. | +| [template](https://github.com/gofiber/template) | Bu paket, Fiber `v1.10.x`, Go versiyası 1.13 və ya daha yuxarı olduqda istifadə oluna bilər. 8 template mühərriki var. | +| [websocket](https://github.com/gofiber/websocket) | Yerlilərin dəstəyi ilə WebSocket-ə əsaslanan Fiber üçün Fasthttp. | ## 🕶️ Möhtəşəm Siyahı -Əlavə yazılar, middleware'lər, misallar, və alətlər üçün bizim [möhtəşəm siyahımıza](https://github.com/gofiber/awesome-fiber) göz atın. +Əlavə yazılar, middleware-lər, misallar, və alətlər üçün bizim [möhtəşəm siyahımıza](https://github.com/gofiber/awesome-fiber) göz atın. -## 👍 Dəstək +## 👍 Dəstək Nümayişi -Əgər `Fiber`'ə dəstək olmaq və ya **təşəkkür etmək** istəyirsinizsə: +Əgər `Fiber`-ə dəstək olmaq və ya **təşəkkür etmək** istəyirsinizsə: -1. Proyektə [GitHub Ulduzu](https://github.com/gofiber/fiber/stargazers) əlavə edin. -2. Proyekt haqqında [şəxsi twitter hesabınızda](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber) tweet atın. -3. [Medium](https://medium.com/), [Dev.to](https://dev.to/) və ya şəxsi blogunuz üzərindən bir incələmə və ya tədris verici bir yazı yaza bilərsiniz -4. Bir [stekan kofe alaraq](https://buymeacoff.ee/fenny) bizə daha çox dəstək ola bilərsiniz. +1. Layihəni [GitHub Ulduzu](https://github.com/gofiber/fiber/stargazers) ilə işarələyin. +2. Layihə haqqında [şəxsi twitter hesabınızda](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber) paylaşın. +3. [Medium](https://medium.com/), [Dev.to](https://dev.to/) və ya şəxsi bloqunuz üzərindən bir incələmə və ya tədris yönümlü bir yazı dərc edin. +4. Bizim üçün, sadəcə bir [fincan kofe alın](https://buymeacoff.ee/fenny). -## ☕ Dəstəkçilər +## ☕ "Bir fincan kofe almaq" məsələsi -Fiber açıq qaynaqlı bir proyekt olduğu üçün, gəlirlərini ianələr əsasında idarə edir və bu ianələri domeyn adı, gitbook, netlify, serverless hosting xərcləri üçün istifadə edilir. Əgər Fiber'ə daha çox dəstək olmaq istəyirsinizsə ☕ [**burdan bir stekan kofe alın**](https://buymeacoff.ee/fenny) +Fiber açıq qaynaqlı bir layihə olduğu üçün, gəlirlərini yalnız ianələr vasitəsilə təmin edir və bu da domain adı, gitbook, netlify, serverless hosting xərcləri üçün istifadə olunur. Belə olduğu halda, Fiber-ə ən yaxşı dəstək elə bizim üçün ☕ [**bir kofe almaqdan gələ bilər**](https://buymeacoff.ee/fenny). | | İstifadəçi | İanə | | :--------------------------------------------------------- | :----------------------------------------------- | :------- | @@ -681,19 +682,19 @@ Fiber açıq qaynaqlı bir proyekt olduğu üçün, gəlirlərini ianələr əsa | ![](https://avatars.githubusercontent.com/u/31022056?s=25) | [@marvinjwendt](https://github.com/marvinjwendt) | ☕ x 1 | | ![](https://avatars.githubusercontent.com/u/31921460?s=25) | [@toishy](https://github.com/toishy) | ☕ x 1 | -## ‎‍💻 Koda Dəstək Göstərənlər +## ‎‍💻 Koda Töhfə Verənlər Code Contributors -## ⭐️ Proyekti Ulduzlayanlar +## ⭐️ Layihəni Ulduzlayanlar Stargazers over time -## ⚠️ Lisenziya +## ⚠️ Lisenziya Haqqında -Müəllif Hüququ (c) 2019-bugün [Fenny](https://github.com/fenny) və [Contributors](https://github.com/gofiber/fiber/graphs/contributors). `Fiber` pulsuz və açıq qaynaqlı proqram təminatıdır və [MIT License](https://github.com/gofiber/fiber/blob/master/LICENSE) altında lisenziyalaşmışdır. Rəsmi loqo [Vic Shóstak](https://github.com/koddr) tərəfindən yaradılmışdır və [Creative Commons](https://creativecommons.org/licenses/by-sa/4.0/) lisenziyası altında paylanmışdır (CC BY-SA 4.0 International). +Müəllif Hüququ (c) 2019-bugün [Fenny](https://github.com/fenny) və [Contributors](https://github.com/gofiber/fiber/graphs/contributors). `Fiber` pulsuz və açıq qaynaqlı bir proqram təminatıdır və [MIT License](https://github.com/gofiber/fiber/blob/master/LICENSE) altında lisenziyalaşmışdır. Rəsmi loqo [Vic Shóstak](https://github.com/koddr) tərəfindən yaradılmış və [Creative Commons](https://creativecommons.org/licenses/by-sa/4.0/) lisenziyası altında paylanmışdır (CC BY-SA 4.0 International). -**Üçüncü Parti Kitabxana Lisenziyaları** +**Üçüncü Tərəf Kitabxana Lisenziyaları** - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) - [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) From 3e9575b0fe5dac95686b32ba7e996796d0b2eccf Mon Sep 17 00:00:00 2001 From: Carmelo Riolo <7999770+carmeloriolo@users.noreply.github.com> Date: Sat, 15 Apr 2023 18:26:08 +0200 Subject: [PATCH 082/162] =?UTF-8?q?=F0=9F=93=9A=20Docs:=20Correct=20errors?= =?UTF-8?q?=20in=20Italian=20translation=20(#2417)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs: fix typos and errors in README_it.md --- .github/README_it.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/README_it.md b/.github/README_it.md index 311e324066..ddbbc246ef 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -122,7 +122,7 @@ Questi test sono stati eseguiti da [TechEmpower](https://www.techempower.com/ben ## ⚙️ Installazione -Assicurati di avere Go ([per scaricalro](https://go.dev/dl/)) installato. Devi avere la versione `1.17` o superiore. +Assicurati di avere Go ([per scaricarlo](https://go.dev/dl/)) installato. Devi avere la versione `1.17` o superiore. Inizializza il tuo progetto creando una cartella e successivamente usando il comando `go mod init github.com/la-tua/repo` ([per maggiori informazioni](https://go.dev/blog/using-go-modules)) dentro la cartella. Dopodiche installa Fiber con il comando [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them): @@ -150,7 +150,7 @@ go get -u github.com/gofiber/fiber/v2 I nuovi gopher che passano da [Node.js](https://nodejs.org/en/about/) a [Go](https://go.dev/doc/) hanno a che fare con una curva di apprendimento prima di poter iniziare a creare le proprie applicazioni web o microservizi. Fiber, come **web framework** , è stato creato con l'idea di **minimalismo** e seguendo lo '**UNIX way**' , così i nuovi gopher posso entrare rapidamente nel mondo di Go con un caldo e fidato benvenuto. -Fiber è **ispirato** da Express, il web framework più popolare su internet. Abbiamo combiniamo la **facilità** di Express e **le prestazioni** di Go. Se hai mai implementato una applicazione web in Node.js (_utilizzando Express o simili_), allora i tanti metodi e principi ti saranno **molto familiari**. +Fiber è **ispirato** da Express, il web framework più popolare su internet. Abbiamo combinato la **facilità** di Express e **le prestazioni** di Go. Se hai mai implementato una applicazione web in Node.js (_utilizzando Express o simili_), allora i tanti metodi e principi ti saranno **molto familiari**. ## ⚠️ Limitazioni @@ -602,25 +602,25 @@ Qui una lista dei middleware inclusi con Fiber. | Middleware | Descrizione | | :------------------------------------------------------------------------------------- |:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Middleware basico di autenticazione usando http. Chiama il suo handler se le credenziali sono guiste e il codice 401 Unauthorized per credenziali mancanti o invailde. | +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Middleware basico di autenticazione usando http. Chiama il suo handler se le credenziali sono giuste e il codice 401 Unauthorized per credenziali mancanti o invalide. | | [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Intercetta e mette nella cache la risposta | -| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Middlewere di compressione per Fiber, supporta `deflate`, `gzip` e `brotli` di default. | -| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Ti permette di usare cross-origin resource sharing \(CORS\) con tante optzioni. | +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Middleware di compressione per Fiber, supporta `deflate`, `gzip` e `brotli` di default. | +| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Ti permette di usare cross-origin resource sharing \(CORS\) con tante opzioni. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Ti protegge da attachi CSRF. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Middleware che encrypta i valori dei cookie. | | [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Esporre le variabili di ambiente fornendo una configurazione facoltativa. | -| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Middlewere che permette alle cache di essere piu efficienti e salvare banda, come un web server non deve rimandare il messagio pieno se il contenuto non è cambiato. | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Middleware che permette alle cache di essere più efficienti e salvare banda, come un web server che non deve rimandare il messagio pieno se il contenuto non è cambiato. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Middleware che serve via il suo runtime server HTTP varianti esposte in formato JSON. | -| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignora favicon dai logs o serve dalla memoria se un filepath si dà. | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignora favicon dai logs o serve dalla memoria se un filepath è specificato. | | [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Middleware per il FileSystem per Fiber, grazie tante e crediti a Alireza Salary | -| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Middelwere per Rate-limiting per Fiber. Usato per limitare richieste continue agli APIs publici e/o endpoints come un password reset. | +| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Middleware per Rate-limiting per Fiber. Usato per limitare richieste continue agli APIs publici e/o endpoints come un password reset. | | [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | Logger HTTP per richiesta/risposta. | | [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Middleware per monitorare che riporta metriche server, ispirato da express-status-monitor | | [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Grazie tante a Matthew Lee \(@mthli\) | | [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Ti permette di fare richieste proxy a multipli server. | -| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Middleware per recuperare dai attachi di panico da tutte le parti nella stack chain e da il controllo al centralizzato[ ErrorHandler](https://docs.gofiber.io/guide/error-handling). | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Middleware per recuperare dagli attachi di panico da tutte le parti nella stack chain e affida il controllo al [ ErrorHandler](https://docs.gofiber.io/guide/error-handling) centralizzato. | | [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Aggiunge un requestid a ogni richiesta. | -| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Middelwere per sessioni. NOTA: Questo middleware usa il nostro Storage package. | +| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Middleware per sessioni. NOTA: Questo middleware usa il nostro Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Middleware che salta un wrapped handler se un predicate è vero. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Aggiunge un tempo massimo per una richiesta e lo manda a ErrorHandler se si supera. | @@ -635,7 +635,7 @@ La lista dei moduli middleware hostati esternamente e mantenuti dal [team di Fib | [jwt](https://github.com/gofiber/jwt) | Usa JSON Web Token \(JWT\) auth. | | [keyauth](https://github.com/gofiber/keyauth) | Usa auth basato su chiavi. | | [redirect](https://github.com/gofiber/redirect) | Middleware per reinderizzare | -| [rewrite](https://github.com/gofiber/rewrite) | Riscrive la path all URL con le regole date. Puo essere di aiuto per compatibilita o per creare link puliti e piu descrittivi. | +| [rewrite](https://github.com/gofiber/rewrite) | Riscrive la path all URL con le regole date. Può essere di aiuto per compatibilità o per creare link puliti e più descrittivi. | | [storage](https://github.com/gofiber/storage) | Dirver di storage che implementa la interfaccia Storage, fatto per essere usato con vari Fiber middleware. | | [template](https://github.com/gofiber/template) | Questo pachetto contiene 8 motori template che possono essere usati con Fiber `v1.10.x`. Versione di go neccesaria: 1.13+. | | [websocket](https://github.com/gofiber/websocket) | Basato su Fasthttp WebSocket per Fiber con supporto per Locals! | From c4d2876d64590bb6ddfbcc9c21253c927f35dbca Mon Sep 17 00:00:00 2001 From: James Lucas Date: Fri, 21 Apr 2023 12:37:53 +0100 Subject: [PATCH 083/162] =?UTF-8?q?=F0=9F=90=9B=20fix(cors):=20Changed=20c?= =?UTF-8?q?ondition=20for=20'AllowOriginsFunc'=20(#2423)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 fix(cors): Changed condition for 'AllowOriginsFunc' to check against default config value of 'AllowOrigins' --- middleware/cors/cors.go | 4 ++-- middleware/cors/cors_test.go | 44 ++++++++++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index cf90aee25c..181134ba67 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -97,7 +97,7 @@ func New(config ...Config) fiber.Handler { } // Warning logs if both AllowOrigins and AllowOriginsFunc are set - if cfg.AllowOrigins != "" && cfg.AllowOriginsFunc != nil { + if cfg.AllowOrigins != ConfigDefault.AllowOrigins && cfg.AllowOriginsFunc != nil { log.Printf("[CORS] - [Warning] Both 'AllowOrigins' and 'AllowOriginsFunc' have been defined.\n") } @@ -142,7 +142,7 @@ func New(config ...Config) fiber.Handler { // Run AllowOriginsFunc if the logic for // handling the value in 'AllowOrigins' does // not result in allowOrigin being set. - if allowOrigin == "" && cfg.AllowOriginsFunc != nil { + if (allowOrigin == "" || allowOrigin == ConfigDefault.AllowOrigins) && cfg.AllowOriginsFunc != nil { if cfg.AllowOriginsFunc(origin) { allowOrigin = origin } diff --git a/middleware/cors/cors_test.go b/middleware/cors/cors_test.go index 1f4a3c91d6..15b51a954e 100644 --- a/middleware/cors/cors_test.go +++ b/middleware/cors/cors_test.go @@ -244,7 +244,7 @@ func Test_CORS_Next(t *testing.T) { utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } -func Test_CORS_AllowOriginsFunc(t *testing.T) { +func Test_CORS_AllowOriginsAndAllowOriginsFunc(t *testing.T) { t.Parallel() // New fiber instance app := fiber.New() @@ -267,7 +267,7 @@ func Test_CORS_AllowOriginsFunc(t *testing.T) { // Perform request handler(ctx) - // Allow-Origin header should be "" because http://google.com does not satisfy http://*.example.com + // Allow-Origin header should be "" because http://google.com does not satisfy http://example-1.com or 'strings.Contains(origin, "example-2")' utils.AssertEqual(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) ctx.Request.Reset() @@ -294,3 +294,43 @@ func Test_CORS_AllowOriginsFunc(t *testing.T) { utils.AssertEqual(t, "http://example-2.com", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) } + +func Test_CORS_AllowOriginsFunc(t *testing.T) { + t.Parallel() + // New fiber instance + app := fiber.New() + app.Use("/", New(Config{ + AllowOriginsFunc: func(origin string) bool { + return strings.Contains(origin, "example-2") + }, + })) + + // Get handler pointer + handler := app.Handler() + + // Make request with disallowed origin + ctx := &fasthttp.RequestCtx{} + ctx.Request.SetRequestURI("/") + ctx.Request.Header.SetMethod(fiber.MethodOptions) + ctx.Request.Header.Set(fiber.HeaderOrigin, "http://google.com") + + // Perform request + handler(ctx) + + // Allow-Origin header should be "*" because http://google.com does not satisfy 'strings.Contains(origin, "example-2")' + // and AllowOrigins has not been set so the default "*" is used + utils.AssertEqual(t, "*", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + + ctx.Request.Reset() + ctx.Response.Reset() + + // Make request with allowed origin + ctx.Request.SetRequestURI("/") + ctx.Request.Header.SetMethod(fiber.MethodOptions) + ctx.Request.Header.Set(fiber.HeaderOrigin, "http://example-2.com") + + handler(ctx) + + // Allow-Origin header should be "http://example-2.com" + utils.AssertEqual(t, "http://example-2.com", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) +} From 9feaf2296ea5978ad2fe9476dad5011d93031d3c Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 21 Apr 2023 13:41:19 +0200 Subject: [PATCH 084/162] =?UTF-8?q?=F0=9F=9A=80=20Improve=20error=20handli?= =?UTF-8?q?ng=20for=20net=20error(s)=20(#2421)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * improve error handling for net error(s) fixes: reverse proxy support #2419 * Update app.go Co-authored-by: leonklingele * improve error handling for net error(s) fixes: reverse proxy support #2419 * improve error handling for net error(s) fixes: reverse proxy support #2419 * improve error handling for net error(s) fixes: reverse proxy support #2419 --------- Co-authored-by: leonklingele --- app.go | 7 ++++++- app_test.go | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/app.go b/app.go index 956ff56ce8..6af378e061 100644 --- a/app.go +++ b/app.go @@ -1052,13 +1052,18 @@ func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) { c := app.AcquireCtx(fctx) defer app.ReleaseCtx(c) - var errNetOP *net.OpError + var ( + errNetOP *net.OpError + netErr net.Error + ) switch { case errors.As(err, new(*fasthttp.ErrSmallBuffer)): err = ErrRequestHeaderFieldsTooLarge case errors.As(err, &errNetOP) && errNetOP.Timeout(): err = ErrRequestTimeout + case errors.As(err, &netErr): + err = ErrBadGateway case errors.Is(err, fasthttp.ErrBodyTooLarge): err = ErrRequestEntityTooLarge case errors.Is(err, fasthttp.ErrGetOnly): diff --git a/app_test.go b/app_test.go index 3ef57948b5..8ab6c76ff1 100644 --- a/app_test.go +++ b/app_test.go @@ -268,6 +268,20 @@ func Test_App_serverErrorHandler_Internal_Error(t *testing.T) { utils.AssertEqual(t, c.fasthttp.Response.StatusCode(), StatusBadRequest) } +func Test_App_serverErrorHandler_Network_Error(t *testing.T) { + t.Parallel() + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + app.serverErrorHandler(c.fasthttp, &net.DNSError{ + Err: "test error", + Name: "test host", + IsTimeout: false, + }) + utils.AssertEqual(t, string(c.fasthttp.Response.Body()), utils.StatusMessage(StatusBadGateway)) + utils.AssertEqual(t, c.fasthttp.Response.StatusCode(), StatusBadGateway) +} + func Test_App_Nested_Params(t *testing.T) { t.Parallel() app := New() From 0b5baf522d0af6a59ded5ef3ec9f536fef2c5907 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Apr 2023 23:03:34 +0300 Subject: [PATCH 085/162] Bump github.com/valyala/fasthttp from 1.45.0 to 1.46.0 (#2426) Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.45.0 to 1.46.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.45.0...v1.46.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1a20a8fef8..338e16a589 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 github.com/tinylib/msgp v1.1.8 github.com/valyala/bytebufferpool v1.0.0 - github.com/valyala/fasthttp v1.45.0 + github.com/valyala/fasthttp v1.46.0 golang.org/x/sys v0.7.0 ) diff --git a/go.sum b/go.sum index 7722ba9450..3bbe13c275 100644 --- a/go.sum +++ b/go.sum @@ -26,8 +26,8 @@ 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/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.45.0 h1:zPkkzpIn8tdHZUrVa6PzYd0i5verqiPSkgTd3bSUcpA= -github.com/valyala/fasthttp v1.45.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/fasthttp v1.46.0 h1:6ZRhrFg8zBXTRYY6vdzbFhqsBd7FVv123pV2m9V87U4= +github.com/valyala/fasthttp v1.46.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= 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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 9d8eba55d65bbdc020b32e1cbf51640f2a63a074 Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 26 Apr 2023 21:55:03 +0200 Subject: [PATCH 086/162] Update labeler.yml --- .github/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 1ecaf74253..ea3e61d5ba 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -14,7 +14,7 @@ labels: title: '\b(bumb|bdependencies)\b' - label: '✏️ Feature' matcher: - title: '\b(feature|create|implement|add)\b' + title: '\b(feature|feat|create|implement|add)\b' - label: '🤔 Question' matcher: title: '\b(question|how)\b' From a59d9bac59c552bdca433f27e4dadef999962f09 Mon Sep 17 00:00:00 2001 From: Kousik Mitra Date: Mon, 1 May 2023 13:31:27 +0530 Subject: [PATCH 087/162] :rocket: Consistent way of logging and fix middleware log format (#2432) * :rocket: Replace fmt.Print* with log.Print* (#2402) * :rocket: Fix middleware logging format (#2402) --- internal/template/html/html.go | 5 +++-- middleware/cache/config.go | 4 ++-- middleware/csrf/config.go | 6 +++--- middleware/idempotency/idempotency.go | 2 +- middleware/limiter/config.go | 6 +++--- middleware/proxy/proxy.go | 2 +- middleware/session/config.go | 2 +- middleware/timeout/timeout.go | 6 +++--- 8 files changed, 17 insertions(+), 16 deletions(-) diff --git a/internal/template/html/html.go b/internal/template/html/html.go index 71f6f02cb5..f892fdefe3 100644 --- a/internal/template/html/html.go +++ b/internal/template/html/html.go @@ -4,6 +4,7 @@ import ( "fmt" "html/template" "io" + "log" "net/http" "os" "path/filepath" @@ -112,7 +113,7 @@ func (e *Engine) Debug(enabled bool) *Engine { // Parse is deprecated, please use Load() instead func (e *Engine) Parse() error { - fmt.Println("Parse() is deprecated, please use Load() instead.") + log.Println("[Warning] Parse() is deprecated, please use Load() instead.") return e.Load() } @@ -169,7 +170,7 @@ func (e *Engine) Load() error { } // Debugging if e.debug { - fmt.Printf("views: parsed template: %s\n", name) + log.Printf("views: parsed template: %s\n", name) } return err } diff --git a/middleware/cache/config.go b/middleware/cache/config.go index 98e9e94f5d..7dedede371 100644 --- a/middleware/cache/config.go +++ b/middleware/cache/config.go @@ -102,11 +102,11 @@ func configDefault(config ...Config) Config { // Set default values if cfg.Store != nil { - log.Printf("[CACHE] Store is deprecated, please use Storage\n") + log.Printf("[CACHE] - [Warning] Store is deprecated, please use Storage\n") cfg.Storage = cfg.Store } if cfg.Key != nil { - log.Printf("[CACHE] Key is deprecated, please use KeyGenerator\n") + log.Printf("[CACHE] - [Warning] Key is deprecated, please use KeyGenerator\n") cfg.KeyGenerator = cfg.Key } if cfg.Next == nil { diff --git a/middleware/csrf/config.go b/middleware/csrf/config.go index 18514a25af..12c789a171 100644 --- a/middleware/csrf/config.go +++ b/middleware/csrf/config.go @@ -132,15 +132,15 @@ func configDefault(config ...Config) Config { // Set default values if cfg.TokenLookup != "" { - log.Printf("[CSRF] TokenLookup is deprecated, please use KeyLookup\n") + log.Printf("[CSRF] - [Warning] TokenLookup is deprecated, please use KeyLookup\n") cfg.KeyLookup = cfg.TokenLookup } if int(cfg.CookieExpires.Seconds()) > 0 { - log.Printf("[CSRF] CookieExpires is deprecated, please use Expiration\n") + log.Printf("[CSRF] - [Warning] CookieExpires is deprecated, please use Expiration\n") cfg.Expiration = cfg.CookieExpires } if cfg.Cookie != nil { - log.Printf("[CSRF] Cookie is deprecated, please use Cookie* related fields\n") + log.Printf("[CSRF] - [Warning] Cookie is deprecated, please use Cookie* related fields\n") if cfg.Cookie.Name != "" { cfg.CookieName = cfg.Cookie.Name } diff --git a/middleware/idempotency/idempotency.go b/middleware/idempotency/idempotency.go index 3ad2b9dedc..8f4890abdc 100644 --- a/middleware/idempotency/idempotency.go +++ b/middleware/idempotency/idempotency.go @@ -92,7 +92,7 @@ func New(config ...Config) fiber.Handler { } defer func() { if err := cfg.Lock.Unlock(key); err != nil { - log.Printf("middleware/idempotency: failed to unlock key %q: %v", key, err) + log.Printf("[IDEMPOTENCY] - [Error] failed to unlock key %q: %v", key, err) } }() diff --git a/middleware/limiter/config.go b/middleware/limiter/config.go index 92d078623d..cd225aebc0 100644 --- a/middleware/limiter/config.go +++ b/middleware/limiter/config.go @@ -95,15 +95,15 @@ func configDefault(config ...Config) Config { // Set default values if int(cfg.Duration.Seconds()) > 0 { - log.Printf("[LIMITER] Duration is deprecated, please use Expiration\n") + log.Printf("[LIMITER] - [Warning] Duration is deprecated, please use Expiration\n") cfg.Expiration = cfg.Duration } if cfg.Key != nil { - log.Printf("[LIMITER] Key is deprecated, please us KeyGenerator\n") + log.Printf("[LIMITER] - [Warning] Key is deprecated, please us KeyGenerator\n") cfg.KeyGenerator = cfg.Key } if cfg.Store != nil { - log.Printf("[LIMITER] Store is deprecated, please use Storage\n") + log.Printf("[LIMITER] - [Warning] Store is deprecated, please use Storage\n") cfg.Storage = cfg.Store } if cfg.Next == nil { diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index 3a464d48d2..7f5ac9f052 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -17,7 +17,7 @@ import ( // New is deprecated func New(config Config) fiber.Handler { - log.Printf("proxy.New is deprecated, please use proxy.Balancer instead\n") + log.Printf("[PROXY] - [Warning] proxy.New is deprecated, please use proxy.Balancer instead\n") return Balancer(config) } diff --git a/middleware/session/config.go b/middleware/session/config.go index 758db5538a..f72bbc7479 100644 --- a/middleware/session/config.go +++ b/middleware/session/config.go @@ -97,7 +97,7 @@ func configDefault(config ...Config) Config { cfg.Expiration = ConfigDefault.Expiration } if cfg.CookieName != "" { - log.Printf("[session] CookieName is deprecated, please use KeyLookup\n") + log.Printf("[SESSION] - [Warning] CookieName is deprecated, please use KeyLookup\n") cfg.KeyLookup = fmt.Sprintf("cookie:%s", cfg.CookieName) } if cfg.KeyLookup == "" { diff --git a/middleware/timeout/timeout.go b/middleware/timeout/timeout.go index 1485704dff..1fab0fe592 100644 --- a/middleware/timeout/timeout.go +++ b/middleware/timeout/timeout.go @@ -18,7 +18,7 @@ var once sync.Once // Find documentation and sample usage on https://docs.gofiber.io/api/middleware/timeout func New(handler fiber.Handler, timeout time.Duration) fiber.Handler { once.Do(func() { - log.Printf("[Warning] timeout contains data race issues, not ready for production!") + log.Printf("[TIMEOUT] - [Warning] timeout contains data race issues, not ready for production!") }) if timeout <= 0 { @@ -32,11 +32,11 @@ func New(handler fiber.Handler, timeout time.Duration) fiber.Handler { go func() { defer func() { if err := recover(); err != nil { - log.Printf("[Warning] recover error %v", err) + log.Printf("[TIMEOUT] - [Warning] recover error %v", err) } }() if err := handler(ctx); err != nil { - log.Printf("[Warning] handler error %v", err) + log.Printf("[TIMEOUT] - [Warning] handler error %v", err) } ch <- struct{}{} }() From 3a7dbd0b48ea66845fb124757ceb1aed0ef0cb5b Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 1 May 2023 18:52:30 +0200 Subject: [PATCH 088/162] =?UTF-8?q?=F0=9F=9A=80=20Consistent=20way=20of=20?= =?UTF-8?q?logging=20and=20fix=20middleware=20log=20format=20#2432=20(#244?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - change log patter --- middleware/cache/config.go | 4 ++-- middleware/cors/cors.go | 2 +- middleware/csrf/config.go | 6 +++--- middleware/idempotency/idempotency.go | 2 +- middleware/limiter/config.go | 6 +++--- middleware/proxy/proxy.go | 2 +- middleware/session/config.go | 2 +- middleware/timeout/timeout.go | 6 +++--- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/middleware/cache/config.go b/middleware/cache/config.go index 7dedede371..734e85357b 100644 --- a/middleware/cache/config.go +++ b/middleware/cache/config.go @@ -102,11 +102,11 @@ func configDefault(config ...Config) Config { // Set default values if cfg.Store != nil { - log.Printf("[CACHE] - [Warning] Store is deprecated, please use Storage\n") + log.Printf("[Warning] - [CACHE] Store is deprecated, please use Storage\n") cfg.Storage = cfg.Store } if cfg.Key != nil { - log.Printf("[CACHE] - [Warning] Key is deprecated, please use KeyGenerator\n") + log.Printf("[Warning] - [CACHE] Key is deprecated, please use KeyGenerator\n") cfg.KeyGenerator = cfg.Key } if cfg.Next == nil { diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index 181134ba67..302b1591b1 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -98,7 +98,7 @@ func New(config ...Config) fiber.Handler { // Warning logs if both AllowOrigins and AllowOriginsFunc are set if cfg.AllowOrigins != ConfigDefault.AllowOrigins && cfg.AllowOriginsFunc != nil { - log.Printf("[CORS] - [Warning] Both 'AllowOrigins' and 'AllowOriginsFunc' have been defined.\n") + log.Printf("[Warning] - [CORS] Both 'AllowOrigins' and 'AllowOriginsFunc' have been defined.\n") } // Convert string to slice diff --git a/middleware/csrf/config.go b/middleware/csrf/config.go index 12c789a171..1e2d875a4f 100644 --- a/middleware/csrf/config.go +++ b/middleware/csrf/config.go @@ -132,15 +132,15 @@ func configDefault(config ...Config) Config { // Set default values if cfg.TokenLookup != "" { - log.Printf("[CSRF] - [Warning] TokenLookup is deprecated, please use KeyLookup\n") + log.Printf("[Warning] - [CSRF] TokenLookup is deprecated, please use KeyLookup\n") cfg.KeyLookup = cfg.TokenLookup } if int(cfg.CookieExpires.Seconds()) > 0 { - log.Printf("[CSRF] - [Warning] CookieExpires is deprecated, please use Expiration\n") + log.Printf("[Warning] - [CSRF] CookieExpires is deprecated, please use Expiration\n") cfg.Expiration = cfg.CookieExpires } if cfg.Cookie != nil { - log.Printf("[CSRF] - [Warning] Cookie is deprecated, please use Cookie* related fields\n") + log.Printf("[Warning] - [CSRF] Cookie is deprecated, please use Cookie* related fields\n") if cfg.Cookie.Name != "" { cfg.CookieName = cfg.Cookie.Name } diff --git a/middleware/idempotency/idempotency.go b/middleware/idempotency/idempotency.go index 8f4890abdc..f1d3db310e 100644 --- a/middleware/idempotency/idempotency.go +++ b/middleware/idempotency/idempotency.go @@ -92,7 +92,7 @@ func New(config ...Config) fiber.Handler { } defer func() { if err := cfg.Lock.Unlock(key); err != nil { - log.Printf("[IDEMPOTENCY] - [Error] failed to unlock key %q: %v", key, err) + log.Printf("[Error] - [IDEMPOTENCY] failed to unlock key %q: %v", key, err) } }() diff --git a/middleware/limiter/config.go b/middleware/limiter/config.go index cd225aebc0..827debe795 100644 --- a/middleware/limiter/config.go +++ b/middleware/limiter/config.go @@ -95,15 +95,15 @@ func configDefault(config ...Config) Config { // Set default values if int(cfg.Duration.Seconds()) > 0 { - log.Printf("[LIMITER] - [Warning] Duration is deprecated, please use Expiration\n") + log.Printf("[Warning] - [LIMITER] Duration is deprecated, please use Expiration\n") cfg.Expiration = cfg.Duration } if cfg.Key != nil { - log.Printf("[LIMITER] - [Warning] Key is deprecated, please us KeyGenerator\n") + log.Printf("[Warning] - [LIMITER] Key is deprecated, please us KeyGenerator\n") cfg.KeyGenerator = cfg.Key } if cfg.Store != nil { - log.Printf("[LIMITER] - [Warning] Store is deprecated, please use Storage\n") + log.Printf("[Warning] - [LIMITER] Store is deprecated, please use Storage\n") cfg.Storage = cfg.Store } if cfg.Next == nil { diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index 7f5ac9f052..9ca5e4bc08 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -17,7 +17,7 @@ import ( // New is deprecated func New(config Config) fiber.Handler { - log.Printf("[PROXY] - [Warning] proxy.New is deprecated, please use proxy.Balancer instead\n") + log.Printf("[Warning] - [PROXY] proxy.New is deprecated, please use proxy.Balancer instead\n") return Balancer(config) } diff --git a/middleware/session/config.go b/middleware/session/config.go index f72bbc7479..aa476eb808 100644 --- a/middleware/session/config.go +++ b/middleware/session/config.go @@ -97,7 +97,7 @@ func configDefault(config ...Config) Config { cfg.Expiration = ConfigDefault.Expiration } if cfg.CookieName != "" { - log.Printf("[SESSION] - [Warning] CookieName is deprecated, please use KeyLookup\n") + log.Printf("[Warning] - [SESSION] CookieName is deprecated, please use KeyLookup\n") cfg.KeyLookup = fmt.Sprintf("cookie:%s", cfg.CookieName) } if cfg.KeyLookup == "" { diff --git a/middleware/timeout/timeout.go b/middleware/timeout/timeout.go index 1fab0fe592..e40cc4f88d 100644 --- a/middleware/timeout/timeout.go +++ b/middleware/timeout/timeout.go @@ -18,7 +18,7 @@ var once sync.Once // Find documentation and sample usage on https://docs.gofiber.io/api/middleware/timeout func New(handler fiber.Handler, timeout time.Duration) fiber.Handler { once.Do(func() { - log.Printf("[TIMEOUT] - [Warning] timeout contains data race issues, not ready for production!") + log.Printf("[Warning] - [TIMEOUT] timeout contains data race issues, not ready for production!") }) if timeout <= 0 { @@ -32,11 +32,11 @@ func New(handler fiber.Handler, timeout time.Duration) fiber.Handler { go func() { defer func() { if err := recover(); err != nil { - log.Printf("[TIMEOUT] - [Warning] recover error %v", err) + log.Printf("[Warning] - [TIMEOUT] recover error %v", err) } }() if err := handler(ctx); err != nil { - log.Printf("[TIMEOUT] - [Warning] handler error %v", err) + log.Printf("[Warning] - [TIMEOUT] handler error %v", err) } ch <- struct{}{} }() From 3c3f12b76c6ae849471bb6bffa6bbf30dd74648f Mon Sep 17 00:00:00 2001 From: bcd <471267877@qq.com> Date: Tue, 2 May 2023 14:40:20 +0800 Subject: [PATCH 089/162] [Feature]: Add filesystem config contentTypeCharset support (#2438) * Update filesystem.go * Update filesystem_test.go * Update filesystem.md * fmt --- docs/api/middleware/filesystem.md | 23 ++++++++++++++-------- middleware/filesystem/filesystem.go | 25 +++++++++++++++++------- middleware/filesystem/filesystem_test.go | 14 +++++++++++++ 3 files changed, 47 insertions(+), 15 deletions(-) diff --git a/docs/api/middleware/filesystem.md b/docs/api/middleware/filesystem.md index a3029f96f1..9bc3864cda 100644 --- a/docs/api/middleware/filesystem.md +++ b/docs/api/middleware/filesystem.md @@ -249,13 +249,13 @@ type Config struct { // Required. Default: nil Root http.FileSystem `json:"-"` - // PathPrefix defines a prefix to be added to a filepath when - // reading a file from the FileSystem. - // - // Use when using Go 1.16 embed.FS - // - // Optional. Default "" - PathPrefix string `json:"path_prefix"` + // PathPrefix defines a prefix to be added to a filepath when + // reading a file from the FileSystem. + // + // Use when using Go 1.16 embed.FS + // + // Optional. Default "" + PathPrefix string `json:"path_prefix"` // Enable directory browsing. // @@ -277,6 +277,12 @@ type Config struct { // // Optional. Default: "" NotFoundFile string `json:"not_found_file"` + + // The value for the Content-Type HTTP-header + // that is set on the file response + // + // Optional. Default: "" + ContentTypeCharset string `json:"content_type_charset"` } ``` @@ -286,9 +292,10 @@ type Config struct { var ConfigDefault = Config{ Next: nil, Root: nil, - PathPrefix: "", + PathPrefix: "", Browse: false, Index: "/index.html", MaxAge: 0, + ContentTypeCharset: "", } ``` diff --git a/middleware/filesystem/filesystem.go b/middleware/filesystem/filesystem.go index 02169d57c9..4b5b6c2928 100644 --- a/middleware/filesystem/filesystem.go +++ b/middleware/filesystem/filesystem.go @@ -53,16 +53,23 @@ type Config struct { // // Optional. Default: "" NotFoundFile string `json:"not_found_file"` + + // The value for the Content-Type HTTP-header + // that is set on the file response + // + // Optional. Default: "" + ContentTypeCharset string `json:"content_type_charset"` } // ConfigDefault is the default config var ConfigDefault = Config{ - Next: nil, - Root: nil, - PathPrefix: "", - Browse: false, - Index: "/index.html", - MaxAge: 0, + Next: nil, + Root: nil, + PathPrefix: "", + Browse: false, + Index: "/index.html", + MaxAge: 0, + ContentTypeCharset: "", } // New creates a new middleware handler. @@ -176,7 +183,11 @@ func New(config ...Config) fiber.Handler { contentLength := int(stat.Size()) // Set Content Type header - c.Type(getFileExtension(stat.Name())) + if cfg.ContentTypeCharset == "" { + c.Type(getFileExtension(stat.Name())) + } else { + c.Type(getFileExtension(stat.Name()), cfg.ContentTypeCharset) + } // Set Last Modified header if !modTime.IsZero() { diff --git a/middleware/filesystem/filesystem_test.go b/middleware/filesystem/filesystem_test.go index 56c113e0c5..d592046c45 100644 --- a/middleware/filesystem/filesystem_test.go +++ b/middleware/filesystem/filesystem_test.go @@ -218,3 +218,17 @@ func Test_FileSystem_UsingParam_NonFile(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 404, resp.StatusCode) } + +func Test_FileSystem_UsingContentTypeCharset(t *testing.T) { + t.Parallel() + app := fiber.New() + app.Use(New(Config{ + Root: http.Dir("../../.github/testdata/fs/index.html"), + ContentTypeCharset: "UTF-8", + })) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + utils.AssertEqual(t, "text/html; charset=UTF-8", resp.Header.Get("Content-Type")) +} From 3ed1c8dd3ecc19ce1e5fc6de92546f29e62c5560 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 May 2023 19:26:46 +0300 Subject: [PATCH 090/162] Bump github.com/valyala/fasthttp from 1.46.0 to 1.47.0 (#2445) Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.46.0 to 1.47.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.46.0...v1.47.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 338e16a589..760dfe423c 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 github.com/tinylib/msgp v1.1.8 github.com/valyala/bytebufferpool v1.0.0 - github.com/valyala/fasthttp v1.46.0 + github.com/valyala/fasthttp v1.47.0 golang.org/x/sys v0.7.0 ) diff --git a/go.sum b/go.sum index 3bbe13c275..f5cab0b026 100644 --- a/go.sum +++ b/go.sum @@ -26,8 +26,8 @@ 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/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.46.0 h1:6ZRhrFg8zBXTRYY6vdzbFhqsBd7FVv123pV2m9V87U4= -github.com/valyala/fasthttp v1.46.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= +github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= 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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 9abb24597fb2cd58a45241326f104ce915116d0e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 May 2023 16:24:44 +0300 Subject: [PATCH 091/162] Bump golang.org/x/sys from 0.7.0 to 0.8.0 (#2449) Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.7.0 to 0.8.0. - [Commits](https://github.com/golang/sys/compare/v0.7.0...v0.8.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 760dfe423c..d1c2f5c4b9 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/tinylib/msgp v1.1.8 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.47.0 - golang.org/x/sys v0.7.0 + golang.org/x/sys v0.8.0 ) require ( diff --git a/go.sum b/go.sum index f5cab0b026..2c6217899e 100644 --- a/go.sum +++ b/go.sum @@ -59,8 +59,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc 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.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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= From 6770e45590fecea4c43a749e374c73868638a4d1 Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 7 May 2023 16:49:42 +0200 Subject: [PATCH 092/162] Update app.go prepare release v2.45.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 6af378e061..db2104356d 100644 --- a/app.go +++ b/app.go @@ -30,7 +30,7 @@ import ( ) // Version of current fiber package -const Version = "2.44.0" +const Version = "2.45.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From 9cc10e942a6be89ed6a77e52f6f2c553f564029b Mon Sep 17 00:00:00 2001 From: RW Date: Tue, 9 May 2023 09:01:56 +0200 Subject: [PATCH 093/162] Update benchmark.yml --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index d7db1b84aa..96750a2eaa 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -31,7 +31,7 @@ jobs: path: ./cache key: ${{ runner.os }}-benchmark - name: Save Benchmark Results - uses: benchmark-action/github-action-benchmark@v1 + uses: benchmark-action/github-action-benchmark@v1.16.2 with: tool: 'go' output-file-path: output.txt From c7c37d9b504f530a23ee1b03df872e8e0f17002f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Wed, 10 May 2023 09:01:49 +0300 Subject: [PATCH 094/162] :recycle: refactor: merge some external middlewares to core (#2453) * :recycle: refactor: merge adaptor, helmet, keyauth, redirect, rewrite middlewares to core * fix linting issues * fix linting issues * fix linting issues * update --- .github/README.md | 10 +- .github/README_az.md | 10 +- .github/README_ckb.md | 10 +- .github/README_de.md | 10 +- .github/README_es.md | 10 +- .github/README_fa.md | 10 +- .github/README_fr.md | 10 +- .github/README_he.md | 10 +- .github/README_id.md | 10 +- .github/README_it.md | 10 +- .github/README_ja.md | 10 +- .github/README_ko.md | 10 +- .github/README_nl.md | 10 +- .github/README_pt.md | 10 +- .github/README_ru.md | 10 +- .github/README_sa.md | 10 +- .github/README_tr.md | 10 +- .github/README_uk.md | 10 +- .github/README_zh-CN.md | 10 +- .github/README_zh-TW.md | 10 +- docs/api/middleware/adaptor.md | 135 ++++++++ docs/api/middleware/filesystem.md | 7 - docs/api/middleware/helmet.md | 138 ++++++++ docs/api/middleware/keyauth.md | 272 ++++++++++++++++ docs/api/middleware/pprof.md | 3 - docs/api/middleware/redirect.md | 86 +++++ docs/api/middleware/rewrite.md | 51 +++ middleware/adaptor/adaptor.go | 163 ++++++++++ middleware/adaptor/adaptor_test.go | 462 +++++++++++++++++++++++++++ middleware/helmet/config.go | 154 +++++++++ middleware/helmet/helmet.go | 94 ++++++ middleware/helmet/helmet_test.go | 201 ++++++++++++ middleware/keyauth/config.go | 95 ++++++ middleware/keyauth/keyauth.go | 121 +++++++ middleware/keyauth/keyauth_test.go | 461 ++++++++++++++++++++++++++ middleware/redirect/config.go | 53 +++ middleware/redirect/redirect.go | 57 ++++ middleware/redirect/redirect_test.go | 283 ++++++++++++++++ middleware/rewrite/config.go | 38 +++ middleware/rewrite/rewrite.go | 54 ++++ middleware/rewrite/rewrite_test.go | 173 ++++++++++ 41 files changed, 3191 insertions(+), 110 deletions(-) create mode 100644 docs/api/middleware/adaptor.md create mode 100644 docs/api/middleware/helmet.md create mode 100644 docs/api/middleware/keyauth.md create mode 100644 docs/api/middleware/redirect.md create mode 100644 docs/api/middleware/rewrite.md create mode 100644 middleware/adaptor/adaptor.go create mode 100644 middleware/adaptor/adaptor_test.go create mode 100644 middleware/helmet/config.go create mode 100644 middleware/helmet/helmet.go create mode 100644 middleware/helmet/helmet_test.go create mode 100644 middleware/keyauth/config.go create mode 100644 middleware/keyauth/keyauth.go create mode 100644 middleware/keyauth/keyauth_test.go create mode 100644 middleware/redirect/config.go create mode 100644 middleware/redirect/redirect.go create mode 100644 middleware/redirect/redirect_test.go create mode 100644 middleware/rewrite/config.go create mode 100644 middleware/rewrite/rewrite.go create mode 100644 middleware/rewrite/rewrite_test.go diff --git a/.github/README.md b/.github/README.md index 92dd13b1dd..924bf47c7f 100644 --- a/.github/README.md +++ b/.github/README.md @@ -627,7 +627,12 @@ Here is a list of middleware that are included within the Fiber framework. | [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Adds a requestid to every request. | | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler if a predicate is true. | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | ## 🧬 External Middleware @@ -635,12 +640,7 @@ List of externally hosted middleware modules and maintained by the [Fiber team]( | Middleware | Description | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_az.md b/.github/README_az.md index 85f0826c83..38543efcee 100644 --- a/.github/README_az.md +++ b/.github/README_az.md @@ -627,6 +627,11 @@ Aşağıda Fiber-in daxilində olan middleware-lər siyahı şəklində göstər | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session üçün middleware. Qeyd: Bu middleware Fiber-in öz storage struktrunu istifadə edir. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware-i verilən şərt true olduğu halda handler-i görməyərək üstündən ötüb keçir. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Bir request üçün maksimum vaxt əlavə edir. Əgər arada fasilə yaranarsa, onda proses məhz ErrorHandler-ə göndərilərək icra edilir. | +| [keyauth](https://github.com/gofiber/keyauth) | Key giriş middleware-i, key əsaslı bir authentication metodudur. | +| [redirect](https://github.com/gofiber/redirect) | Yönləndirmə üçün middleware. | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware-i verilən qanunlara əsasən URL yolunu (path) yenidən yazır. Geri dönüşün icrası üçün uyğunluq təşkil edən təsviri linklərin yaradılması üçün nəzərdə tutulmuşdur. | +| [adaptor](https://github.com/gofiber/adaptor) | Fiber request handler-dən net/http handler-ə çevirici. @arsmn-ə xüsusi təşəkkürlər! | +| [helmet](https://github.com/gofiber/helmet) | Fərqli HTTP header istifadə edərək tətbiqi daha təhlükəsiz saxlamağa kömək edir. | ## 🧬 Xarici Middleware @@ -634,12 +639,7 @@ Aşağıda Fiber-in daxilində olan middleware-lər siyahı şəklində göstər | Middleware | Description | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Fiber request handler-dən net/http handler-ə çevirici. @arsmn-ə xüsusi təşəkkürlər! | -| [helmet](https://github.com/gofiber/helmet) | Fərqli HTTP header istifadə edərək tətbiqi daha təhlükəsiz saxlamağa kömək edir. | | [jwt](https://github.com/gofiber/jwt) | JWT, JSON Web Token(JWT) girişi qaytaran bir middleware-dir. | -| [keyauth](https://github.com/gofiber/keyauth) | Key giriş middleware-i, key əsaslı bir authentication metodudur. | -| [redirect](https://github.com/gofiber/redirect) | Yönləndirmə üçün middleware. | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware-i verilən qanunlara əsasən URL yolunu (path) yenidən yazır. Geri dönüşün icrası üçün uyğunluq təşkil edən təsviri linklərin yaradılması üçün nəzərdə tutulmuşdur. | | [storage](https://github.com/gofiber/storage) | Fiber-in Storage arxitekturasını dəstəkləyən bir sıra storage driver verir. Bu sayədə storage-ə ehtiyac duyan Fiber middleware-də rahatlıqla istifadə oluna bilər. | | [template](https://github.com/gofiber/template) | Bu paket, Fiber `v1.10.x`, Go versiyası 1.13 və ya daha yuxarı olduqda istifadə oluna bilər. 8 template mühərriki var. | | [websocket](https://github.com/gofiber/websocket) | Yerlilərin dəstəyi ilə WebSocket-ə əsaslanan Fiber üçün Fasthttp. | diff --git a/.github/README_ckb.md b/.github/README_ckb.md index 04583f8e2c..fe38ead798 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -627,6 +627,11 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 کاڵا دەرەکییەکان @@ -634,12 +639,7 @@ func main() { | کاڵا | دەربارە | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_de.md b/.github/README_de.md index 94f88aa46a..05c868d716 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -597,6 +597,11 @@ Hier finden Sie eine Liste der Middleware, die im Fiber-Framework enthalten ist. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 External Middleware @@ -604,12 +609,7 @@ Liste der extern gehosteten Middleware-Module, die vom [Fiber team](https://gith | Middleware | Description | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. || [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_es.md b/.github/README_es.md index 3f16fe8bd9..6fcc7fa521 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -597,6 +597,11 @@ Aquí está una lista del middleware incluido en el marco web Fiber. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 Middleware Externo @@ -604,12 +609,7 @@ Lista de módulos de middleware alojados externamente, y mantenidos por el [equi | Middleware | Descripción | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_fa.md b/.github/README_fa.md index 0442e2af05..4e442540f7 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -712,6 +712,11 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) |برای ذخیره و مدیریت شناسه کاربری یا session بازدید کنندگان استفاده .میشود| | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) |این میدلور میتواند با استفاده از شرط های تعیین شده درخواست هایی را نادیده بگیرد.| | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) |این میدلور محدودیت زمانی ای را برای درخواست ها تنظیم میکند، در صورتی که محدودیت به پایان برسد ErrorHandler صدا زده میشود.| +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. |


@@ -728,12 +733,7 @@ func main() { | Middleware | توضیحات | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_fr.md b/.github/README_fr.md index 6e19a47115..d6211f3186 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -599,6 +599,11 @@ Here is a list of middleware that are included within the Fiber framework. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 External Middleware @@ -606,12 +611,7 @@ List of externally hosted middleware modules and maintained by the [Fiber team]( | Middleware | Description | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_he.md b/.github/README_he.md index dfd43cc3c3..08e677ba66 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -715,6 +715,11 @@ Here is a list of middleware that are included within the Fiber framework. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. |
@@ -734,12 +739,7 @@ Here is a list of middleware that are included within the Fiber framework. | Middleware | Description | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_id.md b/.github/README_id.md index f0968fe207..d8aa83eb43 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -600,6 +600,11 @@ Kumpulan `middleware` yang ada didalam kerangka kerja Fiber. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 Middleware External @@ -607,12 +612,7 @@ Kumpulan `middleware` yang dihost external dan diurus oleh [Tim Fiber](https://g | Middleware | Description | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_it.md b/.github/README_it.md index ddbbc246ef..f1a4c6ed41 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -623,6 +623,11 @@ Qui una lista dei middleware inclusi con Fiber. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Middleware per sessioni. NOTA: Questo middleware usa il nostro Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Middleware che salta un wrapped handler se un predicate è vero. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Aggiunge un tempo massimo per una richiesta e lo manda a ErrorHandler se si supera. | +| [keyauth](https://github.com/gofiber/keyauth) | Usa auth basato su chiavi. | +| [redirect](https://github.com/gofiber/redirect) | Middleware per reinderizzare | +| [rewrite](https://github.com/gofiber/rewrite) | Riscrive la path all URL con le regole date. Può essere di aiuto per compatibilità o per creare link puliti e più descrittivi. | +| [adaptor](https://github.com/gofiber/adaptor) | Converte gli handler net/http a/da i request handlers di Fiber, grazie tante a @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Aiuta a mettere sicurezza alla tua app usando vari header HTTP. | ## 🧬 Middleware Esterni @@ -630,12 +635,7 @@ La lista dei moduli middleware hostati esternamente e mantenuti dal [team di Fib | Middleware | Descrizione | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converte gli handler net/http a/da i request handlers di Fiber, grazie tante a @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Aiuta a mettere sicurezza alla tua app usando vari header HTTP. | | [jwt](https://github.com/gofiber/jwt) | Usa JSON Web Token \(JWT\) auth. | -| [keyauth](https://github.com/gofiber/keyauth) | Usa auth basato su chiavi. | -| [redirect](https://github.com/gofiber/redirect) | Middleware per reinderizzare | -| [rewrite](https://github.com/gofiber/rewrite) | Riscrive la path all URL con le regole date. Può essere di aiuto per compatibilità o per creare link puliti e più descrittivi. | | [storage](https://github.com/gofiber/storage) | Dirver di storage che implementa la interfaccia Storage, fatto per essere usato con vari Fiber middleware. | | [template](https://github.com/gofiber/template) | Questo pachetto contiene 8 motori template che possono essere usati con Fiber `v1.10.x`. Versione di go neccesaria: 1.13+. | | [websocket](https://github.com/gofiber/websocket) | Basato su Fasthttp WebSocket per Fiber con supporto per Locals! | diff --git a/.github/README_ja.md b/.github/README_ja.md index 637c20d5f7..cc1a50efcd 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -602,6 +602,11 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 外部ミドルウェア @@ -609,12 +614,7 @@ func main() { | Middleware | Description | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_ko.md b/.github/README_ko.md index 3a9fb4256b..49eae5d670 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -603,6 +603,11 @@ Fiber 프레임워크에 포함되는 미들웨어 목록입니다. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 External Middleware @@ -610,12 +615,7 @@ Fiber 프레임워크에 포함되는 미들웨어 목록입니다. | Middleware | Description | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_nl.md b/.github/README_nl.md index 405bcb871a..6ba600f845 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -603,6 +603,11 @@ Here is a list of middleware that are included within the Fiber framework. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | ## 🧬 External Middleware @@ -610,12 +615,7 @@ List of externally hosted middleware modules and maintained by the [Fiber team]( | Middleware | Description | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_pt.md b/.github/README_pt.md index 783489b965..c81632bdab 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -597,6 +597,11 @@ Here is a list of middleware that are included within the Fiber framework. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 External Middleware @@ -604,12 +609,7 @@ List of externally hosted middleware modules and maintained by the [Fiber team]( | Middleware | Description | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_ru.md b/.github/README_ru.md index 17c98d4321..f413ebc074 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -604,6 +604,11 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 Внешние Middleware @@ -611,12 +616,7 @@ func main() { | Middleware | Описание | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_sa.md b/.github/README_sa.md index af7519e2dd..ea4e7bed17 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -668,6 +668,11 @@ Here is a list of middleware that are included within the Fiber framework. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 External Middleware @@ -675,12 +680,7 @@ List of externally hosted middleware modules and maintained by the [Fiber team]( | Middleware | Description | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | | [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | diff --git a/.github/README_tr.md b/.github/README_tr.md index f229c8de66..511bee9b32 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -597,6 +597,11 @@ Fiber'a dahil edilen middlewareların bir listesi aşağıda verilmiştir. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session için middleware. NOTE: Bu middleware Fiber'in Storage yapısını kullanır. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware'ı verilen koşul `true` olduğunda handler'ı atlar ve işlemez. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Bir request için maksimum süre ekler ve aşılırsa ErrorHandler'a iletir. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware, key tabanlı bir authentication sağlar. | +| [redirect](https://github.com/gofiber/redirect) | Yönlendirme middleware 'ı. | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware, sağlanan kurallara göre URL yolunu yeniden yazar. Geriye dönük uyumluluk için veya yalnızca daha temiz ve daha açıklayıcı bağlantılar oluşturmak için yardımcı olabilir. | +| [adaptor](https://github.com/gofiber/adaptor) | Fiber request handlerdan net/http handlerları için dönüştürücü, @arsmn'a özel teşekkürler! | +| [helmet](https://github.com/gofiber/helmet) | Çeşitli HTTP headerları ayarlayarak uygulamalarınızın güvenliğini sağlamaya yardımcı olur. | ## 🧬 Harici Middlewarelar @@ -604,12 +609,7 @@ Harici olarak barındırılan middlewareların modüllerinin listesi. Bu middlew | Middleware | Açıklama | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [adaptor](https://github.com/gofiber/adaptor) | Fiber request handlerdan net/http handlerları için dönüştürücü, @arsmn'a özel teşekkürler! | -| [helmet](https://github.com/gofiber/helmet) | Çeşitli HTTP headerları ayarlayarak uygulamalarınızın güvenliğini sağlamaya yardımcı olur. | | [jwt](https://github.com/gofiber/jwt) | JWT, bir JSON Web Token \(JWT\) yetkilendirmesi döndüren middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware, key tabanlı bir authentication sağlar. | -| [redirect](https://github.com/gofiber/redirect) | Yönlendirme middleware 'ı. | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware, sağlanan kurallara göre URL yolunu yeniden yazar. Geriye dönük uyumluluk için veya yalnızca daha temiz ve daha açıklayıcı bağlantılar oluşturmak için yardımcı olabilir. | | [storage](https://github.com/gofiber/storage) | Fiber'in Storage yapısını destekleyen birçok storage driver'ı verir. Bu sayede depolama gerektiren Fiber middlewarelarında kolaylıkla kullanılabilir. | | [template](https://github.com/gofiber/template) | Bu paket, Fiber `v2.x.x`, Go sürüm 1.17 veya üzeri gerekli olduğunda kullanılabilecek 9 template motoru içerir. | | [websocket](https://github.com/gofiber/websocket) | Yereller desteğiyle Fiber için Fasthttp WebSocket'a dayalıdır! | diff --git a/.github/README_uk.md b/.github/README_uk.md index d68ff24c08..d62cb2ffb7 100644 --- a/.github/README_uk.md +++ b/.github/README_uk.md @@ -632,6 +632,11 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Middleware для сеансів. ПРИМІТКА: Цей middleware використовує наш пакет зберігання. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Middleware який пропускає упакований обробник, якщо предикат є істинним. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Додає максимальний час для запиту та пересилає до ErrorHandler, якщо його перевищено. | +| [keyauth](https://github.com/gofiber/keyauth) | Middleware для автентифікації по ключам. | +| [redirect](https://github.com/gofiber/redirect) | Middleware для перенаправлення. | +| [rewrite](https://github.com/gofiber/rewrite) | Middleware для перезапису URL-адреси на основі наданих правил. | +| [adaptor](https://github.com/gofiber/adaptor) | Конвентор для обробників net/http до/з обробників запитів Fiber, особлива подяка @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Допомагає захистити ваші програми, встановлюючи різні заголовки HTTP. | ## 🧬 Зовнішні Middleware @@ -639,12 +644,7 @@ func main() { | Middleware | Опис | | :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | -| [adaptor](https://github.com/gofiber/adaptor) | Конвентор для обробників net/http до/з обробників запитів Fiber, особлива подяка @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Допомагає захистити ваші програми, встановлюючи різні заголовки HTTP. | | [jwt](https://github.com/gofiber/jwt) | JWT повертає middleware автентифікації JSON Web Token \(JWT\). | -| [keyauth](https://github.com/gofiber/keyauth) | Middleware для автентифікації по ключам. | -| [redirect](https://github.com/gofiber/redirect) | Middleware для перенаправлення. | -| [rewrite](https://github.com/gofiber/rewrite) | Middleware для перезапису URL-адреси на основі наданих правил. | | [storage](https://github.com/gofiber/storage) | Драйвер зберігання який може використовуватися в різних middleware. | | [template](https://github.com/gofiber/template) | Цей пакет містить 8 модулів шаблонів, які можна використовувати з Fiber `v1.10.x` Потрібно версія Go 1.13 або новішу. | | [websocket](https://github.com/gofiber/websocket) | На основі Fasthttp WebSocket для Fiber з підтримкою місцевих користувачів! | diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index 2520ca4d2a..2558a9da7f 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -605,6 +605,11 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session 中间件. 注意: 此中间件使用了我们的存储包. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip 中间件会在判断条为 true 时忽略此次请求 | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | 添加请求的最大时间,如果超时则发送给ErrorHandler 进行处理. | +| [adaptor](https://github.com/gofiber/adaptor) | net/http 处理程序与 Fiber 请求处理程序之间的转换器,特别感谢 @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | 通过设置各种 HTTP 头帮助保护您的应用程序 | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth 中间件提供基于密钥的身份验证 | +| [redirect](https://github.com/gofiber/redirect) | 用于重定向请求的中间件 | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite 中间件根据提供的规则重写URL路径。它有助于向后兼容或者创建更清晰、更具描述性的链接 | ## 🧬 外部中间件 @@ -612,12 +617,7 @@ func main() { | 中间件 | 描述 | |:--------------------------------------------------|:-------------------------------------------------------------------------------------------| -| [adaptor](https://github.com/gofiber/adaptor) | net/http 处理程序与 Fiber 请求处理程序之间的转换器,特别感谢 @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | 通过设置各种 HTTP 头帮助保护您的应用程序 | | [jwt](https://github.com/gofiber/jwt) | JWT 返回一个 JSON Web Token\(JWT\) 身份验证中间件 | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth 中间件提供基于密钥的身份验证 | -| [redirect](https://github.com/gofiber/redirect) | 用于重定向请求的中间件 | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite 中间件根据提供的规则重写URL路径。它有助于向后兼容或者创建更清晰、更具描述性的链接 | | [storage](https://github.com/gofiber/storage) | 包含实现 Storage 接口的数据库驱动,它的设计旨在配合 fiber 的其他中间件来进行使用 | | [template](https://github.com/gofiber/template) | 该中间件包含 8 个模板引擎,可与 Fiber `v1.10.x` Go 1.13或更高版本一起使用 | | [websocket](https://github.com/gofiber/websocket) | 基于 Fasthttp WebSocket for Fiber 实现,支持使用 [Locals](https://docs.gofiber.io/api/ctx#locals) ! | diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index cb5cffc941..9e2acb9c53 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -636,6 +636,11 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | 連線階段中介模組。注意:這個中介模組有用到我們的 Storage 套件。 | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | 略過中介模組,會在條件成立時略過封裝過的處理常式。 | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | 為請求加上最長時限,並在逾時後轉送至錯誤處理常式 (ErrorHandler)。 | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth 中介模組提供以金鑰為基礎的認證模式。 | +| [redirect](https://github.com/gofiber/redirect) | 用來重新導向的中介模組。 | +| [rewrite](https://github.com/gofiber/rewrite) | 重寫 (Rewrite) 中介模組:根據提供規則重寫 URL 路徑,適合用來向後相容,或者是製作更乾淨且更好懂的連結。 | +| [adaptor](https://github.com/gofiber/adaptor) | 將 net/http 處理常式轉換至 Fiber 處理常式,或者是反著做。特別感謝 @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | 透過設定多種 HTTP 標頭,協助保護您應用程式的安全。 | ## 🧬 外掛中介模組 @@ -643,12 +648,7 @@ func main() { | 中介模組 | 描述 | | :------------------------------------------------ | :----------------------------------------------------------------------------------------------------- | -| [adaptor](https://github.com/gofiber/adaptor) | 將 net/http 處理常式轉換至 Fiber 處理常式,或者是反著做。特別感謝 @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | 透過設定多種 HTTP 標頭,協助保護您應用程式的安全。 | | [jwt](https://github.com/gofiber/jwt) | JWT 回傳 JSON Web Token \(JWT\) 認證中介模組。 | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth 中介模組提供以金鑰為基礎的認證模式。 | -| [redirect](https://github.com/gofiber/redirect) | 用來重新導向的中介模組。 | -| [rewrite](https://github.com/gofiber/rewrite) | 重寫 (Rewrite) 中介模組:根據提供規則重寫 URL 路徑,適合用來向後相容,或者是製作更乾淨且更好懂的連結。 | | [storage](https://github.com/gofiber/storage) | 已經做好,實作 Storage 介面的儲存區驅動模組,設計用來與各種 Fiber 中介模組搭配使用。 | | [template](https://github.com/gofiber/template) | 本套件包含 8 種樣板引擎,可以和 Fiber `v1.10.x` 一起使用。需要 Go 1.13 或更新版本。 | | [websocket](https://github.com/gofiber/websocket) | 適用於 Fiber,建基於 Fasthttp 的 WebSocket。支援本機空間 (Locals)! | diff --git a/docs/api/middleware/adaptor.md b/docs/api/middleware/adaptor.md new file mode 100644 index 0000000000..a033fdd733 --- /dev/null +++ b/docs/api/middleware/adaptor.md @@ -0,0 +1,135 @@ +--- +id: adaptor +title: Adaptor +--- + +Converter for net/http handlers to/from Fiber request handlers, special thanks to [@arsmn](https://github.com/arsmn)! + +## Signatures +| Name | Signature | Description +| :--- | :--- | :--- +| HTTPHandler | `HTTPHandler(h http.Handler) fiber.Handler` | http.Handler -> fiber.Handler +| HTTPHandlerFunc | `HTTPHandlerFunc(h http.HandlerFunc) fiber.Handler` | http.HandlerFunc -> fiber.Handler +| HTTPMiddleware | `HTTPHandlerFunc(mw func(http.Handler) http.Handler) fiber.Handler` | func(http.Handler) http.Handler -> fiber.Handler +| FiberHandler | `FiberHandler(h fiber.Handler) http.Handler` | fiber.Handler -> http.Handler +| FiberHandlerFunc | `FiberHandlerFunc(h fiber.Handler) http.HandlerFunc` | fiber.Handler -> http.HandlerFunc +| FiberApp | `FiberApp(app *fiber.App) http.HandlerFunc` | Fiber app -> http.HandlerFunc +| CopyContextToFiberContex | `CopyContextToFiberContext(context interface{}, requestContext *fasthttp.RequestCtx)` | context.Context -> fasthttp.RequestCtx + +## Examples + +### net/http to Fiber +```go +package main + +import ( + "fmt" + "net/http" + + "github.com/gofiber/v2/middleware/adaptor" + "github.com/gofiber/fiber/v2" +) + +func main() { + // New fiber app + app := fiber.New() + + // http.Handler -> fiber.Handler + app.Get("/", adaptor.HTTPHandler(handler(greet))) + + // http.HandlerFunc -> fiber.Handler + app.Get("/func", adaptor.HTTPHandlerFunc(greet)) + + // Listen on port 3000 + app.Listen(":3000") +} + +func handler(f http.HandlerFunc) http.Handler { + return http.HandlerFunc(f) +} + +func greet(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, "Hello World!") +} +``` + +### net/http middleware to Fiber +```go +package main + +import ( + "log" + "net/http" + + "github.com/gofiber/v2/middleware/adaptor" + "github.com/gofiber/fiber/v2" +) + +func main() { + // New fiber app + app := fiber.New() + + // http middleware -> fiber.Handler + app.Use(adaptor.HTTPMiddleware(logMiddleware)) + + // Listen on port 3000 + app.Listen(":3000") +} + +func logMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Println("log middleware") + next.ServeHTTP(w, r) + }) +} +``` + +### Fiber Handler to net/http +```go +package main + +import ( + "net/http" + + "github.com/gofiber/v2/middleware/adaptor" + "github.com/gofiber/fiber/v2" +) + +func main() { + // fiber.Handler -> http.Handler + http.Handle("/", adaptor.FiberHandler(greet)) + + // fiber.Handler -> http.HandlerFunc + http.HandleFunc("/func", adaptor.FiberHandlerFunc(greet)) + + // Listen on port 3000 + http.ListenAndServe(":3000", nil) +} + +func greet(c *fiber.Ctx) error { + return c.SendString("Hello World!") +} +``` + +### Fiber App to net/http +```go +package main + +import ( + "github.com/gofiber/v2/middleware/adaptor" + "github.com/gofiber/fiber/v2" + "net/http" +) +func main() { + app := fiber.New() + + app.Get("/greet", greet) + + // Listen on port 3000 + http.ListenAndServe(":3000", adaptor.FiberApp(app)) +} + +func greet(c *fiber.Ctx) error { + return c.SendString("Hello World!") +} +``` diff --git a/docs/api/middleware/filesystem.md b/docs/api/middleware/filesystem.md index 9bc3864cda..86bc585cfc 100644 --- a/docs/api/middleware/filesystem.md +++ b/docs/api/middleware/filesystem.md @@ -11,13 +11,6 @@ Filesystem middleware for [Fiber](https://github.com/gofiber/fiber) that enables **To handle paths with spaces (or other url encoded values) make sure to set `fiber.Config{ UnescapePath: true }`** ::: -## Table of Contents - -* [Signatures](filesystem.md#signatures) -* [Examples](filesystem.md#examples) -* [Config](filesystem.md#config) -* [Default Config](filesystem.md#default-config) - ## Signatures ```go diff --git a/docs/api/middleware/helmet.md b/docs/api/middleware/helmet.md new file mode 100644 index 0000000000..1392c5879b --- /dev/null +++ b/docs/api/middleware/helmet.md @@ -0,0 +1,138 @@ +--- +id: helmet +title: Helmet +--- + +Helmet middleware helps secure your apps by setting various HTTP headers. + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/helmet" +) + +func main() { + app := fiber.New() + + app.Use(helmet.New()) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Welcome!") + }) + + app.Listen(":3000") +} +``` + +**Test:** + +```curl +curl -I http://localhost:3000 +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip middleware. + // Optional. Default: nil + Next func(*fiber.Ctx) bool + + // XSSProtection + // Optional. Default value "0". + XSSProtection string + + // ContentTypeNosniff + // Optional. Default value "nosniff". + ContentTypeNosniff string + + // XFrameOptions + // Optional. Default value "SAMEORIGIN". + // Possible values: "SAMEORIGIN", "DENY", "ALLOW-FROM uri" + XFrameOptions string + + // HSTSMaxAge + // Optional. Default value 0. + HSTSMaxAge int + + // HSTSExcludeSubdomains + // Optional. Default value false. + HSTSExcludeSubdomains bool + + // ContentSecurityPolicy + // Optional. Default value "". + ContentSecurityPolicy string + + // CSPReportOnly + // Optional. Default value false. + CSPReportOnly bool + + // HSTSPreloadEnabled + // Optional. Default value false. + HSTSPreloadEnabled bool + + // ReferrerPolicy + // Optional. Default value "ReferrerPolicy". + ReferrerPolicy string + + // Permissions-Policy + // Optional. Default value "". + PermissionPolicy string + + // Cross-Origin-Embedder-Policy + // Optional. Default value "require-corp". + CrossOriginEmbedderPolicy string + + // Cross-Origin-Opener-Policy + // Optional. Default value "same-origin". + CrossOriginOpenerPolicy string + + // Cross-Origin-Resource-Policy + // Optional. Default value "same-origin". + CrossOriginResourcePolicy string + + // Origin-Agent-Cluster + // Optional. Default value "?1". + OriginAgentCluster string + + // X-DNS-Prefetch-Control + // Optional. Default value "off". + XDNSPrefetchControl string + + // X-Download-Options + // Optional. Default value "noopen". + XDownloadOptions string + + // X-Permitted-Cross-Domain-Policies + // Optional. Default value "none". + XPermittedCrossDomain string +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + XSSProtection: "0", + ContentTypeNosniff: "nosniff", + XFrameOptions: "SAMEORIGIN", + ReferrerPolicy: "no-referrer", + CrossOriginEmbedderPolicy: "require-corp", + CrossOriginOpenerPolicy: "same-origin", + CrossOriginResourcePolicy: "same-origin", + OriginAgentCluster: "?1", + XDNSPrefetchControl: "off", + XDownloadOptions: "noopen", + XPermittedCrossDomain: "none", +} +``` diff --git a/docs/api/middleware/keyauth.md b/docs/api/middleware/keyauth.md new file mode 100644 index 0000000000..786d16fd3d --- /dev/null +++ b/docs/api/middleware/keyauth.md @@ -0,0 +1,272 @@ +--- +id: keyauth +title: Keyauth +--- + +Key auth middleware provides a key based authentication. + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +```go +package main + +import ( + "crypto/sha256" + "crypto/subtle" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/keyauth" +) + +var ( + apiKey = "correct horse battery staple" +) + +func validateAPIKey(c *fiber.Ctx, key string) (bool, error) { + hashedAPIKey := sha256.Sum256([]byte(apiKey)) + hashedKey := sha256.Sum256([]byte(key)) + + if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 { + return true, nil + } + return false, keyauth.ErrMissingOrMalformedAPIKey +} + +func main() { + app := fiber.New() + + // note that the keyauth middleware needs to be defined before the routes are defined! + app.Use(keyauth.New(keyauth.Config{ + KeyLookup: "cookie:access_token", + Validator: validateAPIKey, + })) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Successfully authenticated!") + }) + + app.Listen(":3000") +} +``` + +**Test:** + +```bash +# No api-key specified -> 400 missing +curl http://localhost:3000 +#> missing or malformed API Key + +curl --cookie "access_token=correct horse battery staple" http://localhost:3000 +#> Successfully authenticated! + +curl --cookie "access_token=Clearly A Wrong Key" http://localhost:3000 +#> missing or malformed API Key +``` + +For a more detailed example, see also the [`github.com/gofiber/recipes`](https://github.com/gofiber/recipes) repository and specifically the `fiber-envoy-extauthz` repository and the [`keyauth example`](https://github.com/gofiber/recipes/blob/master/fiber-envoy-extauthz/authz/main.go) code. + + +### Authenticate only certain endpoints + +If you want to authenticate only certain endpoints, you can use the `Config` of keyauth and apply a filter function (eg. `authFilter`) like so + +```go +package main + +import ( + "crypto/sha256" + "crypto/subtle" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/keyauth" + "regexp" + "strings" +) + +var ( + apiKey = "correct horse battery staple" + protectedURLs = []*regexp.Regexp{ + regexp.MustCompile("^/authenticated$"), + regexp.MustCompile("^/auth2$"), + } +) + +func validateAPIKey(c *fiber.Ctx, key string) (bool, error) { + hashedAPIKey := sha256.Sum256([]byte(apiKey)) + hashedKey := sha256.Sum256([]byte(key)) + + if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 { + return true, nil + } + return false, keyauth.ErrMissingOrMalformedAPIKey +} + +func authFilter(c *fiber.Ctx) bool { + originalURL := strings.ToLower(c.OriginalURL()) + + for _, pattern := range protectedURLs { + if pattern.MatchString(originalURL) { + return false + } + } + return true +} + +func main() { + app := fiber.New() + + app.Use(keyauth.New(keyauth.Config{ + Next: authFilter, + KeyLookup: "cookie:access_token", + Validator: validateAPIKey, + })) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Welcome") + }) + app.Get("/authenticated", func(c *fiber.Ctx) error { + return c.SendString("Successfully authenticated!") + }) + app.Get("/auth2", func(c *fiber.Ctx) error { + return c.SendString("Successfully authenticated 2!") + }) + + app.Listen(":3000") +} +``` + +Which results in this + +```bash +# / does not need to be authenticated +curl http://localhost:3000 +#> Welcome + +# /authenticated needs to be authenticated +curl --cookie "access_token=correct horse battery staple" http://localhost:3000/authenticated +#> Successfully authenticated! + +# /auth2 needs to be authenticated too +curl --cookie "access_token=correct horse battery staple" http://localhost:3000/auth2 +#> Successfully authenticated 2! +``` + +### Specifying middleware in the handler + +```go +package main + +import ( + "crypto/sha256" + "crypto/subtle" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/keyauth" +) + +const ( + apiKey = "my-super-secret-key" +) + +func main() { + app := fiber.New() + + authMiddleware := keyauth.New(keyauth.Config{ + Validator: func(c *fiber.Ctx, key string) (bool, error) { + hashedAPIKey := sha256.Sum256([]byte(apiKey)) + hashedKey := sha256.Sum256([]byte(key)) + + if subtle.ConstantTimeCompare(hashedAPIKey[:], hashedKey[:]) == 1 { + return true, nil + } + return false, keyauth.ErrMissingOrMalformedAPIKey + }, + }) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Welcome") + }) + + app.Get("/allowed", authMiddleware, func(c *fiber.Ctx) error { + return c.SendString("Successfully authenticated!") + }) + + app.Listen(":3000") +} +``` + +Which results in this + +```bash +# / does not need to be authenticated +curl http://localhost:3000 +#> Welcome + +# /allowed needs to be authenticated too +curl --header "Authorization: Bearer my-super-secret-key" http://localhost:3000/allowed +#> Successfully authenticated! +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip middleware. + // Optional. Default: nil + Next func(*fiber.Ctx) bool + + // SuccessHandler defines a function which is executed for a valid key. + // Optional. Default: nil + SuccessHandler fiber.Handler + + // ErrorHandler defines a function which is executed for an invalid key. + // It may be used to define a custom error. + // Optional. Default: 401 Invalid or expired key + ErrorHandler fiber.ErrorHandler + + // KeyLookup is a string in the form of ":" that is used + // to extract key from the request. + // Optional. Default value "header:Authorization". + // Possible values: + // - "header:" + // - "query:" + // - "form:" + // - "param:" + // - "cookie:" + KeyLookup string + + // AuthScheme to be used in the Authorization header. + // Optional. Default value "Bearer". + AuthScheme string + + // Validator is a function to validate key. + Validator func(*fiber.Ctx, string) (bool, error) + + // Context key to store the bearertoken from the token into context. + // Optional. Default: "token". + ContextKey string +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + SuccessHandler: func(c *fiber.Ctx) error { + return c.Next() + }, + ErrorHandler: func(c *fiber.Ctx, err error) error { + if err == ErrMissingOrMalformedAPIKey { + return c.Status(fiber.StatusUnauthorized).SendString(err.Error()) + } + return c.Status(fiber.StatusUnauthorized).SendString("Invalid or expired API Key") + }, + KeyLookup: "header:" + fiber.HeaderAuthorization, + AuthScheme: "Bearer", + ContextKey: "token", +} +``` diff --git a/docs/api/middleware/pprof.md b/docs/api/middleware/pprof.md index c56971e566..e716a4f78d 100644 --- a/docs/api/middleware/pprof.md +++ b/docs/api/middleware/pprof.md @@ -5,9 +5,6 @@ title: Pprof Pprof middleware for [Fiber](https://github.com/gofiber/fiber) that serves via its HTTP server runtime profiling data in the format expected by the pprof visualization tool. The package is typically only imported for the side effect of registering its HTTP handlers. The handled paths all begin with /debug/pprof/. -* [Signatures](pprof.md#signatures) -* [Examples](pprof.md#examples) - ## Signatures ```go diff --git a/docs/api/middleware/redirect.md b/docs/api/middleware/redirect.md new file mode 100644 index 0000000000..ebb47c844b --- /dev/null +++ b/docs/api/middleware/redirect.md @@ -0,0 +1,86 @@ +--- +id: redirect +title: Redirect +--- + +Redirection middleware for Fiber. + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/redirect" +) + +func main() { + app := fiber.New() + + app.Use(redirect.New(redirect.Config{ + Rules: map[string]string{ + "/old": "/new", + "/old/*": "/new/$1", + }, + StatusCode: 301, + })) + + app.Get("/new", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + app.Get("/new/*", func(c *fiber.Ctx) error { + return c.SendString("Wildcard: " + c.Params("*")) + }) + + app.Listen(":3000") +} +``` + +**Test:** + +```curl +curl http://localhost:3000/old +curl http://localhost:3000/old/hello +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // Filter defines a function to skip middleware. + // Optional. Default: nil + Next func(*fiber.Ctx) bool + + // Rules defines the URL path rewrite rules. The values captured in asterisk can be + // retrieved by index e.g. $1, $2 and so on. + // Required. Example: + // "/old": "/new", + // "/api/*": "/$1", + // "/js/*": "/public/javascripts/$1", + // "/users/*/orders/*": "/user/$1/order/$2", + Rules map[string]string + + // The status code when redirecting + // This is ignored if Redirect is disabled + // Optional. Default: 302 (fiber.StatusFound) + StatusCode int + + rulesRegex map[*regexp.Regexp]string +} +``` + +## Default Config + +```go +var ConfigDefault = Config{ + StatusCode: fiber.StatusFound, +} +``` diff --git a/docs/api/middleware/rewrite.md b/docs/api/middleware/rewrite.md new file mode 100644 index 0000000000..7111cbdde5 --- /dev/null +++ b/docs/api/middleware/rewrite.md @@ -0,0 +1,51 @@ +--- +id: rewrite +title: Rewrite +--- + +Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. + + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +### Examples +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/rewrite" +) + +func main() { + app := fiber.New() + + app.Use(rewrite.New(rewrite.Config{ + Rules: map[string]string{ + "/old": "/new", + "/old/*": "/new/$1", + }, + })) + + app.Get("/new", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + app.Get("/new/*", func(c *fiber.Ctx) error { + return c.SendString("Wildcard: " + c.Params("*")) + }) + + app.Listen(":3000") +} + +``` + +**Test:** + +```curl +curl http://localhost:3000/old +curl http://localhost:3000/old/hello +``` diff --git a/middleware/adaptor/adaptor.go b/middleware/adaptor/adaptor.go new file mode 100644 index 0000000000..dac0973edc --- /dev/null +++ b/middleware/adaptor/adaptor.go @@ -0,0 +1,163 @@ +package adaptor + +import ( + "io" + "net" + "net/http" + "reflect" + "unsafe" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" + "github.com/valyala/fasthttp/fasthttpadaptor" +) + +// HTTPHandlerFunc wraps net/http handler func to fiber handler +func HTTPHandlerFunc(h http.HandlerFunc) fiber.Handler { + return HTTPHandler(h) +} + +// HTTPHandler wraps net/http handler to fiber handler +func HTTPHandler(h http.Handler) fiber.Handler { + return func(c *fiber.Ctx) error { + handler := fasthttpadaptor.NewFastHTTPHandler(h) + handler(c.Context()) + return nil + } +} + +// CopyContextToFiberContext copies the values of context.Context to a fasthttp.RequestCtx +func CopyContextToFiberContext(context interface{}, requestContext *fasthttp.RequestCtx) { + contextValues := reflect.ValueOf(context).Elem() + contextKeys := reflect.TypeOf(context).Elem() + if contextKeys.Kind() == reflect.Struct { + var lastKey interface{} + for i := 0; i < contextValues.NumField(); i++ { + reflectValue := contextValues.Field(i) + /* #nosec */ + reflectValue = reflect.NewAt(reflectValue.Type(), unsafe.Pointer(reflectValue.UnsafeAddr())).Elem() + + reflectField := contextKeys.Field(i) + + if reflectField.Name == "noCopy" { + break + } else if reflectField.Name == "Context" { + CopyContextToFiberContext(reflectValue.Interface(), requestContext) + } else if reflectField.Name == "key" { + lastKey = reflectValue.Interface() + } else if lastKey != nil && reflectField.Name == "val" { + requestContext.SetUserValue(lastKey, reflectValue.Interface()) + } else { + lastKey = nil + } + } + } +} + +// HTTPMiddleware wraps net/http middleware to fiber middleware +func HTTPMiddleware(mw func(http.Handler) http.Handler) fiber.Handler { + return func(c *fiber.Ctx) error { + var next bool + nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + next = true + // Convert again in case request may modify by middleware + c.Request().Header.SetMethod(r.Method) + c.Request().SetRequestURI(r.RequestURI) + c.Request().SetHost(r.Host) + for key, val := range r.Header { + for _, v := range val { + c.Request().Header.Set(key, v) + } + } + CopyContextToFiberContext(r.Context(), c.Context()) + }) + + if err := HTTPHandler(mw(nextHandler))(c); err != nil { + return err + } + + if next { + return c.Next() + } + return nil + } +} + +// FiberHandler wraps fiber handler to net/http handler +func FiberHandler(h fiber.Handler) http.Handler { + return FiberHandlerFunc(h) +} + +// FiberHandlerFunc wraps fiber handler to net/http handler func +func FiberHandlerFunc(h fiber.Handler) http.HandlerFunc { + return handlerFunc(fiber.New(), h) +} + +// FiberApp wraps fiber app to net/http handler func +func FiberApp(app *fiber.App) http.HandlerFunc { + return handlerFunc(app) +} + +func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + // New fasthttp request + req := fasthttp.AcquireRequest() + defer fasthttp.ReleaseRequest(req) + // Convert net/http -> fasthttp request + if r.Body != nil { + body, err := io.ReadAll(r.Body) + if err != nil { + http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError) + return + } + + req.Header.SetContentLength(len(body)) + _, err = req.BodyWriter().Write(body) + if err != nil { + http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError) + return + } + } + req.Header.SetMethod(r.Method) + req.SetRequestURI(r.RequestURI) + req.SetHost(r.Host) + for key, val := range r.Header { + for _, v := range val { + req.Header.Set(key, v) + } + } + if _, _, err := net.SplitHostPort(r.RemoteAddr); err != nil && err.(*net.AddrError).Err == "missing port in address" { //nolint:errorlint, forcetypeassert // overlinting + r.RemoteAddr = net.JoinHostPort(r.RemoteAddr, "80") + } + remoteAddr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr) + if err != nil { + http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError) + return + } + + // New fasthttp Ctx + var fctx fasthttp.RequestCtx + fctx.Init(req, remoteAddr, nil) + if len(h) > 0 { + // New fiber Ctx + ctx := app.AcquireCtx(&fctx) + defer app.ReleaseCtx(ctx) + // Execute fiber Ctx + err := h[0](ctx) + if err != nil { + _ = app.Config().ErrorHandler(ctx, err) //nolint:errcheck // not needed + } + } else { + // Execute fasthttp Ctx though app.Handler + app.Handler()(&fctx) + } + + // Convert fasthttp Ctx > net/http + fctx.Response.Header.VisitAll(func(k, v []byte) { + w.Header().Add(string(k), string(v)) + }) + w.WriteHeader(fctx.Response.StatusCode()) + _, _ = w.Write(fctx.Response.Body()) //nolint:errcheck // not needed + } +} diff --git a/middleware/adaptor/adaptor_test.go b/middleware/adaptor/adaptor_test.go new file mode 100644 index 0000000000..fd413a02d7 --- /dev/null +++ b/middleware/adaptor/adaptor_test.go @@ -0,0 +1,462 @@ +//nolint:bodyclose, contextcheck, revive // Much easier to just ignore memory leaks in tests +package adaptor + +import ( + "context" + "fmt" + "io" + "net" + "net/http" + "net/url" + "reflect" + "testing" + + "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp" +) + +func Test_HTTPHandler(t *testing.T) { + expectedMethod := fiber.MethodPost + expectedProto := "HTTP/1.1" + expectedProtoMajor := 1 + expectedProtoMinor := 1 + expectedRequestURI := "/foo/bar?baz=123" + expectedBody := "body 123 foo bar baz" + expectedContentLength := len(expectedBody) + expectedHost := "foobar.com" + expectedRemoteAddr := "1.2.3.4:6789" + expectedHeader := map[string]string{ + "Foo-Bar": "baz", + "Abc": "defg", + "XXX-Remote-Addr": "123.43.4543.345", + } + expectedURL, err := url.ParseRequestURI(expectedRequestURI) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + expectedContextKey := "contextKey" + expectedContextValue := "contextValue" + + callsCount := 0 + nethttpH := func(w http.ResponseWriter, r *http.Request) { + callsCount++ + if r.Method != expectedMethod { + t.Fatalf("unexpected method %q. Expecting %q", r.Method, expectedMethod) + } + if r.Proto != expectedProto { + t.Fatalf("unexpected proto %q. Expecting %q", r.Proto, expectedProto) + } + if r.ProtoMajor != expectedProtoMajor { + t.Fatalf("unexpected protoMajor %d. Expecting %d", r.ProtoMajor, expectedProtoMajor) + } + if r.ProtoMinor != expectedProtoMinor { + t.Fatalf("unexpected protoMinor %d. Expecting %d", r.ProtoMinor, expectedProtoMinor) + } + if r.RequestURI != expectedRequestURI { + t.Fatalf("unexpected requestURI %q. Expecting %q", r.RequestURI, expectedRequestURI) + } + if r.ContentLength != int64(expectedContentLength) { + t.Fatalf("unexpected contentLength %d. Expecting %d", r.ContentLength, expectedContentLength) + } + if len(r.TransferEncoding) != 0 { + t.Fatalf("unexpected transferEncoding %q. Expecting []", r.TransferEncoding) + } + if r.Host != expectedHost { + t.Fatalf("unexpected host %q. Expecting %q", r.Host, expectedHost) + } + if r.RemoteAddr != expectedRemoteAddr { + t.Fatalf("unexpected remoteAddr %q. Expecting %q", r.RemoteAddr, expectedRemoteAddr) + } + body, err := io.ReadAll(r.Body) + if err != nil { + t.Fatalf("unexpected error when reading request body: %s", err) + } + if string(body) != expectedBody { + t.Fatalf("unexpected body %q. Expecting %q", body, expectedBody) + } + if !reflect.DeepEqual(r.URL, expectedURL) { + t.Fatalf("unexpected URL: %#v. Expecting %#v", r.URL, expectedURL) + } + if r.Context().Value(expectedContextKey) != expectedContextValue { + t.Fatalf("unexpected context value for key %q. Expecting %q", expectedContextKey, expectedContextValue) + } + + for k, expectedV := range expectedHeader { + v := r.Header.Get(k) + if v != expectedV { + t.Fatalf("unexpected header value %q for key %q. Expecting %q", v, k, expectedV) + } + } + + w.Header().Set("Header1", "value1") + w.Header().Set("Header2", "value2") + w.WriteHeader(http.StatusBadRequest) + fmt.Fprintf(w, "request body is %q", body) + } + fiberH := HTTPHandlerFunc(http.HandlerFunc(nethttpH)) + fiberH = setFiberContextValueMiddleware(fiberH, expectedContextKey, expectedContextValue) + + var fctx fasthttp.RequestCtx + var req fasthttp.Request + + req.Header.SetMethod(expectedMethod) + req.SetRequestURI(expectedRequestURI) + req.Header.SetHost(expectedHost) + req.BodyWriter().Write([]byte(expectedBody)) //nolint:errcheck, gosec // not needed + for k, v := range expectedHeader { + req.Header.Set(k, v) + } + + remoteAddr, err := net.ResolveTCPAddr("tcp", expectedRemoteAddr) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + fctx.Init(&req, remoteAddr, nil) + app := fiber.New() + ctx := app.AcquireCtx(&fctx) + defer app.ReleaseCtx(ctx) + + err = fiberH(ctx) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if callsCount != 1 { + t.Fatalf("unexpected callsCount: %d. Expecting 1", callsCount) + } + + resp := &fctx.Response + if resp.StatusCode() != fiber.StatusBadRequest { + t.Fatalf("unexpected statusCode: %d. Expecting %d", resp.StatusCode(), fiber.StatusBadRequest) + } + if string(resp.Header.Peek("Header1")) != "value1" { + t.Fatalf("unexpected header value: %q. Expecting %q", resp.Header.Peek("Header1"), "value1") + } + if string(resp.Header.Peek("Header2")) != "value2" { + t.Fatalf("unexpected header value: %q. Expecting %q", resp.Header.Peek("Header2"), "value2") + } + expectedResponseBody := fmt.Sprintf("request body is %q", expectedBody) + if string(resp.Body()) != expectedResponseBody { + t.Fatalf("unexpected response body %q. Expecting %q", resp.Body(), expectedResponseBody) + } +} + +type contextKey string + +func (c contextKey) String() string { + return "test-" + string(c) +} + +var ( + TestContextKey = contextKey("TestContextKey") + TestContextSecondKey = contextKey("TestContextSecondKey") +) + +func Test_HTTPMiddleware(t *testing.T) { + tests := []struct { + name string + url string + method string + statusCode int + }{ + { + name: "Should return 200", + url: "/", + method: "POST", + statusCode: 200, + }, + { + name: "Should return 405", + url: "/", + method: "GET", + statusCode: 405, + }, + { + name: "Should return 400", + url: "/unknown", + method: "POST", + statusCode: 404, + }, + } + + 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 + } + r = r.WithContext(context.WithValue(r.Context(), TestContextKey, "okay")) + r = r.WithContext(context.WithValue(r.Context(), TestContextSecondKey, "not_okay")) + r = r.WithContext(context.WithValue(r.Context(), TestContextSecondKey, "okay")) + + next.ServeHTTP(w, r) + }) + } + + app := fiber.New() + app.Use(HTTPMiddleware(nethttpMW)) + app.Post("/", func(c *fiber.Ctx) error { + value := c.Context().Value(TestContextKey) + val, ok := value.(string) + if !ok { + t.Error("unexpected error on type-assertion") + } + if value != nil { + c.Set("context_okay", val) + } + value = c.Context().Value(TestContextSecondKey) + if value != nil { + val, ok := value.(string) + if !ok { + t.Error("unexpected error on type-assertion") + } + c.Set("context_second_okay", val) + } + return c.SendStatus(fiber.StatusOK) + }) + + for _, tt := range tests { + req, err := http.NewRequestWithContext(context.Background(), tt.method, tt.url, nil) + if err != nil { + t.Fatalf(`%s: %s`, t.Name(), err) + } + resp, err := app.Test(req) + if err != nil { + t.Fatalf(`%s: %s`, t.Name(), err) + } + if resp.StatusCode != tt.statusCode { + t.Fatalf(`%s: StatusCode: got %v - expected %v`, t.Name(), resp.StatusCode, tt.statusCode) + } + } + + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodPost, "/", nil) + if err != nil { + t.Fatalf(`%s: %s`, t.Name(), err) + } + resp, err := app.Test(req) + if err != nil { + t.Fatalf(`%s: %s`, t.Name(), err) + } + if resp.Header.Get("context_okay") != "okay" { + t.Fatalf(`%s: Header context_okay: got %v - expected %v`, t.Name(), resp.Header.Get("context_okay"), "okay") + } + if resp.Header.Get("context_second_okay") != "okay" { + t.Fatalf(`%s: Header context_second_okay: got %v - expected %v`, t.Name(), resp.Header.Get("context_second_okay"), "okay") + } +} + +func Test_FiberHandler(t *testing.T) { + testFiberToHandlerFunc(t, false) +} + +func Test_FiberApp(t *testing.T) { + testFiberToHandlerFunc(t, false, fiber.New()) +} + +func Test_FiberHandlerDefaultPort(t *testing.T) { + testFiberToHandlerFunc(t, true) +} + +func Test_FiberAppDefaultPort(t *testing.T) { + testFiberToHandlerFunc(t, true, fiber.New()) +} + +func testFiberToHandlerFunc(t *testing.T, checkDefaultPort bool, app ...*fiber.App) { + t.Helper() + + expectedMethod := fiber.MethodPost + expectedRequestURI := "/foo/bar?baz=123" + expectedBody := "body 123 foo bar baz" + expectedContentLength := len(expectedBody) + expectedHost := "foobar.com" + expectedRemoteAddr := "1.2.3.4:6789" + if checkDefaultPort { + expectedRemoteAddr = "1.2.3.4:80" + } + expectedHeader := map[string]string{ + "Foo-Bar": "baz", + "Abc": "defg", + "XXX-Remote-Addr": "123.43.4543.345", + } + expectedURL, err := url.ParseRequestURI(expectedRequestURI) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + callsCount := 0 + fiberH := func(c *fiber.Ctx) error { + callsCount++ + if c.Method() != expectedMethod { + t.Fatalf("unexpected method %q. Expecting %q", c.Method(), expectedMethod) + } + if string(c.Context().RequestURI()) != expectedRequestURI { + t.Fatalf("unexpected requestURI %q. Expecting %q", string(c.Context().RequestURI()), expectedRequestURI) + } + contentLength := c.Context().Request.Header.ContentLength() + if contentLength != expectedContentLength { + t.Fatalf("unexpected contentLength %d. Expecting %d", contentLength, expectedContentLength) + } + if c.Hostname() != expectedHost { + t.Fatalf("unexpected host %q. Expecting %q", c.Hostname(), expectedHost) + } + remoteAddr := c.Context().RemoteAddr().String() + if remoteAddr != expectedRemoteAddr { + t.Fatalf("unexpected remoteAddr %q. Expecting %q", remoteAddr, expectedRemoteAddr) + } + body := string(c.Body()) + if body != expectedBody { + t.Fatalf("unexpected body %q. Expecting %q", body, expectedBody) + } + if c.OriginalURL() != expectedURL.String() { + t.Fatalf("unexpected URL: %#v. Expecting %#v", c.OriginalURL(), expectedURL) + } + + for k, expectedV := range expectedHeader { + v := c.Get(k) + if v != expectedV { + t.Fatalf("unexpected header value %q for key %q. Expecting %q", v, k, expectedV) + } + } + + c.Set("Header1", "value1") + c.Set("Header2", "value2") + c.Status(fiber.StatusBadRequest) + _, err := c.Write([]byte(fmt.Sprintf("request body is %q", body))) + return err + } + + var handlerFunc http.HandlerFunc + if len(app) > 0 { + app[0].Post("/foo/bar", fiberH) + handlerFunc = FiberApp(app[0]) + } else { + handlerFunc = FiberHandlerFunc(fiberH) + } + + var r http.Request + + r.Method = expectedMethod + r.Body = &netHTTPBody{[]byte(expectedBody)} + r.RequestURI = expectedRequestURI + r.ContentLength = int64(expectedContentLength) + r.Host = expectedHost + r.RemoteAddr = expectedRemoteAddr + if checkDefaultPort { + r.RemoteAddr = "1.2.3.4" + } + + hdr := make(http.Header) + for k, v := range expectedHeader { + hdr.Set(k, v) + } + r.Header = hdr + + var w netHTTPResponseWriter + handlerFunc.ServeHTTP(&w, &r) + + if w.StatusCode() != http.StatusBadRequest { + t.Fatalf("unexpected statusCode: %d. Expecting %d", w.StatusCode(), http.StatusBadRequest) + } + if w.Header().Get("Header1") != "value1" { + t.Fatalf("unexpected header value: %q. Expecting %q", w.Header().Get("Header1"), "value1") + } + if w.Header().Get("Header2") != "value2" { + t.Fatalf("unexpected header value: %q. Expecting %q", w.Header().Get("Header2"), "value2") + } + expectedResponseBody := fmt.Sprintf("request body is %q", expectedBody) + if string(w.body) != expectedResponseBody { + t.Fatalf("unexpected response body %q. Expecting %q", string(w.body), expectedResponseBody) + } +} + +func setFiberContextValueMiddleware(next fiber.Handler, key string, value interface{}) fiber.Handler { + return func(c *fiber.Ctx) error { + c.Locals(key, value) + return next(c) + } +} + +func Test_FiberHandler_RequestNilBody(t *testing.T) { + expectedMethod := fiber.MethodGet + expectedRequestURI := "/foo/bar" + expectedContentLength := 0 + + callsCount := 0 + fiberH := func(c *fiber.Ctx) error { + callsCount++ + if c.Method() != expectedMethod { + t.Fatalf("unexpected method %q. Expecting %q", c.Method(), expectedMethod) + } + if string(c.Request().RequestURI()) != expectedRequestURI { + t.Fatalf("unexpected requestURI %q. Expecting %q", string(c.Request().RequestURI()), expectedRequestURI) + } + contentLength := c.Request().Header.ContentLength() + if contentLength != expectedContentLength { + t.Fatalf("unexpected contentLength %d. Expecting %d", contentLength, expectedContentLength) + } + + _, err := c.Write([]byte("request body is nil")) + return err + } + nethttpH := FiberHandler(fiberH) + + var r http.Request + + r.Method = expectedMethod + r.RequestURI = expectedRequestURI + + var w netHTTPResponseWriter + nethttpH.ServeHTTP(&w, &r) + + expectedResponseBody := "request body is nil" + if string(w.body) != expectedResponseBody { + t.Fatalf("unexpected response body %q. Expecting %q", string(w.body), expectedResponseBody) + } +} + +type netHTTPBody struct { + b []byte +} + +func (r *netHTTPBody) Read(p []byte) (int, error) { + if len(r.b) == 0 { + return 0, io.EOF + } + n := copy(p, r.b) + r.b = r.b[n:] + return n, nil +} + +func (r *netHTTPBody) Close() error { + r.b = r.b[:0] + return nil +} + +type netHTTPResponseWriter struct { + statusCode int + h http.Header + body []byte +} + +func (w *netHTTPResponseWriter) StatusCode() int { + if w.statusCode == 0 { + return http.StatusOK + } + return w.statusCode +} + +func (w *netHTTPResponseWriter) Header() http.Header { + if w.h == nil { + w.h = make(http.Header) + } + return w.h +} + +func (w *netHTTPResponseWriter) WriteHeader(statusCode int) { + w.statusCode = statusCode +} + +func (w *netHTTPResponseWriter) Write(p []byte) (int, error) { + w.body = append(w.body, p...) + return len(p), nil +} diff --git a/middleware/helmet/config.go b/middleware/helmet/config.go new file mode 100644 index 0000000000..49c059e094 --- /dev/null +++ b/middleware/helmet/config.go @@ -0,0 +1,154 @@ +package helmet + +import ( + "github.com/gofiber/fiber/v2" +) + +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip middleware. + // Optional. Default: nil + Next func(*fiber.Ctx) bool + + // XSSProtection + // Optional. Default value "0". + XSSProtection string + + // ContentTypeNosniff + // Optional. Default value "nosniff". + ContentTypeNosniff string + + // XFrameOptions + // Optional. Default value "SAMEORIGIN". + // Possible values: "SAMEORIGIN", "DENY", "ALLOW-FROM uri" + XFrameOptions string + + // HSTSMaxAge + // Optional. Default value 0. + HSTSMaxAge int + + // HSTSExcludeSubdomains + // Optional. Default value false. + HSTSExcludeSubdomains bool + + // ContentSecurityPolicy + // Optional. Default value "". + ContentSecurityPolicy string + + // CSPReportOnly + // Optional. Default value false. + CSPReportOnly bool + + // HSTSPreloadEnabled + // Optional. Default value false. + HSTSPreloadEnabled bool + + // ReferrerPolicy + // Optional. Default value "ReferrerPolicy". + ReferrerPolicy string + + // Permissions-Policy + // Optional. Default value "". + PermissionPolicy string + + // Cross-Origin-Embedder-Policy + // Optional. Default value "require-corp". + CrossOriginEmbedderPolicy string + + // Cross-Origin-Opener-Policy + // Optional. Default value "same-origin". + CrossOriginOpenerPolicy string + + // Cross-Origin-Resource-Policy + // Optional. Default value "same-origin". + CrossOriginResourcePolicy string + + // Origin-Agent-Cluster + // Optional. Default value "?1". + OriginAgentCluster string + + // X-DNS-Prefetch-Control + // Optional. Default value "off". + XDNSPrefetchControl string + + // X-Download-Options + // Optional. Default value "noopen". + XDownloadOptions string + + // X-Permitted-Cross-Domain-Policies + // Optional. Default value "none". + XPermittedCrossDomain string +} + +// ConfigDefault is the default config +var ConfigDefault = Config{ + XSSProtection: "0", + ContentTypeNosniff: "nosniff", + XFrameOptions: "SAMEORIGIN", + ReferrerPolicy: "no-referrer", + CrossOriginEmbedderPolicy: "require-corp", + CrossOriginOpenerPolicy: "same-origin", + CrossOriginResourcePolicy: "same-origin", + OriginAgentCluster: "?1", + XDNSPrefetchControl: "off", + XDownloadOptions: "noopen", + XPermittedCrossDomain: "none", +} + +// Helper function to set default values +func configDefault(config ...Config) Config { + // Return default config if nothing provided + if len(config) < 1 { + return ConfigDefault + } + + // Override default config + cfg := config[0] + + // Set default values + if cfg.XSSProtection == "" { + cfg.XSSProtection = ConfigDefault.XSSProtection + } + + if cfg.ContentTypeNosniff == "" { + cfg.ContentTypeNosniff = ConfigDefault.ContentTypeNosniff + } + + if cfg.XFrameOptions == "" { + cfg.XFrameOptions = ConfigDefault.XFrameOptions + } + + if cfg.ReferrerPolicy == "" { + cfg.ReferrerPolicy = ConfigDefault.ReferrerPolicy + } + + if cfg.CrossOriginEmbedderPolicy == "" { + cfg.CrossOriginEmbedderPolicy = ConfigDefault.CrossOriginEmbedderPolicy + } + + if cfg.CrossOriginOpenerPolicy == "" { + cfg.CrossOriginOpenerPolicy = ConfigDefault.CrossOriginOpenerPolicy + } + + if cfg.CrossOriginResourcePolicy == "" { + cfg.CrossOriginResourcePolicy = ConfigDefault.CrossOriginResourcePolicy + } + + if cfg.OriginAgentCluster == "" { + cfg.OriginAgentCluster = ConfigDefault.OriginAgentCluster + } + + if cfg.XDNSPrefetchControl == "" { + cfg.XDNSPrefetchControl = ConfigDefault.XDNSPrefetchControl + } + + if cfg.XDownloadOptions == "" { + cfg.XDownloadOptions = ConfigDefault.XDownloadOptions + } + + if cfg.XPermittedCrossDomain == "" { + cfg.XPermittedCrossDomain = ConfigDefault.XPermittedCrossDomain + } + + return cfg +} diff --git a/middleware/helmet/helmet.go b/middleware/helmet/helmet.go new file mode 100644 index 0000000000..993036ebbd --- /dev/null +++ b/middleware/helmet/helmet.go @@ -0,0 +1,94 @@ +package helmet + +import ( + "fmt" + + "github.com/gofiber/fiber/v2" +) + +// New creates a new middleware handler +func New(config ...Config) fiber.Handler { + // Init config + cfg := configDefault(config...) + + // Return middleware handler + return func(c *fiber.Ctx) error { + // Next request to skip middleware + if cfg.Next != nil && cfg.Next(c) { + return c.Next() + } + + // Set headers + if cfg.XSSProtection != "" { + c.Set(fiber.HeaderXXSSProtection, cfg.XSSProtection) + } + + if cfg.ContentTypeNosniff != "" { + c.Set(fiber.HeaderXContentTypeOptions, cfg.ContentTypeNosniff) + } + + if cfg.XFrameOptions != "" { + c.Set(fiber.HeaderXFrameOptions, cfg.XFrameOptions) + } + + if cfg.CrossOriginEmbedderPolicy != "" { + c.Set("Cross-Origin-Embedder-Policy", cfg.CrossOriginEmbedderPolicy) + } + + if cfg.CrossOriginOpenerPolicy != "" { + c.Set("Cross-Origin-Opener-Policy", cfg.CrossOriginOpenerPolicy) + } + + if cfg.CrossOriginResourcePolicy != "" { + c.Set("Cross-Origin-Resource-Policy", cfg.CrossOriginResourcePolicy) + } + + if cfg.OriginAgentCluster != "" { + c.Set("Origin-Agent-Cluster", cfg.OriginAgentCluster) + } + + if cfg.ReferrerPolicy != "" { + c.Set("Referrer-Policy", cfg.ReferrerPolicy) + } + + if cfg.XDNSPrefetchControl != "" { + c.Set("X-DNS-Prefetch-Control", cfg.XDNSPrefetchControl) + } + + if cfg.XDownloadOptions != "" { + c.Set("X-Download-Options", cfg.XDownloadOptions) + } + + if cfg.XPermittedCrossDomain != "" { + c.Set("X-Permitted-Cross-Domain-Policies", cfg.XPermittedCrossDomain) + } + + // Handle HSTS headers + if c.Protocol() == "https" && cfg.HSTSMaxAge != 0 { + subdomains := "" + if !cfg.HSTSExcludeSubdomains { + subdomains = "; includeSubDomains" + } + if cfg.HSTSPreloadEnabled { + subdomains = fmt.Sprintf("%s; preload", subdomains) + } + c.Set(fiber.HeaderStrictTransportSecurity, fmt.Sprintf("max-age=%d%s", cfg.HSTSMaxAge, subdomains)) + } + + // Handle Content-Security-Policy headers + if cfg.ContentSecurityPolicy != "" { + if cfg.CSPReportOnly { + c.Set(fiber.HeaderContentSecurityPolicyReportOnly, cfg.ContentSecurityPolicy) + } else { + c.Set(fiber.HeaderContentSecurityPolicy, cfg.ContentSecurityPolicy) + } + } + + // Handle Permissions-Policy headers + if cfg.PermissionPolicy != "" { + c.Set(fiber.HeaderPermissionsPolicy, cfg.PermissionPolicy) + } + + return c.Next() + } +} diff --git a/middleware/helmet/helmet_test.go b/middleware/helmet/helmet_test.go new file mode 100644 index 0000000000..1dfbfc7bf4 --- /dev/null +++ b/middleware/helmet/helmet_test.go @@ -0,0 +1,201 @@ +package helmet + +import ( + "net/http/httptest" + "testing" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" +) + +func Test_Default(t *testing.T) { + app := fiber.New() + + app.Use(New()) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "0", resp.Header.Get(fiber.HeaderXXSSProtection)) + utils.AssertEqual(t, "nosniff", resp.Header.Get(fiber.HeaderXContentTypeOptions)) + utils.AssertEqual(t, "SAMEORIGIN", resp.Header.Get(fiber.HeaderXFrameOptions)) + utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderContentSecurityPolicy)) + utils.AssertEqual(t, "no-referrer", resp.Header.Get(fiber.HeaderReferrerPolicy)) + utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderPermissionsPolicy)) + utils.AssertEqual(t, "require-corp", resp.Header.Get("Cross-Origin-Embedder-Policy")) + utils.AssertEqual(t, "same-origin", resp.Header.Get("Cross-Origin-Opener-Policy")) + utils.AssertEqual(t, "same-origin", resp.Header.Get("Cross-Origin-Resource-Policy")) + utils.AssertEqual(t, "?1", resp.Header.Get("Origin-Agent-Cluster")) + utils.AssertEqual(t, "off", resp.Header.Get("X-DNS-Prefetch-Control")) + utils.AssertEqual(t, "noopen", resp.Header.Get("X-Download-Options")) + utils.AssertEqual(t, "none", resp.Header.Get("X-Permitted-Cross-Domain-Policies")) +} + +func Test_CustomValues_AllHeaders(t *testing.T) { + app := fiber.New() + + app.Use(New(Config{ + // Custom values for all headers + XSSProtection: "0", + ContentTypeNosniff: "custom-nosniff", + XFrameOptions: "DENY", + HSTSExcludeSubdomains: true, + ContentSecurityPolicy: "default-src 'none'", + CSPReportOnly: true, + HSTSPreloadEnabled: true, + ReferrerPolicy: "origin", + PermissionPolicy: "geolocation=(self)", + CrossOriginEmbedderPolicy: "custom-value", + CrossOriginOpenerPolicy: "custom-value", + CrossOriginResourcePolicy: "custom-value", + OriginAgentCluster: "custom-value", + XDNSPrefetchControl: "custom-control", + XDownloadOptions: "custom-options", + XPermittedCrossDomain: "custom-policies", + })) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) + // Assertions for custom header values + utils.AssertEqual(t, "0", resp.Header.Get(fiber.HeaderXXSSProtection)) + utils.AssertEqual(t, "custom-nosniff", resp.Header.Get(fiber.HeaderXContentTypeOptions)) + utils.AssertEqual(t, "DENY", resp.Header.Get(fiber.HeaderXFrameOptions)) + utils.AssertEqual(t, "default-src 'none'", resp.Header.Get(fiber.HeaderContentSecurityPolicyReportOnly)) + utils.AssertEqual(t, "origin", resp.Header.Get(fiber.HeaderReferrerPolicy)) + utils.AssertEqual(t, "geolocation=(self)", resp.Header.Get(fiber.HeaderPermissionsPolicy)) + utils.AssertEqual(t, "custom-value", resp.Header.Get("Cross-Origin-Embedder-Policy")) + utils.AssertEqual(t, "custom-value", resp.Header.Get("Cross-Origin-Opener-Policy")) + utils.AssertEqual(t, "custom-value", resp.Header.Get("Cross-Origin-Resource-Policy")) + utils.AssertEqual(t, "custom-value", resp.Header.Get("Origin-Agent-Cluster")) + utils.AssertEqual(t, "custom-control", resp.Header.Get("X-DNS-Prefetch-Control")) + utils.AssertEqual(t, "custom-options", resp.Header.Get("X-Download-Options")) + utils.AssertEqual(t, "custom-policies", resp.Header.Get("X-Permitted-Cross-Domain-Policies")) +} + +func Test_RealWorldValues_AllHeaders(t *testing.T) { + app := fiber.New() + + app.Use(New(Config{ + // Real-world values for all headers + XSSProtection: "0", + ContentTypeNosniff: "nosniff", + XFrameOptions: "SAMEORIGIN", + HSTSExcludeSubdomains: false, + ContentSecurityPolicy: "default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests", + CSPReportOnly: false, + HSTSPreloadEnabled: true, + ReferrerPolicy: "no-referrer", + PermissionPolicy: "geolocation=(self)", + CrossOriginEmbedderPolicy: "require-corp", + CrossOriginOpenerPolicy: "same-origin", + CrossOriginResourcePolicy: "same-origin", + OriginAgentCluster: "?1", + XDNSPrefetchControl: "off", + XDownloadOptions: "noopen", + XPermittedCrossDomain: "none", + })) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) + // Assertions for real-world header values + utils.AssertEqual(t, "0", resp.Header.Get(fiber.HeaderXXSSProtection)) + utils.AssertEqual(t, "nosniff", resp.Header.Get(fiber.HeaderXContentTypeOptions)) + utils.AssertEqual(t, "SAMEORIGIN", resp.Header.Get(fiber.HeaderXFrameOptions)) + utils.AssertEqual(t, "default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests", resp.Header.Get(fiber.HeaderContentSecurityPolicy)) + utils.AssertEqual(t, "no-referrer", resp.Header.Get(fiber.HeaderReferrerPolicy)) + utils.AssertEqual(t, "geolocation=(self)", resp.Header.Get(fiber.HeaderPermissionsPolicy)) + utils.AssertEqual(t, "require-corp", resp.Header.Get("Cross-Origin-Embedder-Policy")) + utils.AssertEqual(t, "same-origin", resp.Header.Get("Cross-Origin-Opener-Policy")) + utils.AssertEqual(t, "same-origin", resp.Header.Get("Cross-Origin-Resource-Policy")) + utils.AssertEqual(t, "?1", resp.Header.Get("Origin-Agent-Cluster")) + utils.AssertEqual(t, "off", resp.Header.Get("X-DNS-Prefetch-Control")) + utils.AssertEqual(t, "noopen", resp.Header.Get("X-Download-Options")) + utils.AssertEqual(t, "none", resp.Header.Get("X-Permitted-Cross-Domain-Policies")) +} + +func Test_Next(t *testing.T) { + app := fiber.New() + + app.Use(New(Config{ + Next: func(ctx *fiber.Ctx) bool { + return ctx.Path() == "/next" + }, + ReferrerPolicy: "no-referrer", + })) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + app.Get("/next", func(c *fiber.Ctx) error { + return c.SendString("Skipped!") + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "no-referrer", resp.Header.Get(fiber.HeaderReferrerPolicy)) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/next", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderReferrerPolicy)) +} + +func Test_ContentSecurityPolicy(t *testing.T) { + app := fiber.New() + + app.Use(New(Config{ + ContentSecurityPolicy: "default-src 'none'", + })) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "default-src 'none'", resp.Header.Get(fiber.HeaderContentSecurityPolicy)) +} + +func Test_ContentSecurityPolicyReportOnly(t *testing.T) { + app := fiber.New() + + app.Use(New(Config{ + ContentSecurityPolicy: "default-src 'none'", + CSPReportOnly: true, + })) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "default-src 'none'", resp.Header.Get(fiber.HeaderContentSecurityPolicyReportOnly)) + utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderContentSecurityPolicy)) +} + +func Test_PermissionsPolicy(t *testing.T) { + app := fiber.New() + + app.Use(New(Config{ + PermissionPolicy: "microphone=()", + })) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "microphone=()", resp.Header.Get(fiber.HeaderPermissionsPolicy)) +} diff --git a/middleware/keyauth/config.go b/middleware/keyauth/config.go new file mode 100644 index 0000000000..39d71b6305 --- /dev/null +++ b/middleware/keyauth/config.go @@ -0,0 +1,95 @@ +package keyauth + +import ( + "errors" + + "github.com/gofiber/fiber/v2" +) + +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip middleware. + // Optional. Default: nil + Next func(*fiber.Ctx) bool + + // SuccessHandler defines a function which is executed for a valid key. + // Optional. Default: nil + SuccessHandler fiber.Handler + + // ErrorHandler defines a function which is executed for an invalid key. + // It may be used to define a custom error. + // Optional. Default: 401 Invalid or expired key + ErrorHandler fiber.ErrorHandler + + // KeyLookup is a string in the form of ":" that is used + // to extract key from the request. + // Optional. Default value "header:Authorization". + // Possible values: + // - "header:" + // - "query:" + // - "form:" + // - "param:" + // - "cookie:" + KeyLookup string + + // AuthScheme to be used in the Authorization header. + // Optional. Default value "Bearer". + AuthScheme string + + // Validator is a function to validate key. + Validator func(*fiber.Ctx, string) (bool, error) + + // Context key to store the bearertoken from the token into context. + // Optional. Default: "token". + ContextKey string +} + +// ConfigDefault is the default config +var ConfigDefault = Config{ + SuccessHandler: func(c *fiber.Ctx) error { + return c.Next() + }, + ErrorHandler: func(c *fiber.Ctx, err error) error { + if errors.Is(err, ErrMissingOrMalformedAPIKey) { + return c.Status(fiber.StatusUnauthorized).SendString(err.Error()) + } + return c.Status(fiber.StatusUnauthorized).SendString("Invalid or expired API Key") + }, + KeyLookup: "header:" + fiber.HeaderAuthorization, + AuthScheme: "Bearer", + ContextKey: "token", +} + +// Helper function to set default values +func configDefault(config ...Config) Config { + // Return default config if nothing provided + if len(config) < 1 { + return ConfigDefault + } + + // Override default config + cfg := config[0] + + // Set default values + if cfg.SuccessHandler == nil { + cfg.SuccessHandler = ConfigDefault.SuccessHandler + } + if cfg.ErrorHandler == nil { + cfg.ErrorHandler = ConfigDefault.ErrorHandler + } + if cfg.KeyLookup == "" { + cfg.KeyLookup = ConfigDefault.KeyLookup + // set AuthScheme as "Bearer" only if KeyLookup is set to default. + if cfg.AuthScheme == "" { + cfg.AuthScheme = ConfigDefault.AuthScheme + } + } + if cfg.Validator == nil { + panic("fiber: keyauth middleware requires a validator function") + } + if cfg.ContextKey == "" { + cfg.ContextKey = ConfigDefault.ContextKey + } + + return cfg +} diff --git a/middleware/keyauth/keyauth.go b/middleware/keyauth/keyauth.go new file mode 100644 index 0000000000..ce185240c2 --- /dev/null +++ b/middleware/keyauth/keyauth.go @@ -0,0 +1,121 @@ +// Special thanks to Echo: https://github.com/labstack/echo/blob/master/middleware/key_auth.go +package keyauth + +import ( + "errors" + "net/url" + "strings" + + "github.com/gofiber/fiber/v2" +) + +// When there is no request of the key thrown ErrMissingOrMalformedAPIKey +var ErrMissingOrMalformedAPIKey = errors.New("missing or malformed API Key") + +const ( + query = "query" + form = "form" + param = "param" + cookie = "cookie" +) + +// New creates a new middleware handler +func New(config ...Config) fiber.Handler { + // Init config + cfg := configDefault(config...) + + // Initialize + parts := strings.Split(cfg.KeyLookup, ":") + extractor := keyFromHeader(parts[1], cfg.AuthScheme) + switch parts[0] { + case query: + extractor = keyFromQuery(parts[1]) + case form: + extractor = keyFromForm(parts[1]) + case param: + extractor = keyFromParam(parts[1]) + case cookie: + extractor = keyFromCookie(parts[1]) + } + + // Return middleware handler + return func(c *fiber.Ctx) error { + // Filter request to skip middleware + if cfg.Next != nil && cfg.Next(c) { + return c.Next() + } + + // Extract and verify key + key, err := extractor(c) + if err != nil { + return cfg.ErrorHandler(c, err) + } + + valid, err := cfg.Validator(c, key) + + if err == nil && valid { + c.Locals(cfg.ContextKey, key) + return cfg.SuccessHandler(c) + } + return cfg.ErrorHandler(c, err) + } +} + +// keyFromHeader returns a function that extracts api key from the request header. +func keyFromHeader(header, authScheme string) func(c *fiber.Ctx) (string, error) { + return func(c *fiber.Ctx) (string, error) { + auth := c.Get(header) + l := len(authScheme) + if len(auth) > 0 && l == 0 { + return auth, nil + } + if len(auth) > l+1 && auth[:l] == authScheme { + return auth[l+1:], nil + } + return "", ErrMissingOrMalformedAPIKey + } +} + +// keyFromQuery returns a function that extracts api key from the query string. +func keyFromQuery(param string) func(c *fiber.Ctx) (string, error) { + return func(c *fiber.Ctx) (string, error) { + key := c.Query(param) + if key == "" { + return "", ErrMissingOrMalformedAPIKey + } + return key, nil + } +} + +// keyFromForm returns a function that extracts api key from the form. +func keyFromForm(param string) func(c *fiber.Ctx) (string, error) { + return func(c *fiber.Ctx) (string, error) { + key := c.FormValue(param) + if key == "" { + return "", ErrMissingOrMalformedAPIKey + } + return key, nil + } +} + +// keyFromParam returns a function that extracts api key from the url param string. +func keyFromParam(param string) func(c *fiber.Ctx) (string, error) { + return func(c *fiber.Ctx) (string, error) { + key, err := url.PathUnescape(c.Params(param)) + if err != nil { + return "", ErrMissingOrMalformedAPIKey + } + return key, nil + } +} + +// keyFromCookie returns a function that extracts api key from the named cookie. +func keyFromCookie(name string) func(c *fiber.Ctx) (string, error) { + return func(c *fiber.Ctx) (string, error) { + key := c.Cookies(name) + if key == "" { + return "", ErrMissingOrMalformedAPIKey + } + return key, nil + } +} diff --git a/middleware/keyauth/keyauth_test.go b/middleware/keyauth/keyauth_test.go new file mode 100644 index 0000000000..9d9b3395da --- /dev/null +++ b/middleware/keyauth/keyauth_test.go @@ -0,0 +1,461 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests +package keyauth + +import ( + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" +) + +const CorrectKey = "specials: !$%,.#\"!?~`<>@$^*(){}[]|/\\123" + +func TestAuthSources(t *testing.T) { + // define test cases + testSources := []string{"header", "cookie", "query", "param", "form"} + + tests := []struct { + route string + authTokenName string + description string + APIKey string + expectedCode int + expectedBody string + }{ + { + route: "/", + authTokenName: "access_token", + description: "auth with correct key", + APIKey: CorrectKey, + expectedCode: 200, + expectedBody: "Success!", + }, + { + route: "/", + authTokenName: "access_token", + description: "auth with no key", + APIKey: "", + expectedCode: 401, // 404 in case of param authentication + expectedBody: "missing or malformed API Key", + }, + { + route: "/", + authTokenName: "access_token", + description: "auth with wrong key", + APIKey: "WRONGKEY", + expectedCode: 401, + expectedBody: "missing or malformed API Key", + }, + } + + for _, authSource := range testSources { + t.Run(authSource, func(t *testing.T) { + for _, test := range tests { + // setup the fiber endpoint + // note that if UnescapePath: false (the default) + // escaped characters (such as `\"`) will not be handled correctly in the tests + app := fiber.New(fiber.Config{UnescapePath: true}) + + authMiddleware := New(Config{ + KeyLookup: authSource + ":" + test.authTokenName, + Validator: func(c *fiber.Ctx, key string) (bool, error) { + if key == CorrectKey { + return true, nil + } + return false, ErrMissingOrMalformedAPIKey + }, + }) + + var route string + if authSource == param { + route = test.route + ":" + test.authTokenName + app.Use(route, authMiddleware) + } else { + route = test.route + app.Use(authMiddleware) + } + + app.Get(route, func(c *fiber.Ctx) error { + return c.SendString("Success!") + }) + + // construct the test HTTP request + var req *http.Request + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, test.route, nil) + utils.AssertEqual(t, err, nil) + + // setup the apikey for the different auth schemes + if authSource == "header" { + req.Header.Set(test.authTokenName, test.APIKey) + } else if authSource == "cookie" { + req.Header.Set("Cookie", test.authTokenName+"="+test.APIKey) + } else if authSource == "query" || authSource == "form" { + q := req.URL.Query() + q.Add(test.authTokenName, test.APIKey) + req.URL.RawQuery = q.Encode() + } else if authSource == "param" { + r := req.URL.Path + r += url.PathEscape(test.APIKey) + req.URL.Path = r + } + + res, err := app.Test(req, -1) + + utils.AssertEqual(t, nil, err, test.description) + + // test the body of the request + body, err := io.ReadAll(res.Body) + // for param authentication, the route would be /:access_token + // when the access_token is empty, it leads to a 404 (not found) + // not a 401 (auth error) + if authSource == "param" && test.APIKey == "" { + test.expectedCode = 404 + test.expectedBody = "Cannot GET /" + } + utils.AssertEqual(t, test.expectedCode, res.StatusCode, test.description) + + // body + utils.AssertEqual(t, nil, err, test.description) + utils.AssertEqual(t, test.expectedBody, string(body), test.description) + + err = res.Body.Close() + utils.AssertEqual(t, err, nil) + } + }) + } +} + +func TestMultipleKeyAuth(t *testing.T) { + // setup the fiber endpoint + app := fiber.New() + + // setup keyauth for /auth1 + app.Use(New(Config{ + Next: func(c *fiber.Ctx) bool { + return c.OriginalURL() != "/auth1" + }, + KeyLookup: "header:key", + Validator: func(c *fiber.Ctx, key string) (bool, error) { + if key == "password1" { + return true, nil + } + return false, ErrMissingOrMalformedAPIKey + }, + })) + + // setup keyauth for /auth2 + app.Use(New(Config{ + Next: func(c *fiber.Ctx) bool { + return c.OriginalURL() != "/auth2" + }, + KeyLookup: "header:key", + Validator: func(c *fiber.Ctx, key string) (bool, error) { + if key == "password2" { + return true, nil + } + return false, ErrMissingOrMalformedAPIKey + }, + })) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("No auth needed!") + }) + + app.Get("/auth1", func(c *fiber.Ctx) error { + return c.SendString("Successfully authenticated for auth1!") + }) + + app.Get("/auth2", func(c *fiber.Ctx) error { + return c.SendString("Successfully authenticated for auth2!") + }) + + // define test cases + tests := []struct { + route string + description string + APIKey string + expectedCode int + expectedBody string + }{ + // No auth needed for / + { + route: "/", + description: "No password needed", + APIKey: "", + expectedCode: 200, + expectedBody: "No auth needed!", + }, + + // auth needed for auth1 + { + route: "/auth1", + description: "Normal Authentication Case", + APIKey: "password1", + expectedCode: 200, + expectedBody: "Successfully authenticated for auth1!", + }, + { + route: "/auth1", + description: "Wrong API Key", + APIKey: "WRONG KEY", + expectedCode: 401, + expectedBody: "missing or malformed API Key", + }, + { + route: "/auth1", + description: "Wrong API Key", + APIKey: "", // NO KEY + expectedCode: 401, + expectedBody: "missing or malformed API Key", + }, + + // Auth 2 has a different password + { + route: "/auth2", + description: "Normal Authentication Case for auth2", + APIKey: "password2", + expectedCode: 200, + expectedBody: "Successfully authenticated for auth2!", + }, + { + route: "/auth2", + description: "Wrong API Key", + APIKey: "WRONG KEY", + expectedCode: 401, + expectedBody: "missing or malformed API Key", + }, + { + route: "/auth2", + description: "Wrong API Key", + APIKey: "", // NO KEY + expectedCode: 401, + expectedBody: "missing or malformed API Key", + }, + } + + // run the tests + for _, test := range tests { + var req *http.Request + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, test.route, nil) + utils.AssertEqual(t, err, nil) + if test.APIKey != "" { + req.Header.Set("key", test.APIKey) + } + + res, err := app.Test(req, -1) + + utils.AssertEqual(t, nil, err, test.description) + + // test the body of the request + body, err := io.ReadAll(res.Body) + utils.AssertEqual(t, test.expectedCode, res.StatusCode, test.description) + + // body + utils.AssertEqual(t, nil, err, test.description) + utils.AssertEqual(t, test.expectedBody, string(body), test.description) + } +} + +func TestCustomSuccessAndFailureHandlers(t *testing.T) { + app := fiber.New() + + app.Use(New(Config{ + SuccessHandler: func(c *fiber.Ctx) error { + return c.Status(fiber.StatusOK).SendString("API key is valid and request was handled by custom success handler") + }, + ErrorHandler: func(c *fiber.Ctx, err error) error { + return c.Status(fiber.StatusUnauthorized).SendString("API key is invalid and request was handled by custom error handler") + }, + Validator: func(c *fiber.Ctx, key string) (bool, error) { + if key == CorrectKey { + return true, nil + } + return false, ErrMissingOrMalformedAPIKey + }, + })) + + // Define a test handler that should not be called + app.Get("/", func(c *fiber.Ctx) error { + t.Error("Test handler should not be called") + return nil + }) + + // Create a request without an API key and send it to the app + res, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, err, nil) + + // Read the response body into a string + body, err := io.ReadAll(res.Body) + utils.AssertEqual(t, err, nil) + + // Check that the response has the expected status code and body + utils.AssertEqual(t, res.StatusCode, http.StatusUnauthorized) + utils.AssertEqual(t, string(body), "API key is invalid and request was handled by custom error handler") + + // Create a request with a valid API key in the Authorization header + req := httptest.NewRequest(fiber.MethodGet, "/", nil) + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", CorrectKey)) + + // Send the request to the app + res, err = app.Test(req) + utils.AssertEqual(t, err, nil) + + // Read the response body into a string + body, err = io.ReadAll(res.Body) + utils.AssertEqual(t, err, nil) + + // Check that the response has the expected status code and body + utils.AssertEqual(t, res.StatusCode, http.StatusOK) + utils.AssertEqual(t, string(body), "API key is valid and request was handled by custom success handler") +} + +func TestCustomNextFunc(t *testing.T) { + app := fiber.New() + + app.Use(New(Config{ + Next: func(c *fiber.Ctx) bool { + return c.Path() == "/allowed" + }, + Validator: func(c *fiber.Ctx, key string) (bool, error) { + if key == CorrectKey { + return true, nil + } + return false, ErrMissingOrMalformedAPIKey + }, + })) + + // Define a test handler + app.Get("/allowed", func(c *fiber.Ctx) error { + return c.SendString("API key is valid and request was allowed by custom filter") + }) + + // Create a request with the "/allowed" path and send it to the app + req := httptest.NewRequest(fiber.MethodGet, "/allowed", nil) + res, err := app.Test(req) + utils.AssertEqual(t, err, nil) + + // Read the response body into a string + body, err := io.ReadAll(res.Body) + utils.AssertEqual(t, err, nil) + + // Check that the response has the expected status code and body + utils.AssertEqual(t, res.StatusCode, http.StatusOK) + utils.AssertEqual(t, string(body), "API key is valid and request was allowed by custom filter") + + // Create a request with a different path and send it to the app without correct key + req = httptest.NewRequest(fiber.MethodGet, "/not-allowed", nil) + res, err = app.Test(req) + utils.AssertEqual(t, err, nil) + + // Read the response body into a string + body, err = io.ReadAll(res.Body) + utils.AssertEqual(t, err, nil) + + // Check that the response has the expected status code and body + utils.AssertEqual(t, res.StatusCode, http.StatusUnauthorized) + utils.AssertEqual(t, string(body), ErrMissingOrMalformedAPIKey.Error()) + + // Create a request with a different path and send it to the app with correct key + req = httptest.NewRequest(fiber.MethodGet, "/not-allowed", nil) + req.Header.Add("Authorization", fmt.Sprintf("Basic %s", CorrectKey)) + + res, err = app.Test(req) + utils.AssertEqual(t, err, nil) + + // Read the response body into a string + body, err = io.ReadAll(res.Body) + utils.AssertEqual(t, err, nil) + + // Check that the response has the expected status code and body + utils.AssertEqual(t, res.StatusCode, http.StatusUnauthorized) + utils.AssertEqual(t, string(body), ErrMissingOrMalformedAPIKey.Error()) +} + +func TestAuthSchemeToken(t *testing.T) { + app := fiber.New() + + app.Use(New(Config{ + AuthScheme: "Token", + Validator: func(c *fiber.Ctx, key string) (bool, error) { + if key == CorrectKey { + return true, nil + } + return false, ErrMissingOrMalformedAPIKey + }, + })) + + // Define a test handler + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("API key is valid") + }) + + // Create a request with a valid API key in the "Token" Authorization header + req := httptest.NewRequest(fiber.MethodGet, "/", nil) + req.Header.Add("Authorization", fmt.Sprintf("Token %s", CorrectKey)) + + // Send the request to the app + res, err := app.Test(req) + utils.AssertEqual(t, err, nil) + + // Read the response body into a string + body, err := io.ReadAll(res.Body) + utils.AssertEqual(t, err, nil) + + // Check that the response has the expected status code and body + utils.AssertEqual(t, res.StatusCode, http.StatusOK) + utils.AssertEqual(t, string(body), "API key is valid") +} + +func TestAuthSchemeBasic(t *testing.T) { + app := fiber.New() + + app.Use(New(Config{ + KeyLookup: "header:Authorization", + AuthScheme: "Basic", + Validator: func(c *fiber.Ctx, key string) (bool, error) { + if key == CorrectKey { + return true, nil + } + return false, ErrMissingOrMalformedAPIKey + }, + })) + + // Define a test handler + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("API key is valid") + }) + + // Create a request without an API key and Send the request to the app + res, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, err, nil) + + // Read the response body into a string + body, err := io.ReadAll(res.Body) + utils.AssertEqual(t, err, nil) + + // Check that the response has the expected status code and body + utils.AssertEqual(t, res.StatusCode, http.StatusUnauthorized) + utils.AssertEqual(t, string(body), ErrMissingOrMalformedAPIKey.Error()) + + // Create a request with a valid API key in the "Authorization" header using the "Basic" scheme + req := httptest.NewRequest(fiber.MethodGet, "/", nil) + req.Header.Add("Authorization", fmt.Sprintf("Basic %s", CorrectKey)) + + // Send the request to the app + res, err = app.Test(req) + utils.AssertEqual(t, err, nil) + + // Read the response body into a string + body, err = io.ReadAll(res.Body) + utils.AssertEqual(t, err, nil) + + // Check that the response has the expected status code and body + utils.AssertEqual(t, res.StatusCode, http.StatusOK) + utils.AssertEqual(t, string(body), "API key is valid") +} diff --git a/middleware/redirect/config.go b/middleware/redirect/config.go new file mode 100644 index 0000000000..62b868cf7f --- /dev/null +++ b/middleware/redirect/config.go @@ -0,0 +1,53 @@ +package redirect + +import ( + "regexp" + + "github.com/gofiber/fiber/v2" +) + +// Config defines the config for middleware. +type Config struct { + // Filter defines a function to skip middleware. + // Optional. Default: nil + Next func(*fiber.Ctx) bool + + // Rules defines the URL path rewrite rules. The values captured in asterisk can be + // retrieved by index e.g. $1, $2 and so on. + // Required. Example: + // "/old": "/new", + // "/api/*": "/$1", + // "/js/*": "/public/javascripts/$1", + // "/users/*/orders/*": "/user/$1/order/$2", + Rules map[string]string + + // The status code when redirecting + // This is ignored if Redirect is disabled + // Optional. Default: 302 Temporary Redirect + StatusCode int + + rulesRegex map[*regexp.Regexp]string +} + +// ConfigDefault is the default config +var ConfigDefault = Config{ + StatusCode: fiber.StatusFound, +} + +// Helper function to set default values +func configDefault(config ...Config) Config { + // Return default config if nothing provided + if len(config) < 1 { + return ConfigDefault + } + + // Override default config + cfg := config[0] + + // Set default values + if cfg.StatusCode == 0 { + cfg.StatusCode = ConfigDefault.StatusCode + } + + return cfg +} diff --git a/middleware/redirect/redirect.go b/middleware/redirect/redirect.go new file mode 100644 index 0000000000..7af21320fe --- /dev/null +++ b/middleware/redirect/redirect.go @@ -0,0 +1,57 @@ +package redirect + +import ( + "regexp" + "strconv" + "strings" + + "github.com/gofiber/fiber/v2" +) + +// New creates a new middleware handler +func New(config ...Config) fiber.Handler { + cfg := configDefault(config...) + + // Initialize + cfg.rulesRegex = map[*regexp.Regexp]string{} + for k, v := range cfg.Rules { + k = strings.ReplaceAll(k, "*", "(.*)") + k += "$" + cfg.rulesRegex[regexp.MustCompile(k)] = v + } + + // Middleware function + return func(c *fiber.Ctx) error { + // Next request to skip middleware + if cfg.Next != nil && cfg.Next(c) { + return c.Next() + } + // Rewrite + for k, v := range cfg.rulesRegex { + replacer := captureTokens(k, c.Path()) + if replacer != nil { + return c.Redirect(replacer.Replace(v), cfg.StatusCode) + } + } + return c.Next() + } +} + +// https://github.com/labstack/echo/blob/master/middleware/rewrite.go +func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer { + if len(input) > 1 { + input = strings.TrimSuffix(input, "/") + } + groups := pattern.FindAllStringSubmatch(input, -1) + if groups == nil { + return nil + } + values := groups[0][1:] + replace := make([]string, 2*len(values)) + for i, v := range values { + j := 2 * i + replace[j] = "$" + strconv.Itoa(i+1) + replace[j+1] = v + } + return strings.NewReplacer(replace...) +} diff --git a/middleware/redirect/redirect_test.go b/middleware/redirect/redirect_test.go new file mode 100644 index 0000000000..b6323ab54b --- /dev/null +++ b/middleware/redirect/redirect_test.go @@ -0,0 +1,283 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests +package redirect + +import ( + "context" + "net/http" + "testing" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" +) + +func Test_Redirect(t *testing.T) { + app := *fiber.New() + + app.Use(New(Config{ + Rules: map[string]string{ + "/default": "google.com", + }, + StatusCode: fiber.StatusMovedPermanently, + })) + app.Use(New(Config{ + Rules: map[string]string{ + "/default/*": "fiber.wiki", + }, + StatusCode: fiber.StatusTemporaryRedirect, + })) + app.Use(New(Config{ + Rules: map[string]string{ + "/redirect/*": "$1", + }, + StatusCode: fiber.StatusSeeOther, + })) + app.Use(New(Config{ + Rules: map[string]string{ + "/pattern/*": "golang.org", + }, + StatusCode: fiber.StatusFound, + })) + + app.Use(New(Config{ + Rules: map[string]string{ + "/": "/swagger", + }, + StatusCode: fiber.StatusMovedPermanently, + })) + + app.Get("/api/*", func(c *fiber.Ctx) error { + return c.SendString("API") + }) + + app.Get("/new", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + tests := []struct { + name string + url string + redirectTo string + statusCode int + }{ + { + name: "should be returns status StatusFound without a wildcard", + url: "/default", + redirectTo: "google.com", + statusCode: fiber.StatusMovedPermanently, + }, + { + name: "should be returns status StatusTemporaryRedirect using wildcard", + url: "/default/xyz", + redirectTo: "fiber.wiki", + statusCode: fiber.StatusTemporaryRedirect, + }, + { + name: "should be returns status StatusSeeOther without set redirectTo to use the default", + url: "/redirect/github.com/gofiber/redirect", + redirectTo: "github.com/gofiber/redirect", + statusCode: fiber.StatusSeeOther, + }, + { + name: "should return the status code default", + url: "/pattern/xyz", + redirectTo: "golang.org", + statusCode: fiber.StatusFound, + }, + { + name: "access URL without rule", + url: "/new", + statusCode: fiber.StatusOK, + }, + { + name: "redirect to swagger route", + url: "/", + redirectTo: "/swagger", + statusCode: fiber.StatusMovedPermanently, + }, + { + name: "no redirect to swagger route", + url: "/api/", + statusCode: fiber.StatusOK, + }, + { + name: "no redirect to swagger route #2", + url: "/api/test", + statusCode: fiber.StatusOK, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, tt.url, nil) + utils.AssertEqual(t, err, nil) + req.Header.Set("Location", "github.com/gofiber/redirect") + resp, err := app.Test(req) + + utils.AssertEqual(t, err, nil) + utils.AssertEqual(t, tt.statusCode, resp.StatusCode) + utils.AssertEqual(t, tt.redirectTo, resp.Header.Get("Location")) + }) + } +} + +func Test_Next(t *testing.T) { + // Case 1 : Next function always returns true + app := *fiber.New() + app.Use(New(Config{ + Next: func(*fiber.Ctx) bool { + return true + }, + Rules: map[string]string{ + "/default": "google.com", + }, + StatusCode: fiber.StatusMovedPermanently, + })) + + app.Use(func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/default", nil) + utils.AssertEqual(t, err, nil) + resp, err := app.Test(req) + utils.AssertEqual(t, err, nil) + + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + + // Case 2 : Next function always returns false + app = *fiber.New() + app.Use(New(Config{ + Next: func(*fiber.Ctx) bool { + return false + }, + Rules: map[string]string{ + "/default": "google.com", + }, + StatusCode: fiber.StatusMovedPermanently, + })) + + req, err = http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/default", nil) + utils.AssertEqual(t, err, nil) + resp, err = app.Test(req) + utils.AssertEqual(t, err, nil) + + utils.AssertEqual(t, fiber.StatusMovedPermanently, resp.StatusCode) + utils.AssertEqual(t, "google.com", resp.Header.Get("Location")) +} + +func Test_NoRules(t *testing.T) { + // Case 1: No rules with default route defined + app := *fiber.New() + + app.Use(New(Config{ + StatusCode: fiber.StatusMovedPermanently, + })) + + app.Use(func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/default", nil) + utils.AssertEqual(t, err, nil) + resp, err := app.Test(req) + utils.AssertEqual(t, err, nil) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + + // Case 2: No rules and no default route defined + app = *fiber.New() + + app.Use(New(Config{ + StatusCode: fiber.StatusMovedPermanently, + })) + + req, err = http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/default", nil) + utils.AssertEqual(t, err, nil) + resp, err = app.Test(req) + utils.AssertEqual(t, err, nil) + utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) +} + +func Test_DefaultConfig(t *testing.T) { + // Case 1: Default config and no default route + app := *fiber.New() + + app.Use(New()) + + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/default", nil) + utils.AssertEqual(t, err, nil) + resp, err := app.Test(req) + + utils.AssertEqual(t, err, nil) + utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + + // Case 2: Default config and default route + app = *fiber.New() + + app.Use(New()) + app.Use(func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + req, err = http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/default", nil) + utils.AssertEqual(t, err, nil) + resp, err = app.Test(req) + + utils.AssertEqual(t, err, nil) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) +} + +func Test_RegexRules(t *testing.T) { + // Case 1: Rules regex is empty + app := *fiber.New() + app.Use(New(Config{ + Rules: map[string]string{}, + StatusCode: fiber.StatusMovedPermanently, + })) + + app.Use(func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/default", nil) + utils.AssertEqual(t, err, nil) + resp, err := app.Test(req) + + utils.AssertEqual(t, err, nil) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + + // Case 2: Rules regex map contains valid regex and well-formed replacement URLs + app = *fiber.New() + app.Use(New(Config{ + Rules: map[string]string{ + "/default": "google.com", + }, + StatusCode: fiber.StatusMovedPermanently, + })) + + app.Use(func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + req, err = http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/default", nil) + utils.AssertEqual(t, err, nil) + resp, err = app.Test(req) + + utils.AssertEqual(t, err, nil) + utils.AssertEqual(t, fiber.StatusMovedPermanently, resp.StatusCode) + utils.AssertEqual(t, "google.com", resp.Header.Get("Location")) + + // Case 3: Test invalid regex throws panic + defer func() { + if r := recover(); r != nil { + t.Log("Recovered from invalid regex: ", r) + } + }() + + app = *fiber.New() + app.Use(New(Config{ + Rules: map[string]string{ + "(": "google.com", + }, + StatusCode: fiber.StatusMovedPermanently, + })) + t.Error("Expected panic, got nil") +} diff --git a/middleware/rewrite/config.go b/middleware/rewrite/config.go new file mode 100644 index 0000000000..8873253f7e --- /dev/null +++ b/middleware/rewrite/config.go @@ -0,0 +1,38 @@ +package rewrite + +import ( + "regexp" + + "github.com/gofiber/fiber/v2" +) + +// Config defines the config for middleware. +type Config struct { + // Next defines a function to skip middleware. + // Optional. Default: nil + Next func(*fiber.Ctx) bool + + // Rules defines the URL path rewrite rules. The values captured in asterisk can be + // retrieved by index e.g. $1, $2 and so on. + // Required. Example: + // "/old": "/new", + // "/api/*": "/$1", + // "/js/*": "/public/javascripts/$1", + // "/users/*/orders/*": "/user/$1/order/$2", + Rules map[string]string + + rulesRegex map[*regexp.Regexp]string +} + +// Helper function to set default values +func configDefault(config ...Config) Config { + // Return default config if nothing provided + if len(config) < 1 { + return Config{} + } + + // Override default config + cfg := config[0] + + return cfg +} diff --git a/middleware/rewrite/rewrite.go b/middleware/rewrite/rewrite.go new file mode 100644 index 0000000000..4465df17d6 --- /dev/null +++ b/middleware/rewrite/rewrite.go @@ -0,0 +1,54 @@ +package rewrite + +import ( + "regexp" + "strconv" + "strings" + + "github.com/gofiber/fiber/v2" +) + +// New creates a new middleware handler +func New(config ...Config) fiber.Handler { + cfg := configDefault(config...) + + // Initialize + cfg.rulesRegex = map[*regexp.Regexp]string{} + for k, v := range cfg.Rules { + k = strings.ReplaceAll(k, "*", "(.*)") + k += "$" + cfg.rulesRegex[regexp.MustCompile(k)] = v + } + // Middleware function + return func(c *fiber.Ctx) error { + // Next request to skip middleware + if cfg.Next != nil && cfg.Next(c) { + return c.Next() + } + // Rewrite + for k, v := range cfg.rulesRegex { + replacer := captureTokens(k, c.Path()) + if replacer != nil { + c.Path(replacer.Replace(v)) + break + } + } + return c.Next() + } +} + +// https://github.com/labstack/echo/blob/master/middleware/rewrite.go +func captureTokens(pattern *regexp.Regexp, input string) *strings.Replacer { + groups := pattern.FindAllStringSubmatch(input, -1) + if groups == nil { + return nil + } + values := groups[0][1:] + replace := make([]string, 2*len(values)) + for i, v := range values { + j := 2 * i + replace[j] = "$" + strconv.Itoa(i+1) + replace[j+1] = v + } + return strings.NewReplacer(replace...) +} diff --git a/middleware/rewrite/rewrite_test.go b/middleware/rewrite/rewrite_test.go new file mode 100644 index 0000000000..cfd6565d9d --- /dev/null +++ b/middleware/rewrite/rewrite_test.go @@ -0,0 +1,173 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests +package rewrite + +import ( + "context" + "fmt" + "io" + "net/http" + "testing" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" +) + +func Test_New(t *testing.T) { + // Test with no config + m := New() + + if m == nil { + t.Error("Expected middleware to be returned, got nil") + } + + // Test with config + m = New(Config{ + Rules: map[string]string{ + "/old": "/new", + }, + }) + + if m == nil { + t.Error("Expected middleware to be returned, got nil") + } + + // Test with full config + m = New(Config{ + Next: func(*fiber.Ctx) bool { + return true + }, + Rules: map[string]string{ + "/old": "/new", + }, + }) + + if m == nil { + t.Error("Expected middleware to be returned, got nil") + } +} + +func Test_Rewrite(t *testing.T) { + // Case 1: Next function always returns true + app := fiber.New() + app.Use(New(Config{ + Next: func(*fiber.Ctx) bool { + return true + }, + Rules: map[string]string{ + "/old": "/new", + }, + })) + + app.Get("/old", func(c *fiber.Ctx) error { + return c.SendString("Rewrite Successful") + }) + + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/old", nil) + utils.AssertEqual(t, err, nil) + resp, err := app.Test(req) + utils.AssertEqual(t, err, nil) + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, err, nil) + bodyString := string(body) + + utils.AssertEqual(t, err, nil) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + utils.AssertEqual(t, "Rewrite Successful", bodyString) + + // Case 2: Next function always returns false + app = fiber.New() + app.Use(New(Config{ + Next: func(*fiber.Ctx) bool { + return false + }, + Rules: map[string]string{ + "/old": "/new", + }, + })) + + app.Get("/new", func(c *fiber.Ctx) error { + return c.SendString("Rewrite Successful") + }) + + req, err = http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/old", nil) + utils.AssertEqual(t, err, nil) + resp, err = app.Test(req) + utils.AssertEqual(t, err, nil) + body, err = io.ReadAll(resp.Body) + utils.AssertEqual(t, err, nil) + bodyString = string(body) + + utils.AssertEqual(t, err, nil) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + utils.AssertEqual(t, "Rewrite Successful", bodyString) + + // Case 3: check for captured tokens in rewrite rule + app = fiber.New() + app.Use(New(Config{ + Rules: map[string]string{ + "/users/*/orders/*": "/user/$1/order/$2", + }, + })) + + app.Get("/user/:userID/order/:orderID", func(c *fiber.Ctx) error { + return c.SendString(fmt.Sprintf("User ID: %s, Order ID: %s", c.Params("userID"), c.Params("orderID"))) + }) + + req, err = http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/users/123/orders/456", nil) + utils.AssertEqual(t, err, nil) + resp, err = app.Test(req) + utils.AssertEqual(t, err, nil) + body, err = io.ReadAll(resp.Body) + utils.AssertEqual(t, err, nil) + bodyString = string(body) + + utils.AssertEqual(t, err, nil) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + utils.AssertEqual(t, "User ID: 123, Order ID: 456", bodyString) + + // Case 4: Send non-matching request, handled by default route + app = fiber.New() + app.Use(New(Config{ + Rules: map[string]string{ + "/users/*/orders/*": "/user/$1/order/$2", + }, + })) + + app.Get("/user/:userID/order/:orderID", func(c *fiber.Ctx) error { + return c.SendString(fmt.Sprintf("User ID: %s, Order ID: %s", c.Params("userID"), c.Params("orderID"))) + }) + + app.Use(func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + req, err = http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/not-matching-any-rule", nil) + utils.AssertEqual(t, err, nil) + resp, err = app.Test(req) + utils.AssertEqual(t, err, nil) + body, err = io.ReadAll(resp.Body) + utils.AssertEqual(t, err, nil) + bodyString = string(body) + + utils.AssertEqual(t, err, nil) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + utils.AssertEqual(t, "OK", bodyString) + + // Case 4: Send non-matching request, with no default route + app = fiber.New() + app.Use(New(Config{ + Rules: map[string]string{ + "/users/*/orders/*": "/user/$1/order/$2", + }, + })) + + app.Get("/user/:userID/order/:orderID", func(c *fiber.Ctx) error { + return c.SendString(fmt.Sprintf("User ID: %s, Order ID: %s", c.Params("userID"), c.Params("orderID"))) + }) + + req, err = http.NewRequestWithContext(context.Background(), fiber.MethodGet, "/not-matching-any-rule", nil) + utils.AssertEqual(t, err, nil) + resp, err = app.Test(req) + utils.AssertEqual(t, err, nil) + utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) +} From 77c1b4868a686254a839fbbb88d7f46cbadb055a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B5=D0=B9=20=D0=9A=D0=BE?= =?UTF-8?q?=D0=B2=D1=80=D0=B8=D0=B3=D0=B8=D0=BD?= <83507071+alekseikovrigin@users.noreply.github.com> Date: Wed, 10 May 2023 23:51:56 +0300 Subject: [PATCH 095/162] :memo: docs: update README_ru.md (#2456) :memo: docs: update README_ru.md Translated 3 blocks: Limitations, Awesome list, License --- .github/README_ru.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/README_ru.md b/.github/README_ru.md index f413ebc074..28d2d31264 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -154,9 +154,9 @@ Fiber **вдохновлен** Express, самым популярным веб Мы **прислушиваемся** к нашим пользователям в [issues](https://github.com/gofiber/fiber/issues), Discord [канале](https://gofiber.io/discord) _и в остальном Интернете_, чтобы создать **быстрый**, **гибкий** и **дружелюбный** веб фреймворк на Go для **любых** задач, **дедлайнов** и **уровней** разработчиков! Как это делает Express в мире JavaScript. -## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. -* Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. +## ⚠️ Ограничения +* Из-за того, что Fiber использует пакет unsafe, библиотека не всегда может быть совместима с последней версией Go. Fiber 2.40.0 был протестирован с версиями Go от 1.17 до 1.20. +* Fiber не совместим с интерфейсами net/http. Это означает, что вы не сможете использовать такие проекты, как gqlgen, go-swagger или любые другие, которые являются частью экосистемы net/http. ## 👀 Примеры @@ -621,9 +621,9 @@ func main() { | [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | | [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | -## 🕶️ Awesome List +## 🕶️ Полезный список -For more articles, middlewares, examples or tools check our [awesome list](https://github.com/gofiber/awesome-fiber). +Дополнительные статьи, middleware, примеры или инструменты смотри в нашем [полезном списке](https://github.com/gofiber/awesome-fiber). ## 👍 Помощь проекту @@ -669,9 +669,9 @@ Fiber — это проект с открытым исходным кодом, Stargazers over time -## ⚠️ License +## ⚠️ Лицензия -Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](https://github.com/gofiber/fiber/graphs/contributors). `Fiber` is free and open-source software licensed under the [MIT License](https://github.com/gofiber/fiber/blob/master/LICENSE). Official logo was created by [Vic Shóstak](https://github.com/koddr) and distributed under [Creative Commons](https://creativecommons.org/licenses/by-sa/4.0/) license (CC BY-SA 4.0 International). +Copyright (c) 2019-настоящее время [Fenny](https://github.com/fenny) и [Контрибьютеры](https://github.com/gofiber/fiber/graphs/contributors). `Fiber` - это свободное программное обсепечение с открытым исходным кодом лицензированное под [MIT License](https://github.com/gofiber/fiber/blob/master/LICENSE). Официальный логотип создан [Vic Shóstak](https://github.com/koddr) и распространяется под [Creative Commons](https://creativecommons.org/licenses/by-sa/4.0/) лицензией (CC BY-SA 4.0 International). **Third-party library licenses** From eced39c47e3703078bbf2484e17e315be2f91f17 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Mon, 15 May 2023 07:21:16 +0200 Subject: [PATCH 096/162] utils: add Go 1.20+ way of converting string to byte slice (#2462) * utils: add Go 1.20+ way of converting string to byte slice Ref. https://github.com/valyala/fasthttp/blob/d2f97fc426ed451e64dc8e35e7f87a1d4a2d7bde/s2b_old.go. Ref. https://github.com/valyala/fasthttp/blob/d2f97fc426ed451e64dc8e35e7f87a1d4a2d7bde/s2b_new.go. * utils: fix golangci-lint apparently running with Go < 1.20 See https://github.com/gofiber/fiber/actions/runs/4968641325/jobs/8891360463?pr=2462. --- utils/convert.go | 16 ---------------- utils/convert_s2b_new.go | 13 +++++++++++++ utils/convert_s2b_old.go | 25 +++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 utils/convert_s2b_new.go create mode 100644 utils/convert_s2b_old.go diff --git a/utils/convert.go b/utils/convert.go index 233dfbce31..672ea7f42a 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -13,8 +13,6 @@ import ( "unsafe" ) -const MaxStringLen = 0x7fff0000 // Maximum string length for UnsafeBytes. (decimal: 2147418112) - // UnsafeString returns a string pointer without allocation // //nolint:gosec // unsafe is used for better performance here @@ -22,20 +20,6 @@ func UnsafeString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } -// UnsafeBytes returns a byte pointer without allocation. -// String length shouldn't be more than 2147418112. -// -//nolint:gosec // unsafe is used for better performance here -func UnsafeBytes(s string) []byte { - if s == "" { - return nil - } - - return (*[MaxStringLen]byte)(unsafe.Pointer( - (*reflect.StringHeader)(unsafe.Pointer(&s)).Data), - )[:len(s):len(s)] -} - // CopyString copies a string to make it immutable func CopyString(s string) string { return string(UnsafeBytes(s)) diff --git a/utils/convert_s2b_new.go b/utils/convert_s2b_new.go new file mode 100644 index 0000000000..abe9a8e82a --- /dev/null +++ b/utils/convert_s2b_new.go @@ -0,0 +1,13 @@ +//go:build go1.20 +// +build go1.20 + +package utils + +import ( + "unsafe" +) + +// UnsafeBytes returns a byte pointer without allocation. +func UnsafeBytes(s string) []byte { + return unsafe.Slice(unsafe.StringData(s), len(s)) +} diff --git a/utils/convert_s2b_old.go b/utils/convert_s2b_old.go new file mode 100644 index 0000000000..e817dc3bee --- /dev/null +++ b/utils/convert_s2b_old.go @@ -0,0 +1,25 @@ +//go:build !go1.20 +// +build !go1.20 + +package utils + +import ( + "reflect" + "unsafe" +) + +const MaxStringLen = 0x7fff0000 // Maximum string length for UnsafeBytes. (decimal: 2147418112) + +// UnsafeBytes returns a byte pointer without allocation. +// String length shouldn't be more than 2147418112. +// +//nolint:gosec // unsafe is used for better performance here +func UnsafeBytes(s string) []byte { + if s == "" { + return nil + } + + return (*[MaxStringLen]byte)(unsafe.Pointer( + (*reflect.StringHeader)(unsafe.Pointer(&s)).Data), + )[:len(s):len(s)] +} From c56b4e66a0e4c6fe8ce070e95f1ee39773d01a0c Mon Sep 17 00:00:00 2001 From: leonklingele Date: Mon, 15 May 2023 13:04:58 +0200 Subject: [PATCH 097/162] middleware/adaptor: allow to convert fiber.Ctx to (net/http).Request (#2461) --- docs/api/middleware/adaptor.md | 45 ++++++++++++++++++++++++++---- middleware/adaptor/adaptor.go | 10 +++++++ middleware/adaptor/adaptor_test.go | 25 +++++++++++++++++ 3 files changed, 74 insertions(+), 6 deletions(-) diff --git a/docs/api/middleware/adaptor.md b/docs/api/middleware/adaptor.md index a033fdd733..7e73dd4705 100644 --- a/docs/api/middleware/adaptor.md +++ b/docs/api/middleware/adaptor.md @@ -14,7 +14,8 @@ Converter for net/http handlers to/from Fiber request handlers, special thanks t | FiberHandler | `FiberHandler(h fiber.Handler) http.Handler` | fiber.Handler -> http.Handler | FiberHandlerFunc | `FiberHandlerFunc(h fiber.Handler) http.HandlerFunc` | fiber.Handler -> http.HandlerFunc | FiberApp | `FiberApp(app *fiber.App) http.HandlerFunc` | Fiber app -> http.HandlerFunc -| CopyContextToFiberContex | `CopyContextToFiberContext(context interface{}, requestContext *fasthttp.RequestCtx)` | context.Context -> fasthttp.RequestCtx +| ConvertRequest | `ConvertRequest(c *fiber.Ctx, forServer bool) (*http.Request, error)` | fiber.Ctx -> http.Request +| CopyContextToFiberContext | `CopyContextToFiberContext(context interface{}, requestContext *fasthttp.RequestCtx)` | context.Context -> fasthttp.RequestCtx ## Examples @@ -26,8 +27,8 @@ import ( "fmt" "net/http" - "github.com/gofiber/v2/middleware/adaptor" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/adaptor" ) func main() { @@ -61,8 +62,8 @@ import ( "log" "net/http" - "github.com/gofiber/v2/middleware/adaptor" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/adaptor" ) func main() { @@ -91,8 +92,8 @@ package main import ( "net/http" - "github.com/gofiber/v2/middleware/adaptor" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/adaptor" ) func main() { @@ -116,10 +117,12 @@ func greet(c *fiber.Ctx) error { package main import ( - "github.com/gofiber/v2/middleware/adaptor" - "github.com/gofiber/fiber/v2" "net/http" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/adaptor" ) + func main() { app := fiber.New() @@ -133,3 +136,33 @@ func greet(c *fiber.Ctx) error { return c.SendString("Hello World!") } ``` + +### Fiber Context to (net/http).Request +```go +package main + +import ( + "net/http" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/adaptor" +) + +func main() { + app := fiber.New() + + app.Get("/greet", greetWithHTTPReq) + + // Listen on port 3000 + http.ListenAndServe(":3000", adaptor.FiberApp(app)) +} + +func greetWithHTTPReq(c *fiber.Ctx) error { + httpReq, err := adaptor.ConvertRequest(c, false) + if err != nil { + return err + } + + return c.SendString("Request URL: " + httpReq.URL.String()) +} +``` diff --git a/middleware/adaptor/adaptor.go b/middleware/adaptor/adaptor.go index dac0973edc..7e7b73d307 100644 --- a/middleware/adaptor/adaptor.go +++ b/middleware/adaptor/adaptor.go @@ -27,6 +27,16 @@ func HTTPHandler(h http.Handler) fiber.Handler { } } +// ConvertRequest converts a fiber.Ctx to an http.Request. +// forServer should be set to true when the http.Request is going to be passed to a http.Handler. +func ConvertRequest(c *fiber.Ctx, forServer bool) (*http.Request, error) { + var req http.Request + if err := fasthttpadaptor.ConvertRequest(c.Context(), &req, forServer); err != nil { + return nil, err //nolint:wrapcheck // This must not be wrapped + } + return &req, nil +} + // CopyContextToFiberContext copies the values of context.Context to a fasthttp.RequestCtx func CopyContextToFiberContext(context interface{}, requestContext *fasthttp.RequestCtx) { contextValues := reflect.ValueOf(context).Elem() diff --git a/middleware/adaptor/adaptor_test.go b/middleware/adaptor/adaptor_test.go index fd413a02d7..d04aab9fe1 100644 --- a/middleware/adaptor/adaptor_test.go +++ b/middleware/adaptor/adaptor_test.go @@ -7,11 +7,13 @@ import ( "io" "net" "net/http" + "net/http/httptest" "net/url" "reflect" "testing" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" ) @@ -460,3 +462,26 @@ func (w *netHTTPResponseWriter) Write(p []byte) (int, error) { w.body = append(w.body, p...) return len(p), nil } + +func Test_ConvertRequest(t *testing.T) { + t.Parallel() + + app := fiber.New() + + app.Get("/test", func(c *fiber.Ctx) error { + httpReq, err := ConvertRequest(c, false) + if err != nil { + return err + } + + return c.SendString("Request URL: " + httpReq.URL.String()) + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test?hello=world&another=test", http.NoBody)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, http.StatusOK, resp.StatusCode, "Status code") + + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "Request URL: /test?hello=world&another=test", string(body)) +} From df87a82d5a650ab63c8772419a05d976d59c1d76 Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 17 May 2023 10:51:05 +0200 Subject: [PATCH 098/162] =?UTF-8?q?=F0=9F=90=9B=20Fix=20mount=20route=20po?= =?UTF-8?q?sitioning=20(#2463)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 [Bug-fix]: Wrong handlers execution order in some mount cases #2460 * 🐛 [Bug-fix]: Wrong handlers execution order in some mount cases #2460 * 🐛 [Bug-fix]: Wrong handlers execution order in some mount cases #2460 * [Bug-fix]: Wrong handlers execution order in some mount cases #2460 * [Bug-fix]: Wrong handlers execution order in some mount cases #2460 --- mount.go | 9 ++-- mount_test.go | 124 +++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 101 insertions(+), 32 deletions(-) diff --git a/mount.go b/mount.go index 71a7e61dc1..abb5695e9f 100644 --- a/mount.go +++ b/mount.go @@ -174,27 +174,24 @@ func (app *App) processSubAppsRoutes() { } } var handlersCount uint32 + var routePos uint32 // Iterate over the stack of the parent app for m := range app.stack { - // Keep track of the position shift caused by adding routes for mounted apps - var positionShift uint32 // Iterate over each route in the stack stackLen := len(app.stack[m]) for i := 0; i < stackLen; i++ { route := app.stack[m][i] // Check if the route has a mounted app if !route.mount { + routePos++ // If not, update the route's position and continue - route.pos += positionShift + route.pos = routePos if !route.use || (route.use && m == 0) { handlersCount += uint32(len(route.Handlers)) } continue } - // Update the position shift to account for the mounted app's routes - positionShift = route.pos - // Create a slice to hold the sub-app's routes subRoutes := make([]*Route, len(route.group.app.stack[m])) diff --git a/mount_test.go b/mount_test.go index 8481cf54e5..2ae67fb142 100644 --- a/mount_test.go +++ b/mount_test.go @@ -8,6 +8,7 @@ package fiber import ( "errors" "io" + "net/http" "net/http/httptest" "testing" @@ -25,7 +26,7 @@ func Test_App_Mount(t *testing.T) { app := New() app.Mount("/john", micro) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", http.NoBody)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") utils.AssertEqual(t, uint32(2), app.handlersCount) @@ -45,7 +46,7 @@ func Test_App_Mount_RootPath_Nested(t *testing.T) { dynamic.Mount("/api", apiserver) app.Mount("/", dynamic) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/v1/home", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/v1/home", http.NoBody)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") utils.AssertEqual(t, uint32(2), app.handlersCount) @@ -75,15 +76,15 @@ func Test_App_Mount_Nested(t *testing.T) { return c.SendStatus(StatusOK) }) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/one/doe", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/one/doe", http.NoBody)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/nested", nil)) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/nested", http.NoBody)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/three/test", nil)) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/three/test", http.NoBody)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") @@ -99,12 +100,13 @@ func Test_App_Mount_Express_Behavior(t *testing.T) { return c.SendString(body) } } - testEndpoint := func(app *App, route, expectedBody string) { - resp, err := app.Test(httptest.NewRequest(MethodGet, route, nil)) + testEndpoint := func(app *App, route, expectedBody string, expectedStatusCode int) { + resp, err := app.Test(httptest.NewRequest(MethodGet, route, http.NoBody)) utils.AssertEqual(t, nil, err, "app.Test(req)") body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, expectedBody, string(body), "Response body") + utils.AssertEqual(t, expectedStatusCode, resp.StatusCode, "Status code") + utils.AssertEqual(t, expectedBody, string(body), "Unexpected response body") } app := New() @@ -130,16 +132,86 @@ func Test_App_Mount_Express_Behavior(t *testing.T) { }) } // expectation check - testEndpoint(app, "/world", "subapp world!") - testEndpoint(app, "/hello", "app hello!") - testEndpoint(app, "/bar", "subapp bar!") - testEndpoint(app, "/foo", "subapp foo!") - testEndpoint(app, "/unknown", ErrNotFound.Message) + testEndpoint(app, "/world", "subapp world!", StatusOK) + testEndpoint(app, "/hello", "app hello!", StatusOK) + testEndpoint(app, "/bar", "subapp bar!", StatusOK) + testEndpoint(app, "/foo", "subapp foo!", StatusOK) + testEndpoint(app, "/unknown", ErrNotFound.Message, StatusNotFound) utils.AssertEqual(t, uint32(17), app.handlersCount) utils.AssertEqual(t, uint32(16+9), app.routesCount) } +// go test -run Test_App_Mount_RoutePositions +func Test_App_Mount_RoutePositions(t *testing.T) { + t.Parallel() + testEndpoint := func(app *App, route, expectedBody string) { + resp, err := app.Test(httptest.NewRequest(MethodGet, route, http.NoBody)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + utils.AssertEqual(t, expectedBody, string(body), "Unexpected response body") + } + + app := New() + subApp1 := New() + subApp2 := New() + // app setup + { + app.Use(func(c *Ctx) error { + // set initial value + c.Locals("world", "world") + return c.Next() + }) + app.Mount("/subApp1", subApp1) + app.Use(func(c *Ctx) error { + return c.Next() + }) + app.Get("/bar", func(c *Ctx) error { + return c.SendString("ok") + }) + app.Use(func(c *Ctx) error { + // is overwritten in case the positioning is not correct + c.Locals("world", "hello") + return c.Next() + }) + methods := subApp2.Group("/subApp2") + methods.Get("/world", func(c *Ctx) error { + v, ok := c.Locals("world").(string) + if !ok { + panic("unexpected data type") + } + return c.SendString(v) + }) + app.Mount("", subApp2) + } + + testEndpoint(app, "/subApp2/world", "hello") + + routeStackGET := app.Stack()[0] + utils.AssertEqual(t, true, routeStackGET[0].use) + utils.AssertEqual(t, "/", routeStackGET[0].path) + + utils.AssertEqual(t, true, routeStackGET[1].use) + utils.AssertEqual(t, "/", routeStackGET[1].path) + utils.AssertEqual(t, true, routeStackGET[0].pos < routeStackGET[1].pos, "wrong position of route 0") + + utils.AssertEqual(t, false, routeStackGET[2].use) + utils.AssertEqual(t, "/bar", routeStackGET[2].path) + utils.AssertEqual(t, true, routeStackGET[1].pos < routeStackGET[2].pos, "wrong position of route 1") + + utils.AssertEqual(t, true, routeStackGET[3].use) + utils.AssertEqual(t, "/", routeStackGET[3].path) + utils.AssertEqual(t, true, routeStackGET[2].pos < routeStackGET[3].pos, "wrong position of route 2") + + utils.AssertEqual(t, false, routeStackGET[4].use) + utils.AssertEqual(t, "/subapp2/world", routeStackGET[4].path) + utils.AssertEqual(t, true, routeStackGET[3].pos < routeStackGET[4].pos, "wrong position of route 3") + + utils.AssertEqual(t, 5, len(routeStackGET)) +} + // go test -run Test_App_MountPath func Test_App_MountPath(t *testing.T) { t.Parallel() @@ -174,7 +246,7 @@ func Test_App_ErrorHandler_GroupMount(t *testing.T) { v1 := app.Group("/v1") v1.Mount("/john", micro) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", http.NoBody)) testErrorResponse(t, err, resp, "1: custom error") } @@ -194,7 +266,7 @@ func Test_App_ErrorHandler_GroupMountRootLevel(t *testing.T) { v1 := app.Group("/v1") v1.Mount("/", micro) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", http.NoBody)) testErrorResponse(t, err, resp, "1: custom error") } @@ -210,7 +282,7 @@ func Test_App_Group_Mount(t *testing.T) { v1 := app.Group("/v1") v1.Mount("/john", micro) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", http.NoBody)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") utils.AssertEqual(t, uint32(2), app.handlersCount) @@ -231,7 +303,7 @@ func Test_App_UseParentErrorHandler(t *testing.T) { app.Mount("/api", fiber) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", http.NoBody)) testErrorResponse(t, err, resp, "hi, i'm a custom error") } @@ -250,7 +322,7 @@ func Test_App_UseMountedErrorHandler(t *testing.T) { app.Mount("/api", fiber) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", http.NoBody)) testErrorResponse(t, err, resp, "hi, i'm a custom error") } @@ -269,7 +341,7 @@ func Test_App_UseMountedErrorHandlerRootLevel(t *testing.T) { app.Mount("/", fiber) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", http.NoBody)) testErrorResponse(t, err, resp, "hi, i'm a custom error") } @@ -311,7 +383,7 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { app.Mount("/api", fiber) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub", http.NoBody)) utils.AssertEqual(t, nil, err, "/api/sub req") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") @@ -319,7 +391,7 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") utils.AssertEqual(t, "hi, i'm a custom sub fiber error", string(b), "Response body") - resp2, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub/third", nil)) + resp2, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub/third", http.NoBody)) utils.AssertEqual(t, nil, err, "/api/sub/third req") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") @@ -345,7 +417,7 @@ func Test_Ctx_Render_Mount(t *testing.T) { app := New() app.Mount("/hello", sub) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/a", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/a", http.NoBody)) utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, nil, err, "app.Test(req)") @@ -397,7 +469,7 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { sub.Mount("/bruh", sub2) app.Mount("/hello", sub) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/world/a", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/world/a", http.NoBody)) utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, nil, err, "app.Test(req)") @@ -405,7 +477,7 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

Hello a!

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

Hello, World!

", string(body)) - resp, err = app.Test(httptest.NewRequest(MethodGet, "/hello/bruh/moment", nil)) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/hello/bruh/moment", http.NoBody)) utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, nil, err, "app.Test(req)") @@ -439,7 +511,7 @@ func Test_Ctx_Render_MountGroup(t *testing.T) { v1 := app.Group("/v1") v1.Mount("/john", micro) - resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", http.NoBody)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") From 182f9f09705eab40c61a618835d46faee79c1e49 Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 17 May 2023 15:45:38 +0200 Subject: [PATCH 099/162] =?UTF-8?q?=F0=9F=9A=80=20Speedup=20and=20cleanup?= =?UTF-8?q?=20"path"=20testcases=20and=20benchmarks=20(#2465)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- path_test.go | 1373 ++-------------------------------------- path_testcases_test.go | 718 +++++++++++++++++++++ 2 files changed, 759 insertions(+), 1332 deletions(-) create mode 100644 path_testcases_test.go diff --git a/path_test.go b/path_test.go index 62a421ed3a..65dfdd6f72 100644 --- a/path_test.go +++ b/path_test.go @@ -6,7 +6,6 @@ package fiber import ( "fmt" - "strings" "testing" "github.com/gofiber/fiber/v2/utils" @@ -138,932 +137,38 @@ func Test_Path_parseRoute(t *testing.T) { // go test -race -run Test_Path_matchParams func Test_Path_matchParams(t *testing.T) { t.Parallel() - type testparams struct { - url string - params []string - match bool - partialCheck bool - } var ctxParams [maxParams]string - testCase := func(r string, cases []testparams) { - parser := parseRoute(r) - for _, c := range cases { + testCaseFn := func(testCollection routeCaseCollection) { + parser := parseRoute(testCollection.pattern) + for _, c := range testCollection.testCases { match := parser.getMatch(c.url, c.url, &ctxParams, c.partialCheck) - utils.AssertEqual(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) + utils.AssertEqual(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", testCollection.pattern, c.url)) if match && len(c.params) > 0 { - utils.AssertEqual(t, c.params[0:len(c.params)], ctxParams[0:len(c.params)], fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) + utils.AssertEqual(t, c.params[0:len(c.params)], ctxParams[0:len(c.params)], fmt.Sprintf("route: '%s', url: '%s'", testCollection.pattern, c.url)) } } } - testCase("/api/v1/:param/*", []testparams{ - {url: "/api/v1/entity", params: []string{"entity", ""}, match: true}, - {url: "/api/v1/entity/", params: []string{"entity", ""}, match: true}, - {url: "/api/v1/entity/1", params: []string{"entity", "1"}, match: true}, - {url: "/api/v", params: nil, match: false}, - {url: "/api/v2", params: nil, match: false}, - {url: "/api/v1/", params: nil, match: false}, - }) - testCase("/api/v1/:param/+", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/entity/", params: nil, match: false}, - {url: "/api/v1/entity/1", params: []string{"entity", "1"}, match: true}, - {url: "/api/v", params: nil, match: false}, - {url: "/api/v2", params: nil, match: false}, - {url: "/api/v1/", params: nil, match: false}, - }) - testCase("/api/v1/:param?", []testparams{ - {url: "/api/v1", params: []string{""}, match: true}, - {url: "/api/v1/", params: []string{""}, match: true}, - {url: "/api/v1/optional", params: []string{"optional"}, match: true}, - {url: "/api/v", params: nil, match: false}, - {url: "/api/v2", params: nil, match: false}, - {url: "/api/xyz", params: nil, match: false}, - }) - testCase("/v1/some/resource/name\\:customVerb", []testparams{ - {url: "/v1/some/resource/name:customVerb", params: nil, match: true}, - {url: "/v1/some/resource/name:test", params: nil, match: false}, - }) - testCase("/v1/some/resource/:name\\:customVerb", []testparams{ - {url: "/v1/some/resource/test:customVerb", params: []string{"test"}, match: true}, - {url: "/v1/some/resource/test:test", params: nil, match: false}, - }) - testCase("/v1/some/resource/name\\\\:customVerb?\\?/:param/*", []testparams{ - {url: "/v1/some/resource/name:customVerb??/test/optionalWildCard/character", params: []string{"test", "optionalWildCard/character"}, match: true}, - {url: "/v1/some/resource/name:customVerb??/test", params: []string{"test", ""}, match: true}, - }) - testCase("/api/v1/*", []testparams{ - {url: "/api/v1", params: []string{""}, match: true}, - {url: "/api/v1/", params: []string{""}, match: true}, - {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/entity/1/2", params: []string{"entity/1/2"}, match: true}, - {url: "/api/v1/Entity/1/2", params: []string{"Entity/1/2"}, match: true}, - {url: "/api/v", params: nil, match: false}, - {url: "/api/v2", params: nil, match: false}, - {url: "/api/abc", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/entity/8728382", params: nil, match: false}, - {url: "/api/v1", params: nil, match: false}, - {url: "/api/v1/", params: nil, match: false}, - }) - testCase("/api/v1/:param-:param2", []testparams{ - {url: "/api/v1/entity-entity2", params: []string{"entity", "entity2"}, match: true}, - {url: "/api/v1/entity/8728382", params: nil, match: false}, - {url: "/api/v1/entity-8728382", params: []string{"entity", "8728382"}, match: true}, - {url: "/api/v1", params: nil, match: false}, - {url: "/api/v1/", params: nil, match: false}, - }) - testCase("/api/v1/:filename.:extension", []testparams{ - {url: "/api/v1/test.pdf", params: []string{"test", "pdf"}, match: true}, - {url: "/api/v1/test/pdf", params: nil, match: false}, - {url: "/api/v1/test-pdf", params: nil, match: false}, - {url: "/api/v1/test_pdf", params: nil, match: false}, - {url: "/api/v1", params: nil, match: false}, - {url: "/api/v1/", params: nil, match: false}, - }) - testCase("/api/v1/const", []testparams{ - {url: "/api/v1/const", params: []string{}, match: true}, - {url: "/api/v1", params: nil, match: false}, - {url: "/api/v1/", params: nil, match: false}, - {url: "/api/v1/something", params: nil, match: false}, - }) - testCase("/api/:param/fixedEnd", []testparams{ - {url: "/api/abc/fixedEnd", params: []string{"abc"}, match: true}, - {url: "/api/abc/def/fixedEnd", params: nil, match: false}, - }) - testCase("/shop/product/::filter/color::color/size::size", []testparams{ - {url: "/shop/product/:test/color:blue/size:xs", params: []string{"test", "blue", "xs"}, match: true}, - {url: "/shop/product/test/color:blue/size:xs", params: nil, match: false}, - }) - testCase("/::param?", []testparams{ - {url: "/:hello", params: []string{"hello"}, match: true}, - {url: "/:", params: []string{""}, match: true}, - {url: "/", params: nil, match: false}, - }) - // successive parameters, each take one character and the last parameter gets everything - testCase("/test:sign:param", []testparams{ - {url: "/test-abc", params: []string{"-", "abc"}, match: true}, - {url: "/test", params: nil, match: false}, - }) - // optional parameters are not greedy - testCase("/:param1:param2?:param3", []testparams{ - {url: "/abbbc", params: []string{"a", "b", "bbc"}, match: true}, - // {url: "/ac", params: []string{"a", "", "c"}, match: true}, // TODO: fix it - {url: "/test", params: []string{"t", "e", "st"}, match: true}, - }) - testCase("/test:optional?:mandatory", []testparams{ - // {url: "/testo", params: []string{"", "o"}, match: true}, // TODO: fix it - {url: "/testoaaa", params: []string{"o", "aaa"}, match: true}, - {url: "/test", params: nil, match: false}, - }) - testCase("/test:optional?:optional2?", []testparams{ - {url: "/testo", params: []string{"o", ""}, match: true}, - {url: "/testoaaa", params: []string{"o", "aaa"}, match: true}, - {url: "/test", params: []string{"", ""}, match: true}, - {url: "/tes", params: nil, match: false}, - }) - testCase("/foo:param?bar", []testparams{ - {url: "/foofaselbar", params: []string{"fasel"}, match: true}, - {url: "/foobar", params: []string{""}, match: true}, - {url: "/fooba", params: nil, match: false}, - {url: "/fobar", params: nil, match: false}, - }) - testCase("/foo*bar", []testparams{ - {url: "/foofaselbar", params: []string{"fasel"}, match: true}, - {url: "/foobar", params: []string{""}, match: true}, - {url: "/", params: nil, match: false}, - }) - testCase("/foo+bar", []testparams{ - {url: "/foofaselbar", params: []string{"fasel"}, match: true}, - {url: "/foobar", params: nil, match: false}, - {url: "/", params: nil, match: false}, - }) - testCase("/a*cde*g/", []testparams{ - {url: "/abbbcdefffg", params: []string{"bbb", "fff"}, match: true}, - {url: "/acdeg", params: []string{"", ""}, match: true}, - {url: "/", params: nil, match: false}, - }) - testCase("/*v1*/proxy", []testparams{ - {url: "/customer/v1/cart/proxy", params: []string{"customer/", "/cart"}, match: true}, - {url: "/v1/proxy", params: []string{"", ""}, match: true}, - {url: "/v1/", params: nil, match: false}, - }) - // successive wildcard -> first wildcard is greedy - testCase("/foo***bar", []testparams{ - {url: "/foo*abar", params: []string{"*a", "", ""}, match: true}, - {url: "/foo*bar", params: []string{"*", "", ""}, match: true}, - {url: "/foobar", params: []string{"", "", ""}, match: true}, - {url: "/fooba", params: nil, match: false}, - }) - // chars in front of an parameter - testCase("/name::name", []testparams{ - {url: "/name:john", params: []string{"john"}, match: true}, - }) - testCase("/@:name", []testparams{ - {url: "/@john", params: []string{"john"}, match: true}, - }) - testCase("/-:name", []testparams{ - {url: "/-john", params: []string{"john"}, match: true}, - }) - testCase("/.:name", []testparams{ - {url: "/.john", params: []string{"john"}, match: true}, - }) - testCase("/api/v1/:param/abc/*", []testparams{ - {url: "/api/v1/well/abc/wildcard", params: []string{"well", "wildcard"}, match: true}, - {url: "/api/v1/well/abc/", params: []string{"well", ""}, match: true}, - {url: "/api/v1/well/abc", params: []string{"well", ""}, match: true}, - {url: "/api/v1/well/ttt", params: nil, match: false}, - }) - testCase("/api/:day/:month?/:year?", []testparams{ - {url: "/api/1", params: []string{"1", "", ""}, match: true}, - {url: "/api/1/", params: []string{"1", "", ""}, match: true}, - {url: "/api/1//", params: []string{"1", "", ""}, match: true}, - {url: "/api/1/-/", params: []string{"1", "-", ""}, match: true}, - {url: "/api/1-", params: []string{"1-", "", ""}, match: true}, - {url: "/api/1.", params: []string{"1.", "", ""}, match: true}, - {url: "/api/1/2", params: []string{"1", "2", ""}, match: true}, - {url: "/api/1/2/3", params: []string{"1", "2", "3"}, match: true}, - {url: "/api/", params: nil, match: false}, - }) - testCase("/api/:day.:month?.:year?", []testparams{ - {url: "/api/1", params: nil, match: false}, - {url: "/api/1/", params: nil, match: false}, - {url: "/api/1.", params: nil, match: false}, - {url: "/api/1..", params: []string{"1", "", ""}, match: true}, - {url: "/api/1.2", params: nil, match: false}, - {url: "/api/1.2.", params: []string{"1", "2", ""}, match: true}, - {url: "/api/1.2.3", params: []string{"1", "2", "3"}, match: true}, - {url: "/api/", params: nil, match: false}, - }) - testCase("/api/:day-:month?-:year?", []testparams{ - {url: "/api/1", params: nil, match: false}, - {url: "/api/1/", params: nil, match: false}, - {url: "/api/1-", params: nil, match: false}, - {url: "/api/1--", params: []string{"1", "", ""}, match: true}, - {url: "/api/1-/", params: nil, match: false}, - // {url: "/api/1-/-", params: nil, match: false}, // TODO: fix this part - {url: "/api/1-2", params: nil, match: false}, - {url: "/api/1-2-", params: []string{"1", "2", ""}, match: true}, - {url: "/api/1-2-3", params: []string{"1", "2", "3"}, match: true}, - {url: "/api/", params: nil, match: false}, - }) - testCase("/api/*", []testparams{ - {url: "/api/", params: []string{""}, match: true}, - {url: "/api/joker", params: []string{"joker"}, match: true}, - {url: "/api", params: []string{""}, match: true}, - {url: "/api/v1/entity", params: []string{"v1/entity"}, match: true}, - {url: "/api2/v1/entity", params: nil, match: false}, - {url: "/api_ignore/v1/entity", params: nil, match: false}, - }) - testCase("/partialCheck/foo/bar/:param", []testparams{ - {url: "/partialCheck/foo/bar/test", params: []string{"test"}, match: true, partialCheck: true}, - {url: "/partialCheck/foo/bar/test/test2", params: []string{"test"}, match: true, partialCheck: true}, - {url: "/partialCheck/foo/bar", params: nil, match: false, partialCheck: true}, - {url: "/partiaFoo", params: nil, match: false, partialCheck: true}, - }) - testCase("/", []testparams{ - {url: "/api", params: nil, match: false}, - {url: "", params: []string{}, match: true}, - {url: "/", params: []string{}, match: true}, - }) - testCase("/config/abc.json", []testparams{ - {url: "/config/abc.json", params: []string{}, match: true}, - {url: "config/abc.json", params: nil, match: false}, - {url: "/config/efg.json", params: nil, match: false}, - {url: "/config", params: nil, match: false}, - }) - testCase("/config/*.json", []testparams{ - {url: "/config/abc.json", params: []string{"abc"}, match: true}, - {url: "/config/efg.json", params: []string{"efg"}, match: true}, - {url: "/config/.json", params: []string{""}, match: true}, - {url: "/config/efg.csv", params: nil, match: false}, - {url: "config/abc.json", params: nil, match: false}, - {url: "/config", params: nil, match: false}, - }) - testCase("/config/+.json", []testparams{ - {url: "/config/abc.json", params: []string{"abc"}, match: true}, - {url: "/config/.json", params: nil, match: false}, - {url: "/config/efg.json", params: []string{"efg"}, match: true}, - {url: "/config/efg.csv", params: nil, match: false}, - {url: "config/abc.json", params: nil, match: false}, - {url: "/config", params: nil, match: false}, - }) - testCase("/xyz", []testparams{ - {url: "xyz", params: nil, match: false}, - {url: "xyz/", params: nil, match: false}, - }) - testCase("/api/*/:param?", []testparams{ - {url: "/api/", params: []string{"", ""}, match: true}, - {url: "/api/joker", params: []string{"joker", ""}, match: true}, - {url: "/api/joker/batman", params: []string{"joker", "batman"}, match: true}, - {url: "/api/joker//batman", params: []string{"joker/", "batman"}, match: true}, - {url: "/api/joker/batman/robin", params: []string{"joker/batman", "robin"}, match: true}, - {url: "/api/joker/batman/robin/1", params: []string{"joker/batman/robin", "1"}, match: true}, - {url: "/api/joker/batman/robin/1/", params: []string{"joker/batman/robin/1", ""}, match: true}, - {url: "/api/joker-batman/robin/1", params: []string{"joker-batman/robin", "1"}, match: true}, - {url: "/api/joker-batman-robin/1", params: []string{"joker-batman-robin", "1"}, match: true}, - {url: "/api/joker-batman-robin-1", params: []string{"joker-batman-robin-1", ""}, match: true}, - {url: "/api", params: []string{"", ""}, match: true}, - }) - testCase("/api/*/:param", []testparams{ - {url: "/api/test/abc", params: []string{"test", "abc"}, match: true}, - {url: "/api/joker/batman", params: []string{"joker", "batman"}, match: true}, - {url: "/api/joker/batman/robin", params: []string{"joker/batman", "robin"}, match: true}, - {url: "/api/joker/batman/robin/1", params: []string{"joker/batman/robin", "1"}, match: true}, - {url: "/api/joker/batman-robin/1", params: []string{"joker/batman-robin", "1"}, match: true}, - {url: "/api/joker-batman-robin-1", params: nil, match: false}, - {url: "/api", params: nil, match: false}, - }) - testCase("/api/+/:param", []testparams{ - {url: "/api/test/abc", params: []string{"test", "abc"}, match: true}, - {url: "/api/joker/batman/robin/1", params: []string{"joker/batman/robin", "1"}, match: true}, - {url: "/api/joker", params: nil, match: false}, - {url: "/api", params: nil, match: false}, - }) - testCase("/api/*/:param/:param2", []testparams{ - {url: "/api/test/abc/1", params: []string{"test", "abc", "1"}, match: true}, - {url: "/api/joker/batman", params: nil, match: false}, - {url: "/api/joker/batman-robin/1", params: []string{"joker", "batman-robin", "1"}, match: true}, - {url: "/api/joker-batman-robin-1", params: nil, match: false}, - {url: "/api/test/abc", params: nil, match: false}, - {url: "/api/joker/batman/robin", params: []string{"joker", "batman", "robin"}, match: true}, - {url: "/api/joker/batman/robin/1", params: []string{"joker/batman", "robin", "1"}, match: true}, - {url: "/api/joker/batman/robin/1/2", params: []string{"joker/batman/robin", "1", "2"}, match: true}, - {url: "/api", params: nil, match: false}, - {url: "/api/:test", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/true", params: []string{"true"}, match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/8728382.5", params: []string{"8728382.5"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/#!?", params: nil, match: false}, - {url: "/api/v1/8728382", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/f0fa66cc-d22e-445b-866d-1d76e776371d", params: []string{"f0fa66cc-d22e-445b-866d-1d76e776371d"}, match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/123", params: nil, match: false}, - {url: "/api/v1/12345", params: []string{"12345"}, match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/ent", params: []string{"ent"}, match: true}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/12345", params: []string{"12345"}, match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/123", params: nil, match: false}, - {url: "/api/v1/12345", params: []string{"12345"}, match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/ent", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/e", params: nil, match: false}, - {url: "/api/v1/en", params: []string{"en"}, match: true}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/12345", params: []string{"12345"}, match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/e", params: nil, match: false}, - {url: "/api/v1/en", params: []string{"en"}, match: true}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/12345", params: []string{"12345"}, match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/1", params: nil, match: false}, - {url: "/api/v1/5", params: []string{"5"}, match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/1", params: []string{"1"}, match: true}, - {url: "/api/v1/5", params: []string{"5"}, match: true}, - {url: "/api/v1/15", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/9", params: []string{"9"}, match: true}, - {url: "/api/v1/5", params: []string{"5"}, match: true}, - {url: "/api/v1/15", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/15", params: nil, match: false}, - {url: "/api/v1/peach", params: []string{"peach"}, match: true}, - {url: "/api/v1/p34ch", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/12", params: nil, match: false}, - {url: "/api/v1/xy", params: nil, match: false}, - {url: "/api/v1/test", params: []string{"test"}, match: true}, - {url: "/api/v1/" + strings.Repeat("a", 64), params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/15", params: nil, match: false}, - {url: "/api/v1/2022-08-27", params: []string{"2022-08-27"}, match: true}, - {url: "/api/v1/2022/08-27", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/87283827683", params: nil, match: false}, - {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/87283827683", params: nil, match: false}, - {url: "/api/v1/25", params: []string{"25"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/87283827683", params: []string{"87283827683"}, match: true}, - {url: "/api/v1/25", params: []string{"25"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/87283827683", params: nil, match: false}, - {url: "/api/v1/25", params: []string{"25"}, match: true}, - {url: "/api/v1/1200", params: []string{"1200"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - testCase("/api/v1/:lang/videos/:page", []testparams{ - {url: "/api/v1/try/videos/200", params: nil, match: false}, - {url: "/api/v1/tr/videos/1800", params: nil, match: false}, - {url: "/api/v1/tr/videos/100", params: []string{"tr", "100"}, match: true}, - {url: "/api/v1/e/videos/10", params: nil, match: false}, - }) - testCase("/api/v1/:lang/:page", []testparams{ - {url: "/api/v1/try/200", params: nil, match: false}, - {url: "/api/v1/tr/1800", params: nil, match: false}, - {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, - {url: "/api/v1/e/10", params: nil, match: false}, - }) - testCase("/api/v1/:lang/:page", []testparams{ - {url: "/api/v1/try/200", params: []string{"try", "200"}, match: true}, - {url: "/api/v1/tr/1800", params: nil, match: false}, - {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, - {url: "/api/v1/e/10", params: nil, match: false}, - }) - testCase("/api/v1/:lang/:page", []testparams{ - {url: "/api/v1/try/200", params: nil, match: false}, - {url: "/api/v1/tr/1800", params: []string{"tr", "1800"}, match: true}, - {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, - {url: "/api/v1/e/10", params: nil, match: false}, - }) - testCase("/api/v1/:date/:regex", []testparams{ - {url: "/api/v1/2005-11-01/a", params: nil, match: false}, - {url: "/api/v1/2005-1101/paach", params: nil, match: false}, - {url: "/api/v1/2005-11-01/peach", params: []string{"2005-11-01", "peach"}, match: true}, - }) - testCase("/api/v1/:param?", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - {url: "/api/v1/", params: []string{""}, match: true}, - }) + for _, testCaseCollection := range routeTestCases { + testCaseFn(testCaseCollection) + } } // go test -race -run Test_RoutePatternMatch func Test_RoutePatternMatch(t *testing.T) { t.Parallel() - type testparams struct { - url string - match bool - } - testCase := func(pattern string, cases []testparams) { + testCaseFn := func(pattern string, cases []routeTestCase) { for _, c := range cases { + // skip all cases for partial checks + if c.partialCheck { + continue + } match := RoutePatternMatch(c.url, pattern) utils.AssertEqual(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", pattern, c.url)) } } - testCase("/api/v1/:param/*", []testparams{ - {url: "/api/v1/entity", match: true}, - {url: "/api/v1/entity/", match: true}, - {url: "/api/v1/entity/1", match: true}, - {url: "/api/v", match: false}, - {url: "/api/v2", match: false}, - {url: "/api/v1/", match: false}, - }) - testCase("/api/v1/:param/+", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/entity/", match: false}, - {url: "/api/v1/entity/1", match: true}, - {url: "/api/v", match: false}, - {url: "/api/v2", match: false}, - {url: "/api/v1/", match: false}, - }) - testCase("/api/v1/:param?", []testparams{ - {url: "/api/v1", match: true}, - {url: "/api/v1/", match: true}, - {url: "/api/v1/optional", match: true}, - {url: "/api/v", match: false}, - {url: "/api/v2", match: false}, - {url: "/api/xyz", match: false}, - }) - testCase("/v1/some/resource/name\\:customVerb", []testparams{ - {url: "/v1/some/resource/name:customVerb", match: true}, - {url: "/v1/some/resource/name:test", match: false}, - }) - testCase("/v1/some/resource/:name\\:customVerb", []testparams{ - {url: "/v1/some/resource/test:customVerb", match: true}, - {url: "/v1/some/resource/test:test", match: false}, - }) - testCase("/v1/some/resource/name\\\\:customVerb?\\?/:param/*", []testparams{ - {url: "/v1/some/resource/name:customVerb??/test/optionalWildCard/character", match: true}, - {url: "/v1/some/resource/name:customVerb??/test", match: true}, - }) - testCase("/api/v1/*", []testparams{ - {url: "/api/v1", match: true}, - {url: "/api/v1/", match: true}, - {url: "/api/v1/entity", match: true}, - {url: "/api/v1/entity/1/2", match: true}, - {url: "/api/v1/Entity/1/2", match: true}, - {url: "/api/v", match: false}, - {url: "/api/v2", match: false}, - {url: "/api/abc", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: true}, - {url: "/api/v1/entity/8728382", match: false}, - {url: "/api/v1", match: false}, - {url: "/api/v1/", match: false}, - }) - testCase("/api/v1/:param-:param2", []testparams{ - {url: "/api/v1/entity-entity2", match: true}, - {url: "/api/v1/entity/8728382", match: false}, - {url: "/api/v1/entity-8728382", match: true}, - {url: "/api/v1", match: false}, - {url: "/api/v1/", match: false}, - }) - testCase("/api/v1/:filename.:extension", []testparams{ - {url: "/api/v1/test.pdf", match: true}, - {url: "/api/v1/test/pdf", match: false}, - {url: "/api/v1/test-pdf", match: false}, - {url: "/api/v1/test_pdf", match: false}, - {url: "/api/v1", match: false}, - {url: "/api/v1/", match: false}, - }) - testCase("/api/v1/const", []testparams{ - {url: "/api/v1/const", match: true}, - {url: "/api/v1", match: false}, - {url: "/api/v1/", match: false}, - {url: "/api/v1/something", match: false}, - }) - testCase("/api/:param/fixedEnd", []testparams{ - {url: "/api/abc/fixedEnd", match: true}, - {url: "/api/abc/def/fixedEnd", match: false}, - }) - testCase("/shop/product/::filter/color::color/size::size", []testparams{ - {url: "/shop/product/:test/color:blue/size:xs", match: true}, - {url: "/shop/product/test/color:blue/size:xs", match: false}, - }) - testCase("/::param?", []testparams{ - {url: "/:hello", match: true}, - {url: "/:", match: true}, - {url: "/", match: false}, - }) - // successive parameters, each take one character and the last parameter gets everything - testCase("/test:sign:param", []testparams{ - {url: "/test-abc", match: true}, - {url: "/test", match: false}, - }) - // optional parameters are not greedy - testCase("/:param1:param2?:param3", []testparams{ - {url: "/abbbc", match: true}, - // {url: "/ac", params: []string{"a", "", "c"}, match: true}, // TODO: fix it - {url: "/test", match: true}, - }) - testCase("/test:optional?:mandatory", []testparams{ - // {url: "/testo", params: []string{"", "o"}, match: true}, // TODO: fix it - {url: "/testoaaa", match: true}, - {url: "/test", match: false}, - }) - testCase("/test:optional?:optional2?", []testparams{ - {url: "/testo", match: true}, - {url: "/testoaaa", match: true}, - {url: "/test", match: true}, - {url: "/tes", match: false}, - }) - testCase("/foo:param?bar", []testparams{ - {url: "/foofaselbar", match: true}, - {url: "/foobar", match: true}, - {url: "/fooba", match: false}, - {url: "/fobar", match: false}, - }) - testCase("/foo*bar", []testparams{ - {url: "/foofaselbar", match: true}, - {url: "/foobar", match: true}, - {url: "/", match: false}, - }) - testCase("/foo+bar", []testparams{ - {url: "/foofaselbar", match: true}, - {url: "/foobar", match: false}, - {url: "/", match: false}, - }) - testCase("/a*cde*g/", []testparams{ - {url: "/abbbcdefffg", match: true}, - {url: "/acdeg", match: true}, - {url: "/", match: false}, - }) - testCase("/*v1*/proxy", []testparams{ - {url: "/customer/v1/cart/proxy", match: true}, - {url: "/v1/proxy", match: true}, - {url: "/v1/", match: false}, - }) - // successive wildcard -> first wildcard is greedy - testCase("/foo***bar", []testparams{ - {url: "/foo*abar", match: true}, - {url: "/foo*bar", match: true}, - {url: "/foobar", match: true}, - {url: "/fooba", match: false}, - }) - // chars in front of an parameter - testCase("/name::name", []testparams{ - {url: "/name:john", match: true}, - }) - testCase("/@:name", []testparams{ - {url: "/@john", match: true}, - }) - testCase("/-:name", []testparams{ - {url: "/-john", match: true}, - }) - testCase("/.:name", []testparams{ - {url: "/.john", match: true}, - }) - testCase("/api/v1/:param/abc/*", []testparams{ - {url: "/api/v1/well/abc/wildcard", match: true}, - {url: "/api/v1/well/abc/", match: true}, - {url: "/api/v1/well/abc", match: true}, - {url: "/api/v1/well/ttt", match: false}, - }) - testCase("/api/:day/:month?/:year?", []testparams{ - {url: "/api/1", match: true}, - {url: "/api/1/", match: true}, - {url: "/api/1//", match: true}, - {url: "/api/1/-/", match: true}, - {url: "/api/1-", match: true}, - {url: "/api/1.", match: true}, - {url: "/api/1/2", match: true}, - {url: "/api/1/2/3", match: true}, - {url: "/api/", match: false}, - }) - testCase("/api/:day.:month?.:year?", []testparams{ - {url: "/api/1", match: false}, - {url: "/api/1/", match: false}, - {url: "/api/1.", match: false}, - {url: "/api/1..", match: true}, - {url: "/api/1.2", match: false}, - {url: "/api/1.2.", match: true}, - {url: "/api/1.2.3", match: true}, - {url: "/api/", match: false}, - }) - testCase("/api/:day-:month?-:year?", []testparams{ - {url: "/api/1", match: false}, - {url: "/api/1/", match: false}, - {url: "/api/1-", match: false}, - {url: "/api/1--", match: true}, - {url: "/api/1-/", match: false}, - // {url: "/api/1-/-", params: nil, match: false}, // TODO: fix this part - {url: "/api/1-2", match: false}, - {url: "/api/1-2-", match: true}, - {url: "/api/1-2-3", match: true}, - {url: "/api/", match: false}, - }) - testCase("/api/*", []testparams{ - {url: "/api/", match: true}, - {url: "/api/joker", match: true}, - {url: "/api", match: true}, - {url: "/api/v1/entity", match: true}, - {url: "/api2/v1/entity", match: false}, - {url: "/api_ignore/v1/entity", match: false}, - }) - testCase("/", []testparams{ - {url: "/api", match: false}, - {url: "", match: true}, - {url: "/", match: true}, - }) - testCase("/config/abc.json", []testparams{ - {url: "/config/abc.json", match: true}, - {url: "config/abc.json", match: false}, - {url: "/config/efg.json", match: false}, - {url: "/config", match: false}, - }) - testCase("/config/*.json", []testparams{ - {url: "/config/abc.json", match: true}, - {url: "/config/efg.json", match: true}, - {url: "/config/.json", match: true}, - {url: "/config/efg.csv", match: false}, - {url: "config/abc.json", match: false}, - {url: "/config", match: false}, - }) - testCase("/config/+.json", []testparams{ - {url: "/config/abc.json", match: true}, - {url: "/config/.json", match: false}, - {url: "/config/efg.json", match: true}, - {url: "/config/efg.csv", match: false}, - {url: "config/abc.json", match: false}, - {url: "/config", match: false}, - }) - testCase("/xyz", []testparams{ - {url: "xyz", match: false}, - {url: "xyz/", match: false}, - }) - testCase("/api/*/:param?", []testparams{ - {url: "/api/", match: true}, - {url: "/api/joker", match: true}, - {url: "/api/joker/batman", match: true}, - {url: "/api/joker//batman", match: true}, - {url: "/api/joker/batman/robin", match: true}, - {url: "/api/joker/batman/robin/1", match: true}, - {url: "/api/joker/batman/robin/1/", match: true}, - {url: "/api/joker-batman/robin/1", match: true}, - {url: "/api/joker-batman-robin/1", match: true}, - {url: "/api/joker-batman-robin-1", match: true}, - {url: "/api", match: true}, - }) - testCase("/api/*/:param", []testparams{ - {url: "/api/test/abc", match: true}, - {url: "/api/joker/batman", match: true}, - {url: "/api/joker/batman/robin", match: true}, - {url: "/api/joker/batman/robin/1", match: true}, - {url: "/api/joker/batman-robin/1", match: true}, - {url: "/api/joker-batman-robin-1", match: false}, - {url: "/api", match: false}, - }) - testCase("/api/+/:param", []testparams{ - {url: "/api/test/abc", match: true}, - {url: "/api/joker/batman/robin/1", match: true}, - {url: "/api/joker", match: false}, - {url: "/api", match: false}, - }) - testCase("/api/*/:param/:param2", []testparams{ - {url: "/api/test/abc/1", match: true}, - {url: "/api/joker/batman", match: false}, - {url: "/api/joker/batman-robin/1", match: true}, - {url: "/api/joker-batman-robin-1", match: false}, - {url: "/api/test/abc", match: false}, - {url: "/api/joker/batman/robin", match: true}, - {url: "/api/joker/batman/robin/1", match: true}, - {url: "/api/joker/batman/robin/1/2", match: true}, - {url: "/api", match: false}, - {url: "/api/:test", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: true}, - {url: "/api/v1/true", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/true", match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: true}, - {url: "/api/v1/8728382.5", match: true}, - {url: "/api/v1/true", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: true}, - {url: "/api/v1/#!?", match: false}, - {url: "/api/v1/8728382", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/f0fa66cc-d22e-445b-866d-1d76e776371d", match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: true}, - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/8728382", match: true}, - {url: "/api/v1/123", match: false}, - {url: "/api/v1/12345", match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/ent", match: true}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/123", match: true}, - {url: "/api/v1/12345", match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/123", match: false}, - {url: "/api/v1/12345", match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/ent", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/e", match: false}, - {url: "/api/v1/en", match: true}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/123", match: true}, - {url: "/api/v1/12345", match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/e", match: false}, - {url: "/api/v1/en", match: true}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/123", match: true}, - {url: "/api/v1/12345", match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/1", match: false}, - {url: "/api/v1/5", match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/1", match: true}, - {url: "/api/v1/5", match: true}, - {url: "/api/v1/15", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/9", match: true}, - {url: "/api/v1/5", match: true}, - {url: "/api/v1/15", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/2005-11-01", match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/15", match: false}, - {url: "/api/v1/peach", match: true}, - {url: "/api/v1/p34ch", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/15", match: false}, - {url: "/api/v1/2022-08-27", match: true}, - {url: "/api/v1/2022/08-27", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: true}, - {url: "/api/v1/true", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/123", match: true}, - {url: "/api/v1/true", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/87283827683", match: false}, - {url: "/api/v1/123", match: true}, - {url: "/api/v1/true", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/87283827683", match: false}, - {url: "/api/v1/25", match: true}, - {url: "/api/v1/true", match: false}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: true}, - {url: "/api/v1/87283827683", match: true}, - {url: "/api/v1/25", match: true}, - {url: "/api/v1/true", match: true}, - }) - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/87283827683", match: false}, - {url: "/api/v1/25", match: true}, - {url: "/api/v1/1200", match: true}, - {url: "/api/v1/true", match: false}, - }) - testCase("/api/v1/:lang/videos/:page", []testparams{ - {url: "/api/v1/try/videos/200", match: false}, - {url: "/api/v1/tr/videos/1800", match: false}, - {url: "/api/v1/tr/videos/100", match: true}, - {url: "/api/v1/e/videos/10", match: false}, - }) - testCase("/api/v1/:lang/:page", []testparams{ - {url: "/api/v1/try/200", match: false}, - {url: "/api/v1/tr/1800", match: false}, - {url: "/api/v1/tr/100", match: true}, - {url: "/api/v1/e/10", match: false}, - }) - testCase("/api/v1/:lang/:page", []testparams{ - {url: "/api/v1/try/200", match: true}, - {url: "/api/v1/tr/1800", match: false}, - {url: "/api/v1/tr/100", match: true}, - {url: "/api/v1/e/10", match: false}, - }) - testCase("/api/v1/:lang/:page", []testparams{ - {url: "/api/v1/try/200", match: false}, - {url: "/api/v1/tr/1800", match: true}, - {url: "/api/v1/tr/100", match: true}, - {url: "/api/v1/e/10", match: false}, - }) - testCase("/api/v1/:date/:regex", []testparams{ - {url: "/api/v1/2005-11-01/a", match: false}, - {url: "/api/v1/2005-1101/paach", match: false}, - {url: "/api/v1/2005-11-01/peach", match: true}, - }) + for _, testCase := range routeTestCases { + testCaseFn(testCase.pattern, testCase.testCases) + } } func Test_Utils_GetTrimmedParam(t *testing.T) { @@ -1092,457 +197,61 @@ func Test_Utils_RemoveEscapeChar(t *testing.T) { // go test -race -run Test_Path_matchParams func Benchmark_Path_matchParams(t *testing.B) { - type testparams struct { - url string - params []string - match bool - partialCheck bool - } var ctxParams [maxParams]string - benchCase := func(r string, cases []testparams) { - parser := parseRoute(r) - for _, c := range cases { + benchCaseFn := func(testCollection routeCaseCollection) { + parser := parseRoute(testCollection.pattern) + for _, c := range testCollection.testCases { var matchRes bool state := "match" if !c.match { state = "not match" } - t.Run(r+" | "+state+" | "+c.url, func(b *testing.B) { + t.Run(testCollection.pattern+" | "+state+" | "+c.url, func(b *testing.B) { for i := 0; i <= b.N; i++ { if match := parser.getMatch(c.url, c.url, &ctxParams, c.partialCheck); match { - // Get params from the original path + // Get testCases from the original path matchRes = true } } - utils.AssertEqual(t, c.match, matchRes, fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) + utils.AssertEqual(t, c.match, matchRes, fmt.Sprintf("route: '%s', url: '%s'", testCollection.pattern, c.url)) if matchRes && len(c.params) > 0 { - utils.AssertEqual(t, c.params[0:len(c.params)-1], ctxParams[0:len(c.params)-1], fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) + utils.AssertEqual(t, c.params[0:len(c.params)-1], ctxParams[0:len(c.params)-1], fmt.Sprintf("route: '%s', url: '%s'", testCollection.pattern, c.url)) } }) } } - benchCase("/api/:param/fixedEnd", []testparams{ - {url: "/api/abc/fixedEnd", params: []string{"abc"}, match: true}, - {url: "/api/abc/def/fixedEnd", params: nil, match: false}, - }) - benchCase("/api/v1/:param/*", []testparams{ - {url: "/api/v1/entity", params: []string{"entity", ""}, match: true}, - {url: "/api/v1/entity/", params: []string{"entity", ""}, match: true}, - {url: "/api/v1/entity/1", params: []string{"entity", "1"}, match: true}, - {url: "/api/v", params: nil, match: false}, - {url: "/api/v2", params: nil, match: false}, - {url: "/api/v1/", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/entity/8728382", params: nil, match: false}, - {url: "/api/v1", params: nil, match: false}, - {url: "/api/v1/", params: nil, match: false}, - }) - benchCase("/api/v1", []testparams{ - {url: "/api/v1", params: []string{}, match: true}, - {url: "/api/v2", params: nil, match: false}, - }) - benchCase("/api/v1/:param/*", []testparams{ - {url: "/api/v1/entity", params: []string{"entity", ""}, match: true}, - {url: "/api/v1/entity/", params: []string{"entity", ""}, match: true}, - {url: "/api/v1/entity/1", params: []string{"entity", "1"}, match: true}, - {url: "/api/v", params: nil, match: false}, - {url: "/api/v2", params: nil, match: false}, - {url: "/api/v1/", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/true", params: []string{"true"}, match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/8728382.5", params: []string{"8728382.5"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/#!?", params: nil, match: false}, - {url: "/api/v1/8728382", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/f0fa66cc-d22e-445b-866d-1d76e776371d", params: []string{"f0fa66cc-d22e-445b-866d-1d76e776371d"}, match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/123", params: nil, match: false}, - {url: "/api/v1/12345", params: []string{"12345"}, match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/ent", params: []string{"ent"}, match: true}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/12345", params: []string{"12345"}, match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/123", params: nil, match: false}, - {url: "/api/v1/12345", params: []string{"12345"}, match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/ent", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/e", params: nil, match: false}, - {url: "/api/v1/en", params: []string{"en"}, match: true}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/12345", params: []string{"12345"}, match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/e", params: nil, match: false}, - {url: "/api/v1/en", params: []string{"en"}, match: true}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/12345", params: []string{"12345"}, match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/1", params: nil, match: false}, - {url: "/api/v1/5", params: []string{"5"}, match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/1", params: []string{"1"}, match: true}, - {url: "/api/v1/5", params: []string{"5"}, match: true}, - {url: "/api/v1/15", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/9", params: []string{"9"}, match: true}, - {url: "/api/v1/5", params: []string{"5"}, match: true}, - {url: "/api/v1/15", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/15", params: nil, match: false}, - {url: "/api/v1/peach", params: []string{"peach"}, match: true}, - {url: "/api/v1/p34ch", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: nil, match: false}, - {url: "/api/v1/15", params: nil, match: false}, - {url: "/api/v1/2022-08-27", params: []string{"2022-08-27"}, match: true}, - {url: "/api/v1/2022/08-27", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: nil, match: false}, - {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/87283827683", params: nil, match: false}, - {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/87283827683", params: nil, match: false}, - {url: "/api/v1/25", params: []string{"25"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/87283827683", params: []string{"87283827683"}, match: true}, - {url: "/api/v1/25", params: []string{"25"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/87283827683", params: nil, match: false}, - {url: "/api/v1/25", params: []string{"25"}, match: true}, - {url: "/api/v1/1200", params: []string{"1200"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - }) - benchCase("/api/v1/:lang/videos/:page", []testparams{ - {url: "/api/v1/try/videos/200", params: nil, match: false}, - {url: "/api/v1/tr/videos/1800", params: nil, match: false}, - {url: "/api/v1/tr/videos/100", params: []string{"tr", "100"}, match: true}, - {url: "/api/v1/e/videos/10", params: nil, match: false}, - }) - benchCase("/api/v1/:lang/:page", []testparams{ - {url: "/api/v1/try/200", params: nil, match: false}, - {url: "/api/v1/tr/1800", params: nil, match: false}, - {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, - {url: "/api/v1/e/10", params: nil, match: false}, - }) - benchCase("/api/v1/:lang/:page", []testparams{ - {url: "/api/v1/try/200", params: []string{"try", "200"}, match: true}, - {url: "/api/v1/tr/1800", params: nil, match: false}, - {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, - {url: "/api/v1/e/10", params: nil, match: false}, - }) - benchCase("/api/v1/:lang/:page", []testparams{ - {url: "/api/v1/try/200", params: nil, match: false}, - {url: "/api/v1/tr/1800", params: []string{"tr", "1800"}, match: true}, - {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, - {url: "/api/v1/e/10", params: nil, match: false}, - }) - benchCase("/api/v1/:date/:regex", []testparams{ - {url: "/api/v1/2005-11-01/a", params: nil, match: false}, - {url: "/api/v1/2005-1101/paach", params: nil, match: false}, - {url: "/api/v1/2005-11-01/peach", params: []string{"2005-11-01", "peach"}, match: true}, - }) - benchCase("/api/v1/:param?", []testparams{ - {url: "/api/v1/entity", params: nil, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/true", params: nil, match: false}, - {url: "/api/v1/", params: []string{""}, match: true}, - }) + + for _, testCollection := range benchmarkCases { + benchCaseFn(testCollection) + } } // go test -race -run Test_RoutePatternMatch func Benchmark_RoutePatternMatch(t *testing.B) { - type testparams struct { - url string - match bool - } - benchCase := func(pattern string, cases []testparams) { - for _, c := range cases { + benchCaseFn := func(testCollection routeCaseCollection) { + for _, c := range testCollection.testCases { + // skip all cases for partial checks + if c.partialCheck { + continue + } var matchRes bool state := "match" if !c.match { state = "not match" } - t.Run(pattern+" | "+state+" | "+c.url, func(b *testing.B) { + t.Run(testCollection.pattern+" | "+state+" | "+c.url, func(b *testing.B) { for i := 0; i <= b.N; i++ { - if match := RoutePatternMatch(c.url, pattern); match { - // Get params from the original path + if match := RoutePatternMatch(c.url, testCollection.pattern); match { + // Get testCases from the original path matchRes = true } } - utils.AssertEqual(t, c.match, matchRes, fmt.Sprintf("route: '%s', url: '%s'", pattern, c.url)) + utils.AssertEqual(t, c.match, matchRes, fmt.Sprintf("route: '%s', url: '%s'", testCollection.pattern, c.url)) }) } } - benchCase("/api/:param/fixedEnd", []testparams{ - {url: "/api/abc/fixedEnd", match: true}, - {url: "/api/abc/def/fixedEnd", match: false}, - }) - benchCase("/api/v1/:param/*", []testparams{ - {url: "/api/v1/entity", match: true}, - {url: "/api/v1/entity/", match: true}, - {url: "/api/v1/entity/1", match: true}, - {url: "/api/v", match: false}, - {url: "/api/v2", match: false}, - {url: "/api/v1/", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: true}, - {url: "/api/v1/entity/8728382", match: false}, - {url: "/api/v1", match: false}, - {url: "/api/v1/", match: false}, - }) - benchCase("/api/v1", []testparams{ - {url: "/api/v1", match: true}, - {url: "/api/v2", match: false}, - }) - benchCase("/api/v1/:param/*", []testparams{ - {url: "/api/v1/entity", match: true}, - {url: "/api/v1/entity/", match: true}, - {url: "/api/v1/entity/1", match: true}, - {url: "/api/v", match: false}, - {url: "/api/v2", match: false}, - {url: "/api/v1/", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: true}, - {url: "/api/v1/true", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/true", match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: true}, - {url: "/api/v1/8728382.5", match: true}, - {url: "/api/v1/true", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: true}, - {url: "/api/v1/#!?", match: false}, - {url: "/api/v1/8728382", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/f0fa66cc-d22e-445b-866d-1d76e776371d", match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: true}, - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/8728382", match: true}, - {url: "/api/v1/123", match: false}, - {url: "/api/v1/12345", match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/ent", match: true}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/123", match: true}, - {url: "/api/v1/12345", match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/123", match: false}, - {url: "/api/v1/12345", match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/ent", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/e", match: false}, - {url: "/api/v1/en", match: true}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/123", match: true}, - {url: "/api/v1/12345", match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/e", match: false}, - {url: "/api/v1/en", match: true}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/123", match: true}, - {url: "/api/v1/12345", match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/1", match: false}, - {url: "/api/v1/5", match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/1", match: true}, - {url: "/api/v1/5", match: true}, - {url: "/api/v1/15", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/9", match: true}, - {url: "/api/v1/5", match: true}, - {url: "/api/v1/15", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/2005-11-01", match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/15", match: false}, - {url: "/api/v1/peach", match: true}, - {url: "/api/v1/p34ch", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", match: false}, - {url: "/api/v1/15", match: false}, - {url: "/api/v1/2022-08-27", match: true}, - {url: "/api/v1/2022/08-27", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: true}, - {url: "/api/v1/true", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/8728382", match: false}, - {url: "/api/v1/123", match: true}, - {url: "/api/v1/true", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/87283827683", match: false}, - {url: "/api/v1/123", match: true}, - {url: "/api/v1/true", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/87283827683", match: false}, - {url: "/api/v1/25", match: true}, - {url: "/api/v1/true", match: false}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: true}, - {url: "/api/v1/87283827683", match: true}, - {url: "/api/v1/25", match: true}, - {url: "/api/v1/true", match: true}, - }) - benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", match: false}, - {url: "/api/v1/87283827683", match: false}, - {url: "/api/v1/25", match: true}, - {url: "/api/v1/1200", match: true}, - {url: "/api/v1/true", match: false}, - }) - benchCase("/api/v1/:lang/videos/:page", []testparams{ - {url: "/api/v1/try/videos/200", match: false}, - {url: "/api/v1/tr/videos/1800", match: false}, - {url: "/api/v1/tr/videos/100", match: true}, - {url: "/api/v1/e/videos/10", match: false}, - }) - benchCase("/api/v1/:lang/:page", []testparams{ - {url: "/api/v1/try/200", match: false}, - {url: "/api/v1/tr/1800", match: false}, - {url: "/api/v1/tr/100", match: true}, - {url: "/api/v1/e/10", match: false}, - }) - benchCase("/api/v1/:lang/:page", []testparams{ - {url: "/api/v1/try/200", match: true}, - {url: "/api/v1/tr/1800", match: false}, - {url: "/api/v1/tr/100", match: true}, - {url: "/api/v1/e/10", match: false}, - }) - benchCase("/api/v1/:lang/:page", []testparams{ - {url: "/api/v1/try/200", match: false}, - {url: "/api/v1/tr/1800", match: true}, - {url: "/api/v1/tr/100", match: true}, - {url: "/api/v1/e/10", match: false}, - }) - benchCase("/api/v1/:date/:regex", []testparams{ - {url: "/api/v1/2005-11-01/a", match: false}, - {url: "/api/v1/2005-1101/paach", match: false}, - {url: "/api/v1/2005-11-01/peach", match: true}, - }) + + for _, testCollection := range benchmarkCases { + benchCaseFn(testCollection) + } } diff --git a/path_testcases_test.go b/path_testcases_test.go new file mode 100644 index 0000000000..5602a0284d --- /dev/null +++ b/path_testcases_test.go @@ -0,0 +1,718 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 📝 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package fiber + +import ( + "strings" +) + +type routeTestCase struct { + url string + match bool + params []string + partialCheck bool +} + +type routeCaseCollection struct { + pattern string + testCases []routeTestCase +} + +var ( + benchmarkCases []routeCaseCollection + routeTestCases []routeCaseCollection +) + +func init() { + // smaller list for benchmark cases + benchmarkCases = []routeCaseCollection{ + { + pattern: "/api/v1/const", + testCases: []routeTestCase{ + {url: "/api/v1/const", params: []string{}, match: true}, + {url: "/api/v1", params: nil, match: false}, + {url: "/api/v1/", params: nil, match: false}, + {url: "/api/v1/something", params: nil, match: false}, + }, + }, + { + pattern: "/api/:param/fixedEnd", + testCases: []routeTestCase{ + {url: "/api/abc/fixedEnd", params: []string{"abc"}, match: true}, + {url: "/api/abc/def/fixedEnd", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param/*", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: []string{"entity", ""}, match: true}, + {url: "/api/v1/entity/", params: []string{"entity", ""}, match: true}, + {url: "/api/v1/entity/1", params: []string{"entity", "1"}, match: true}, + {url: "/api/v", params: nil, match: false}, + {url: "/api/v2", params: nil, match: false}, + {url: "/api/v1/", params: nil, match: false}, + }, + }, + } + + // combine benchmark cases and other cases + routeTestCases = benchmarkCases + routeTestCases = append( + routeTestCases, + []routeCaseCollection{ + { + pattern: "/api/v1/:param/+", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/entity/", params: nil, match: false}, + {url: "/api/v1/entity/1", params: []string{"entity", "1"}, match: true}, + {url: "/api/v", params: nil, match: false}, + {url: "/api/v2", params: nil, match: false}, + {url: "/api/v1/", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param?", + testCases: []routeTestCase{ + {url: "/api/v1", params: []string{""}, match: true}, + {url: "/api/v1/", params: []string{""}, match: true}, + {url: "/api/v1/optional", params: []string{"optional"}, match: true}, + {url: "/api/v", params: nil, match: false}, + {url: "/api/v2", params: nil, match: false}, + {url: "/api/xyz", params: nil, match: false}, + }, + }, + { + pattern: "/v1/some/resource/name\\:customVerb", + testCases: []routeTestCase{ + {url: "/v1/some/resource/name:customVerb", params: nil, match: true}, + {url: "/v1/some/resource/name:test", params: nil, match: false}, + }, + }, + { + pattern: "/v1/some/resource/:name\\:customVerb", + testCases: []routeTestCase{ + {url: "/v1/some/resource/test:customVerb", params: []string{"test"}, match: true}, + {url: "/v1/some/resource/test:test", params: nil, match: false}, + }, + }, + { + pattern: "/v1/some/resource/name\\\\:customVerb?\\?/:param/*", + testCases: []routeTestCase{ + {url: "/v1/some/resource/name:customVerb??/test/optionalWildCard/character", params: []string{"test", "optionalWildCard/character"}, match: true}, + {url: "/v1/some/resource/name:customVerb??/test", params: []string{"test", ""}, match: true}, + }, + }, + { + pattern: "/api/v1/*", + testCases: []routeTestCase{ + {url: "/api/v1", params: []string{""}, match: true}, + {url: "/api/v1/", params: []string{""}, match: true}, + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/entity/1/2", params: []string{"entity/1/2"}, match: true}, + {url: "/api/v1/Entity/1/2", params: []string{"Entity/1/2"}, match: true}, + {url: "/api/v", params: nil, match: false}, + {url: "/api/v2", params: nil, match: false}, + {url: "/api/abc", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/entity/8728382", params: nil, match: false}, + {url: "/api/v1", params: nil, match: false}, + {url: "/api/v1/", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param-:param2", + testCases: []routeTestCase{ + {url: "/api/v1/entity-entity2", params: []string{"entity", "entity2"}, match: true}, + {url: "/api/v1/entity/8728382", params: nil, match: false}, + {url: "/api/v1/entity-8728382", params: []string{"entity", "8728382"}, match: true}, + {url: "/api/v1", params: nil, match: false}, + {url: "/api/v1/", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:filename.:extension", + testCases: []routeTestCase{ + {url: "/api/v1/test.pdf", params: []string{"test", "pdf"}, match: true}, + {url: "/api/v1/test/pdf", params: nil, match: false}, + {url: "/api/v1/test-pdf", params: nil, match: false}, + {url: "/api/v1/test_pdf", params: nil, match: false}, + {url: "/api/v1", params: nil, match: false}, + {url: "/api/v1/", params: nil, match: false}, + }, + }, + { + pattern: "/shop/product/::filter/color::color/size::size", + testCases: []routeTestCase{ + {url: "/shop/product/:test/color:blue/size:xs", params: []string{"test", "blue", "xs"}, match: true}, + {url: "/shop/product/test/color:blue/size:xs", params: nil, match: false}, + }, + }, + { + pattern: "/::param?", + testCases: []routeTestCase{ + {url: "/:hello", params: []string{"hello"}, match: true}, + {url: "/:", params: []string{""}, match: true}, + {url: "/", params: nil, match: false}, + }, + }, + // successive parameters, each take one character and the last parameter gets everything + { + pattern: "/test:sign:param", + testCases: []routeTestCase{ + {url: "/test-abc", params: []string{"-", "abc"}, match: true}, + {url: "/test", params: nil, match: false}, + }, + }, + // optional parameters are not greedy + { + pattern: "/:param1:param2?:param3", + testCases: []routeTestCase{ + {url: "/abbbc", params: []string{"a", "b", "bbc"}, match: true}, + // {url: "/ac", testCases: []string{"a", "", "c"}, match: true}, // TODO: fix it + {url: "/test", params: []string{"t", "e", "st"}, match: true}, + }, + }, + { + pattern: "/test:optional?:mandatory", + testCases: []routeTestCase{ + // {url: "/testo", testCases: []string{"", "o"}, match: true}, // TODO: fix it + {url: "/testoaaa", params: []string{"o", "aaa"}, match: true}, + {url: "/test", params: nil, match: false}, + }, + }, + { + pattern: "/test:optional?:optional2?", + testCases: []routeTestCase{ + {url: "/testo", params: []string{"o", ""}, match: true}, + {url: "/testoaaa", params: []string{"o", "aaa"}, match: true}, + {url: "/test", params: []string{"", ""}, match: true}, + {url: "/tes", params: nil, match: false}, + }, + }, + { + pattern: "/foo:param?bar", + testCases: []routeTestCase{ + {url: "/foofaselbar", params: []string{"fasel"}, match: true}, + {url: "/foobar", params: []string{""}, match: true}, + {url: "/fooba", params: nil, match: false}, + {url: "/fobar", params: nil, match: false}, + }, + }, + { + pattern: "/foo*bar", + testCases: []routeTestCase{ + {url: "/foofaselbar", params: []string{"fasel"}, match: true}, + {url: "/foobar", params: []string{""}, match: true}, + {url: "/", params: nil, match: false}, + }, + }, + { + pattern: "/foo+bar", + testCases: []routeTestCase{ + {url: "/foofaselbar", params: []string{"fasel"}, match: true}, + {url: "/foobar", params: nil, match: false}, + {url: "/", params: nil, match: false}, + }, + }, + { + pattern: "/a*cde*g/", + testCases: []routeTestCase{ + {url: "/abbbcdefffg", params: []string{"bbb", "fff"}, match: true}, + {url: "/acdeg", params: []string{"", ""}, match: true}, + {url: "/", params: nil, match: false}, + }, + }, + { + pattern: "/*v1*/proxy", + testCases: []routeTestCase{ + {url: "/customer/v1/cart/proxy", params: []string{"customer/", "/cart"}, match: true}, + {url: "/v1/proxy", params: []string{"", ""}, match: true}, + {url: "/v1/", params: nil, match: false}, + }, + }, + // successive wildcard -> first wildcard is greedy + { + pattern: "/foo***bar", + testCases: []routeTestCase{ + {url: "/foo*abar", params: []string{"*a", "", ""}, match: true}, + {url: "/foo*bar", params: []string{"*", "", ""}, match: true}, + {url: "/foobar", params: []string{"", "", ""}, match: true}, + {url: "/fooba", params: nil, match: false}, + }, + }, + // chars in front of an parameter + { + pattern: "/name::name", + testCases: []routeTestCase{ + {url: "/name:john", params: []string{"john"}, match: true}, + }, + }, + { + pattern: "/@:name", + testCases: []routeTestCase{ + {url: "/@john", params: []string{"john"}, match: true}, + }, + }, + { + pattern: "/-:name", + testCases: []routeTestCase{ + {url: "/-john", params: []string{"john"}, match: true}, + }, + }, + { + pattern: "/.:name", + testCases: []routeTestCase{ + {url: "/.john", params: []string{"john"}, match: true}, + }, + }, + { + pattern: "/api/v1/:param/abc/*", + testCases: []routeTestCase{ + {url: "/api/v1/well/abc/wildcard", params: []string{"well", "wildcard"}, match: true}, + {url: "/api/v1/well/abc/", params: []string{"well", ""}, match: true}, + {url: "/api/v1/well/abc", params: []string{"well", ""}, match: true}, + {url: "/api/v1/well/ttt", params: nil, match: false}, + }, + }, + { + pattern: "/api/:day/:month?/:year?", + testCases: []routeTestCase{ + {url: "/api/1", params: []string{"1", "", ""}, match: true}, + {url: "/api/1/", params: []string{"1", "", ""}, match: true}, + {url: "/api/1//", params: []string{"1", "", ""}, match: true}, + {url: "/api/1/-/", params: []string{"1", "-", ""}, match: true}, + {url: "/api/1-", params: []string{"1-", "", ""}, match: true}, + {url: "/api/1.", params: []string{"1.", "", ""}, match: true}, + {url: "/api/1/2", params: []string{"1", "2", ""}, match: true}, + {url: "/api/1/2/3", params: []string{"1", "2", "3"}, match: true}, + {url: "/api/", params: nil, match: false}, + }, + }, + { + pattern: "/api/:day.:month?.:year?", + testCases: []routeTestCase{ + {url: "/api/1", params: nil, match: false}, + {url: "/api/1/", params: nil, match: false}, + {url: "/api/1.", params: nil, match: false}, + {url: "/api/1..", params: []string{"1", "", ""}, match: true}, + {url: "/api/1.2", params: nil, match: false}, + {url: "/api/1.2.", params: []string{"1", "2", ""}, match: true}, + {url: "/api/1.2.3", params: []string{"1", "2", "3"}, match: true}, + {url: "/api/", params: nil, match: false}, + }, + }, + { + pattern: "/api/:day-:month?-:year?", + testCases: []routeTestCase{ + {url: "/api/1", params: nil, match: false}, + {url: "/api/1/", params: nil, match: false}, + {url: "/api/1-", params: nil, match: false}, + {url: "/api/1--", params: []string{"1", "", ""}, match: true}, + {url: "/api/1-/", params: nil, match: false}, + // {url: "/api/1-/-", testCases: nil, match: false}, // TODO: fix this part + {url: "/api/1-2", params: nil, match: false}, + {url: "/api/1-2-", params: []string{"1", "2", ""}, match: true}, + {url: "/api/1-2-3", params: []string{"1", "2", "3"}, match: true}, + {url: "/api/", params: nil, match: false}, + }, + }, + { + pattern: "/api/*", + testCases: []routeTestCase{ + {url: "/api/", params: []string{""}, match: true}, + {url: "/api/joker", params: []string{"joker"}, match: true}, + {url: "/api", params: []string{""}, match: true}, + {url: "/api/v1/entity", params: []string{"v1/entity"}, match: true}, + {url: "/api2/v1/entity", params: nil, match: false}, + {url: "/api_ignore/v1/entity", params: nil, match: false}, + }, + }, + { + pattern: "/partialCheck/foo/bar/:param", + testCases: []routeTestCase{ + {url: "/partialCheck/foo/bar/test", params: []string{"test"}, match: true, partialCheck: true}, + {url: "/partialCheck/foo/bar/test/test2", params: []string{"test"}, match: true, partialCheck: true}, + {url: "/partialCheck/foo/bar", params: nil, match: false, partialCheck: true}, + {url: "/partiaFoo", params: nil, match: false, partialCheck: true}, + }, + }, + { + pattern: "/", + testCases: []routeTestCase{ + {url: "/api", params: nil, match: false}, + {url: "", params: []string{}, match: true}, + {url: "/", params: []string{}, match: true}, + }, + }, + { + pattern: "/config/abc.json", + testCases: []routeTestCase{ + {url: "/config/abc.json", params: []string{}, match: true}, + {url: "config/abc.json", params: nil, match: false}, + {url: "/config/efg.json", params: nil, match: false}, + {url: "/config", params: nil, match: false}, + }, + }, + { + pattern: "/config/*.json", + testCases: []routeTestCase{ + {url: "/config/abc.json", params: []string{"abc"}, match: true}, + {url: "/config/efg.json", params: []string{"efg"}, match: true}, + {url: "/config/.json", params: []string{""}, match: true}, + {url: "/config/efg.csv", params: nil, match: false}, + {url: "config/abc.json", params: nil, match: false}, + {url: "/config", params: nil, match: false}, + }, + }, + { + pattern: "/config/+.json", + testCases: []routeTestCase{ + {url: "/config/abc.json", params: []string{"abc"}, match: true}, + {url: "/config/.json", params: nil, match: false}, + {url: "/config/efg.json", params: []string{"efg"}, match: true}, + {url: "/config/efg.csv", params: nil, match: false}, + {url: "config/abc.json", params: nil, match: false}, + {url: "/config", params: nil, match: false}, + }, + }, + { + pattern: "/xyz", + testCases: []routeTestCase{ + {url: "xyz", params: nil, match: false}, + {url: "xyz/", params: nil, match: false}, + }, + }, + { + pattern: "/api/*/:param?", + testCases: []routeTestCase{ + {url: "/api/", params: []string{"", ""}, match: true}, + {url: "/api/joker", params: []string{"joker", ""}, match: true}, + {url: "/api/joker/batman", params: []string{"joker", "batman"}, match: true}, + {url: "/api/joker//batman", params: []string{"joker/", "batman"}, match: true}, + {url: "/api/joker/batman/robin", params: []string{"joker/batman", "robin"}, match: true}, + {url: "/api/joker/batman/robin/1", params: []string{"joker/batman/robin", "1"}, match: true}, + {url: "/api/joker/batman/robin/1/", params: []string{"joker/batman/robin/1", ""}, match: true}, + {url: "/api/joker-batman/robin/1", params: []string{"joker-batman/robin", "1"}, match: true}, + {url: "/api/joker-batman-robin/1", params: []string{"joker-batman-robin", "1"}, match: true}, + {url: "/api/joker-batman-robin-1", params: []string{"joker-batman-robin-1", ""}, match: true}, + {url: "/api", params: []string{"", ""}, match: true}, + }, + }, + { + pattern: "/api/*/:param", + testCases: []routeTestCase{ + {url: "/api/test/abc", params: []string{"test", "abc"}, match: true}, + {url: "/api/joker/batman", params: []string{"joker", "batman"}, match: true}, + {url: "/api/joker/batman/robin", params: []string{"joker/batman", "robin"}, match: true}, + {url: "/api/joker/batman/robin/1", params: []string{"joker/batman/robin", "1"}, match: true}, + {url: "/api/joker/batman-robin/1", params: []string{"joker/batman-robin", "1"}, match: true}, + {url: "/api/joker-batman-robin-1", params: nil, match: false}, + {url: "/api", params: nil, match: false}, + }, + }, + { + pattern: "/api/+/:param", + testCases: []routeTestCase{ + {url: "/api/test/abc", params: []string{"test", "abc"}, match: true}, + {url: "/api/joker/batman/robin/1", params: []string{"joker/batman/robin", "1"}, match: true}, + {url: "/api/joker", params: nil, match: false}, + {url: "/api", params: nil, match: false}, + }, + }, + { + pattern: "/api/*/:param/:param2", + testCases: []routeTestCase{ + {url: "/api/test/abc/1", params: []string{"test", "abc", "1"}, match: true}, + {url: "/api/joker/batman", params: nil, match: false}, + {url: "/api/joker/batman-robin/1", params: []string{"joker", "batman-robin", "1"}, match: true}, + {url: "/api/joker-batman-robin-1", params: nil, match: false}, + {url: "/api/test/abc", params: nil, match: false}, + {url: "/api/joker/batman/robin", params: []string{"joker", "batman", "robin"}, match: true}, + {url: "/api/joker/batman/robin/1", params: []string{"joker/batman", "robin", "1"}, match: true}, + {url: "/api/joker/batman/robin/1/2", params: []string{"joker/batman/robin", "1", "2"}, match: true}, + {url: "/api", params: nil, match: false}, + {url: "/api/:test", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/true", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, + {url: "/api/v1/true", params: []string{"true"}, match: true}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/8728382.5", params: []string{"8728382.5"}, match: true}, + {url: "/api/v1/true", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/#!?", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, + {url: "/api/v1/f0fa66cc-d22e-445b-866d-1d76e776371d", params: []string{"f0fa66cc-d22e-445b-866d-1d76e776371d"}, match: true}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/123", params: nil, match: false}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/ent", params: []string{"ent"}, match: true}, + {url: "/api/v1/8728382", params: nil, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/123", params: nil, match: false}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/e", params: nil, match: false}, + {url: "/api/v1/en", params: []string{"en"}, match: true}, + {url: "/api/v1/8728382", params: nil, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/e", params: nil, match: false}, + {url: "/api/v1/en", params: []string{"en"}, match: true}, + {url: "/api/v1/8728382", params: nil, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/1", params: nil, match: false}, + {url: "/api/v1/5", params: []string{"5"}, match: true}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/1", params: []string{"1"}, match: true}, + {url: "/api/v1/5", params: []string{"5"}, match: true}, + {url: "/api/v1/15", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/9", params: []string{"9"}, match: true}, + {url: "/api/v1/5", params: []string{"5"}, match: true}, + {url: "/api/v1/15", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, + {url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/15", params: nil, match: false}, + {url: "/api/v1/peach", params: []string{"peach"}, match: true}, + {url: "/api/v1/p34ch", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/12", params: nil, match: false}, + {url: "/api/v1/xy", params: nil, match: false}, + {url: "/api/v1/test", params: []string{"test"}, match: true}, + {url: "/api/v1/" + strings.Repeat("a", 64), params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/15", params: nil, match: false}, + {url: "/api/v1/2022-08-27", params: []string{"2022-08-27"}, match: true}, + {url: "/api/v1/2022/08-27", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/true", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/true", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/87283827683", params: nil, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/true", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/87283827683", params: nil, match: false}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/true", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/87283827683", params: []string{"87283827683"}, match: true}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: true}, + }, + }, + { + pattern: "/api/v1/:param", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/87283827683", params: nil, match: false}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/1200", params: []string{"1200"}, match: true}, + {url: "/api/v1/true", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:lang/videos/:page", + testCases: []routeTestCase{ + {url: "/api/v1/try/videos/200", params: nil, match: false}, + {url: "/api/v1/tr/videos/1800", params: nil, match: false}, + {url: "/api/v1/tr/videos/100", params: []string{"tr", "100"}, match: true}, + {url: "/api/v1/e/videos/10", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:lang/:page", + testCases: []routeTestCase{ + {url: "/api/v1/try/200", params: nil, match: false}, + {url: "/api/v1/tr/1800", params: nil, match: false}, + {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, + {url: "/api/v1/e/10", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:lang/:page", + testCases: []routeTestCase{ + {url: "/api/v1/try/200", params: []string{"try", "200"}, match: true}, + {url: "/api/v1/tr/1800", params: nil, match: false}, + {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, + {url: "/api/v1/e/10", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:lang/:page", + testCases: []routeTestCase{ + {url: "/api/v1/try/200", params: nil, match: false}, + {url: "/api/v1/tr/1800", params: []string{"tr", "1800"}, match: true}, + {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, + {url: "/api/v1/e/10", params: nil, match: false}, + }, + }, + { + pattern: "/api/v1/:date/:regex", + testCases: []routeTestCase{ + {url: "/api/v1/2005-11-01/a", params: nil, match: false}, + {url: "/api/v1/2005-1101/paach", params: nil, match: false}, + {url: "/api/v1/2005-11-01/peach", params: []string{"2005-11-01", "peach"}, match: true}, + }, + }, + { + pattern: "/api/v1/:param?", + testCases: []routeTestCase{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/true", params: nil, match: false}, + {url: "/api/v1/", params: []string{""}, match: true}, + }, + }, + }..., + ) +} From fe487934f939ae53db82fb7a543db6f0c6b41ca2 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Fri, 19 May 2023 11:07:20 +0200 Subject: [PATCH 100/162] utils: add Go 1.20+ way of converting byte slice to string (#2468) Ref. https://github.com/valyala/fasthttp/blob/d2f97fc426ed451e64dc8e35e7f87a1d4a2d7bde/b2s_old.go. Ref. https://github.com/valyala/fasthttp/blob/d2f97fc426ed451e64dc8e35e7f87a1d4a2d7bde/b2s_new.go. --- utils/convert.go | 8 -------- utils/convert_b2s_new.go | 13 +++++++++++++ utils/convert_b2s_old.go | 15 +++++++++++++++ 3 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 utils/convert_b2s_new.go create mode 100644 utils/convert_b2s_old.go diff --git a/utils/convert.go b/utils/convert.go index 672ea7f42a..a5317bf5de 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -10,16 +10,8 @@ import ( "strconv" "strings" "time" - "unsafe" ) -// UnsafeString returns a string pointer without allocation -// -//nolint:gosec // unsafe is used for better performance here -func UnsafeString(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) -} - // CopyString copies a string to make it immutable func CopyString(s string) string { return string(UnsafeBytes(s)) diff --git a/utils/convert_b2s_new.go b/utils/convert_b2s_new.go new file mode 100644 index 0000000000..2a5b394cc9 --- /dev/null +++ b/utils/convert_b2s_new.go @@ -0,0 +1,13 @@ +//go:build go1.20 +// +build go1.20 + +package utils + +import ( + "unsafe" +) + +// UnsafeString returns a string pointer without allocation +func UnsafeString(b []byte) string { + return unsafe.String(unsafe.SliceData(b), len(b)) +} diff --git a/utils/convert_b2s_old.go b/utils/convert_b2s_old.go new file mode 100644 index 0000000000..e00be0ee89 --- /dev/null +++ b/utils/convert_b2s_old.go @@ -0,0 +1,15 @@ +//go:build !go1.20 +// +build !go1.20 + +package utils + +import ( + "unsafe" +) + +// UnsafeString returns a string pointer without allocation +// +//nolint:gosec // unsafe is used for better performance here +func UnsafeString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} From 1207d5055bbea7066d4752494a7bb084b106d12e Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 19 May 2023 12:22:16 +0200 Subject: [PATCH 101/162] Update app.go prepare release 2.46.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index db2104356d..d5f07003a4 100644 --- a/app.go +++ b/app.go @@ -30,7 +30,7 @@ import ( ) // Version of current fiber package -const Version = "2.45.0" +const Version = "2.46.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From 5a84fa0e8e901d86669798cddd9c0e787dfa375d Mon Sep 17 00:00:00 2001 From: Alexander <113010734+kaazedev@users.noreply.github.com> Date: Sat, 20 May 2023 12:09:24 +0300 Subject: [PATCH 102/162] =?UTF-8?q?=F0=9F=93=92=20=20Correcting=20a=20synt?= =?UTF-8?q?ax=20error=20in=20the=20README=20(#2473)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update README.md * 📝 docs: fix typo in some readme --- .github/README.md | 2 +- .github/README_az.md | 2 +- .github/README_ckb.md | 2 +- .github/README_it.md | 2 +- .github/README_uk.md | 2 +- .github/README_zh-TW.md | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/README.md b/.github/README.md index 924bf47c7f..fbc2dd64af 100644 --- a/.github/README.md +++ b/.github/README.md @@ -590,7 +590,7 @@ func main() { app := fiber.New(fiber.Config{ EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range - ProxyHeader: fiber.HeaderXForwardedFor}, + ProxyHeader: fiber.HeaderXForwardedFor, }) // ... diff --git a/.github/README_az.md b/.github/README_az.md index 38543efcee..bb7b5a8a23 100644 --- a/.github/README_az.md +++ b/.github/README_az.md @@ -589,7 +589,7 @@ func main() { app := fiber.New(fiber.Config{ EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range - ProxyHeader: fiber.HeaderXForwardedFor}, + ProxyHeader: fiber.HeaderXForwardedFor, }) // ... diff --git a/.github/README_ckb.md b/.github/README_ckb.md index fe38ead798..fe84c7f6b9 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -589,7 +589,7 @@ func main() { app := fiber.New(fiber.Config{ EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range - ProxyHeader: fiber.HeaderXForwardedFor}, + ProxyHeader: fiber.HeaderXForwardedFor, }) // ... diff --git a/.github/README_it.md b/.github/README_it.md index f1a4c6ed41..0cca820c32 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -585,7 +585,7 @@ func main() { app := fiber.New(fiber.Config{ EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range - ProxyHeader: fiber.HeaderXForwardedFor}, + ProxyHeader: fiber.HeaderXForwardedFor, }) // ... diff --git a/.github/README_uk.md b/.github/README_uk.md index d62cb2ffb7..002321b06d 100644 --- a/.github/README_uk.md +++ b/.github/README_uk.md @@ -594,7 +594,7 @@ func main() { app := fiber.New(fiber.Config{ EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range - ProxyHeader: fiber.HeaderXForwardedFor}, + ProxyHeader: fiber.HeaderXForwardedFor, }) // ... diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index 9e2acb9c53..b58b09066b 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -598,7 +598,7 @@ func main() { app := fiber.New(fiber.Config{ EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP 地址或 IP 地址區間 - ProxyHeader: fiber.HeaderXForwardedFor}, + ProxyHeader: fiber.HeaderXForwardedFor, }) // ... From 9217820f2324312c2173ea9876d24ef5f401a396 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 23 May 2023 18:49:28 +0300 Subject: [PATCH 103/162] Bump github.com/mattn/go-isatty from 0.0.18 to 0.0.19 (#2474) Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.18 to 0.0.19. - [Commits](https://github.com/mattn/go-isatty/compare/v0.0.18...v0.0.19) --- updated-dependencies: - dependency-name: github.com/mattn/go-isatty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d1c2f5c4b9..fde5e92724 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/google/uuid v1.3.0 github.com/mattn/go-colorable v0.1.13 - github.com/mattn/go-isatty v0.0.18 + github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-runewidth v0.0.14 github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 github.com/tinylib/msgp v1.1.8 diff --git a/go.sum b/go.sum index 2c6217899e..779de1af3c 100644 --- a/go.sum +++ b/go.sum @@ -7,8 +7,8 @@ github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= From 369494cf25343a09de97c4ce86115f52acf7bcea Mon Sep 17 00:00:00 2001 From: Satont <42675886+Satont@users.noreply.github.com> Date: Sun, 28 May 2023 10:34:14 +0300 Subject: [PATCH 104/162] fix: reset terminal colors after print routes (#2481) * fix: reset terminal colors after print routes * Remove unnecessary `\t` for routes --- listen.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/listen.go b/listen.go index d212855cbb..00d3fabe32 100644 --- a/listen.go +++ b/listen.go @@ -457,10 +457,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\n", colors.Blue, colors.White, colors.Green, colors.White, colors.Cyan, colors.White, colors.Yellow) - _, _ = fmt.Fprintf(w, "%s------\t%s| %s----\t%s| %s----\t%s| %s--------\n", colors.Blue, colors.White, colors.Green, colors.White, colors.Cyan, colors.White, colors.Yellow) + _, _ = 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 { - _, _ = fmt.Fprintf(w, "%s%s\t%s| %s%s\t%s| %s%s\t%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) + _, _ = 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) } _ = w.Flush() //nolint:errcheck // It is fine to ignore the error here From 5d53263572159e8522eaeb2aba4e796fd5fd850e Mon Sep 17 00:00:00 2001 From: Lucas Lemos Date: Wed, 31 May 2023 03:01:13 -0300 Subject: [PATCH 105/162] :bug: fix: treat case for possible timer memory leak (#2488) --- internal/gopsutil/common/sleep.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/gopsutil/common/sleep.go b/internal/gopsutil/common/sleep.go index ee27e54d46..57f5a35fc4 100644 --- a/internal/gopsutil/common/sleep.go +++ b/internal/gopsutil/common/sleep.go @@ -11,6 +11,9 @@ func Sleep(ctx context.Context, interval time.Duration) error { var timer = time.NewTimer(interval) select { case <-ctx.Done(): + if !timer.Stop() { + <-timer.C + } return ctx.Err() case <-timer.C: return nil From b9e93ccd4abfcdb8efd7dc352d9320071ce7c898 Mon Sep 17 00:00:00 2001 From: Oleg <74712214+obakumen@users.noreply.github.com> Date: Thu, 1 Jun 2023 09:00:31 +0300 Subject: [PATCH 106/162] Fix Sliding Window limiter when SkipSuccessfulRequests/SkipFailedRequests is used. (#2484) * Fix Sliding Window limiter when SkipSuccessfulRequests/SkipFailedRequests is used. * Add tests. * Fix linter. --------- Co-authored-by: Oleg Bakumenko --- middleware/limiter/limiter_sliding.go | 4 + middleware/limiter/limiter_test.go | 378 +++++++++++++++++++++++++- 2 files changed, 376 insertions(+), 6 deletions(-) diff --git a/middleware/limiter/limiter_sliding.go b/middleware/limiter/limiter_sliding.go index 7f49863d7a..5043486f4f 100644 --- a/middleware/limiter/limiter_sliding.go +++ b/middleware/limiter/limiter_sliding.go @@ -117,9 +117,13 @@ func (SlidingWindow) New(cfg Config) fiber.Handler { // Check for SkipFailedRequests and SkipSuccessfulRequests if (cfg.SkipSuccessfulRequests && c.Response().StatusCode() < fiber.StatusBadRequest) || (cfg.SkipFailedRequests && c.Response().StatusCode() >= fiber.StatusBadRequest) { + // Lock entry mux.Lock() + e = manager.get(key) e.currHits-- remaining++ + manager.set(key, e, cfg.Expiration) + // Unlock entry mux.Unlock() } diff --git a/middleware/limiter/limiter_test.go b/middleware/limiter/limiter_test.go index a57ca477f4..d27b3b80c9 100644 --- a/middleware/limiter/limiter_test.go +++ b/middleware/limiter/limiter_test.go @@ -107,8 +107,8 @@ func Test_Limiter_Concurrency(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode) } -// go test -run Test_Limiter_No_Skip_Choices -v -func Test_Limiter_No_Skip_Choices(t *testing.T) { +// go test -run Test_Limiter_Fixed_Window_No_Skip_Choices -v +func Test_Limiter_Fixed_Window_No_Skip_Choices(t *testing.T) { t.Parallel() app := fiber.New() @@ -117,6 +117,7 @@ func Test_Limiter_No_Skip_Choices(t *testing.T) { Expiration: 2 * time.Second, SkipFailedRequests: false, SkipSuccessfulRequests: false, + LimiterMiddleware: FixedWindow{}, })) app.Get("/:status", func(c *fiber.Ctx) error { @@ -137,17 +138,182 @@ func Test_Limiter_No_Skip_Choices(t *testing.T) { resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 429, resp.StatusCode) + + time.Sleep(3 * time.Second) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) +} + +// go test -run Test_Limiter_Fixed_Window_Custom_Storage_No_Skip_Choices -v +func Test_Limiter_Fixed_Window_Custom_Storage_No_Skip_Choices(t *testing.T) { + t.Parallel() + app := fiber.New() + + app.Use(New(Config{ + Max: 2, + Expiration: 2 * time.Second, + SkipFailedRequests: false, + SkipSuccessfulRequests: false, + Storage: memory.New(), + LimiterMiddleware: FixedWindow{}, + })) + + app.Get("/:status", func(c *fiber.Ctx) error { + if c.Params("status") == "fail" { + return c.SendStatus(400) + } + return c.SendStatus(200) + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 400, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 429, resp.StatusCode) + + time.Sleep(3 * time.Second) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) } -// go test -run Test_Limiter_Skip_Failed_Requests -v -func Test_Limiter_Skip_Failed_Requests(t *testing.T) { +// go test -run Test_Limiter_Sliding_Window_No_Skip_Choices -v +func Test_Limiter_Sliding_Window_No_Skip_Choices(t *testing.T) { + t.Parallel() + app := fiber.New() + + app.Use(New(Config{ + Max: 2, + Expiration: 2 * time.Second, + SkipFailedRequests: false, + SkipSuccessfulRequests: false, + LimiterMiddleware: SlidingWindow{}, + })) + + app.Get("/:status", func(c *fiber.Ctx) error { + if c.Params("status") == "fail" { + return c.SendStatus(400) + } + return c.SendStatus(200) + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 400, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 429, resp.StatusCode) + + time.Sleep(4 * time.Second) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) +} + +// go test -run Test_Limiter_Sliding_Window_Custom_Storage_No_Skip_Choices -v +func Test_Limiter_Sliding_Window_Custom_Storage_No_Skip_Choices(t *testing.T) { + t.Parallel() + app := fiber.New() + + app.Use(New(Config{ + Max: 2, + Expiration: 2 * time.Second, + SkipFailedRequests: false, + SkipSuccessfulRequests: false, + Storage: memory.New(), + LimiterMiddleware: SlidingWindow{}, + })) + + app.Get("/:status", func(c *fiber.Ctx) error { + if c.Params("status") == "fail" { + return c.SendStatus(400) + } + return c.SendStatus(200) + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 400, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 429, resp.StatusCode) + + time.Sleep(4 * time.Second) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) +} + +// go test -run Test_Limiter_Fixed_Window_Skip_Failed_Requests -v +func Test_Limiter_Fixed_Window_Skip_Failed_Requests(t *testing.T) { + t.Parallel() + app := fiber.New() + + app.Use(New(Config{ + Max: 1, + Expiration: 2 * time.Second, + SkipFailedRequests: true, + LimiterMiddleware: FixedWindow{}, + })) + + app.Get("/:status", func(c *fiber.Ctx) error { + if c.Params("status") == "fail" { + return c.SendStatus(400) + } + return c.SendStatus(200) + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 400, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 429, resp.StatusCode) + + time.Sleep(3 * time.Second) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) +} + +// go test -run Test_Limiter_Fixed_Window_Custom_Storage_Skip_Failed_Requests -v +func Test_Limiter_Fixed_Window_Custom_Storage_Skip_Failed_Requests(t *testing.T) { t.Parallel() app := fiber.New() app.Use(New(Config{ Max: 1, Expiration: 2 * time.Second, + Storage: memory.New(), SkipFailedRequests: true, + LimiterMiddleware: FixedWindow{}, })) app.Get("/:status", func(c *fiber.Ctx) error { @@ -176,8 +342,85 @@ func Test_Limiter_Skip_Failed_Requests(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode) } -// go test -run Test_Limiter_Skip_Successful_Requests -v -func Test_Limiter_Skip_Successful_Requests(t *testing.T) { +// go test -run Test_Limiter_Sliding_Window_Skip_Failed_Requests -v +func Test_Limiter_Sliding_Window_Skip_Failed_Requests(t *testing.T) { + t.Parallel() + app := fiber.New() + + app.Use(New(Config{ + Max: 1, + Expiration: 2 * time.Second, + SkipFailedRequests: true, + LimiterMiddleware: SlidingWindow{}, + })) + + app.Get("/:status", func(c *fiber.Ctx) error { + if c.Params("status") == "fail" { + return c.SendStatus(400) + } + return c.SendStatus(200) + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 400, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 429, resp.StatusCode) + + time.Sleep(4 * time.Second) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) +} + +// go test -run Test_Limiter_Sliding_Window_Custom_Storage_Skip_Failed_Requests -v +func Test_Limiter_Sliding_Window_Custom_Storage_Skip_Failed_Requests(t *testing.T) { + t.Parallel() + app := fiber.New() + + app.Use(New(Config{ + Max: 1, + Expiration: 2 * time.Second, + Storage: memory.New(), + SkipFailedRequests: true, + LimiterMiddleware: SlidingWindow{}, + })) + + app.Get("/:status", func(c *fiber.Ctx) error { + if c.Params("status") == "fail" { + return c.SendStatus(400) + } + return c.SendStatus(200) + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 400, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 429, resp.StatusCode) + + time.Sleep(4 * time.Second) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) +} + +// go test -run Test_Limiter_Fixed_Window_Skip_Successful_Requests -v +func Test_Limiter_Fixed_Window_Skip_Successful_Requests(t *testing.T) { t.Parallel() // Test concurrency using a default store @@ -187,6 +430,7 @@ func Test_Limiter_Skip_Successful_Requests(t *testing.T) { Max: 1, Expiration: 2 * time.Second, SkipSuccessfulRequests: true, + LimiterMiddleware: FixedWindow{}, })) app.Get("/:status", func(c *fiber.Ctx) error { @@ -215,6 +459,128 @@ func Test_Limiter_Skip_Successful_Requests(t *testing.T) { utils.AssertEqual(t, 400, resp.StatusCode) } +// go test -run Test_Limiter_Fixed_Window_Custom_Storage_Skip_Successful_Requests -v +func Test_Limiter_Fixed_Window_Custom_Storage_Skip_Successful_Requests(t *testing.T) { + t.Parallel() + // Test concurrency using a default store + + app := fiber.New() + + app.Use(New(Config{ + Max: 1, + Expiration: 2 * time.Second, + Storage: memory.New(), + SkipSuccessfulRequests: true, + LimiterMiddleware: FixedWindow{}, + })) + + app.Get("/:status", func(c *fiber.Ctx) error { + if c.Params("status") == "fail" { + return c.SendStatus(400) + } + return c.SendStatus(200) + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 400, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 429, resp.StatusCode) + + time.Sleep(3 * time.Second) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 400, resp.StatusCode) +} + +// go test -run Test_Limiter_Sliding_Window_Skip_Successful_Requests -v +func Test_Limiter_Sliding_Window_Skip_Successful_Requests(t *testing.T) { + t.Parallel() + // Test concurrency using a default store + + app := fiber.New() + + app.Use(New(Config{ + Max: 1, + Expiration: 2 * time.Second, + SkipSuccessfulRequests: true, + LimiterMiddleware: SlidingWindow{}, + })) + + app.Get("/:status", func(c *fiber.Ctx) error { + if c.Params("status") == "fail" { + return c.SendStatus(400) + } + return c.SendStatus(200) + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 400, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 429, resp.StatusCode) + + time.Sleep(4 * time.Second) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 400, resp.StatusCode) +} + +// go test -run Test_Limiter_Sliding_Window_Custom_Storage_Skip_Successful_Requests -v +func Test_Limiter_Sliding_Window_Custom_Storage_Skip_Successful_Requests(t *testing.T) { + t.Parallel() + // Test concurrency using a default store + + app := fiber.New() + + app.Use(New(Config{ + Max: 1, + Expiration: 2 * time.Second, + Storage: memory.New(), + SkipSuccessfulRequests: true, + LimiterMiddleware: SlidingWindow{}, + })) + + app.Get("/:status", func(c *fiber.Ctx) error { + if c.Params("status") == "fail" { + return c.SendStatus(400) + } + return c.SendStatus(200) + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 400, resp.StatusCode) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 429, resp.StatusCode) + + time.Sleep(4 * time.Second) + + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 400, resp.StatusCode) +} + // go test -v -run=^$ -bench=Benchmark_Limiter_Custom_Store -benchmem -count=4 func Benchmark_Limiter_Custom_Store(b *testing.B) { app := fiber.New() From 4c12938309cfc10637fa949a27f149d5a4c50b27 Mon Sep 17 00:00:00 2001 From: cmd777 <83428931+cmd777@users.noreply.github.com> Date: Thu, 1 Jun 2023 10:47:07 +0200 Subject: [PATCH 107/162] :recycle: Refactor: use c.app.getString instead of string(...) (#2489) use c.app.getString instead of string(...) --- ctx.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctx.go b/ctx.go index 2f9394bd8e..8bb29d3149 100644 --- a/ctx.go +++ b/ctx.go @@ -597,7 +597,7 @@ func (c *Ctx) GetRespHeader(key string, defaultValue ...string) string { func (c *Ctx) GetReqHeaders() map[string]string { headers := make(map[string]string) c.Request().Header.VisitAll(func(k, v []byte) { - headers[string(k)] = c.app.getString(v) + headers[c.app.getString(k)] = c.app.getString(v) }) return headers @@ -609,7 +609,7 @@ func (c *Ctx) GetReqHeaders() map[string]string { func (c *Ctx) GetRespHeaders() map[string]string { headers := make(map[string]string) c.Response().Header.VisitAll(func(k, v []byte) { - headers[string(k)] = c.app.getString(v) + headers[c.app.getString(k)] = c.app.getString(v) }) return headers From c955d76f5d45a251fde2197deef59a6ec07e6aa1 Mon Sep 17 00:00:00 2001 From: Chris Hurst <46472228+ytsruh@users.noreply.github.com> Date: Mon, 5 Jun 2023 12:00:51 +0100 Subject: [PATCH 108/162] :bug: bug: fix middleware naming and returned values of group methods (#2477) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bug fix: route names not updating * fixed lint error * updated tests with renaming edge case * fix group naming partially * add todo * fix todo * fix todo --------- Co-authored-by: Muhammed Efe Çetin --- app.go | 25 +++++++++++++++------- app_test.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ group.go | 39 +++++++++++++++++++++++++++------- hooks_test.go | 13 +++++++++++- router.go | 6 ++---- 5 files changed, 121 insertions(+), 21 deletions(-) diff --git a/app.go b/app.go index d5f07003a4..69b7d089bd 100644 --- a/app.go +++ b/app.go @@ -609,18 +609,23 @@ func (app *App) SetTLSHandler(tlsHandler *TLSHandler) { // Name Assign name to specific route. func (app *App) Name(name string) Router { app.mutex.Lock() + defer app.mutex.Unlock() - latestGroup := app.latestRoute.group - if latestGroup != nil { - app.latestRoute.Name = latestGroup.name + name - } else { - app.latestRoute.Name = name + for _, routes := range app.stack { + for _, route := range routes { + if route.Path == app.latestRoute.path { + route.Name = name + + if route.group != nil { + route.Name = route.group.name + route.Name + } + } + } } if err := app.hooks.executeOnNameHooks(*app.latestRoute); err != nil { panic(err) } - app.mutex.Unlock() return app } @@ -754,12 +759,16 @@ func (app *App) Patch(path string, handlers ...Handler) Router { // Add allows you to specify a HTTP method to register a route func (app *App) Add(method, path string, handlers ...Handler) Router { - return app.register(method, path, nil, handlers...) + app.register(method, path, nil, handlers...) + + return app } // Static will create a file server serving static files func (app *App) Static(prefix, root string, config ...Static) Router { - return app.registerStatic(prefix, root, config...) + app.registerStatic(prefix, root, config...) + + return app } // All will register the handler on all HTTP methods diff --git a/app_test.go b/app_test.go index 8ab6c76ff1..09600b72be 100644 --- a/app_test.go +++ b/app_test.go @@ -1803,3 +1803,62 @@ func TestApp_GetRoutes(t *testing.T) { utils.AssertEqual(t, name, route.Name) } } + +func Test_Middleware_Route_Naming_With_Use(t *testing.T) { + named := "named" + app := New() + + app.Get("/unnamed", func(c *Ctx) error { + return c.Next() + }) + + app.Post("/named", func(c *Ctx) error { + return c.Next() + }).Name(named) + + app.Use(func(c *Ctx) error { + return c.Next() + }) // no name - logging MW + + app.Use(func(c *Ctx) error { + return c.Next() + }).Name("corsMW") + + app.Use(func(c *Ctx) error { + return c.Next() + }).Name("compressMW") + + app.Use(func(c *Ctx) error { + return c.Next() + }) // no name - cache MW + + grp := app.Group("/pages").Name("pages.") + grp.Use(func(c *Ctx) error { + return c.Next() + }).Name("csrfMW") + + grp.Get("/home", func(c *Ctx) error { + return c.Next() + }).Name("home") + + grp.Get("/unnamed", func(c *Ctx) error { + return c.Next() + }) + + for _, route := range app.GetRoutes() { + switch route.Path { + case "/": + utils.AssertEqual(t, "compressMW", route.Name) + case "/unnamed": + utils.AssertEqual(t, "", route.Name) + case "named": + utils.AssertEqual(t, named, route.Name) + case "/pages": + utils.AssertEqual(t, "pages.csrfMW", route.Name) + case "/pages/home": + utils.AssertEqual(t, "pages.home", route.Name) + case "/pages/unnamed": + utils.AssertEqual(t, "", route.Name) + } + } +} diff --git a/group.go b/group.go index 91c2806244..0e546a3fff 100644 --- a/group.go +++ b/group.go @@ -11,17 +11,26 @@ import ( // Group struct type Group struct { - app *App - parentGroup *Group - name string + app *App + parentGroup *Group + name string + anyRouteDefined bool Prefix string } -// Name Assign name to specific route. +// Name Assign name to specific route or group itself. +// +// If this method is used before any route added to group, it'll set group name and OnGroupNameHook will be used. +// Otherwise, it'll set route name and OnName hook will be used. func (grp *Group) Name(name string) Router { - grp.app.mutex.Lock() + if grp.anyRouteDefined { + grp.app.Name(name) + + return grp + } + grp.app.mutex.Lock() if grp.parentGroup != nil { grp.name = grp.parentGroup.name + name } else { @@ -76,6 +85,10 @@ func (grp *Group) Use(args ...interface{}) Router { grp.app.register(methodUse, getGroupPath(grp.Prefix, prefix), grp, handlers...) } + if !grp.anyRouteDefined { + grp.anyRouteDefined = true + } + return grp } @@ -135,12 +148,22 @@ func (grp *Group) Patch(path string, handlers ...Handler) Router { // Add allows you to specify a HTTP method to register a route func (grp *Group) Add(method, path string, handlers ...Handler) Router { - return grp.app.register(method, getGroupPath(grp.Prefix, path), grp, handlers...) + grp.app.register(method, getGroupPath(grp.Prefix, path), grp, handlers...) + if !grp.anyRouteDefined { + grp.anyRouteDefined = true + } + + return grp } // Static will create a file server serving static files func (grp *Group) Static(prefix, root string, config ...Static) Router { - return grp.app.registerStatic(getGroupPath(grp.Prefix, prefix), root, config...) + grp.app.registerStatic(getGroupPath(grp.Prefix, prefix), root, config...) + if !grp.anyRouteDefined { + grp.anyRouteDefined = true + } + + return grp } // All will register the handler on all HTTP methods @@ -158,7 +181,7 @@ func (grp *Group) All(path string, handlers ...Handler) Router { func (grp *Group) Group(prefix string, handlers ...Handler) Router { prefix = getGroupPath(grp.Prefix, prefix) if len(handlers) > 0 { - _ = grp.app.register(methodUse, prefix, grp, handlers...) + grp.app.register(methodUse, prefix, grp, handlers...) } // Create new group diff --git a/hooks_test.go b/hooks_test.go index 6a2fc3bfbb..83bd8a914f 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -139,6 +139,9 @@ func Test_Hook_OnGroupName(t *testing.T) { buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) + buf2 := bytebufferpool.Get() + defer bytebufferpool.Put(buf2) + app.Hooks().OnGroupName(func(g Group) error { _, err := buf.WriteString(g.name) utils.AssertEqual(t, nil, err) @@ -146,11 +149,19 @@ func Test_Hook_OnGroupName(t *testing.T) { return nil }) + app.Hooks().OnName(func(r Route) error { + _, err := buf2.WriteString(r.Name) + utils.AssertEqual(t, nil, err) + + return nil + }) + grp := app.Group("/x").Name("x.") - grp.Get("/test", testSimpleHandler) + grp.Get("/test", testSimpleHandler).Name("test") grp.Get("/test2", testSimpleHandler) utils.AssertEqual(t, "x.", buf.String()) + utils.AssertEqual(t, "x.test", buf2.String()) } func Test_Hook_OnGroupName_Error(t *testing.T) { diff --git a/router.go b/router.go index a5aac7f6f5..312e161ef2 100644 --- a/router.go +++ b/router.go @@ -225,7 +225,7 @@ func (*App) copyRoute(route *Route) *Route { } } -func (app *App) register(method, pathRaw string, group *Group, handlers ...Handler) Router { +func (app *App) register(method, pathRaw string, group *Group, handlers ...Handler) { // Uppercase HTTP methods method = utils.ToUpper(method) // Check if the HTTP method is valid unless it's USE @@ -302,10 +302,9 @@ func (app *App) register(method, pathRaw string, group *Group, handlers ...Handl // Add route to stack app.addRoute(method, &route, isMount) } - return app } -func (app *App) registerStatic(prefix, root string, config ...Static) Router { +func (app *App) registerStatic(prefix, root string, config ...Static) { // For security we want to restrict to the current work directory. if root == "" { root = "." @@ -441,7 +440,6 @@ func (app *App) registerStatic(prefix, root string, config ...Static) Router { app.addRoute(MethodGet, &route) // Add HEAD route app.addRoute(MethodHead, &route) - return app } func (app *App) addRoute(method string, route *Route, isMounted ...bool) { From 06ef450a8af7d13484712693c57f45a409f7dde5 Mon Sep 17 00:00:00 2001 From: Anzhi <70408571+Skyenought@users.noreply.github.com> Date: Mon, 5 Jun 2023 19:20:45 +0800 Subject: [PATCH 109/162] :rocket: Add DisableColors to set the default output format (#2493) Add DisableColor for default logger format --- docs/api/middleware/logger.md | 100 +++++++++++++++++-------------- middleware/logger/config.go | 23 +++---- middleware/logger/logger.go | 48 ++++++++++----- middleware/logger/logger_test.go | 35 ++++++++++- 4 files changed, 128 insertions(+), 78 deletions(-) diff --git a/docs/api/middleware/logger.md b/docs/api/middleware/logger.md index 1c0860eb2f..775e39fcac 100644 --- a/docs/api/middleware/logger.md +++ b/docs/api/middleware/logger.md @@ -80,56 +80,66 @@ app.Use(logger.New(logger.Config{ } }, })) + +// Disable colors when outputting to default format +app.Use(logger.New(logger.Config{ + DisableColors: true, +})) ``` ## Config ```go // Config defines the config for middleware. type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Done is a function that is called after the log string for a request is written to Output, - // and pass the log string as parameter. - // - // Optional. Default: nil - Done func(c *fiber.Ctx, logString []byte) - - // tagFunctions defines the custom tag action - // - // Optional. Default: map[string]LogFunc - CustomTags map[string]LogFunc - - // Format defines the logging tags - // - // Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n - Format string - - // TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html - // - // Optional. Default: 15:04:05 - TimeFormat string - - // TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc - // - // Optional. Default: "Local" - TimeZone string - - // TimeInterval is the delay before the timestamp is updated - // - // Optional. Default: 500 * time.Millisecond - TimeInterval time.Duration - - // Output is a writer where logs are written - // - // Default: os.Stdout - Output io.Writer - - enableColors bool - enableLatency bool - timeZoneLocation *time.Location + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // Done is a function that is called after the log string for a request is written to Output, + // and pass the log string as parameter. + // + // Optional. Default: nil + Done func(c *fiber.Ctx, logString []byte) + + // tagFunctions defines the custom tag action + // + // Optional. Default: map[string]LogFunc + CustomTags map[string]LogFunc + + // Format defines the logging tags + // + // Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n + Format string + + // TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html + // + // Optional. Default: 15:04:05 + TimeFormat string + + // TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc + // + // Optional. Default: "Local" + TimeZone string + + // TimeInterval is the delay before the timestamp is updated + // + // Optional. Default: 500 * time.Millisecond + TimeInterval time.Duration + + // Output is a writer where logs are written + // + // Default: os.Stdout + Output io.Writer + + // DisableColors defines if the logs output should be colorized + // + // Default: false + DisableColors bool + + enableColors bool + enableLatency bool + timeZoneLocation *time.Location } type LogFunc func(buf logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) ``` @@ -143,7 +153,7 @@ var ConfigDefault = Config{ TimeZone: "Local", TimeInterval: 500 * time.Millisecond, Output: os.Stdout, - enableColors: true, + DisableColors: true, } ``` diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 21f34aad7c..ea440cf66d 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -3,7 +3,6 @@ package logger import ( "io" "os" - "strings" "time" "github.com/gofiber/fiber/v2" @@ -52,6 +51,11 @@ type Config struct { // Default: os.Stdout Output io.Writer + // DisableColors defines if the logs output should be colorized + // + // Default: false + DisableColors bool + enableColors bool enableLatency bool timeZoneLocation *time.Location @@ -87,18 +91,7 @@ var ConfigDefault = Config{ TimeZone: "Local", TimeInterval: 500 * time.Millisecond, Output: os.Stdout, - enableColors: true, -} - -// Function to check if the logger format is compatible for coloring -func checkColorEnable(format string) bool { - validTemplates := []string{"${status}", "${method}"} - for _, template := range validTemplates { - if strings.Contains(format, template) { - return true - } - } - return false + enableColors: false, } // Helper function to set default values @@ -121,7 +114,6 @@ func configDefault(config ...Config) Config { if cfg.Format == "" { cfg.Format = ConfigDefault.Format } - if cfg.TimeZone == "" { cfg.TimeZone = ConfigDefault.TimeZone } @@ -135,8 +127,7 @@ func configDefault(config ...Config) Config { cfg.Output = ConfigDefault.Output } - // Enable colors if no custom format or output is given - if cfg.Output == ConfigDefault.Output && checkColorEnable(cfg.Format) { + if !cfg.DisableColors && cfg.Output == ConfigDefault.Output { cfg.enableColors = true } diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index b34c1908bc..ec4aa3cafc 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -67,6 +67,7 @@ func New(config ...Config) fiber.Handler { cfg.Output = colorable.NewNonColorable(os.Stdout) } } + errPadding := 15 errPaddingStr := strconv.Itoa(errPadding) @@ -137,26 +138,41 @@ func New(config ...Config) fiber.Handler { buf := bytebufferpool.Get() // Default output when no custom Format or io.Writer is given - if cfg.enableColors && cfg.Format == ConfigDefault.Format { + if cfg.Format == ConfigDefault.Format { // Format error if exist formatErr := "" - if chainErr != nil { - formatErr = colors.Red + " | " + chainErr.Error() + colors.Reset + if cfg.enableColors { + if chainErr != nil { + formatErr = colors.Red + " | " + chainErr.Error() + colors.Reset + } + _, _ = buf.WriteString( //nolint:errcheck // This will never fail + fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n", + timestamp.Load().(string), + statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset, + data.Stop.Sub(data.Start).Round(time.Millisecond), + c.IP(), + methodColor(c.Method(), colors), c.Method(), colors.Reset, + c.Path(), + formatErr, + ), + ) + } else { + if chainErr != nil { + formatErr = " | " + chainErr.Error() + } + _, _ = buf.WriteString( //nolint:errcheck // This will never fail + fmt.Sprintf("%s | %3d | %7v | %15s | %-7s | %-"+errPaddingStr+"s %s\n", + timestamp.Load().(string), + c.Response().StatusCode(), + data.Stop.Sub(data.Start).Round(time.Millisecond), + c.IP(), + c.Method(), + c.Path(), + formatErr, + ), + ) } - // Format log to buffer - _, _ = buf.WriteString( //nolint:errcheck // This will never fail - fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n", - timestamp.Load().(string), - statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset, - data.Stop.Sub(data.Start).Round(time.Millisecond), - c.IP(), - methodColor(c.Method(), colors), c.Method(), colors.Reset, - c.Path(), - formatErr, - ), - ) - // Write buffer to output _, _ = cfg.Output.Write(buf.Bytes()) //nolint:errcheck // This will never fail diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index df4045c2ab..d3006c88de 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -149,6 +149,23 @@ func (o *fakeOutput) Write([]byte) (int, error) { return 0, errors.New("fake output") } +// go test -run Test_Logger_ErrorOutput_WithoutColor +func Test_Logger_ErrorOutput_WithoutColor(t *testing.T) { + t.Parallel() + o := new(fakeOutput) + app := fiber.New() + app.Use(New(Config{ + Output: o, + DisableColors: true, + })) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + + utils.AssertEqual(t, 1, int(*o)) +} + // go test -run Test_Logger_ErrorOutput func Test_Logger_ErrorOutput(t *testing.T) { t.Parallel() @@ -162,7 +179,7 @@ func Test_Logger_ErrorOutput(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) - utils.AssertEqual(t, 2, int(*o)) + utils.AssertEqual(t, 1, int(*o)) } // go test -run Test_Logger_All @@ -502,3 +519,19 @@ func Test_Logger_ByteSent_Streaming(t *testing.T) { utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "0 0 200", buf.String()) } + +// go test -run Test_Logger_EnableColors +func Test_Logger_EnableColors(t *testing.T) { + t.Parallel() + o := new(fakeOutput) + app := fiber.New() + app.Use(New(Config{ + Output: o, + })) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + + utils.AssertEqual(t, 1, int(*o)) +} From d91ea9e01e9910fe39e5bc67a998b587a2d7e7b8 Mon Sep 17 00:00:00 2001 From: Amir Hossein Date: Wed, 7 Jun 2023 18:51:36 +0330 Subject: [PATCH 110/162] =?UTF-8?q?=F0=9F=93=9A=20translate=20README=5Ffa.?= =?UTF-8?q?md=20(#2496)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * translate README_fa.md (#1) * add "Using Trusted Proxy" section under the "show more code examples" --- .github/README_fa.md | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/.github/README_fa.md b/.github/README_fa.md index 4e442540f7..eebd093fce 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -674,6 +674,28 @@ func main() { } ``` +### Using Trusted Proxy + +📖 [Config](https://docs.gofiber.io/api/fiber#config) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/recover" +) + +func main() { + app := fiber.New(fiber.Config{ + EnableTrustedProxyCheck: true, + TrustedProxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range + ProxyHeader: fiber.HeaderXForwardedFor, + }) + + // ... + + log.Fatal(app.Listen(":3000")) +} +```
@@ -712,11 +734,11 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) |برای ذخیره و مدیریت شناسه کاربری یا session بازدید کنندگان استفاده .میشود| | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) |این میدلور میتواند با استفاده از شرط های تعیین شده درخواست هایی را نادیده بگیرد.| | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) |این میدلور محدودیت زمانی ای را برای درخواست ها تنظیم میکند، در صورتی که محدودیت به پایان برسد ErrorHandler صدا زده میشود.| -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [keyauth](https://github.com/gofiber/keyauth) | این میدلور احراز هویت مبتنی بر کلید را فراهم می کند. | +| [redirect](https://github.com/gofiber/redirect) | برای ریدایرکت کردن از این میدلور میتوانید استفاده کنید. | +| [rewrite](https://github.com/gofiber/rewrite) | مسیر URL را براساس قوانین مشخص شده بازنویسی می کند. این میتواند برای سازگاری با ورژن های قبلی یا برای ساخت لینک های تمیز تر و توصیفی تر مفید باشد. | | [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [helmet](https://github.com/gofiber/helmet) | با استفاده از HTTP هدر های مختلف به ایمن سازی برنامه شما کمک می کند. |


@@ -740,7 +762,7 @@ func main() { ## 🕶️ Awesome List -For more articles, middlewares, examples or tools check our [awesome list](https://github.com/gofiber/awesome-fiber). + [awesome list](https://github.com/gofiber/awesome-fiber) برای مقاله، میدلور، مثال ها و ابزار های بیشتر لطفا از این لینک بازدید کنید
From 0f5ffed3cc72eb7d4ef36f31c35e9967d18684b9 Mon Sep 17 00:00:00 2001 From: Jason McNeil Date: Wed, 7 Jun 2023 12:51:45 -0300 Subject: [PATCH 111/162] =?UTF-8?q?=F0=9F=90=9Bfix:=20update=20getOffer=20?= =?UTF-8?q?to=20consider=20quality=20and=20specificity=20(#2486)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: getOffer consider q value and specificity * fix: ignore q=0 * fix: float * test: client-prefered order and q=0 not acceptable * fix: always use my insertion sort. * fix: sort.SliceStable if > 20 * fix: zero allocations * perf: optimize the sort * chore: fix lint issue * fix: consider order * chore: fix test func name * chore: fix helper test func name * chore: revert fix * perf: use fasthttp.ParseUfloat * test: GetOffer and SortAcceptedTypes * chore: remote nil check * test: sortAcceptedTypes * fix: use utils.UnsafeBytes * docs: update docs for fiber PR #2486 * docs: update docs for fiber PR #2486 * test: add test from docs * fix: yaml --- ctx_test.go | 4 ++ docs/api/ctx.md | 14 +++++- helpers.go | 131 +++++++++++++++++++++++++++++++++++++++++------- helpers_test.go | 129 ++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 252 insertions(+), 26 deletions(-) diff --git a/ctx_test.go b/ctx_test.go index b3192a8f3d..c15b6d0892 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -45,6 +45,10 @@ func Test_Ctx_Accepts(t *testing.T) { utils.AssertEqual(t, "", c.Accepts()) utils.AssertEqual(t, ".xml", c.Accepts(".xml")) utils.AssertEqual(t, "", c.Accepts(".john")) + utils.AssertEqual(t, "application/xhtml+xml", c.Accepts("application/xml", "application/xml+rss", "application/yaml", "application/xhtml+xml"), "must use client-preferred mime type") + + c.Request().Header.Set(HeaderAccept, "application/json, text/plain, */*;q=0") + utils.AssertEqual(t, "", c.Accepts("html"), "must treat */*;q=0 as not acceptable") c.Request().Header.Set(HeaderAccept, "text/*, application/json") utils.AssertEqual(t, "html", c.Accepts("html")) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index f102ee374e..5286a19408 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -24,19 +24,31 @@ func (c *Ctx) AcceptsLanguages(offers ...string) string ``` ```go title="Example" -// Accept: text/*, application/json +// Accept: text/html, application/json; q=0.8, text/plain; q=0.5; charset="utf-8" app.Get("/", func(c *fiber.Ctx) error { c.Accepts("html") // "html" c.Accepts("text/html") // "text/html" c.Accepts("json", "text") // "json" c.Accepts("application/json") // "application/json" + c.Accepts("text/plain", "application/json") // "application/json", due to quality c.Accepts("image/png") // "" c.Accepts("png") // "" // ... }) ``` +```go title="Example 2" +// Accept: text/html, text/*, application/json, */*; q=0 + +app.Get("/", func(c *fiber.Ctx) error { + c.Accepts("text/plain", "application/json") // "application/json", due to specificity + c.Accepts("application/json", "text/html") // "text/html", due to first match + c.Accepts("image/png") // "", due to */* without q factor 0 is Not Acceptable + // ... +}) +``` + Fiber provides similar functions for the other accept headers. ```go diff --git a/helpers.go b/helpers.go index a211d787b2..cedab4c5f5 100644 --- a/helpers.go +++ b/helpers.go @@ -25,6 +25,16 @@ import ( "github.com/valyala/fasthttp" ) +// acceptType is a struct that holds the parsed value of an Accept header +// along with quality, specificity, and order. +// used for sorting accept headers. +type acceptedType struct { + spec string + quality float64 + specificity int + order int +} + // getTLSConfig returns a net listener's tls config func getTLSConfig(ln net.Listener) *tls.Config { // Get listener type @@ -263,33 +273,89 @@ func acceptsOfferType(spec, offerType string) bool { func getOffer(header string, isAccepted func(spec, offer string) bool, offers ...string) string { if len(offers) == 0 { return "" - } else if header == "" { + } + if header == "" { return offers[0] } - for _, offer := range offers { - if len(offer) == 0 { - continue + // Parse header and get accepted types with their quality and specificity + // See: https://www.rfc-editor.org/rfc/rfc9110#name-content-negotiation-fields + spec, commaPos, order := "", 0, 0 + acceptedTypes := make([]acceptedType, 0, 20) + for len(header) > 0 { + order++ + + // Skip spaces + header = utils.TrimLeft(header, ' ') + + // Get spec + commaPos = strings.IndexByte(header, ',') + if commaPos != -1 { + spec = utils.Trim(header[:commaPos], ' ') + } else { + spec = utils.TrimLeft(header, ' ') } - spec, commaPos := "", 0 - for len(header) > 0 && commaPos != -1 { - commaPos = strings.IndexByte(header, ',') - if commaPos != -1 { - spec = utils.Trim(header[:commaPos], ' ') - } else { - spec = utils.TrimLeft(header, ' ') - } - if factorSign := strings.IndexByte(spec, ';'); factorSign != -1 { - spec = spec[:factorSign] - } - // isAccepted if the current offer is accepted - if isAccepted(spec, offer) { - return offer + // Get quality + quality := 1.0 + if factorSign := strings.IndexByte(spec, ';'); factorSign != -1 { + factor := utils.Trim(spec[factorSign+1:], ' ') + if strings.HasPrefix(factor, "q=") { + if q, err := fasthttp.ParseUfloat(utils.UnsafeBytes(factor[2:])); err == nil { + quality = q + } } + spec = spec[:factorSign] + } + // Skip if quality is 0.0 + // See: https://www.rfc-editor.org/rfc/rfc9110#quality.values + if quality == 0.0 { if commaPos != -1 { header = header[commaPos+1:] + } else { + break + } + continue + } + + // Get specificity + specificity := 0 + // check for wildcard this could be a mime */* or a wildcard character * + if spec == "*/*" || spec == "*" { + specificity = 1 + } else if strings.HasSuffix(spec, "/*") { + specificity = 2 + } else if strings.IndexByte(spec, '/') != -1 { + specificity = 3 + } else { + specificity = 4 + } + + // Add to accepted types + acceptedTypes = append(acceptedTypes, acceptedType{spec, quality, specificity, order}) + + // Next + if commaPos != -1 { + header = header[commaPos+1:] + } else { + break + } + } + + if len(acceptedTypes) > 1 { + // Sort accepted types by quality and specificity, preserving order of equal elements + sortAcceptedTypes(&acceptedTypes) + } + + // Find the first offer that matches the accepted types + for _, acceptedType := range acceptedTypes { + for _, offer := range offers { + if len(offer) == 0 { + continue + } + if isAccepted(acceptedType.spec, offer) { + return offer } } } @@ -297,6 +363,35 @@ func getOffer(header string, isAccepted func(spec, offer string) bool, offers .. return "" } +// sortAcceptedTypes sorts accepted types by quality and specificity, preserving order of equal elements +// +// Parameters are not supported, they are ignored when sorting by specificity. +// +// See: https://www.rfc-editor.org/rfc/rfc9110#name-content-negotiation-fields +func sortAcceptedTypes(at *[]acceptedType) { + if at == nil || len(*at) < 2 { + return + } + acceptedTypes := *at + + for i := 1; i < len(acceptedTypes); i++ { + lo, hi := 0, i-1 + for lo <= hi { + mid := (lo + hi) / 2 + if acceptedTypes[i].quality < acceptedTypes[mid].quality || + (acceptedTypes[i].quality == acceptedTypes[mid].quality && acceptedTypes[i].specificity < acceptedTypes[mid].specificity) || + (acceptedTypes[i].quality == acceptedTypes[mid].quality && acceptedTypes[i].specificity == acceptedTypes[mid].specificity && acceptedTypes[i].order > acceptedTypes[mid].order) { + lo = mid + 1 + } else { + hi = mid - 1 + } + } + for j := i; j > lo; j-- { + acceptedTypes[j-1], acceptedTypes[j] = acceptedTypes[j], acceptedTypes[j-1] + } + } +} + func matchEtag(s, etag string) bool { if s == etag || s == "W/"+etag || "W/"+s == etag { return true diff --git a/helpers_test.go b/helpers_test.go index 389ff416fe..5ecab49034 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -62,6 +62,128 @@ func Test_Utils_ETag(t *testing.T) { }) } +func Test_Utils_GetOffer(t *testing.T) { + t.Parallel() + utils.AssertEqual(t, "", getOffer("hello", acceptsOffer)) + utils.AssertEqual(t, "1", getOffer("", acceptsOffer, "1")) + utils.AssertEqual(t, "", getOffer("2", acceptsOffer, "1")) + + utils.AssertEqual(t, "", getOffer("", acceptsOfferType)) + utils.AssertEqual(t, "", getOffer("text/html", acceptsOfferType)) + utils.AssertEqual(t, "", getOffer("text/html", acceptsOfferType, "application/json")) + utils.AssertEqual(t, "", getOffer("text/html;q=0", acceptsOfferType, "text/html")) + utils.AssertEqual(t, "", getOffer("application/json, */*; q=0", acceptsOfferType, "image/png")) + utils.AssertEqual(t, "application/xml", getOffer("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", acceptsOfferType, "application/xml", "application/json")) + utils.AssertEqual(t, "text/html", getOffer("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", acceptsOfferType, "text/html")) + utils.AssertEqual(t, "application/pdf", getOffer("text/plain;q=0,application/pdf;q=0.9,*/*;q=0.000", acceptsOfferType, "application/pdf", "application/json")) + utils.AssertEqual(t, "application/pdf", getOffer("text/plain;q=0,application/pdf;q=0.9,*/*;q=0.000", acceptsOfferType, "application/pdf", "application/json")) + + utils.AssertEqual(t, "", getOffer("utf-8, iso-8859-1;q=0.5", acceptsOffer)) + utils.AssertEqual(t, "", getOffer("utf-8, iso-8859-1;q=0.5", acceptsOffer, "ascii")) + utils.AssertEqual(t, "utf-8", getOffer("utf-8, iso-8859-1;q=0.5", acceptsOffer, "utf-8")) + utils.AssertEqual(t, "iso-8859-1", getOffer("utf-8;q=0, iso-8859-1;q=0.5", acceptsOffer, "utf-8", "iso-8859-1")) + + utils.AssertEqual(t, "deflate", getOffer("gzip, deflate", acceptsOffer, "deflate")) + utils.AssertEqual(t, "", getOffer("gzip, deflate;q=0", acceptsOffer, "deflate")) +} + +func Benchmark_Utils_GetOffer(b *testing.B) { + headers := []string{ + "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", + "application/json", + "utf-8, iso-8859-1;q=0.5", + "gzip, deflate", + } + offers := [][]string{ + {"text/html", "application/xml", "application/xml+xhtml"}, + {"application/json"}, + {"utf-8"}, + {"deflate"}, + } + for n := 0; n < b.N; n++ { + for i, header := range headers { + getOffer(header, acceptsOfferType, offers[i]...) + } + } +} + +func Test_Utils_SortAcceptedTypes(t *testing.T) { + t.Parallel() + acceptedTypes := []acceptedType{ + {spec: "text/html", quality: 1, specificity: 3, order: 0}, + {spec: "text/*", quality: 0.5, specificity: 2, order: 1}, + {spec: "*/*", quality: 0.1, specificity: 1, order: 2}, + {spec: "application/json", quality: 0.999, specificity: 3, order: 3}, + {spec: "application/xml", quality: 1, specificity: 3, order: 4}, + {spec: "application/pdf", quality: 1, specificity: 3, order: 5}, + {spec: "image/png", quality: 1, specificity: 3, order: 6}, + {spec: "image/jpeg", quality: 1, specificity: 3, order: 7}, + {spec: "image/*", quality: 1, specificity: 2, order: 8}, + {spec: "image/gif", quality: 1, specificity: 3, order: 9}, + {spec: "text/plain", quality: 1, specificity: 3, order: 10}, + } + sortAcceptedTypes(&acceptedTypes) + utils.AssertEqual(t, acceptedTypes, []acceptedType{ + {spec: "text/html", quality: 1, specificity: 3, order: 0}, + {spec: "application/xml", quality: 1, specificity: 3, order: 4}, + {spec: "application/pdf", quality: 1, specificity: 3, order: 5}, + {spec: "image/png", quality: 1, specificity: 3, order: 6}, + {spec: "image/jpeg", quality: 1, specificity: 3, order: 7}, + {spec: "image/gif", quality: 1, specificity: 3, order: 9}, + {spec: "text/plain", quality: 1, specificity: 3, order: 10}, + {spec: "image/*", quality: 1, specificity: 2, order: 8}, + {spec: "application/json", quality: 0.999, specificity: 3, order: 3}, + {spec: "text/*", quality: 0.5, specificity: 2, order: 1}, + {spec: "*/*", quality: 0.1, specificity: 1, order: 2}, + }) +} + +// go test -v -run=^$ -bench=Benchmark_Utils_SortAcceptedTypes_Sorted -benchmem -count=4 +func Benchmark_Utils_SortAcceptedTypes_Sorted(b *testing.B) { + acceptedTypes := make([]acceptedType, 3) + for n := 0; n < b.N; n++ { + acceptedTypes[0] = acceptedType{spec: "text/html", quality: 1, specificity: 1, order: 0} + acceptedTypes[1] = acceptedType{spec: "text/*", quality: 0.5, specificity: 1, order: 1} + acceptedTypes[2] = acceptedType{spec: "*/*", quality: 0.1, specificity: 1, order: 2} + sortAcceptedTypes(&acceptedTypes) + } + utils.AssertEqual(b, "text/html", acceptedTypes[0].spec) + utils.AssertEqual(b, "text/*", acceptedTypes[1].spec) + utils.AssertEqual(b, "*/*", acceptedTypes[2].spec) +} + +// go test -v -run=^$ -bench=Benchmark_Utils_SortAcceptedTypes_Unsorted -benchmem -count=4 +func Benchmark_Utils_SortAcceptedTypes_Unsorted(b *testing.B) { + acceptedTypes := make([]acceptedType, 11) + for n := 0; n < b.N; n++ { + acceptedTypes[0] = acceptedType{spec: "text/html", quality: 1, specificity: 3, order: 0} + acceptedTypes[1] = acceptedType{spec: "text/*", quality: 0.5, specificity: 2, order: 1} + acceptedTypes[2] = acceptedType{spec: "*/*", quality: 0.1, specificity: 1, order: 2} + acceptedTypes[3] = acceptedType{spec: "application/json", quality: 0.999, specificity: 3, order: 3} + acceptedTypes[4] = acceptedType{spec: "application/xml", quality: 1, specificity: 3, order: 4} + acceptedTypes[5] = acceptedType{spec: "application/pdf", quality: 1, specificity: 3, order: 5} + acceptedTypes[6] = acceptedType{spec: "image/png", quality: 1, specificity: 3, order: 6} + acceptedTypes[7] = acceptedType{spec: "image/jpeg", quality: 1, specificity: 3, order: 7} + acceptedTypes[8] = acceptedType{spec: "image/*", quality: 1, specificity: 2, order: 8} + acceptedTypes[9] = acceptedType{spec: "image/gif", quality: 1, specificity: 3, order: 9} + acceptedTypes[10] = acceptedType{spec: "text/plain", quality: 1, specificity: 3, order: 10} + sortAcceptedTypes(&acceptedTypes) + } + utils.AssertEqual(b, acceptedTypes, []acceptedType{ + {spec: "text/html", quality: 1, specificity: 3, order: 0}, + {spec: "application/xml", quality: 1, specificity: 3, order: 4}, + {spec: "application/pdf", quality: 1, specificity: 3, order: 5}, + {spec: "image/png", quality: 1, specificity: 3, order: 6}, + {spec: "image/jpeg", quality: 1, specificity: 3, order: 7}, + {spec: "image/gif", quality: 1, specificity: 3, order: 9}, + {spec: "text/plain", quality: 1, specificity: 3, order: 10}, + {spec: "image/*", quality: 1, specificity: 2, order: 8}, + {spec: "application/json", quality: 0.999, specificity: 3, order: 3}, + {spec: "text/*", quality: 0.5, specificity: 2, order: 1}, + {spec: "*/*", quality: 0.1, specificity: 1, order: 2}, + }) +} + // go test -v -run=^$ -bench=Benchmark_App_ETag -benchmem -count=4 func Benchmark_Utils_ETag(b *testing.B) { app := New() @@ -221,13 +343,6 @@ func Test_Utils_Parse_Address(t *testing.T) { } } -func Test_Utils_GetOffset(t *testing.T) { - t.Parallel() - utils.AssertEqual(t, "", getOffer("hello", acceptsOffer)) - utils.AssertEqual(t, "1", getOffer("", acceptsOffer, "1")) - utils.AssertEqual(t, "", getOffer("2", acceptsOffer, "1")) -} - func Test_Utils_TestConn_Deadline(t *testing.T) { t.Parallel() conn := &testConn{} From b66bcd975aa6e9b302c726a4a5e6025305408949 Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Fri, 9 Jun 2023 03:43:33 -0400 Subject: [PATCH 112/162] Migrate to Golang official govulncheck action (#2501) * Migrate to golang official govulncheck action * Remove unsupported go version from govulncheck * Update vulncheck.yml --- .github/workflows/vulncheck.yml | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/.github/workflows/vulncheck.yml b/.github/workflows/vulncheck.yml index c725128892..29e7682adc 100644 --- a/.github/workflows/vulncheck.yml +++ b/.github/workflows/vulncheck.yml @@ -17,18 +17,5 @@ jobs: Security: runs-on: ubuntu-latest steps: - - name: Install Go - uses: actions/setup-go@v4 - with: - go-version: 1.20.x - check-latest: true - - name: Fetch Repository - uses: actions/checkout@v3 - - name: Install Govulncheck - run: | - export GO111MODULE=on - export PATH=${PATH}:`go env GOPATH`/bin - go install golang.org/x/vuln/cmd/govulncheck@latest - - name: Run Govulncheck - run: "`go env GOPATH`/bin/govulncheck ./..." - + - name: Run govulncheck + uses: golang/govulncheck-action@v0.1.0 From 0822f2e32280e4c2bbcbfc1565e61d412ad24d69 Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 9 Jun 2023 13:34:29 +0200 Subject: [PATCH 113/162] Revert "Migrate to Golang official govulncheck action" (#2502) Revert "Migrate to Golang official govulncheck action (#2501)" This reverts commit b66bcd975aa6e9b302c726a4a5e6025305408949. --- .github/workflows/vulncheck.yml | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/vulncheck.yml b/.github/workflows/vulncheck.yml index 29e7682adc..c725128892 100644 --- a/.github/workflows/vulncheck.yml +++ b/.github/workflows/vulncheck.yml @@ -17,5 +17,18 @@ jobs: Security: runs-on: ubuntu-latest steps: - - name: Run govulncheck - uses: golang/govulncheck-action@v0.1.0 + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: 1.20.x + check-latest: true + - name: Fetch Repository + uses: actions/checkout@v3 + - name: Install Govulncheck + run: | + export GO111MODULE=on + export PATH=${PATH}:`go env GOPATH`/bin + go install golang.org/x/vuln/cmd/govulncheck@latest + - name: Run Govulncheck + run: "`go env GOPATH`/bin/govulncheck ./..." + From 9effdf829a79df49c24318bf753f8332d6e75375 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Fri, 9 Jun 2023 21:14:32 +0200 Subject: [PATCH 114/162] disable golang test cache --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 08137af2b7..b4b5f93f21 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,4 +48,4 @@ jobs: with: max_attempts: 3 timeout_minutes: 15 - command: go test ./... -v -race + command: go test ./... -v -race -count=1 From d4938dad6c6819034fe78822f616272e06babe60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Fri, 9 Jun 2023 23:42:50 +0300 Subject: [PATCH 115/162] :bug: bug: fix onListen hooks when they are used with prefork mode (#2504) * :bug: bug: fix onListen hooks when they are used with prefork mode :bug: bug: fix onListen hooks when they are used with prefork mode * :bug: bug: fix onListen hooks when they are used with prefork mode --- app.go | 11 +++++++---- hooks_test.go | 26 ++++++++++++++++++++++++++ listen.go | 12 ++++++++++++ prefork.go | 4 ++++ 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/app.go b/app.go index 69b7d089bd..8bd21739f6 100644 --- a/app.go +++ b/app.go @@ -1092,10 +1092,6 @@ func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) { // startupProcess Is the method which executes all the necessary processes just before the start of the server. func (app *App) startupProcess() *App { - if err := app.hooks.executeOnListenHooks(); err != nil { - panic(err) - } - app.mutex.Lock() defer app.mutex.Unlock() @@ -1106,3 +1102,10 @@ func (app *App) startupProcess() *App { return app } + +// Run onListen hooks. If they return an error, panic. +func (app *App) runOnListenHooks() { + if err := app.hooks.executeOnListenHooks(); err != nil { + panic(err) + } +} diff --git a/hooks_test.go b/hooks_test.go index 83bd8a914f..d3a6419bda 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -224,6 +224,32 @@ func Test_Hook_OnListen(t *testing.T) { utils.AssertEqual(t, "ready", buf.String()) } +func Test_Hook_OnListenPrefork(t *testing.T) { + t.Parallel() + app := New(Config{ + DisableStartupMessage: true, + Prefork: true, + }) + + buf := bytebufferpool.Get() + defer bytebufferpool.Put(buf) + + app.Hooks().OnListen(func() error { + _, err := buf.WriteString("ready") + utils.AssertEqual(t, nil, err) + + return nil + }) + + go func() { + time.Sleep(1000 * time.Millisecond) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + utils.AssertEqual(t, nil, app.Listen(":9000")) + + utils.AssertEqual(t, "ready", buf.String()) +} + func Test_Hook_OnHook(t *testing.T) { app := New() diff --git a/listen.go b/listen.go index 00d3fabe32..74ec91d8ec 100644 --- a/listen.go +++ b/listen.go @@ -30,6 +30,9 @@ func (app *App) Listener(ln net.Listener) error { // prepare the server for the start app.startupProcess() + // run hooks + app.runOnListenHooks() + // Print startup message if !app.config.DisableStartupMessage { app.startupMessage(ln.Addr().String(), getTLSConfig(ln) != nil, "") @@ -68,6 +71,9 @@ func (app *App) Listen(addr string) error { // prepare the server for the start app.startupProcess() + // run hooks + app.runOnListenHooks() + // Print startup message if !app.config.DisableStartupMessage { app.startupMessage(ln.Addr().String(), false, "") @@ -130,6 +136,9 @@ func (app *App) ListenTLSWithCertificate(addr string, cert tls.Certificate) erro // prepare the server for the start app.startupProcess() + // run hooks + app.runOnListenHooks() + // Print startup message if !app.config.DisableStartupMessage { app.startupMessage(ln.Addr().String(), true, "") @@ -202,6 +211,9 @@ func (app *App) ListenMutualTLSWithCertificate(addr string, cert tls.Certificate // prepare the server for the start app.startupProcess() + // run hooks + app.runOnListenHooks() + // Print startup message if !app.config.DisableStartupMessage { app.startupMessage(ln.Addr().String(), true, "") diff --git a/prefork.go b/prefork.go index 63dc6ce240..e2d505ffa7 100644 --- a/prefork.go +++ b/prefork.go @@ -126,6 +126,10 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) error { }() } + // Run onListen hooks + // Hooks have to be run here as different as non-prefork mode due to they should run as child or master + app.runOnListenHooks() + // Print startup message if !app.config.DisableStartupMessage { app.startupMessage(addr, tlsConfig != nil, ","+strings.Join(pids, ",")) From f5d2abb94820cf1b2687276f7cee19c95c08916d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Sat, 10 Jun 2023 17:16:11 +0300 Subject: [PATCH 116/162] :memo: docs: update version of html template (#2505) --- docs/guide/templates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/templates.md b/docs/guide/templates.md index 2e2178d090..df38fac02a 100644 --- a/docs/guide/templates.md +++ b/docs/guide/templates.md @@ -68,7 +68,7 @@ package main import ( "log" "github.com/gofiber/fiber/v2" - "github.com/gofiber/template/html" + "github.com/gofiber/template/html/v2" ) func main() { From d87065f5f2e81e47642da8bcb4d81ea088ca88d5 Mon Sep 17 00:00:00 2001 From: Iliya Date: Mon, 12 Jun 2023 09:21:57 +0330 Subject: [PATCH 117/162] =?UTF-8?q?=20=F0=9F=9A=80=20FEATURE:=20add=20quer?= =?UTF-8?q?ies=20function=20(#2475)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🚀 FEATURE: add queries method * 📚 DOCS: add documents for queries method. * 🩹 Fix: fix wrap error returned from Queries function * 🚨 tests: add url encoded tests * 🔥 feature: add url encoded support for queries * 🩹 Fix: fix wrap error returned from Queries function * ♻️ Refactor: change error message of url.QueryUnescape * ♻️ Refactor: refactor of mapping key and value queries * 🚨 Test: Validate to fail parse queries * 🚨 Test: Add benchmark test for Queries * 🩹 Fix: remove parsing for encoded urls * ♻️ Refactor: change string function to c.app.getString fucntion Co-authored-by: cmd777 <83428931+cmd777@users.noreply.github.com> * ♻️ Refactor: change name of benchamark function ctx queries Co-authored-by: leonklingele * ♻️ Refactor: remove empty lines Co-authored-by: leonklingele * Revert "♻️ Refactor: change string function to c.app.getString fucntion" This reverts commit 28febf9e602bb13f0761169c26f264e4687da660. * 📚 Docs: add documents for queries method * 🚨 Tests: add more tests for queries function * ♻️ Refactor: change string function to c.app.getString function * 🚨 Tests: add more test for queries function * 📚 Docs: add more documents to queries function --------- Co-authored-by: cmd777 <83428931+cmd777@users.noreply.github.com> Co-authored-by: leonklingele --- ctx.go | 29 ++++++++++++++++++++ ctx_test.go | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ docs/api/ctx.md | 64 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) diff --git a/ctx.go b/ctx.go index 8bb29d3149..9a9694dcf9 100644 --- a/ctx.go +++ b/ctx.go @@ -1054,6 +1054,35 @@ func (c *Ctx) Query(key string, defaultValue ...string) string { return defaultString(c.app.getString(c.fasthttp.QueryArgs().Peek(key)), defaultValue) } +// Queries returns a map of query parameters and their values. +// +// GET /?name=alex&wanna_cake=2&id= +// Queries()["name"] == "alex" +// Queries()["wanna_cake"] == "2" +// Queries()["id"] == "" +// +// GET /?field1=value1&field1=value2&field2=value3 +// Queries()["field1"] == "value2" +// Queries()["field2"] == "value3" +// +// GET /?list_a=1&list_a=2&list_a=3&list_b[]=1&list_b[]=2&list_b[]=3&list_c=1,2,3 +// Queries()["list_a"] == "3" +// Queries()["list_b[]"] == "3" +// Queries()["list_c"] == "1,2,3" +// +// GET /api/search?filters.author.name=John&filters.category.name=Technology&filters[customer][name]=Alice&filters[status]=pending +// Queries()["filters.author.name"] == "John" +// Queries()["filters.category.name"] == "Technology" +// Queries()["filters[customer][name]"] == "Alice" +// Queries()["filters[status]"] == "pending" +func (c *Ctx) Queries() map[string]string { + m := make(map[string]string, c.Context().QueryArgs().Len()) + c.Context().QueryArgs().VisitAll(func(key, value []byte) { + m[c.app.getString(key)] = c.app.getString(value) + }) + return m +} + // QueryInt returns integer value of key string parameter in the url. // Default to empty or invalid key is 0. // diff --git a/ctx_test.go b/ctx_test.go index c15b6d0892..38e83ca9e2 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -3842,6 +3842,78 @@ func Benchmark_Ctx_SendString_B(b *testing.B) { utils.AssertEqual(b, []byte("Hello, world!"), c.Response().Body()) } +// go test -run Test_Ctx_Queries -v +func Test_Ctx_Queries(t *testing.T) { + t.Parallel() + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + + c.Request().SetBody([]byte(``)) + c.Request().Header.SetContentType("") + c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football&favouriteDrinks=milo,coke,pepsi&alloc=&no=1&field1=value1&field1=value2&field2=value3&list_a=1&list_a=2&list_a=3&list_b[]=1&list_b[]=2&list_b[]=3&list_c=1,2,3") + + queries := c.Queries() + utils.AssertEqual(t, "1", queries["id"]) + utils.AssertEqual(t, "tom", queries["name"]) + utils.AssertEqual(t, "basketball,football", queries["hobby"]) + utils.AssertEqual(t, "milo,coke,pepsi", queries["favouriteDrinks"]) + utils.AssertEqual(t, "", queries["alloc"]) + utils.AssertEqual(t, "1", queries["no"]) + utils.AssertEqual(t, "value2", queries["field1"]) + utils.AssertEqual(t, "value3", queries["field2"]) + utils.AssertEqual(t, "3", queries["list_a"]) + utils.AssertEqual(t, "3", queries["list_b[]"]) + utils.AssertEqual(t, "1,2,3", queries["list_c"]) + + c.Request().URI().SetQueryString("filters.author.name=John&filters.category.name=Technology&filters[customer][name]=Alice&filters[status]=pending") + + queries = c.Queries() + utils.AssertEqual(t, "John", queries["filters.author.name"]) + utils.AssertEqual(t, "Technology", queries["filters.category.name"]) + utils.AssertEqual(t, "Alice", queries["filters[customer][name]"]) + utils.AssertEqual(t, "pending", queries["filters[status]"]) + + c.Request().URI().SetQueryString("tags=apple,orange,banana&filters[tags]=apple,orange,banana&filters[category][name]=fruits&filters.tags=apple,orange,banana&filters.category.name=fruits") + + queries = c.Queries() + utils.AssertEqual(t, "apple,orange,banana", queries["tags"]) + utils.AssertEqual(t, "apple,orange,banana", queries["filters[tags]"]) + utils.AssertEqual(t, "fruits", queries["filters[category][name]"]) + utils.AssertEqual(t, "apple,orange,banana", queries["filters.tags"]) + utils.AssertEqual(t, "fruits", queries["filters.category.name"]) + + c.Request().URI().SetQueryString("filters[tags][0]=apple&filters[tags][1]=orange&filters[tags][2]=banana&filters[category][name]=fruits") + + queries = c.Queries() + utils.AssertEqual(t, "apple", queries["filters[tags][0]"]) + utils.AssertEqual(t, "orange", queries["filters[tags][1]"]) + utils.AssertEqual(t, "banana", queries["filters[tags][2]"]) + utils.AssertEqual(t, "fruits", queries["filters[category][name]"]) +} + +// go test -v -run=^$ -bench=Benchmark_Ctx_Queries -benchmem -count=4 +func Benchmark_Ctx_Queries(b *testing.B) { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + b.ReportAllocs() + b.ResetTimer() + c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football&favouriteDrinks=milo,coke,pepsi&alloc=&no=1") + + var queries map[string]string + for n := 0; n < b.N; n++ { + queries = c.Queries() + } + + utils.AssertEqual(b, "1", queries["id"]) + utils.AssertEqual(b, "tom", queries["name"]) + utils.AssertEqual(b, "basketball,football", queries["hobby"]) + utils.AssertEqual(b, "milo,coke,pepsi", queries["favouriteDrinks"]) + utils.AssertEqual(b, "", queries["alloc"]) + utils.AssertEqual(b, "1", queries["no"]) +} + // go test -run Test_Ctx_QueryParser -v func Test_Ctx_QueryParser(t *testing.T) { t.Parallel() diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 5286a19408..5c58bd92e0 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1094,6 +1094,70 @@ app.Get("/", func(c *fiber.Ctx) error { > _Returned value is only valid within the handler. Do not store any references. > Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) +## Queries + +Queries is a function that returns an object containing a property for each query string parameter in the route. + +```go title="Signature" +func (c *Ctx) Queries() (map[string]string, error) +``` + +```go title="Example" +// GET http://example.com/?name=alex&want_pizza=false&id= + +app.Get("/", func(c *fiber.Ctx) error { +m := c.Queries() +m["name"] // "alex" +m["want_pizza"] // "false" +m["id"] // "" +// ... +}) +``` + +```go title="Example" +// GET http://example.com/?field1=value1&field1=value2&field2=value3 + +app.Get("/", func (c *fiber.Ctx) error { + m := c.Queries() + m["field1"] // "value2" + m["field2"] // value3 +}) +``` + +```go title="Example" +// GET http://example.com/?list_a=1&list_a=2&list_a=3&list_b[]=1&list_b[]=2&list_b[]=3&list_c=1,2,3 + +app.Get("/", func(c *fiber.Ctx) error { + m := c.Queries() + m["list_a"] // "3" + m["list_b[]"] // "3" + m["list_c"] // "1,2,3" +}) +``` + +```go title="Example" +// GET /api/posts?filters.author.name=John&filters.category.name=Technology + +app.Get("/", func(c *fiber.Ctx) error { + m := c.Queies() + m["filters.author.name"] // John + m["filters.category.name"] // Technology + }) +``` + +```go title="Example" +// GET /api/posts?tags=apple,orange,banana&filters[tags]=apple,orange,banana&filters[category][name]=fruits&filters.tags=apple,orange,banana&filters.category.name=fruits + +app.Get("/", func(c *fiber.Ctx) error { + m := c.Queries() + m["tags"] // apple,orange,banana + m["filters[tags]"] // apple,orange,banana + m["filters[category][name]"] // fruits + m["filters.tags"] // apple,orange,banana + m["filters.category.name"] // fruits +}) +``` + ## QueryBool This property is an object containing a property for each query boolean parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. From 719a5a2a8f194a2c5a8700289c6b424b441db213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Mon, 12 Jun 2023 07:59:59 +0200 Subject: [PATCH 118/162] repair test workflow --- .github/workflows/test.yml | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b4b5f93f21..0206df5841 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,28 +21,12 @@ jobs: platform: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: + - name: Fetch Repository + uses: actions/checkout@v3 - name: Install Go uses: actions/setup-go@v4 with: go-version: ${{ matrix.go-version }} - - name: Setup Golang caches - uses: actions/cache@v3 - with: - # In order: - # * Module download cache - # * Build cache (Linux) - # * Build cache (Mac) - # * Build cache (Windows) - path: | - ~/go/pkg/mod - ~/.cache/go-build - ~/Library/Caches/go-build - ~\AppData\Local\go-build - key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go-${{ matrix.go-version }}- - - name: Fetch Repository - uses: actions/checkout@v3 - name: Run Test uses: nick-fields/retry@v2 with: From f4a9cb5023b871430b669d5ca36db1478033dad4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jun 2023 20:03:22 +0300 Subject: [PATCH 119/162] Bump golang.org/x/sys from 0.8.0 to 0.9.0 (#2508) Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.8.0 to 0.9.0. - [Commits](https://github.com/golang/sys/compare/v0.8.0...v0.9.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fde5e92724..cdf5aaa7df 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/tinylib/msgp v1.1.8 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.47.0 - golang.org/x/sys v0.8.0 + golang.org/x/sys v0.9.0 ) require ( diff --git a/go.sum b/go.sum index 779de1af3c..f380b7229e 100644 --- a/go.sum +++ b/go.sum @@ -59,8 +59,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc 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.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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= From f86423d625905f8abe6511e19291da5b45376980 Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 19 Jun 2023 10:41:53 +0200 Subject: [PATCH 120/162] Update ctx.md reformat ctx.Queries() example --- docs/api/ctx.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 5c58bd92e0..02b9932846 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1106,11 +1106,11 @@ func (c *Ctx) Queries() (map[string]string, error) // GET http://example.com/?name=alex&want_pizza=false&id= app.Get("/", func(c *fiber.Ctx) error { -m := c.Queries() -m["name"] // "alex" -m["want_pizza"] // "false" -m["id"] // "" -// ... + m := c.Queries() + m["name"] // "alex" + m["want_pizza"] // "false" + m["id"] // "" + // ... }) ``` From fa5935b7e9b3358a82427d2d66ccba8455322336 Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 19 Jun 2023 10:43:15 +0200 Subject: [PATCH 121/162] Update ctx.md reformat queries docu --- docs/api/ctx.md | 60 ++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 02b9932846..4fcfe12d32 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1067,33 +1067,6 @@ app.Get("/", func(c *fiber.Ctx) error { }) ``` -## Query - -This property is an object containing a property for each query string parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. - -:::info -If there is **no** query string, it returns an **empty string**. -::: - -```go title="Signature" -func (c *Ctx) Query(key string, defaultValue ...string) string -``` - -```go title="Example" -// GET http://example.com/?order=desc&brand=nike - -app.Get("/", func(c *fiber.Ctx) error { - c.Query("order") // "desc" - c.Query("brand") // "nike" - c.Query("empty", "nike") // "nike" - - // ... -}) -``` - -> _Returned value is only valid within the handler. Do not store any references. -> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) - ## Queries Queries is a function that returns an object containing a property for each query string parameter in the route. @@ -1129,9 +1102,9 @@ app.Get("/", func (c *fiber.Ctx) error { app.Get("/", func(c *fiber.Ctx) error { m := c.Queries() - m["list_a"] // "3" - m["list_b[]"] // "3" - m["list_c"] // "1,2,3" + m["list_a"] // "3" + m["list_b[]"] // "3" + m["list_c"] // "1,2,3" }) ``` @@ -1158,6 +1131,33 @@ app.Get("/", func(c *fiber.Ctx) error { }) ``` +## Query + +This property is an object containing a property for each query string parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. + +:::info +If there is **no** query string, it returns an **empty string**. +::: + +```go title="Signature" +func (c *Ctx) Query(key string, defaultValue ...string) string +``` + +```go title="Example" +// GET http://example.com/?order=desc&brand=nike + +app.Get("/", func(c *fiber.Ctx) error { + c.Query("order") // "desc" + c.Query("brand") // "nike" + c.Query("empty", "nike") // "nike" + + // ... +}) +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + ## QueryBool This property is an object containing a property for each query boolean parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. From 35ea74a317353132c5bc236d2d6945b4155b184d Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 19 Jun 2023 10:44:23 +0200 Subject: [PATCH 122/162] Update app.go prepare release v2.47.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 8bd21739f6..e82faae316 100644 --- a/app.go +++ b/app.go @@ -30,7 +30,7 @@ import ( ) // Version of current fiber package -const Version = "2.46.0" +const Version = "2.47.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From 204b01aeb247a09c83b4c4ead0abc31efd5c325f Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 19 Jun 2023 10:47:45 +0200 Subject: [PATCH 123/162] Update ctx.md --- docs/api/ctx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 4fcfe12d32..08b029ccf5 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1115,7 +1115,7 @@ app.Get("/", func(c *fiber.Ctx) error { m := c.Queies() m["filters.author.name"] // John m["filters.category.name"] // Technology - }) +}) ``` ```go title="Example" From 9bcdb560dc28f49a0120c13e70534b7109b33654 Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 19 Jun 2023 10:49:25 +0200 Subject: [PATCH 124/162] Update ctx.md --- docs/api/ctx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 08b029ccf5..3274273c35 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1112,7 +1112,7 @@ app.Get("/", func(c *fiber.Ctx) error { // GET /api/posts?filters.author.name=John&filters.category.name=Technology app.Get("/", func(c *fiber.Ctx) error { - m := c.Queies() + m := c.Queries() m["filters.author.name"] // John m["filters.category.name"] // Technology }) From 3dc9e1df80cae9cf1cca7a3c4e66b7c7245f4b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=88=91=E7=9A=84=E5=90=8D=E5=AD=97=E5=8F=AB=E6=B5=A9?= =?UTF-8?q?=E4=BB=94?= Date: Mon, 19 Jun 2023 18:22:55 +0800 Subject: [PATCH 125/162] =?UTF-8?q?=F0=9F=90=9B=20bug:=20fix=20docs=20api?= =?UTF-8?q?=20fiber=20custom=20config=20(#2510)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: docs api fiber custom config Co-authored-by: haoc --- docs/api/fiber.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/fiber.md b/docs/api/fiber.md index 2218b26103..fa0ad2412d 100644 --- a/docs/api/fiber.md +++ b/docs/api/fiber.md @@ -31,7 +31,7 @@ app := fiber.New(fiber.Config{ CaseSensitive: true, StrictRouting: true, ServerHeader: "Fiber", - AppName: "Test App v1.0.1" + AppName: "Test App v1.0.1", }) // ... From ed95fa8c7e267f0d7510916f2e979a59434e246f Mon Sep 17 00:00:00 2001 From: SamanDev <68618538+Saman-Safaei@users.noreply.github.com> Date: Mon, 19 Jun 2023 14:41:22 +0330 Subject: [PATCH 126/162] =?UTF-8?q?=F0=9F=94=A5=20Feature:=20add=20ability?= =?UTF-8?q?=20to=20print=20custom=20message=20on=20startup=20(#2491)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add a variadic parameter on OnListenHandler * feat: accept a variadic ListenData in startupProcess parameters * feat: add startupProcess variadic ListenData to function * refactor: use runOnListenHooks instead of startupProcess for run onListenHooks * refactor: remove variadic to make codes straightforward * fix: add listen data to runOnListenHooks * test: add listenData parameter to OnListen tests * docs: update OnListen docs * fix: remove unused codes * docs: add tabs to onListen hook example * docs: add if statement to docs example * docs: replace fmt with log * docs: fix return value of example * docs: make 0.0.0.0 string a constant * fix: change type of TLS from string to bool * fix: return bool instead of a string * docs: update example with new TLS type * fix: change name tls to isTls to prevent shadowing tls variable * style: make syntax of onListen example shorter * refactor: remove unused no-lint comment * refactor: change isTls to isTLS * fix: add nolint for isTLS bool param * Update listen.go --------- Co-authored-by: M. Efe Çetin --- app.go | 4 ++-- docs/guide/hooks.md | 27 +++++++++++++++++++++++++-- hooks.go | 15 +++++++++++---- hooks_test.go | 4 ++-- listen.go | 38 ++++++++++++++++++++++++++++++-------- prefork.go | 2 +- 6 files changed, 71 insertions(+), 19 deletions(-) diff --git a/app.go b/app.go index e82faae316..40366ce6e6 100644 --- a/app.go +++ b/app.go @@ -1104,8 +1104,8 @@ func (app *App) startupProcess() *App { } // Run onListen hooks. If they return an error, panic. -func (app *App) runOnListenHooks() { - if err := app.hooks.executeOnListenHooks(); err != nil { +func (app *App) runOnListenHooks(listenData ListenData) { + if err := app.hooks.executeOnListenHooks(listenData); err != nil { panic(err) } } diff --git a/docs/guide/hooks.md b/docs/guide/hooks.md index c0470e7bd7..d27c5e9776 100644 --- a/docs/guide/hooks.md +++ b/docs/guide/hooks.md @@ -24,9 +24,9 @@ type OnRouteHandler = func(Route) error type OnNameHandler = OnRouteHandler type OnGroupHandler = func(Group) error type OnGroupNameHandler = OnGroupHandler -type OnListenHandler = func() error +type OnListenHandler = func(ListenData) error type OnForkHandler = func(int) error -type OnShutdownHandler = OnListenHandler +type OnShutdownHandler = func() error type OnMountHandler = func(*App) error ``` @@ -127,6 +127,29 @@ OnListen is a hook to execute user functions on Listen, ListenTLS, Listener. func (app *App) OnListen(handler ...OnListenHandler) ``` + + + +```go +app := fiber.New(fiber.Config{ + DisableStartupMessage: true, +}) + +app.Hooks().OnListen(func(listenData fiber.ListenData) error { + if fiber.IsChild() { + return nil + } + scheme := "http" + if data.TLS { + scheme = "https" + } + log.Println(scheme + "://" + listenData.Host + ":" + listenData.Port) + return nil +}) + +app.Listen(":5000") +``` + ## OnFork OnFork is a hook to execute user functions on Fork. diff --git a/hooks.go b/hooks.go index c66e49fde8..099cf0561e 100644 --- a/hooks.go +++ b/hooks.go @@ -10,8 +10,8 @@ type ( OnNameHandler = OnRouteHandler OnGroupHandler = func(Group) error OnGroupNameHandler = OnGroupHandler - OnListenHandler = func() error - OnShutdownHandler = OnListenHandler + OnListenHandler = func(ListenData) error + OnShutdownHandler = func() error OnForkHandler = func(int) error OnMountHandler = func(*App) error ) @@ -32,6 +32,13 @@ type Hooks struct { onMount []OnMountHandler } +// ListenData is a struct to use it with OnListenHandler +type ListenData struct { + Host string + Port string + TLS bool +} + func newHooks(app *App) *Hooks { return &Hooks{ app: app, @@ -174,9 +181,9 @@ func (h *Hooks) executeOnGroupNameHooks(group Group) error { return nil } -func (h *Hooks) executeOnListenHooks() error { +func (h *Hooks) executeOnListenHooks(listenData ListenData) error { for _, v := range h.onListen { - if err := v(); err != nil { + if err := v(listenData); err != nil { return err } } diff --git a/hooks_test.go b/hooks_test.go index d3a6419bda..fb3cad20b9 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -208,7 +208,7 @@ func Test_Hook_OnListen(t *testing.T) { buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) - app.Hooks().OnListen(func() error { + app.Hooks().OnListen(func(listenData ListenData) error { _, err := buf.WriteString("ready") utils.AssertEqual(t, nil, err) @@ -234,7 +234,7 @@ func Test_Hook_OnListenPrefork(t *testing.T) { buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) - app.Hooks().OnListen(func() error { + app.Hooks().OnListen(func(listenData ListenData) error { _, err := buf.WriteString("ready") utils.AssertEqual(t, nil, err) diff --git a/listen.go b/listen.go index 74ec91d8ec..ef48ab8965 100644 --- a/listen.go +++ b/listen.go @@ -25,13 +25,17 @@ import ( "github.com/mattn/go-runewidth" ) +const ( + globalIpv4Addr = "0.0.0.0" +) + // Listener can be used to pass a custom listener. func (app *App) Listener(ln net.Listener) error { // prepare the server for the start app.startupProcess() // run hooks - app.runOnListenHooks() + app.runOnListenHooks(app.prepareListenData(ln.Addr().String(), getTLSConfig(ln) != nil)) // Print startup message if !app.config.DisableStartupMessage { @@ -72,7 +76,7 @@ func (app *App) Listen(addr string) error { app.startupProcess() // run hooks - app.runOnListenHooks() + app.runOnListenHooks(app.prepareListenData(ln.Addr().String(), false)) // Print startup message if !app.config.DisableStartupMessage { @@ -137,7 +141,7 @@ func (app *App) ListenTLSWithCertificate(addr string, cert tls.Certificate) erro app.startupProcess() // run hooks - app.runOnListenHooks() + app.runOnListenHooks(app.prepareListenData(ln.Addr().String(), getTLSConfig(ln) != nil)) // Print startup message if !app.config.DisableStartupMessage { @@ -212,7 +216,7 @@ func (app *App) ListenMutualTLSWithCertificate(addr string, cert tls.Certificate app.startupProcess() // run hooks - app.runOnListenHooks() + app.runOnListenHooks(app.prepareListenData(ln.Addr().String(), getTLSConfig(ln) != nil)) // Print startup message if !app.config.DisableStartupMessage { @@ -231,8 +235,26 @@ func (app *App) ListenMutualTLSWithCertificate(addr string, cert tls.Certificate return app.server.Serve(ln) } +// prepareListenData create an slice of ListenData +func (app *App) prepareListenData(addr string, isTLS bool) ListenData { //revive:disable-line:flag-parameter // Accepting a bool param named isTLS if fine here + host, port := parseAddr(addr) + if host == "" { + if app.config.Network == NetworkTCP6 { + host = "[::1]" + } else { + host = globalIpv4Addr + } + } + + return ListenData{ + Host: host, + Port: port, + TLS: isTLS, + } +} + // startupMessage prepares the startup message with the handler number, port, address and other information -func (app *App) startupMessage(addr string, tls bool, pids string) { //nolint: revive // Accepting a bool param is fine here +func (app *App) startupMessage(addr string, isTLS bool, pids string) { //nolint: revive // Accepting a bool param named isTLS if fine here // ignore child processes if IsChild() { return @@ -294,12 +316,12 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { //nolint: r if app.config.Network == NetworkTCP6 { host = "[::1]" } else { - host = "0.0.0.0" + host = globalIpv4Addr } } scheme := schemeHTTP - if tls { + if isTLS { scheme = schemeHTTPS } @@ -320,7 +342,7 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { //nolint: r } mainLogo += " │ " + centerValue("Fiber v"+Version, lineLen) + " │\n" - if host == "0.0.0.0" { + if host == globalIpv4Addr { mainLogo += " │ " + center(fmt.Sprintf("%s://127.0.0.1:%s", scheme, port), lineLen) + " │\n" + " │ " + center(fmt.Sprintf("(bound on host 0.0.0.0 and port %s)", port), lineLen) + " │\n" } else { diff --git a/prefork.go b/prefork.go index e2d505ffa7..8639746979 100644 --- a/prefork.go +++ b/prefork.go @@ -128,7 +128,7 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) error { // Run onListen hooks // Hooks have to be run here as different as non-prefork mode due to they should run as child or master - app.runOnListenHooks() + app.runOnListenHooks(app.prepareListenData(addr, tlsConfig != nil)) // Print startup message if !app.config.DisableStartupMessage { From 2eaeb0f3f7d57c638b013a0718e622b6f6201f2c Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 19 Jun 2023 13:24:41 +0200 Subject: [PATCH 127/162] Update hooks.md correct docs syntax error --- docs/guide/hooks.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/guide/hooks.md b/docs/guide/hooks.md index d27c5e9776..0b7db96b33 100644 --- a/docs/guide/hooks.md +++ b/docs/guide/hooks.md @@ -150,6 +150,9 @@ app.Hooks().OnListen(func(listenData fiber.ListenData) error { app.Listen(":5000") ``` + + + ## OnFork OnFork is a hook to execute user functions on Fork. From 78f4510b282381ce4135d40fcea29a80bbfe709e Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Mon, 19 Jun 2023 07:33:18 -0400 Subject: [PATCH 128/162] Disable caching when running govulncheck (#2503) * Migrate to golang official govulncheck action * Remove unsupported go version from govulncheck * Update vulncheck.yml * Update vulncheck.yml * Update template to disable caching * Run checkout before setup-go --- .github/workflows/linter.yml | 2 +- .github/workflows/vulncheck.yml | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 7599009bc6..2b8e893e4c 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -14,11 +14,11 @@ jobs: name: lint runs-on: ubuntu-latest steps: + - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: # NOTE: Keep this in sync with the version from go.mod go-version: 1.20.x - - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: diff --git a/.github/workflows/vulncheck.yml b/.github/workflows/vulncheck.yml index c725128892..4c13423020 100644 --- a/.github/workflows/vulncheck.yml +++ b/.github/workflows/vulncheck.yml @@ -1,3 +1,5 @@ +name: Run govulncheck + on: push: branches: @@ -12,23 +14,22 @@ on: - '**' - '!docs/**' - '!**.md' -name: Vulnerability Check + jobs: - Security: + govulncheck-check: runs-on: ubuntu-latest + env: + GO111MODULE: on steps: + - name: Fetch Repository + uses: actions/checkout@v3 - name: Install Go uses: actions/setup-go@v4 with: - go-version: 1.20.x + go-version: 'stable' check-latest: true - - name: Fetch Repository - uses: actions/checkout@v3 + cache: false - name: Install Govulncheck - run: | - export GO111MODULE=on - export PATH=${PATH}:`go env GOPATH`/bin - go install golang.org/x/vuln/cmd/govulncheck@latest + run: go install golang.org/x/vuln/cmd/govulncheck@latest - name: Run Govulncheck - run: "`go env GOPATH`/bin/govulncheck ./..." - + run: govulncheck ./... \ No newline at end of file From a312a17402001a6a9a27c96b961a9de3c46bc196 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jun 2023 18:53:10 +0200 Subject: [PATCH 129/162] Bump github.com/valyala/fasthttp from 1.47.0 to 1.48.0 (#2511) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bump github.com/valyala/fasthttp from 1.47.0 to 1.48.0 Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.47.0 to 1.48.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.47.0...v1.48.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * repair test setup for this change "Request timeout settings for the same domain name are reused #1558" https://github.com/valyala/fasthttp/pull/1558 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: René Werner --- client_test.go | 8 ++++++++ go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/client_test.go b/client_test.go index 8df0562d61..395a18e0b8 100644 --- a/client_test.go +++ b/client_test.go @@ -605,6 +605,14 @@ func (*readErrorConn) RemoteAddr() net.Addr { return nil } +func (*readErrorConn) SetReadDeadline(_ time.Time) error { + return nil +} + +func (*readErrorConn) SetWriteDeadline(_ time.Time) error { + return nil +} + func Test_Client_Agent_RetryIf(t *testing.T) { t.Parallel() diff --git a/go.mod b/go.mod index cdf5aaa7df..0eea433db1 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 github.com/tinylib/msgp v1.1.8 github.com/valyala/bytebufferpool v1.0.0 - github.com/valyala/fasthttp v1.47.0 + github.com/valyala/fasthttp v1.48.0 golang.org/x/sys v0.9.0 ) diff --git a/go.sum b/go.sum index f380b7229e..76516c5004 100644 --- a/go.sum +++ b/go.sum @@ -26,8 +26,8 @@ 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/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.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c= -github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/fasthttp v1.48.0 h1:oJWvHb9BIZToTQS3MuQ2R3bJZiNSa2KiNdeI8A+79Tc= +github.com/valyala/fasthttp v1.48.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= 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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= From 1b060cb150c6eab80f71e51120b05da555b43c27 Mon Sep 17 00:00:00 2001 From: cmd777 <83428931+cmd777@users.noreply.github.com> Date: Thu, 22 Jun 2023 13:06:37 +0200 Subject: [PATCH 130/162] :adhesive_bandage: Fix: default logger color behaviour (#2513) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix logger colors * Fix tests Basically add ˙enableColors: true˙ back to default config --- docs/api/middleware/logger.md | 2 +- middleware/logger/config.go | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/api/middleware/logger.md b/docs/api/middleware/logger.md index 775e39fcac..1d736de551 100644 --- a/docs/api/middleware/logger.md +++ b/docs/api/middleware/logger.md @@ -153,7 +153,7 @@ var ConfigDefault = Config{ TimeZone: "Local", TimeInterval: 500 * time.Millisecond, Output: os.Stdout, - DisableColors: true, + DisableColors: false, } ``` diff --git a/middleware/logger/config.go b/middleware/logger/config.go index ea440cf66d..5b91a065ee 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -84,14 +84,15 @@ type LogFunc func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (i // ConfigDefault is the default config var ConfigDefault = Config{ - Next: nil, - Done: nil, - Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", - TimeFormat: "15:04:05", - TimeZone: "Local", - TimeInterval: 500 * time.Millisecond, - Output: os.Stdout, - enableColors: false, + Next: nil, + Done: nil, + Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", + TimeFormat: "15:04:05", + TimeZone: "Local", + TimeInterval: 500 * time.Millisecond, + Output: os.Stdout, + DisableColors: false, + enableColors: true, } // Helper function to set default values From b308b2b7a660fbf29790f937211d23f32ff9cb26 Mon Sep 17 00:00:00 2001 From: RW Date: Thu, 22 Jun 2023 13:07:33 +0200 Subject: [PATCH 131/162] Update logger.md correct spacings --- docs/api/middleware/logger.md | 58 +++++++++++++++++------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/docs/api/middleware/logger.md b/docs/api/middleware/logger.md index 1d736de551..265aaa72d3 100644 --- a/docs/api/middleware/logger.md +++ b/docs/api/middleware/logger.md @@ -15,8 +15,8 @@ Import the middleware package that is part of the Fiber web framework ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/logger" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/logger" ) ``` @@ -34,51 +34,51 @@ app.Use(logger.New()) // Or extend your config for customization // Logging remote IP and Port app.Use(logger.New(logger.Config{ - Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", + Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", })) // Logging Request ID 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", + // For more options, see the Config section + Format: "${pid} ${locals:requestid} ${status} - ${method} ${path}​\n", })) // Changing TimeZone & TimeFormat app.Use(logger.New(logger.Config{ - Format: "${pid} ${status} - ${method} ${path}\n", - TimeFormat: "02-Jan-2006", - TimeZone: "America/New_York", + Format: "${pid} ${status} - ${method} ${path}\n", + TimeFormat: "02-Jan-2006", + TimeZone: "America/New_York", })) // Custom File Writer file, err := os.OpenFile("./123.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { - log.Fatalf("error opening file: %v", err) + log.Fatalf("error opening file: %v", err) } defer file.Close() app.Use(logger.New(logger.Config{ - Output: file, + Output: file, })) // Add Custom Tags app.Use(logger.New(logger.Config{ - CustomTags: map[string]logger.LogFunc{ - "custom_tag": func(output logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) { - return output.WriteString("it is a custom tag") - }, - }, + CustomTags: map[string]logger.LogFunc{ + "custom_tag": func(output logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) { + return output.WriteString("it is a custom tag") + }, + }, })) // Callback after log is written app.Use(logger.New(logger.Config{ - TimeFormat: time.RFC3339Nano, - TimeZone: "Asia/Shanghai", - Done: func(c *fiber.Ctx, logString []byte) { - if c.Response().StatusCode() != fiber.StatusOK { - reporter.SendToSlack(logString) - } - }, + TimeFormat: time.RFC3339Nano, + TimeZone: "Asia/Shanghai", + Done: func(c *fiber.Ctx, logString []byte) { + if c.Response().StatusCode() != fiber.StatusOK { + reporter.SendToSlack(logString) + } + }, })) // Disable colors when outputting to default format @@ -146,13 +146,13 @@ type LogFunc func(buf logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam ## Default Config ```go var ConfigDefault = Config{ - Next: nil, - Done: nil, - Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", - TimeFormat: "15:04:05", - TimeZone: "Local", - TimeInterval: 500 * time.Millisecond, - Output: os.Stdout, + Next: nil, + Done: nil, + Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", + TimeFormat: "15:04:05", + TimeZone: "Local", + TimeInterval: 500 * time.Millisecond, + Output: os.Stdout, DisableColors: false, } ``` From 1603a148feb3c0f06ec04b96a81be608ac460326 Mon Sep 17 00:00:00 2001 From: RW Date: Thu, 22 Jun 2023 13:21:14 +0200 Subject: [PATCH 132/162] Update logger.md --- docs/api/middleware/logger.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/api/middleware/logger.md b/docs/api/middleware/logger.md index 265aaa72d3..7b00ff3589 100644 --- a/docs/api/middleware/logger.md +++ b/docs/api/middleware/logger.md @@ -146,13 +146,13 @@ type LogFunc func(buf logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam ## Default Config ```go var ConfigDefault = Config{ - Next: nil, - Done: nil, - Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", - TimeFormat: "15:04:05", - TimeZone: "Local", - TimeInterval: 500 * time.Millisecond, - Output: os.Stdout, + Next: nil, + Done: nil, + Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", + TimeFormat: "15:04:05", + TimeZone: "Local", + TimeInterval: 500 * time.Millisecond, + Output: os.Stdout, DisableColors: false, } ``` From 85bd155bee235b54c2224aa1ce8e5a5fd3b8fc59 Mon Sep 17 00:00:00 2001 From: Moein Halvaei <50274938+mo1ein@users.noreply.github.com> Date: Fri, 23 Jun 2023 13:14:29 +0330 Subject: [PATCH 133/162] Fix comment in client.go (#2514) --- client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.go b/client.go index ab45a58703..a30c15ab6a 100644 --- a/client.go +++ b/client.go @@ -380,7 +380,7 @@ func (a *Agent) ContentTypeBytes(contentType []byte) *Agent { /************************** URI Setting **************************/ -// Host sets host for the uri. +// Host sets host for the URI. func (a *Agent) Host(host string) *Agent { a.req.URI().SetHost(host) From 040aac94c6b045c92e6efbd1d0ca7fb442aba706 Mon Sep 17 00:00:00 2001 From: "W. Xiaoyun" <81742181+ForAeons@users.noreply.github.com> Date: Fri, 23 Jun 2023 17:44:52 +0800 Subject: [PATCH 134/162] :pencil2: Fix: typo in ctx.md (#2516) Fix: typo in ctx.md --- docs/api/ctx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 3274273c35..2ef70af91a 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1718,7 +1718,7 @@ app.Get("/", func(c *fiber.Ctx) error { ## SetParserDecoder -Allow you to config BodyParser/QueryParser decoder, base on schema's options, providing possibility to add custom type for pausing. +Allow you to config BodyParser/QueryParser decoder, base on schema's options, providing possibility to add custom type for parsing. ```go title="Signature" func SetParserDecoder(parserConfig fiber.ParserConfig{ From 5967d36bc0e539d27290eec9fd05a043147f439c Mon Sep 17 00:00:00 2001 From: Moein Halvaei <50274938+mo1ein@users.noreply.github.com> Date: Fri, 23 Jun 2023 17:45:38 +0330 Subject: [PATCH 135/162] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix=20typo=20(#251?= =?UTF-8?q?8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: typo in client.go * Fix: typo in ctx.go * Fix: typo in path.go * Fix: typo in router.go * Fix: typo in adaptor.go --- client.go | 34 +++++++++++++++++----------------- ctx.go | 2 +- middleware/adaptor/adaptor.go | 2 +- path.go | 8 ++++---- router.go | 8 ++++---- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/client.go b/client.go index a30c15ab6a..fe1daf72af 100644 --- a/client.go +++ b/client.go @@ -78,50 +78,50 @@ type Client struct { JSONDecoder utils.JSONUnmarshal } -// Get returns a agent with http method GET. +// Get returns an agent with http method GET. func Get(url string) *Agent { return defaultClient.Get(url) } -// Get returns a agent with http method GET. +// Get returns an agent with http method GET. func (c *Client) Get(url string) *Agent { return c.createAgent(MethodGet, url) } -// Head returns a agent with http method HEAD. +// Head returns an agent with http method HEAD. func Head(url string) *Agent { return defaultClient.Head(url) } -// Head returns a agent with http method GET. +// Head returns an agent with http method GET. func (c *Client) Head(url string) *Agent { return c.createAgent(MethodHead, url) } -// Post sends POST request to the given url. +// Post sends POST request to the given URL. func Post(url string) *Agent { return defaultClient.Post(url) } -// Post sends POST request to the given url. +// Post sends POST request to the given URL. func (c *Client) Post(url string) *Agent { return c.createAgent(MethodPost, url) } -// Put sends PUT request to the given url. +// Put sends PUT request to the given URL. func Put(url string) *Agent { return defaultClient.Put(url) } -// Put sends PUT request to the given url. +// Put sends PUT request to the given URL. func (c *Client) Put(url string) *Agent { return c.createAgent(MethodPut, url) } -// Patch sends PATCH request to the given url. +// Patch sends PATCH request to the given URL. func Patch(url string) *Agent { return defaultClient.Patch(url) } -// Patch sends PATCH request to the given url. +// Patch sends PATCH request to the given URL. func (c *Client) Patch(url string) *Agent { return c.createAgent(MethodPatch, url) } -// Delete sends DELETE request to the given url. +// Delete sends DELETE request to the given URL. func Delete(url string) *Agent { return defaultClient.Delete(url) } -// Delete sends DELETE request to the given url. +// Delete sends DELETE request to the given URL. func (c *Client) Delete(url string) *Agent { return c.createAgent(MethodDelete, url) } @@ -801,7 +801,7 @@ func (a *Agent) String() (int, string, []error) { return code, utils.UnsafeString(body), errs } -// Struct returns the status code, bytes body and errors of url. +// Struct returns the status code, bytes body and errors of URL. // And bytes body will be unmarshalled to given v. // // it's not safe to use Agent after calling [Agent.Struct] @@ -889,7 +889,7 @@ func AcquireClient() *Client { // ReleaseClient returns c acquired via AcquireClient to client pool. // -// It is forbidden accessing req and/or its' members after returning +// It is forbidden accessing req and/or it's members after returning // it to client pool. func ReleaseClient(c *Client) { c.UserAgent = "" @@ -913,9 +913,9 @@ func AcquireAgent() *Agent { return a } -// ReleaseAgent returns a acquired via AcquireAgent to Agent pool. +// ReleaseAgent returns an acquired via AcquireAgent to Agent pool. // -// It is forbidden accessing req and/or its' members after returning +// It is forbidden accessing req and/or it's members after returning // it to Agent pool. func ReleaseAgent(a *Agent) { a.reset() @@ -942,7 +942,7 @@ func AcquireResponse() *Response { // ReleaseResponse return resp acquired via AcquireResponse to response pool. // -// It is forbidden accessing resp and/or its' members after returning +// It is forbidden accessing resp and/or it's members after returning // it to response pool. // Copy from fasthttp func ReleaseResponse(resp *Response) { diff --git a/ctx.go b/ctx.go index 9a9694dcf9..753480ce49 100644 --- a/ctx.go +++ b/ctx.go @@ -904,7 +904,7 @@ func (c *Ctx) Next() error { // Increment handler index c.indexHandler++ var err error - // Did we executed all route handlers? + // Did we execute all route handlers? if c.indexHandler < len(c.route.Handlers) { // Continue route stack err = c.route.Handlers[c.indexHandler](c) diff --git a/middleware/adaptor/adaptor.go b/middleware/adaptor/adaptor.go index 7e7b73d307..a137bc03eb 100644 --- a/middleware/adaptor/adaptor.go +++ b/middleware/adaptor/adaptor.go @@ -27,7 +27,7 @@ func HTTPHandler(h http.Handler) fiber.Handler { } } -// ConvertRequest converts a fiber.Ctx to an http.Request. +// ConvertRequest converts a fiber.Ctx to a http.Request. // forServer should be set to true when the http.Request is going to be passed to a http.Handler. func ConvertRequest(c *fiber.Ctx, forServer bool) (*http.Request, error) { var req http.Request diff --git a/path.go b/path.go index f7e726f8c2..d0a3338abe 100644 --- a/path.go +++ b/path.go @@ -46,7 +46,7 @@ type routeSegment struct { // different special routing signs const ( - wildcardParam byte = '*' // indicates a optional greedy parameter + wildcardParam byte = '*' // indicates an optional greedy parameter plusParam byte = '+' // indicates a required greedy parameter optionalParam byte = '?' // concludes a parameter by name and makes it optional paramStarterChar byte = ':' // start character for a parameter with name @@ -138,7 +138,7 @@ func RoutePatternMatch(path, pattern string, cfg ...Config) bool { patternPretty := pattern - // Case sensitive routing, all to lowercase + // Case-sensitive routing, all to lowercase if !config.CaseSensitive { patternPretty = utils.ToLower(patternPretty) path = utils.ToLower(path) @@ -228,7 +228,7 @@ func addParameterMetaInfo(segs []*routeSegment) []*routeSegment { // check how often the compare part is in the following const parts if segs[i].IsParam { // check if parameter segments are directly after each other and if one of them is greedy - // in case the next parameter or the current parameter is not a wildcard its not greedy, we only want one character + // in case the next parameter or the current parameter is not a wildcard it's not greedy, we only want one character if segLen > i+1 && !segs[i].IsGreedy && segs[i+1].IsParam && !segs[i+1].IsGreedy { segs[i].Length = 1 } @@ -483,7 +483,7 @@ func (routeParser *routeParser) getMatch(detectionPath, path string, params *[ma if !segment.IsParam { i = segment.Length // is optional part or the const part must match with the given string - // check if the end of the segment is a optional slash + // check if the end of the segment is an optional slash if segment.HasOptionalSlash && partLen == i-1 && detectionPath == segment.Const[:i-1] { i-- } else if !(i <= partLen && detectionPath[:i] == segment.Const) { diff --git a/router.go b/router.go index 312e161ef2..2823bbc704 100644 --- a/router.go +++ b/router.go @@ -184,7 +184,7 @@ func (app *App) handler(rctx *fasthttp.RequestCtx) { //revive:disable-line:confu func (app *App) addPrefixToRoute(prefix string, route *Route) *Route { prefixedPath := getGroupPath(prefix, route.Path) prettyPath := prefixedPath - // Case sensitive routing, all to lowercase + // Case-sensitive routing, all to lowercase if !app.config.CaseSensitive { prettyPath = utils.ToLower(prettyPath) } @@ -248,7 +248,7 @@ func (app *App) register(method, pathRaw string, group *Group, handlers ...Handl } // Create a stripped path in-case sensitive / trailing slashes pathPretty := pathRaw - // Case sensitive routing, all to lowercase + // Case-sensitive routing, all to lowercase if !app.config.CaseSensitive { pathPretty = utils.ToLower(pathPretty) } @@ -305,7 +305,7 @@ func (app *App) register(method, pathRaw string, group *Group, handlers ...Handl } func (app *App) registerStatic(prefix, root string, config ...Static) { - // For security we want to restrict to the current work directory. + // For security, we want to restrict to the current work directory. if root == "" { root = "." } @@ -317,7 +317,7 @@ func (app *App) registerStatic(prefix, root string, config ...Static) { if prefix[0] != '/' { prefix = "/" + prefix } - // in case sensitive routing, all to lowercase + // in case-sensitive routing, all to lowercase if !app.config.CaseSensitive { prefix = utils.ToLower(prefix) } From fefc5338347dacf29b007584b1f0e4f515a0799e Mon Sep 17 00:00:00 2001 From: Jiun Lee <70408571+Skyenought@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:16:57 +0800 Subject: [PATCH 136/162] =?UTF-8?q?=F0=9F=9A=80=20Add=20Logger=20interface?= =?UTF-8?q?=20and=20fiberlog=20(#2499)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add log for fiber * replace log in fiber * add Log use to adapt for log libraries * Update app.go Co-authored-by: Tomás Warynyca <41587659+tomaswarynyca@users.noreply.github.com> * wip: add log docs * add WithLogger use to print key and value * remove CtxLogger and add WithContext use to bind Context * fix errcheck * fix errcheck * update log.md --------- Co-authored-by: Tomás Warynyca <41587659+tomaswarynyca@users.noreply.github.com> --- app.go | 10 +- docs/api/log.md | 156 ++++++++++++++++++++ helpers.go | 8 +- hooks.go | 6 +- internal/template/html/html.go | 6 +- listen.go | 4 +- log/default.go | 205 ++++++++++++++++++++++++++ log/default_test.go | 196 ++++++++++++++++++++++++ log/fiberlog.go | 141 ++++++++++++++++++ log/fiberlog_test.go | 24 +++ log/log.go | 100 +++++++++++++ middleware/cache/config.go | 6 +- middleware/cors/cors.go | 4 +- middleware/csrf/config.go | 8 +- middleware/idempotency/idempotency.go | 4 +- middleware/limiter/config.go | 8 +- middleware/proxy/proxy.go | 4 +- middleware/session/config.go | 4 +- middleware/timeout/timeout.go | 9 +- prefork.go | 4 +- 20 files changed, 865 insertions(+), 42 deletions(-) create mode 100644 docs/api/log.md create mode 100644 log/default.go create mode 100644 log/default_test.go create mode 100644 log/fiberlog.go create mode 100644 log/fiberlog_test.go create mode 100644 log/log.go diff --git a/app.go b/app.go index 40366ce6e6..71b874b7de 100644 --- a/app.go +++ b/app.go @@ -14,7 +14,6 @@ import ( "encoding/xml" "errors" "fmt" - "log" "net" "net/http" "net/http/httputil" @@ -24,6 +23,7 @@ import ( "sync" "time" + "github.com/gofiber/fiber/v2/log" "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" @@ -521,7 +521,7 @@ func New(config ...Config) *App { if app.config.ETag { if !IsChild() { - log.Printf("[Warning] Config.ETag is deprecated since v2.0.6, please use 'middleware/etag'.\n") + log.Warn("Config.ETag is deprecated since v2.0.6, please use 'middleware/etag'.") } } @@ -589,7 +589,7 @@ func (app *App) handleTrustedProxy(ipAddress string) { if strings.Contains(ipAddress, "/") { _, ipNet, err := net.ParseCIDR(ipAddress) if err != nil { - log.Printf("[Warning] IP range %q could not be parsed: %v\n", ipAddress, err) + log.Warnf("IP range %q could not be parsed: %v", ipAddress, err) } else { app.config.trustedProxyRanges = append(app.config.trustedProxyRanges, ipNet) } @@ -987,7 +987,7 @@ func (app *App) init() *App { // Only load templates if a view engine is specified if app.config.Views != nil { if err := app.config.Views.Load(); err != nil { - log.Printf("[Warning]: failed to load views: %v\n", err) + log.Warnf("failed to load views: %v", err) } } @@ -1084,7 +1084,7 @@ func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) { } if catch := app.ErrorHandler(c, err); catch != nil { - log.Printf("serverErrorHandler: failed to call ErrorHandler: %v\n", catch) + log.Errorf("serverErrorHandler: failed to call ErrorHandler: %v", catch) _ = c.SendStatus(StatusInternalServerError) //nolint:errcheck // It is fine to ignore the error here return } diff --git a/docs/api/log.md b/docs/api/log.md new file mode 100644 index 0000000000..f73ee29870 --- /dev/null +++ b/docs/api/log.md @@ -0,0 +1,156 @@ +--- +id: log +title: Log +description: Fiber's built-in log package +sidebar_position: 8 +--- +## Log + +We can use logs to observe program behavior, diagnose problems, or configure corresponding alarms. +And defining a well structured log can improve search efficiency and facilitate handling of problems. + +Fiber provides a default way to print logs in the standard output. +It also provides several global functions, such as `log.Info`, `log.Errorf`, `log.Warnw`, etc. + +## Log levels + +```go +const ( + LevelTrace Level = iota + LevelDebug + LevelInfo + LevelWarn + LevelError + LevelFatal + LevelPanic +) +``` + +## Custom log + +Fiber provides the `AllLogger` interface for adapting the various log libraries. + +```go +type CommonLogger interface { + Logger + FormatLogger + WithLogger +} + +type AllLogger interface { + CommonLogger + ControlLogger + WithLogger +} +``` + +## Print log +Note: The method of calling the Fatal level will interrupt the program running after printing the log, please use it with caution. +Directly print logs of different levels, which will be entered into messageKey, the default is msg. + +```go +log.Info("Hello, World!") +log.Debug("Are you OK?") +log.Info("42 is the answer to life, the universe, and everything") +log.Warn("We are under attack!") +log.Error("Houston, we have a problem.") +log.Fatal("So Long, and Thanks for All the Fislog.") +log.Panic("The system is down.") +``` +Format and print logs of different levels, all methods end with f + +```go +log.Debugf("Hello %s", "boy") +log.Infof("%d is the answer to life, the universe, and everything", 233) +log.Warnf("We are under attack %s!", "boss") +log.Errorf("%s, we have a problem.", "Master Shifu") +log.Fatalf("So Long, and Thanks for All the %s.", "banana") +``` + +Print a message with the key and value, or `KEYVALS UNPAIRED` if the key and value are not a pair. + +```go +log.Debugw("", "Hello", "boy") +log.Infow("", "number", 233) +log.Warnw("", "job", "boss") +log.Errorw("", "name", "Master Shifu") +log.Fatalw("", "fruit", "banana") +``` + +## Global log +If you are in a project and just want to use a simple log function that can be printed at any time in the global, we provide a global log. + +```go +import "github.com/gofiber/fiber/v2/log" + +log.Info("info") +log.Warn("warn") +``` + +The above is using the default `log.DefaultLogger` standard output. +You can also find an already implemented adaptation under contrib, or use your own implemented Logger and use `log.SetLogger` to set the global log logger. + +```go +import ( + "log" + fiberlog "github.com/gofiber/fiber/v2/log" +) + +var _ log.AllLogger = (*customLogger)(nil) + +type customLogger struct { + stdlog *log.Logger +} + +// ... +// inject your custom logger +fiberlog.SetLogger(customLogger) +``` + +## Set Level +`log.SetLevel` sets the level of logs below which logs will not be output. +The default logger is LevelTrace. + +Note that this method is not **concurrent-safe**. + +```go +import "github.com/gofiber/fiber/v2/log" + +log.SetLevel(log.LevelInfo) +``` +## Set output + +`log.SetOutput` sets the output destination of the logger. The default logger types the log in the console. + +```go +var logger AllLogger = &defaultLogger{ + stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds), + depth: 4, +} +``` + +Set the output destination to the file. + +```go +// Output to ./test.log file +f, err := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) +if err != nil { + return +} +log.SetOutput(f) +``` +Set the output destination to the console and file. + +```go +// Output to ./test.log file +file, _ := os.OpenFile("test.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) +iw := io.MultiWriter(os.Stdout, file) +log.SetOutput(iw) +``` +## Bind context +Set the context, using the following method will return a `CommonLogger` instance bound to the specified context +```go +commonLogger := log.WithContext(ctx) +commonLogger.Info("info") +``` + diff --git a/helpers.go b/helpers.go index cedab4c5f5..cfe31e0d40 100644 --- a/helpers.go +++ b/helpers.go @@ -10,7 +10,6 @@ import ( "fmt" "hash/crc32" "io" - "log" "net" "os" "path/filepath" @@ -19,6 +18,7 @@ import ( "time" "unsafe" + "github.com/gofiber/fiber/v2/log" "github.com/gofiber/fiber/v2/utils" "github.com/valyala/bytebufferpool" @@ -75,7 +75,7 @@ func readContent(rf io.ReaderFrom, name string) (int64, error) { } defer func() { if err = f.Close(); err != nil { - log.Printf("Error closing file: %s\n", err) + log.Errorf("Error closing file: %s", err) } }() if n, err := rf.ReadFrom(f); err != nil { @@ -192,7 +192,7 @@ func setETag(c *Ctx, weak bool) { //nolint: revive // Accepting a bool param is if clientEtag[2:] == etag || clientEtag[2:] == etag[2:] { // W/1 == 1 || W/1 == W/1 if err := c.SendStatus(StatusNotModified); err != nil { - log.Printf("setETag: failed to SendStatus: %v\n", err) + log.Errorf("setETag: failed to SendStatus: %v", err) } c.fasthttp.ResetBody() return @@ -204,7 +204,7 @@ func setETag(c *Ctx, weak bool) { //nolint: revive // Accepting a bool param is if strings.Contains(clientEtag, etag) { // 1 == 1 if err := c.SendStatus(StatusNotModified); err != nil { - log.Printf("setETag: failed to SendStatus: %v\n", err) + log.Errorf("setETag: failed to SendStatus: %v", err) } c.fasthttp.ResetBody() return diff --git a/hooks.go b/hooks.go index 099cf0561e..6b0b860c90 100644 --- a/hooks.go +++ b/hooks.go @@ -1,7 +1,7 @@ package fiber import ( - "log" + "github.com/gofiber/fiber/v2/log" ) // OnRouteHandler Handlers define a function to create hooks for Fiber. @@ -194,7 +194,7 @@ func (h *Hooks) executeOnListenHooks(listenData ListenData) error { func (h *Hooks) executeOnShutdownHooks() { for _, v := range h.onShutdown { if err := v(); err != nil { - log.Printf("failed to call shutdown hook: %v\n", err) + log.Errorf("failed to call shutdown hook: %v", err) } } } @@ -202,7 +202,7 @@ func (h *Hooks) executeOnShutdownHooks() { func (h *Hooks) executeOnForkHooks(pid int) { for _, v := range h.onFork { if err := v(pid); err != nil { - log.Printf("failed to call fork hook: %v\n", err) + log.Errorf("failed to call fork hook: %v", err) } } } diff --git a/internal/template/html/html.go b/internal/template/html/html.go index f892fdefe3..98e28262b8 100644 --- a/internal/template/html/html.go +++ b/internal/template/html/html.go @@ -4,7 +4,6 @@ import ( "fmt" "html/template" "io" - "log" "net/http" "os" "path/filepath" @@ -12,6 +11,7 @@ import ( "sync" "github.com/gofiber/fiber/v2/internal/template/utils" + "github.com/gofiber/fiber/v2/log" ) // Engine struct @@ -113,7 +113,7 @@ func (e *Engine) Debug(enabled bool) *Engine { // Parse is deprecated, please use Load() instead func (e *Engine) Parse() error { - log.Println("[Warning] Parse() is deprecated, please use Load() instead.") + log.Warn("Parse() is deprecated, please use Load() instead.") return e.Load() } @@ -170,7 +170,7 @@ func (e *Engine) Load() error { } // Debugging if e.debug { - log.Printf("views: parsed template: %s\n", name) + log.Infof("views: parsed template: %s", name) } return err } diff --git a/listen.go b/listen.go index ef48ab8965..de4a86006e 100644 --- a/listen.go +++ b/listen.go @@ -9,7 +9,6 @@ import ( "crypto/x509" "errors" "fmt" - "log" "net" "os" "path/filepath" @@ -20,6 +19,7 @@ import ( "strings" "text/tabwriter" + "github.com/gofiber/fiber/v2/log" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/mattn/go-runewidth" @@ -49,7 +49,7 @@ func (app *App) Listener(ln net.Listener) error { // Prefork is not supported for custom listeners if app.config.Prefork { - log.Printf("[Warning] Prefork isn't supported for custom listeners.\n") + log.Warn("Prefork isn't supported for custom listeners.") } // Start listening diff --git a/log/default.go b/log/default.go new file mode 100644 index 0000000000..e78f1e3daf --- /dev/null +++ b/log/default.go @@ -0,0 +1,205 @@ +package log + +import ( + "context" + "fmt" + "io" + "log" + "os" + "sync" + + "github.com/valyala/bytebufferpool" +) + +var _ AllLogger = (*defaultLogger)(nil) + +type defaultLogger struct { + stdlog *log.Logger + level Level + depth int +} + +// privateLog logs a message at a given level log the default logger. +// when the level is fatal, it will exit the program. +func (l *defaultLogger) privateLog(lv Level, fmtArgs []interface{}) { + if l.level > lv { + return + } + level := lv.toString() + buf := bytebufferpool.Get() + _, _ = buf.WriteString(level) //nolint:errcheck // It is fine to ignore the error + _, _ = buf.WriteString(fmt.Sprint(fmtArgs...)) //nolint:errcheck // It is fine to ignore the error + + _ = l.stdlog.Output(l.depth, buf.String()) //nolint:errcheck // It is fine to ignore the error + buf.Reset() + bytebufferpool.Put(buf) + if lv == LevelFatal { + os.Exit(1) //nolint:revive // we want to exit the program when Fatal is called + } +} + +// privateLog logs a message at a given level log the default logger. +// when the level is fatal, it will exit the program. +func (l *defaultLogger) privateLogf(lv Level, format string, fmtArgs []interface{}) { + if l.level > lv { + return + } + level := lv.toString() + buf := bytebufferpool.Get() + _, _ = buf.WriteString(level) //nolint:errcheck // It is fine to ignore the error + + if len(fmtArgs) > 0 { + _, _ = fmt.Fprintf(buf, format, fmtArgs...) + } else { + _, _ = fmt.Fprint(buf, fmtArgs...) + } + _ = l.stdlog.Output(l.depth, buf.String()) //nolint:errcheck // It is fine to ignore the error + buf.Reset() + bytebufferpool.Put(buf) + if lv == LevelFatal { + os.Exit(1) //nolint:revive // we want to exit the program when Fatal is called + } +} + +// privateLogw logs a message at a given level log the default logger. +// when the level is fatal, it will exit the program. +func (l *defaultLogger) privateLogw(lv Level, format string, keysAndValues []interface{}) { + if l.level > lv { + return + } + level := lv.toString() + buf := bytebufferpool.Get() + _, _ = buf.WriteString(level) //nolint:errcheck // It is fine to ignore the error + + // Write format privateLog buffer + if format != "" { + _, _ = buf.WriteString(format) //nolint:errcheck // It is fine to ignore the error + } + var once sync.Once + isFirst := true + // Write keys and values privateLog buffer + if len(keysAndValues) > 0 { + if (len(keysAndValues) & 1) == 1 { + keysAndValues = append(keysAndValues, "KEYVALS UNPAIRED") + } + + for i := 0; i < len(keysAndValues); i += 2 { + if format == "" && isFirst { + once.Do(func() { + _, _ = fmt.Fprintf(buf, "%s=%v", keysAndValues[i], keysAndValues[i+1]) + isFirst = false + }) + continue + } + _, _ = fmt.Fprintf(buf, " %s=%v", keysAndValues[i], keysAndValues[i+1]) + } + } + + _ = l.stdlog.Output(l.depth, buf.String()) //nolint:errcheck // It is fine to ignore the error + buf.Reset() + bytebufferpool.Put(buf) + if lv == LevelFatal { + os.Exit(1) //nolint:revive // we want to exit the program when Fatal is called + } +} + +func (l *defaultLogger) Trace(v ...interface{}) { + l.privateLog(LevelTrace, v) +} + +func (l *defaultLogger) Debug(v ...interface{}) { + l.privateLog(LevelDebug, v) +} + +func (l *defaultLogger) Info(v ...interface{}) { + l.privateLog(LevelInfo, v) +} + +func (l *defaultLogger) Warn(v ...interface{}) { + l.privateLog(LevelWarn, v) +} + +func (l *defaultLogger) Error(v ...interface{}) { + l.privateLog(LevelError, v) +} + +func (l *defaultLogger) Fatal(v ...interface{}) { + l.privateLog(LevelFatal, v) +} + +func (l *defaultLogger) Panic(v ...interface{}) { + l.privateLog(LevelPanic, v) +} + +func (l *defaultLogger) Tracef(format string, v ...interface{}) { + l.privateLogf(LevelTrace, format, v) +} + +func (l *defaultLogger) Debugf(format string, v ...interface{}) { + l.privateLogf(LevelDebug, format, v) +} + +func (l *defaultLogger) Infof(format string, v ...interface{}) { + l.privateLogf(LevelInfo, format, v) +} + +func (l *defaultLogger) Warnf(format string, v ...interface{}) { + l.privateLogf(LevelWarn, format, v) +} + +func (l *defaultLogger) Errorf(format string, v ...interface{}) { + l.privateLogf(LevelError, format, v) +} + +func (l *defaultLogger) Fatalf(format string, v ...interface{}) { + l.privateLogf(LevelFatal, format, v) +} + +func (l *defaultLogger) Panicf(format string, v ...interface{}) { + l.privateLogf(LevelPanic, format, v) +} + +func (l *defaultLogger) Tracew(msg string, keysAndValues ...interface{}) { + l.privateLogw(LevelTrace, msg, keysAndValues) +} + +func (l *defaultLogger) Debugw(msg string, keysAndValues ...interface{}) { + l.privateLogw(LevelDebug, msg, keysAndValues) +} + +func (l *defaultLogger) Infow(msg string, keysAndValues ...interface{}) { + l.privateLogw(LevelInfo, msg, keysAndValues) +} + +func (l *defaultLogger) Warnw(msg string, keysAndValues ...interface{}) { + l.privateLogw(LevelWarn, msg, keysAndValues) +} + +func (l *defaultLogger) Errorw(msg string, keysAndValues ...interface{}) { + l.privateLogw(LevelError, msg, keysAndValues) +} + +func (l *defaultLogger) Fatalw(msg string, keysAndValues ...interface{}) { + l.privateLogw(LevelFatal, msg, keysAndValues) +} + +func (l *defaultLogger) Panicw(msg string, keysAndValues ...interface{}) { + l.privateLogw(LevelPanic, msg, keysAndValues) +} + +func (l *defaultLogger) WithContext(_ context.Context) CommonLogger { + return l +} + +func (l *defaultLogger) SetLevel(level Level) { + l.level = level +} + +func (l *defaultLogger) SetOutput(writer io.Writer) { + l.stdlog.SetOutput(writer) +} + +// DefaultLogger returns the default logger. +func DefaultLogger() AllLogger { + return logger +} diff --git a/log/default_test.go b/log/default_test.go new file mode 100644 index 0000000000..7562b0a169 --- /dev/null +++ b/log/default_test.go @@ -0,0 +1,196 @@ +package log + +import ( + "bytes" + "context" + "log" + "os" + "testing" + + "github.com/gofiber/fiber/v2/utils" +) + +const work = "work" + +func initDefaultLogger() { + logger = &defaultLogger{ + stdlog: log.New(os.Stderr, "", 0), + depth: 4, + } +} + +type byteSliceWriter struct { + b []byte +} + +func (w *byteSliceWriter) Write(p []byte) (int, error) { + w.b = append(w.b, p...) + return len(p), nil +} + +func Test_DefaultLogger(t *testing.T) { + initDefaultLogger() + + var w byteSliceWriter + SetOutput(&w) + + Trace("trace work") + Debug("received work order") + Info("starting work") + Warn("work may fail") + Error("work failed") + Panic("work panic") + utils.AssertEqual(t, "[Trace] trace work\n"+ + "[Debug] received work order\n"+ + "[Info] starting work\n"+ + "[Warn] work may fail\n"+ + "[Error] work failed\n"+ + "[Panic] work panic\n", string(w.b)) +} + +func Test_DefaultFormatLogger(t *testing.T) { + initDefaultLogger() + + var w byteSliceWriter + SetOutput(&w) + + Tracef("trace %s", work) + Debugf("received %s order", work) + Infof("starting %s", work) + Warnf("%s may fail", work) + Errorf("%s failed", work) + Panicf("%s panic", work) + + utils.AssertEqual(t, "[Trace] trace work\n"+ + "[Debug] received work order\n"+ + "[Info] starting work\n"+ + "[Warn] work may fail\n"+ + "[Error] work failed\n"+ + "[Panic] work panic\n", string(w.b)) +} + +func Test_CtxLogger(t *testing.T) { + initDefaultLogger() + + var w byteSliceWriter + SetOutput(&w) + + ctx := context.Background() + + WithContext(ctx).Tracef("trace %s", work) + WithContext(ctx).Debugf("received %s order", work) + WithContext(ctx).Infof("starting %s", work) + WithContext(ctx).Warnf("%s may fail", work) + WithContext(ctx).Errorf("%s failed", work) + WithContext(ctx).Panicf("%s panic", work) + + utils.AssertEqual(t, "[Trace] trace work\n"+ + "[Debug] received work order\n"+ + "[Info] starting work\n"+ + "[Warn] work may fail\n"+ + "[Error] work failed\n"+ + "[Panic] work panic\n", string(w.b)) +} + +func Test_LogfKeyAndValues(t *testing.T) { + tests := []struct { + name string + level Level + format string + fmtArgs []interface{} + keysAndValues []interface{} + wantOutput string + }{ + { + name: "test logf with debug level and key-values", + level: LevelDebug, + format: "", + fmtArgs: nil, + keysAndValues: []interface{}{"name", "Bob", "age", 30}, + wantOutput: "[Debug] name=Bob age=30\n", + }, + { + name: "test logf with info level and key-values", + level: LevelInfo, + format: "", + fmtArgs: nil, + keysAndValues: []interface{}{"status", "ok", "code", 200}, + wantOutput: "[Info] status=ok code=200\n", + }, + { + name: "test logf with warn level and key-values", + level: LevelWarn, + format: "", + fmtArgs: nil, + keysAndValues: []interface{}{"error", "not found", "id", 123}, + wantOutput: "[Warn] error=not found id=123\n", + }, + { + name: "test logf with format and key-values", + level: LevelWarn, + format: "test", + fmtArgs: nil, + keysAndValues: []interface{}{"error", "not found", "id", 123}, + wantOutput: "[Warn] test error=not found id=123\n", + }, + { + name: "test logf with one key", + level: LevelWarn, + format: "", + fmtArgs: nil, + keysAndValues: []interface{}{"error"}, + wantOutput: "[Warn] error=KEYVALS UNPAIRED\n", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var buf bytes.Buffer + l := &defaultLogger{ + stdlog: log.New(&buf, "", 0), + level: tt.level, + depth: 4, + } + l.privateLogw(tt.level, tt.format, tt.keysAndValues) + utils.AssertEqual(t, tt.wantOutput, buf.String()) + }) + } +} + +func Test_SetLevel(t *testing.T) { + setLogger := &defaultLogger{ + stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds), + depth: 4, + } + + setLogger.SetLevel(LevelTrace) + utils.AssertEqual(t, LevelTrace, setLogger.level) + utils.AssertEqual(t, LevelTrace.toString(), setLogger.level.toString()) + + setLogger.SetLevel(LevelDebug) + utils.AssertEqual(t, LevelDebug, setLogger.level) + utils.AssertEqual(t, LevelDebug.toString(), setLogger.level.toString()) + + setLogger.SetLevel(LevelInfo) + utils.AssertEqual(t, LevelInfo, setLogger.level) + utils.AssertEqual(t, LevelInfo.toString(), setLogger.level.toString()) + + setLogger.SetLevel(LevelWarn) + utils.AssertEqual(t, LevelWarn, setLogger.level) + utils.AssertEqual(t, LevelWarn.toString(), setLogger.level.toString()) + + setLogger.SetLevel(LevelError) + utils.AssertEqual(t, LevelError, setLogger.level) + utils.AssertEqual(t, LevelError.toString(), setLogger.level.toString()) + + setLogger.SetLevel(LevelFatal) + utils.AssertEqual(t, LevelFatal, setLogger.level) + utils.AssertEqual(t, LevelFatal.toString(), setLogger.level.toString()) + + setLogger.SetLevel(LevelPanic) + utils.AssertEqual(t, LevelPanic, setLogger.level) + utils.AssertEqual(t, LevelPanic.toString(), setLogger.level.toString()) + + setLogger.SetLevel(8) + utils.AssertEqual(t, 8, int(setLogger.level)) + utils.AssertEqual(t, "[?8] ", setLogger.level.toString()) +} diff --git a/log/fiberlog.go b/log/fiberlog.go new file mode 100644 index 0000000000..90333eef3d --- /dev/null +++ b/log/fiberlog.go @@ -0,0 +1,141 @@ +package log + +import ( + "context" + "io" +) + +// Fatal calls the default logger's Fatal method and then os.Exit(1). +func Fatal(v ...interface{}) { + logger.Fatal(v...) +} + +// Error calls the default logger's Error method. +func Error(v ...interface{}) { + logger.Error(v...) +} + +// Warn calls the default logger's Warn method. +func Warn(v ...interface{}) { + logger.Warn(v...) +} + +// Info calls the default logger's Info method. +func Info(v ...interface{}) { + logger.Info(v...) +} + +// Debug calls the default logger's Debug method. +func Debug(v ...interface{}) { + logger.Debug(v...) +} + +// Trace calls the default logger's Trace method. +func Trace(v ...interface{}) { + logger.Trace(v...) +} + +// Panic calls the default logger's Panic method. +func Panic(v ...interface{}) { + logger.Panic(v...) +} + +// Fatalf calls the default logger's Fatalf method and then os.Exit(1). +func Fatalf(format string, v ...interface{}) { + logger.Fatalf(format, v...) +} + +// Errorf calls the default logger's Errorf method. +func Errorf(format string, v ...interface{}) { + logger.Errorf(format, v...) +} + +// Warnf calls the default logger's Warnf method. +func Warnf(format string, v ...interface{}) { + logger.Warnf(format, v...) +} + +// Infof calls the default logger's Infof method. +func Infof(format string, v ...interface{}) { + logger.Infof(format, v...) +} + +// Debugf calls the default logger's Debugf method. +func Debugf(format string, v ...interface{}) { + logger.Debugf(format, v...) +} + +// Tracef calls the default logger's Tracef method. +func Tracef(format string, v ...interface{}) { + logger.Tracef(format, v...) +} + +// Panicf calls the default logger's Tracef method. +func Panicf(format string, v ...interface{}) { + logger.Panicf(format, v...) +} + +// Tracew logs a message with some additional context. The variadic key-value +// pairs are treated as they are privateLog With. +func Tracew(msg string, keysAndValues ...interface{}) { + logger.Tracew(msg, keysAndValues...) +} + +// Debugw logs a message with some additional context. The variadic key-value +// pairs are treated as they are privateLog With. +func Debugw(msg string, keysAndValues ...interface{}) { + logger.Debugw(msg, keysAndValues...) +} + +// Infow logs a message with some additional context. The variadic key-value +// pairs are treated as they are privateLog With. +func Infow(msg string, keysAndValues ...interface{}) { + logger.Infow(msg, keysAndValues...) +} + +// Warnw logs a message with some additional context. The variadic key-value +// pairs are treated as they are privateLog With. +func Warnw(msg string, keysAndValues ...interface{}) { + logger.Warnw(msg, keysAndValues...) +} + +// Errorw logs a message with some additional context. The variadic key-value +// pairs are treated as they are privateLog With. +func Errorw(msg string, keysAndValues ...interface{}) { + logger.Errorw(msg, keysAndValues...) +} + +// Fatalw logs a message with some additional context. The variadic key-value +// pairs are treated as they are privateLog With. +func Fatalw(msg string, keysAndValues ...interface{}) { + logger.Fatalw(msg, keysAndValues...) +} + +// Panicw logs a message with some additional context. The variadic key-value +// pairs are treated as they are privateLog With. +func Panicw(msg string, keysAndValues ...interface{}) { + logger.Panicw(msg, keysAndValues...) +} + +func WithContext(ctx context.Context) CommonLogger { + return logger.WithContext(ctx) +} + +// SetLogger sets the default logger and the system logger. +// Note that this method is not concurrent-safe and must not be called +// after the use of DefaultLogger and global functions privateLog this package. +func SetLogger(v AllLogger) { + logger = v +} + +// SetOutput sets the output of default logger and system logger. By default, it is stderr. +func SetOutput(w io.Writer) { + logger.SetOutput(w) +} + +// SetLevel sets the level of logs below which logs will not be output. +// The default logger is LevelTrace. +// Note that this method is not concurrent-safe. +func SetLevel(lv Level) { + logger.SetLevel(lv) +} diff --git a/log/fiberlog_test.go b/log/fiberlog_test.go new file mode 100644 index 0000000000..15b1a2cd9d --- /dev/null +++ b/log/fiberlog_test.go @@ -0,0 +1,24 @@ +package log + +import ( + "log" + "os" + "testing" + + "github.com/gofiber/fiber/v2/utils" +) + +func Test_DefaultSystemLogger(t *testing.T) { + defaultL := DefaultLogger() + utils.AssertEqual(t, logger, defaultL) +} + +func Test_SetLogger(t *testing.T) { + setLog := &defaultLogger{ + stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds), + depth: 6, + } + + SetLogger(setLog) + utils.AssertEqual(t, logger, setLog) +} diff --git a/log/log.go b/log/log.go new file mode 100644 index 0000000000..31b4cc8af9 --- /dev/null +++ b/log/log.go @@ -0,0 +1,100 @@ +package log + +import ( + "context" + "fmt" + "io" + "log" + "os" +) + +var logger AllLogger = &defaultLogger{ + stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds), + depth: 4, +} + +// Logger is a logger interface that provides logging function with levels. +type Logger interface { + Trace(v ...interface{}) + Debug(v ...interface{}) + Info(v ...interface{}) + Warn(v ...interface{}) + Error(v ...interface{}) + Fatal(v ...interface{}) + Panic(v ...interface{}) +} + +// FormatLogger is a logger interface that output logs with a format. +type FormatLogger interface { + Tracef(format string, v ...interface{}) + Debugf(format string, v ...interface{}) + Infof(format string, v ...interface{}) + Warnf(format string, v ...interface{}) + Errorf(format string, v ...interface{}) + Fatalf(format string, v ...interface{}) + Panicf(format string, v ...interface{}) +} + +// WithLogger is a logger interface that output logs with a message and key-value pairs. +type WithLogger interface { + Tracew(msg string, keysAndValues ...interface{}) + Debugw(msg string, keysAndValues ...interface{}) + Infow(msg string, keysAndValues ...interface{}) + Warnw(msg string, keysAndValues ...interface{}) + Errorw(msg string, keysAndValues ...interface{}) + Fatalw(msg string, keysAndValues ...interface{}) + Panicw(msg string, keysAndValues ...interface{}) +} + +type CommonLogger interface { + Logger + FormatLogger + WithLogger +} + +// ControlLogger provides methods to config a logger. +type ControlLogger interface { + SetLevel(Level) + SetOutput(io.Writer) +} + +// AllLogger is the combination of Logger, FormatLogger, CtxLogger and ControlLogger. +// Custom extensions can be made through AllLogger +type AllLogger interface { + CommonLogger + ControlLogger + WithContext(ctx context.Context) CommonLogger +} + +// Level defines the priority of a log message. +// When a logger is configured with a level, any log message with a lower +// log level (smaller by integer comparison) will not be output. +type Level int + +// The levels of logs. +const ( + LevelTrace Level = iota + LevelDebug + LevelInfo + LevelWarn + LevelError + LevelFatal + LevelPanic +) + +var strs = []string{ + "[Trace] ", + "[Debug] ", + "[Info] ", + "[Warn] ", + "[Error] ", + "[Fatal] ", + "[Panic] ", +} + +func (lv Level) toString() string { + if lv >= LevelTrace && lv <= LevelPanic { + return strs[lv] + } + return fmt.Sprintf("[?%d] ", lv) +} diff --git a/middleware/cache/config.go b/middleware/cache/config.go index 734e85357b..6fe61627d7 100644 --- a/middleware/cache/config.go +++ b/middleware/cache/config.go @@ -1,10 +1,10 @@ package cache import ( - "log" "time" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" "github.com/gofiber/fiber/v2/utils" ) @@ -102,11 +102,11 @@ func configDefault(config ...Config) Config { // Set default values if cfg.Store != nil { - log.Printf("[Warning] - [CACHE] Store is deprecated, please use Storage\n") + log.Warn("[CACHE] Store is deprecated, please use Storage") cfg.Storage = cfg.Store } if cfg.Key != nil { - log.Printf("[Warning] - [CACHE] Key is deprecated, please use KeyGenerator\n") + log.Warn("[CACHE] Key is deprecated, please use KeyGenerator") cfg.KeyGenerator = cfg.Key } if cfg.Next == nil { diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index 302b1591b1..281b2b4571 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -1,11 +1,11 @@ package cors import ( - "log" "strconv" "strings" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" ) // Config defines the config for middleware. @@ -98,7 +98,7 @@ func New(config ...Config) fiber.Handler { // Warning logs if both AllowOrigins and AllowOriginsFunc are set if cfg.AllowOrigins != ConfigDefault.AllowOrigins && cfg.AllowOriginsFunc != nil { - log.Printf("[Warning] - [CORS] Both 'AllowOrigins' and 'AllowOriginsFunc' have been defined.\n") + log.Warn("[CORS] Both 'AllowOrigins' and 'AllowOriginsFunc' have been defined.") } // Convert string to slice diff --git a/middleware/csrf/config.go b/middleware/csrf/config.go index 1e2d875a4f..f17f79a836 100644 --- a/middleware/csrf/config.go +++ b/middleware/csrf/config.go @@ -1,12 +1,12 @@ package csrf import ( - "log" "net/textproto" "strings" "time" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" "github.com/gofiber/fiber/v2/utils" ) @@ -132,15 +132,15 @@ func configDefault(config ...Config) Config { // Set default values if cfg.TokenLookup != "" { - log.Printf("[Warning] - [CSRF] TokenLookup is deprecated, please use KeyLookup\n") + log.Warn("[CSRF] TokenLookup is deprecated, please use KeyLookup") cfg.KeyLookup = cfg.TokenLookup } if int(cfg.CookieExpires.Seconds()) > 0 { - log.Printf("[Warning] - [CSRF] CookieExpires is deprecated, please use Expiration\n") + log.Warn("[CSRF] CookieExpires is deprecated, please use Expiration") cfg.Expiration = cfg.CookieExpires } if cfg.Cookie != nil { - log.Printf("[Warning] - [CSRF] Cookie is deprecated, please use Cookie* related fields\n") + log.Warn("[CSRF] Cookie is deprecated, please use Cookie* related fields") if cfg.Cookie.Name != "" { cfg.CookieName = cfg.Cookie.Name } diff --git a/middleware/idempotency/idempotency.go b/middleware/idempotency/idempotency.go index f1d3db310e..ae4097ae9d 100644 --- a/middleware/idempotency/idempotency.go +++ b/middleware/idempotency/idempotency.go @@ -2,10 +2,10 @@ package idempotency import ( "fmt" - "log" "strings" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" "github.com/gofiber/fiber/v2/utils" ) @@ -92,7 +92,7 @@ func New(config ...Config) fiber.Handler { } defer func() { if err := cfg.Lock.Unlock(key); err != nil { - log.Printf("[Error] - [IDEMPOTENCY] failed to unlock key %q: %v", key, err) + log.Errorf("[IDEMPOTENCY] failed to unlock key %q: %v", key, err) } }() diff --git a/middleware/limiter/config.go b/middleware/limiter/config.go index 827debe795..5ec826d427 100644 --- a/middleware/limiter/config.go +++ b/middleware/limiter/config.go @@ -1,10 +1,10 @@ package limiter import ( - "log" "time" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" ) // Config defines the config for middleware. @@ -95,15 +95,15 @@ func configDefault(config ...Config) Config { // Set default values if int(cfg.Duration.Seconds()) > 0 { - log.Printf("[Warning] - [LIMITER] Duration is deprecated, please use Expiration\n") + log.Warn("[LIMITER] Duration is deprecated, please use Expiration") cfg.Expiration = cfg.Duration } if cfg.Key != nil { - log.Printf("[Warning] - [LIMITER] Key is deprecated, please us KeyGenerator\n") + log.Warn("[LIMITER] Key is deprecated, please us KeyGenerator") cfg.KeyGenerator = cfg.Key } if cfg.Store != nil { - log.Printf("[Warning] - [LIMITER] Store is deprecated, please use Storage\n") + log.Warn("[LIMITER] Store is deprecated, please use Storage") cfg.Storage = cfg.Store } if cfg.Next == nil { diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index 9ca5e4bc08..eb23c56c59 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -3,13 +3,13 @@ package proxy import ( "bytes" "crypto/tls" - "log" "net/url" "strings" "sync" "time" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" @@ -17,7 +17,7 @@ import ( // New is deprecated func New(config Config) fiber.Handler { - log.Printf("[Warning] - [PROXY] proxy.New is deprecated, please use proxy.Balancer instead\n") + log.Warn("[PROXY] proxy.New is deprecated, please use proxy.Balancer instead") return Balancer(config) } diff --git a/middleware/session/config.go b/middleware/session/config.go index aa476eb808..62a80279ff 100644 --- a/middleware/session/config.go +++ b/middleware/session/config.go @@ -2,11 +2,11 @@ package session import ( "fmt" - "log" "strings" "time" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" "github.com/gofiber/fiber/v2/utils" ) @@ -97,7 +97,7 @@ func configDefault(config ...Config) Config { cfg.Expiration = ConfigDefault.Expiration } if cfg.CookieName != "" { - log.Printf("[Warning] - [SESSION] CookieName is deprecated, please use KeyLookup\n") + log.Warn("[SESSION] CookieName is deprecated, please use KeyLookup") cfg.KeyLookup = fmt.Sprintf("cookie:%s", cfg.CookieName) } if cfg.KeyLookup == "" { diff --git a/middleware/timeout/timeout.go b/middleware/timeout/timeout.go index e40cc4f88d..2bafb3f528 100644 --- a/middleware/timeout/timeout.go +++ b/middleware/timeout/timeout.go @@ -3,10 +3,11 @@ package timeout import ( "context" "errors" - "log" "sync" "time" + "github.com/gofiber/fiber/v2/log" + "github.com/gofiber/fiber/v2" ) @@ -18,7 +19,7 @@ var once sync.Once // Find documentation and sample usage on https://docs.gofiber.io/api/middleware/timeout func New(handler fiber.Handler, timeout time.Duration) fiber.Handler { once.Do(func() { - log.Printf("[Warning] - [TIMEOUT] timeout contains data race issues, not ready for production!") + log.Warn("[TIMEOUT] timeout contains data race issues, not ready for production!") }) if timeout <= 0 { @@ -32,11 +33,11 @@ func New(handler fiber.Handler, timeout time.Duration) fiber.Handler { go func() { defer func() { if err := recover(); err != nil { - log.Printf("[Warning] - [TIMEOUT] recover error %v", err) + log.Errorf("[TIMEOUT] recover error %v", err) } }() if err := handler(ctx); err != nil { - log.Printf("[Warning] - [TIMEOUT] handler error %v", err) + log.Errorf("[TIMEOUT] handler error %v", err) } ch <- struct{}{} }() diff --git a/prefork.go b/prefork.go index 8639746979..a26d931456 100644 --- a/prefork.go +++ b/prefork.go @@ -4,7 +4,6 @@ import ( "crypto/tls" "errors" "fmt" - "log" "os" "os/exec" "runtime" @@ -13,6 +12,7 @@ import ( "sync/atomic" "time" + "github.com/gofiber/fiber/v2/log" "github.com/valyala/fasthttp/reuseport" ) @@ -77,7 +77,7 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) error { for _, proc := range childs { if err := proc.Process.Kill(); err != nil { if !errors.Is(err, os.ErrProcessDone) { - log.Printf("prefork: failed to kill child: %v\n", err) + log.Errorf("prefork: failed to kill child: %v", err) } } } From f68ec0dfbb8f71623b8517edc736b4d9aee1a809 Mon Sep 17 00:00:00 2001 From: Ruan Heyns <58415372+RHeynsZa@users.noreply.github.com> Date: Mon, 26 Jun 2023 10:24:50 +0200 Subject: [PATCH 137/162] Update ctx.md: Add a warning on security implications when using X-Forwarded-For improperly (#2520) Update ctx.md Add a warning on security implications when using X-Forwarded-For improperly --- docs/api/ctx.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 2ef70af91a..65623089d7 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -652,6 +652,10 @@ app.Get("/", func(c *fiber.Ctx) error { }) ``` +:::caution +Improper use of the X-Forwarded-For header can be a security risk. For details, see the [Security and privacy concerns](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For#security_and_privacy_concerns) section. +::: + ## 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. From 5c1e8a9cff67ea7114abb852afa3e973f4ee215a Mon Sep 17 00:00:00 2001 From: Iliya Date: Tue, 27 Jun 2023 18:50:50 +0330 Subject: [PATCH 138/162] =?UTF-8?q?=F0=9F=93=9A=20Docs:=20fix=20bad=20docu?= =?UTF-8?q?mentation=20on=20queries=20function=20(#2522)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api/ctx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 65623089d7..3549aaa83c 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1076,7 +1076,7 @@ app.Get("/", func(c *fiber.Ctx) error { Queries is a function that returns an object containing a property for each query string parameter in the route. ```go title="Signature" -func (c *Ctx) Queries() (map[string]string, error) +func (c *Ctx) Queries() map[string]string ``` ```go title="Example" From 80fc22264687bae4b36f9f3c96ef5393b369c455 Mon Sep 17 00:00:00 2001 From: z3ntl3 <48758770+Z3NTL3@users.noreply.github.com> Date: Tue, 27 Jun 2023 23:49:32 +0200 Subject: [PATCH 139/162] - fixed validation-guide (#2517) * - fixed validation-guide * 06/23/2023 14:39:08 - small update * 06/23/2023 14:51:31 * 06/23/2023 14:53:47 * () * () * 06/25/2023 18:07:46 fix naming * 06/26/2023 09:31:57 * 06/26/2023 09:35:39 - fix indentation * 06/26/2023 09:37:48 - formatted with go fmt * 06/27/2023 19:24:42 - update to v10 * 06/27/2023 19:27:17 - update validator to v10 * 06/27/2023 23:38:38 - fix var name * 06/27/2023 23:40:47 - fix var name --- docs/guide/validation.md | 193 ++++++++++++++++++++++++++++----------- 1 file changed, 139 insertions(+), 54 deletions(-) diff --git a/docs/guide/validation.md b/docs/guide/validation.md index ea7980727d..417298aebb 100644 --- a/docs/guide/validation.md +++ b/docs/guide/validation.md @@ -8,76 +8,161 @@ sidebar_position: 5 Fiber can make _great_ use of the validator package to ensure correct validation of data to store. -* [Official validator Github page \(Installation, use, examples..\).](https://github.com/go-playground/validator) +- [Official validator Github page \(Installation, use, examples..\).](https://github.com/go-playground/validator) You can find the detailed descriptions of the _validations_ used in the fields contained on the structs below: -* [Detailed docs](https://pkg.go.dev/github.com/go-playground/validator?tab=doc) +- [Detailed docs](https://pkg.go.dev/github.com/go-playground/validator?tab=doc) ```go title="Validation Example" -type Job struct{ - Type string `validate:"required,min=3,max=32"` - Salary int `validate:"required,number"` -} +package main + +import ( + "fmt" + "log" + "strings" + + "github.com/go-playground/validator/v10" + "github.com/gofiber/fiber/v2" +) + +type ( + User struct { + Name string `validate:"required,min=5,max=20"` // Required field, min 5 char long max 20 + Age int `validate:"required,teener"` // Required field, and client needs to implement our 'teener' tag format which we'll see later + } + + ErrorResponse struct { + Error bool + FailedField string + Tag string + Value interface{} + } + + XValidator struct { + validator *validator.Validate + } + + GlobalErrorHandlerResp struct { + Success bool `json:"success"` + Message string `json:"message"` + } +) + +// This is the validator instance +// for more information see: https://github.com/go-playground/validator +var validate = validator.New() -type User struct{ - Name string `validate:"required,min=3,max=32"` - // use `*bool` here otherwise the validation will fail for `false` values - // Ref: https://github.com/go-playground/validator/issues/319#issuecomment-339222389 - IsActive *bool `validate:"required"` - Email string `validate:"required,email,min=6,max=32"` - Job Job `validate:"dive"` -} +func (v XValidator) Validate(data interface{}) []ErrorResponse { + validationErrors := []ErrorResponse{} + + errs := validate.Struct(data) + if errs != nil { + for _, err := range errs.(validator.ValidationErrors) { + // In this case data object is actually holding the User struct + var elem ErrorResponse -type ErrorResponse struct { - FailedField string - Tag string - Value string + elem.FailedField = err.Field() // Export struct field name + elem.Tag = err.Tag() // Export struct tag + elem.Value = err.Value() // Export field value + elem.Error = true + + validationErrors = append(validationErrors, elem) + } + } + + return validationErrors } -var validate = validator.New() -func ValidateStruct(user User) []*ErrorResponse { - var errors []*ErrorResponse - err := validate.Struct(user) - if err != nil { - for _, err := range err.(validator.ValidationErrors) { - var element ErrorResponse - element.FailedField = err.StructNamespace() - element.Tag = err.Tag() - element.Value = err.Param() - errors = append(errors, &element) - } - } - return errors +func main() { + myValidator := &XValidator{ + validator: validate, + } + + app := fiber.New(fiber.Config{ + // Global custom error handler + ErrorHandler: func(c *fiber.Ctx, err error) error { + return c.Status(fiber.StatusBadRequest).JSON(GlobalErrorHandlerResp{ + Success: false, + Message: err.Error(), + }) + }, + }) + + // Custom struct validation tag format + myValidator.validator.RegisterValidation("teener", func(fl validator.FieldLevel) bool { + // User.Age needs to fit our needs, 12-18 years old. + return fl.Field().Int() >= 12 && fl.Field().Int() <= 18 + }) + + app.Get("/", func(c *fiber.Ctx) error { + user := &User{ + Name: c.Query("name"), + Age: c.QueryInt("age"), + } + + // Validation + if errs := myValidator.Validate(user); len(errs) > 0 && errs[0].Error { + errMsgs := make([]string, 0) + + for _, err := range errs { + errMsgs = append(errMsgs, fmt.Sprintf( + "[%s]: '%v' | Needs to implement '%s'", + err.FailedField, + err.Value, + err.Tag, + )) + } + + return &fiber.Error{ + Code: fiber.ErrBadRequest.Code, + Message: strings.Join(errMsgs, " and "), + } + } + + // Logic, validated with success + return c.SendString("Hello, World!") + }) + + log.Fatal(app.Listen(":3000")) } -func AddUser(c *fiber.Ctx) error { - //Connect to database +/** +OUTPUT - user := new(User) +[1] +Request: - if err := c.BodyParser(user); err != nil { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "message": err.Error(), - }) - - } +GET http://127.0.0.1:3000/ - errors := ValidateStruct(*user) - if errors != nil { - return c.Status(fiber.StatusBadRequest).JSON(errors) - - } +Response: - //Do something else here +{"success":false,"message":"[Name]: '' | Needs to implement 'required' and [Age]: '0' | Needs to implement 'required'"} - //Return user - return c.JSON(user) -} +[2] +Request: + +GET http://127.0.0.1:3000/?name=efdal&age=9 + +Response: +{"success":false,"message":"[Age]: '9' | Needs to implement 'teener'"} + +[3] +Request: + +GET http://127.0.0.1:3000/?name=efdal&age= + +Response: +{"success":false,"message":"[Age]: '0' | Needs to implement 'required'"} + +[4] +Request: + +GET http://127.0.0.1:3000/?name=efdal&age=18 + +Response: +Hello, World! -// Running a test with the following curl commands -// curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"john\",\"isactive\":\"True\"}" http://localhost:8080/register/user +**/ -// Results in -// [{"FailedField":"User.Email","Tag":"required","Value":""},{"FailedField":"User.Job.Salary","Tag":"required","Value":""},{"FailedField":"User.Job.Type","Tag":"required","Value":""}]⏎ ``` From 71c6b889d03501c8c0c3636f8637a6075229b8e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:41:19 +0300 Subject: [PATCH 140/162] Bump golang.org/x/sys from 0.9.0 to 0.10.0 (#2530) Bumps [golang.org/x/sys](https://github.com/golang/sys) from 0.9.0 to 0.10.0. - [Commits](https://github.com/golang/sys/compare/v0.9.0...v0.10.0) --- updated-dependencies: - dependency-name: golang.org/x/sys dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0eea433db1..ce67a37496 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/tinylib/msgp v1.1.8 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.48.0 - golang.org/x/sys v0.9.0 + golang.org/x/sys v0.10.0 ) require ( diff --git a/go.sum b/go.sum index 76516c5004..c2cdbb522e 100644 --- a/go.sum +++ b/go.sum @@ -59,8 +59,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc 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.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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= From 453ccadadd09b80155cdde7cc9d1beae9ec91f21 Mon Sep 17 00:00:00 2001 From: RW Date: Thu, 6 Jul 2023 11:39:20 +0200 Subject: [PATCH 141/162] Update sync-docs.yml --- .github/workflows/sync-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sync-docs.yml b/.github/workflows/sync-docs.yml index f556fbeed0..67cc353866 100644 --- a/.github/workflows/sync-docs.yml +++ b/.github/workflows/sync-docs.yml @@ -24,4 +24,4 @@ jobs: run: ./.github/scripts/sync_docs.sh env: EVENT: ${{ github.event_name }} - TOKEN: ${{ secrets.TOKEN }} + TOKEN: ${{ secrets.DOC_SYNC_TOKEN }} From 70aec2b4ca8435a2cca5770378f523bbc5afbf7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Thu, 6 Jul 2023 17:14:57 +0300 Subject: [PATCH 142/162] :memo: docs: update sync_docs.sh script (#2532) * :memo: docs: update sync_docs.sh script * :memo: docs: update sync_docs.sh script --- .github/scripts/sync_docs.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/scripts/sync_docs.sh b/.github/scripts/sync_docs.sh index 38b636ea83..9024d60255 100755 --- a/.github/scripts/sync_docs.sh +++ b/.github/scripts/sync_docs.sh @@ -17,8 +17,8 @@ if [[ $EVENT == "push" ]]; then if [[ $log_output != "" ]]; then git clone https://${TOKEN}@${REPO_URL} fiber-docs - cp -a docs/* fiber-docs/docs - + cp -a docs/* fiber-docs/docs/core + # Push changes for next docs cd fiber-docs/ || return git add . @@ -31,8 +31,8 @@ elif [[ $EVENT == "release" ]]; then # Push changes for stable docs git clone https://${TOKEN}@${REPO_URL} fiber-docs cd fiber-docs/ || return - cp -a docs/* versioned_docs/version-${MAJOR_VERSION}.x + cp -a docs/core/* versioned_docs/version-${MAJOR_VERSION}.x git add . git commit -m "Sync docs for ${latest_tag} release" git push https://${TOKEN}@${REPO_URL} -fi \ No newline at end of file +fi From 032bde9452ce6f3f66b88470f6b6da1a72ab5d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sat, 8 Jul 2023 21:33:15 +0200 Subject: [PATCH 143/162] use new template docs in fiber docs --- docs/api/ctx.md | 2 +- docs/extra/faq.md | 21 +++++++++++---------- docs/guide/templates.md | 21 +++++++++++---------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 3549aaa83c..f04e34fa2e 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -1406,7 +1406,7 @@ app.Get("/back", func(c *fiber.Ctx) error { ## Render -Renders a view with data and sends a `text/html` response. By default `Render` uses the default [**Go Template engine**](https://pkg.go.dev/html/template/). If you want to use another View engine, please take a look at our [**Template middleware**](https://github.com/gofiber/template). +Renders a view with data and sends a `text/html` response. By default `Render` uses the default [**Go Template engine**](https://pkg.go.dev/html/template/). If you want to use another View engine, please take a look at our [**Template middleware**](https://docs.gofiber.io/template). ```go title="Signature" func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error diff --git a/docs/extra/faq.md b/docs/extra/faq.md index efac14b45f..54dbca5d15 100644 --- a/docs/extra/faq.md +++ b/docs/extra/faq.md @@ -76,16 +76,17 @@ We have a dedicated page explaining how error handling works in Fiber, see [Erro ## Which template engines does Fiber support? -Fiber currently supports 8 template engines in our [gofiber/template](https://github.com/gofiber/template) middleware: - -* [Ace](https://github.com/yosssi/ace) -* [Amber](https://github.com/eknkc/amber) -* [Django](https://github.com/flosch/pongo2) -* [Handlebars](https://github.com/aymerick/raymond) -* [HTML](https://pkg.go.dev/html/template/) -* [Jet](https://github.com/CloudyKit/jet) -* [Mustache](https://github.com/cbroglie/mustache) -* [Pug](https://github.com/Joker/jade) +Fiber currently supports 9 template engines in our [gofiber/template](https://docs.gofiber.io/template/) middleware: + +* [ace](https://docs.gofiber.io/template/ace/) +* [amber](https://docs.gofiber.io/template/amber/) +* [django](https://docs.gofiber.io/template/django/) +* [handlebars](https://docs.gofiber.io/template/handlebars) +* [html](https://docs.gofiber.io/template/html) +* [jet](https://docs.gofiber.io/template/jet) +* [mustache](https://docs.gofiber.io/template/mustache) +* [pug](https://docs.gofiber.io/template/pug) +* [slim](https://docs.gofiber.io/template/pug) To learn more about using Templates in Fiber, see [Templates](../guide/templates.md). diff --git a/docs/guide/templates.md b/docs/guide/templates.md index df38fac02a..e37bc59b53 100644 --- a/docs/guide/templates.md +++ b/docs/guide/templates.md @@ -48,16 +48,17 @@ app.Get("/", func(c *fiber.Ctx) error { ## Engines -Fiber team maintains [templates](https://github.com/gofiber/template) package that provides wrappers for multiple template engines: - -* [html](https://github.com/gofiber/template/tree/master/html) -* [ace](https://github.com/gofiber/template/tree/master/ace) -* [amber](https://github.com/gofiber/template/tree/master/amber) -* [django](https://github.com/gofiber/template/tree/master/django) -* [handlebars](https://github.com/gofiber/template/tree/master/handlebars) -* [jet](https://github.com/gofiber/template/tree/master/jet) -* [mustache](https://github.com/gofiber/template/tree/master/mustache) -* [pug](https://github.com/gofiber/template/tree/master/pug) +Fiber team maintains [templates](https://docs.gofiber.io/template) package that provides wrappers for multiple template engines: + +* [ace](https://docs.gofiber.io/template/ace/) +* [amber](https://docs.gofiber.io/template/amber/) +* [django](https://docs.gofiber.io/template/django/) +* [handlebars](https://docs.gofiber.io/template/handlebars) +* [html](https://docs.gofiber.io/template/html) +* [jet](https://docs.gofiber.io/template/jet) +* [mustache](https://docs.gofiber.io/template/mustache) +* [pug](https://docs.gofiber.io/template/pug) +* [slim](https://docs.gofiber.io/template/pug) From 4e5540fcbf7744398502f1aa002eb7e28f4b7113 Mon Sep 17 00:00:00 2001 From: RW Date: Sat, 8 Jul 2023 21:59:24 +0200 Subject: [PATCH 144/162] Update README.md fix dark and light mode picture https://github.blog/changelog/2021-11-24-specify-theme-context-for-images-in-markdown/ --- .github/README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/README.md b/.github/README.md index fbc2dd64af..c815298309 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1,11 +1,7 @@

- - - - Fiber - - + Fiber + Fiber
From 924fefd677f2bdbf3fd325c8114e2b8af7542901 Mon Sep 17 00:00:00 2001 From: RW Date: Sat, 8 Jul 2023 22:02:08 +0200 Subject: [PATCH 145/162] Update README.md --- .github/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/README.md b/.github/README.md index c815298309..ad05b698fb 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1,7 +1,9 @@

- Fiber - Fiber + + + Fiber +
From fb9b57f534c48adf9c11ee22b6b806501ece3d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sun, 9 Jul 2023 18:46:37 +0200 Subject: [PATCH 146/162] improve docs sync script --- .github/scripts/sync_docs.sh | 78 +++++++++++++++++++++++---------- .github/workflows/sync-docs.yml | 11 ++++- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/.github/scripts/sync_docs.sh b/.github/scripts/sync_docs.sh index 9024d60255..4b58a1e128 100755 --- a/.github/scripts/sync_docs.sh +++ b/.github/scripts/sync_docs.sh @@ -2,37 +2,69 @@ # Some env variables BRANCH="master" -MAJOR_VERSION="v2" REPO_URL="github.com/gofiber/docs.git" AUTHOR_EMAIL="github-actions[bot]@users.noreply.github.com" AUTHOR_USERNAME="github-actions[bot]" +VERSION_FILE="versions.json" +REPO_DIR="core" +COMMIT_URL="https://github.com/gofiber/fiber" +DOCUSAURUS_COMMAND="npm run docusaurus -- docs:version" # Set commit author git config --global user.email "${AUTHOR_EMAIL}" git config --global user.name "${AUTHOR_USERNAME}" +git clone https://${TOKEN}@${REPO_URL} fiber-docs + +# Handle push event +if [ "$EVENT" == "push" ]; then + latest_commit=$(git rev-parse --short HEAD) + log_output=$(git log --oneline ${BRANCH} HEAD~1..HEAD --name-status -- docs/) + if [[ $log_output != "" ]]; then + cp -a docs/* fiber-docs/docs/${REPO_DIR} + fi + +# Handle release event +elif [ "$EVENT" == "release" ]; then + major_version="${TAG_NAME%%.*}" + + # Form new version name + new_version="${major_version}.x" + + cd fiber-docs/ || true + npm ci + + # Check if contrib_versions.json exists and modify it if required + if [[ -f $VERSION_FILE ]]; then + jq --arg new_version "$new_version" 'del(.[] | select(. == $new_version))' $VERSION_FILE >temp.json && mv temp.json $VERSION_FILE + jq -S . ${VERSION_FILE} >temp.json && mv temp.json ${VERSION_FILE} + fi + + # Run docusaurus versioning command + $DOCUSAURUS_COMMAND "${new_version}" +fi + +# Push changes +cd fiber-docs/ || true +git add . if [[ $EVENT == "push" ]]; then - latest_commit=$(git rev-parse --short HEAD) - log_output=$(git log --oneline ${BRANCH} HEAD~1..HEAD --name-status -- docs/) - - if [[ $log_output != "" ]]; then - git clone https://${TOKEN}@${REPO_URL} fiber-docs - cp -a docs/* fiber-docs/docs/core - - # Push changes for next docs - cd fiber-docs/ || return - git add . - git commit -m "Add docs from https://github.com/gofiber/fiber/commit/${latest_commit}" - git push https://${TOKEN}@${REPO_URL} - fi + git commit -m "Add docs from ${COMMIT_URL}/commit/${latest_commit}" elif [[ $EVENT == "release" ]]; then - latest_tag=$(git describe --tags --abbrev=0) - - # Push changes for stable docs - git clone https://${TOKEN}@${REPO_URL} fiber-docs - cd fiber-docs/ || return - cp -a docs/core/* versioned_docs/version-${MAJOR_VERSION}.x - git add . - git commit -m "Sync docs for ${latest_tag} release" - git push https://${TOKEN}@${REPO_URL} + git commit -m "Sync docs for release ${COMMIT_URL}/releases/tag/${TAG_NAME}" +fi + +MAX_RETRIES=5 +DELAY=5 +retry=0 + +while ((retry < MAX_RETRIES)); do + git push https://${TOKEN}@${REPO_URL} && break + retry=$((retry + 1)) + git pull --rebase + sleep $DELAY +done + +if ((retry == MAX_RETRIES)); then + echo "Failed to push after $MAX_RETRIES attempts. Exiting with 1." + exit 1 fi diff --git a/.github/workflows/sync-docs.yml b/.github/workflows/sync-docs.yml index 67cc353866..35ec9fbe95 100644 --- a/.github/workflows/sync-docs.yml +++ b/.github/workflows/sync-docs.yml @@ -8,7 +8,7 @@ on: paths: - 'docs/**' release: - types: [published] + types: [ published ] jobs: sync-docs: @@ -20,8 +20,17 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 2 + - name: Setup Node.js environment + uses: actions/setup-node@v3 + with: + node-version: '18' + + - name: Install JQ + run: sudo apt-get install jq + - name: Sync docs run: ./.github/scripts/sync_docs.sh env: EVENT: ${{ github.event_name }} + TAG_NAME: ${{ github.ref_name }} TOKEN: ${{ secrets.DOC_SYNC_TOKEN }} From 99902abc0c038aa1d0bb9d5f80e12ff52a039334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Sun, 9 Jul 2023 18:49:42 +0200 Subject: [PATCH 147/162] improve docs sync script --- .github/workflows/sync-docs.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/sync-docs.yml b/.github/workflows/sync-docs.yml index 35ec9fbe95..c7f895de6a 100644 --- a/.github/workflows/sync-docs.yml +++ b/.github/workflows/sync-docs.yml @@ -19,14 +19,13 @@ jobs: with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 2 + - name: Setup Node.js environment + uses: actions/setup-node@v3 + with: + node-version: '18' - - name: Setup Node.js environment - uses: actions/setup-node@v3 - with: - node-version: '18' - - - name: Install JQ - run: sudo apt-get install jq + - name: Install JQ + run: sudo apt-get install jq - name: Sync docs run: ./.github/scripts/sync_docs.sh From a9933f50c5677a203960daa4d99656ac82944267 Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 9 Jul 2023 18:52:51 +0200 Subject: [PATCH 148/162] Update intro.md --- docs/intro.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/intro.md b/docs/intro.md index 6655b98392..6d1bcec9b5 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -4,7 +4,6 @@ id: welcome title: 👋 Welcome sidebar_position: 1 --- - An online API documentation with examples so you can start building web apps with Fiber right away! **Fiber** is an [Express](https://github.com/expressjs/express) inspired **web framework** built on top of [Fasthttp](https://github.com/valyala/fasthttp), the **fastest** HTTP engine for [Go](https://go.dev/doc/). Designed to **ease** things up for **fast** development with **zero memory allocation** and **performance** in mind. From 58270e2d5b3d585e570d8f008cfe8df642b3ff82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20da=20Silva?= Date: Tue, 11 Jul 2023 09:06:32 +0200 Subject: [PATCH 149/162] :adhesive_bandage: Fix: dictpool is not completely gone (#2540) * Completely remove dictpool Looks like issue: https://github.com/gofiber/fiber/issues/2209 Was not complete. So here dictpool is completely gone * Fix linting error --- .github/README.md | 1 - .github/README_az.md | 1 - .github/README_ckb.md | 1 - .github/README_de.md | 1 - .github/README_es.md | 1 - .github/README_fa.md | 1 - .github/README_fr.md | 1 - .github/README_he.md | 1 - .github/README_id.md | 1 - .github/README_it.md | 1 - .github/README_ja.md | 1 - .github/README_ko.md | 1 - .github/README_nl.md | 1 - .github/README_pt.md | 1 - .github/README_ru.md | 1 - .github/README_sa.md | 1 - .github/README_tr.md | 1 - .github/README_uk.md | 1 - .github/README_zh-CN.md | 1 - .github/README_zh-TW.md | 1 - ctx.go | 30 ++++++++++++------------------ go.mod | 2 -- go.sum | 19 ------------------- 23 files changed, 12 insertions(+), 59 deletions(-) diff --git a/.github/README.md b/.github/README.md index ad05b698fb..b441b4aa48 100644 --- a/.github/README.md +++ b/.github/README.md @@ -700,7 +700,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_az.md b/.github/README_az.md index bb7b5a8a23..f6486333eb 100644 --- a/.github/README_az.md +++ b/.github/README_az.md @@ -701,7 +701,6 @@ Müəllif Hüququ (c) 2019-bugün [Fenny](https://github.com/fenny) və [Contrib - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_ckb.md b/.github/README_ckb.md index fe84c7f6b9..d4508e5945 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -701,7 +701,6 @@ For more articles, middlewares, examples or tools check our [awesome list](https - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_de.md b/.github/README_de.md index 05c868d716..010154e1de 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -671,7 +671,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_es.md b/.github/README_es.md index 6fcc7fa521..e723505f82 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -671,7 +671,6 @@ Copyright (c) 2019-presente [Fenny](https://github.com/fenny) y [contribuyentes] - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_fa.md b/.github/README_fa.md index eebd093fce..506466a026 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -845,7 +845,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_fr.md b/.github/README_fr.md index d6211f3186..374642409c 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -673,7 +673,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_he.md b/.github/README_he.md index 08e677ba66..39a97fde0e 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -848,7 +848,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_id.md b/.github/README_id.md index d8aa83eb43..1bbdf1725c 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -674,7 +674,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_it.md b/.github/README_it.md index 0cca820c32..567d4b4a24 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -697,7 +697,6 @@ Copyright (c) 2019-ora [Fenny](https://github.com/fenny) e [Contributors](https: - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_ja.md b/.github/README_ja.md index cc1a50efcd..8006c60b8b 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -676,7 +676,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_ko.md b/.github/README_ko.md index 49eae5d670..037605c3cc 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -677,7 +677,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_nl.md b/.github/README_nl.md index 6ba600f845..45711c8807 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -677,7 +677,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_pt.md b/.github/README_pt.md index c81632bdab..e08341d749 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -673,7 +673,6 @@ O logo oficial foi criado por [Vic Shóstak](https://github.com/koddr) e distrib - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_ru.md b/.github/README_ru.md index 28d2d31264..9ebbb1d224 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -680,7 +680,6 @@ Copyright (c) 2019-настоящее время [Fenny](https://github.com/fenn - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_sa.md b/.github/README_sa.md index ea4e7bed17..b3f77d6198 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -742,7 +742,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_tr.md b/.github/README_tr.md index 511bee9b32..496c2797a2 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -670,7 +670,6 @@ Telif (c) 2019-günümüz [Fenny](https://github.com/fenny) ve [katkıda bulunan - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_uk.md b/.github/README_uk.md index 002321b06d..9556e924e1 100644 --- a/.github/README_uk.md +++ b/.github/README_uk.md @@ -706,7 +706,6 @@ Fiber – це проект із відкритим вихідним кодом, - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index 2558a9da7f..bcc5c8a066 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -680,7 +680,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index b58b09066b..3a22be4654 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -710,7 +710,6 @@ Fiber 是個仰賴捐款的開放原始碼專案——用來支付如域名、Gi - [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/ctx.go b/ctx.go index 753480ce49..901b51744c 100644 --- a/ctx.go +++ b/ctx.go @@ -27,7 +27,6 @@ import ( "github.com/gofiber/fiber/v2/internal/schema" "github.com/gofiber/fiber/v2/utils" - "github.com/savsgio/dictpool" "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) @@ -97,7 +96,7 @@ type Ctx struct { values [maxParams]string // Route parameter values fasthttp *fasthttp.RequestCtx // Reference to *fasthttp.RequestCtx matched bool // Non use route matched - viewBindMap *dictpool.Dict // Default view map to bind template engine + viewBindMap sync.Map // Default view map to bind template engine } // TLSHandler object @@ -188,10 +187,7 @@ func (app *App) ReleaseCtx(c *Ctx) { // Reset values c.route = nil c.fasthttp = nil - if c.viewBindMap != nil { - dictpool.ReleaseDict(c.viewBindMap) - c.viewBindMap = nil - } + c.viewBindMap = sync.Map{} app.pool.Put(c) } @@ -1353,13 +1349,9 @@ func (c *Ctx) Redirect(location string, status ...int) error { // Variables are read by the Render method and may be overwritten. func (c *Ctx) Bind(vars Map) error { // init viewBindMap - lazy map - if c.viewBindMap == nil { - c.viewBindMap = dictpool.AcquireDict() - } for k, v := range vars { - c.viewBindMap.Set(k, v) + c.viewBindMap.Store(k, v) } - return nil } @@ -1498,14 +1490,16 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error { func (c *Ctx) renderExtensions(bind interface{}) { if bindMap, ok := bind.(Map); ok { // Bind view map - if c.viewBindMap != nil { - for _, v := range c.viewBindMap.D { - // make sure key does not exist already - if _, ok := bindMap[v.Key]; !ok { - bindMap[v.Key] = v.Value - } + c.viewBindMap.Range(func(key, value interface{}) bool { + keyValue, ok := key.(string) + if !ok { + return true } - } + if _, ok := bindMap[keyValue]; !ok { + bindMap[keyValue] = value + } + return true + }) // Check if the PassLocalsToViews option is enabled (by default it is disabled) if c.app.config.PassLocalsToViews { diff --git a/go.mod b/go.mod index ce67a37496..bd7ffd15da 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.19 github.com/mattn/go-runewidth v0.0.14 - github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 github.com/tinylib/msgp v1.1.8 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.48.0 @@ -19,6 +18,5 @@ require ( github.com/klauspost/compress v1.16.3 // indirect github.com/philhofer/fwd v1.1.2 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect github.com/valyala/tcplisten v1.0.0 // indirect ) diff --git a/go.sum b/go.sum index c2cdbb522e..6493ee08d9 100644 --- a/go.sum +++ b/go.sum @@ -11,17 +11,10 @@ github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APP github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4= -github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8= -github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= -github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= -github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= -github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= 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/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -30,28 +23,19 @@ github.com/valyala/fasthttp v1.48.0 h1:oJWvHb9BIZToTQS3MuQ2R3bJZiNSa2KiNdeI8A+79 github.com/valyala/fasthttp v1.48.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= 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.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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= @@ -70,9 +54,6 @@ 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/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.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From 08099b0635f4432e2ded1adda9e951a1144ea97a Mon Sep 17 00:00:00 2001 From: f1rstmehul Date: Sat, 15 Jul 2023 23:50:29 +0530 Subject: [PATCH 150/162] =?UTF-8?q?=F0=9F=93=9A=20Docs:=20Fix=20link=20(#2?= =?UTF-8?q?542)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix link --- docs/api/middleware/recover.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/middleware/recover.md b/docs/api/middleware/recover.md index e60703f2de..9388ff2156 100644 --- a/docs/api/middleware/recover.md +++ b/docs/api/middleware/recover.md @@ -3,7 +3,7 @@ id: recover title: Recover --- -Recover middleware for [Fiber](https://github.com/gofiber/fiber) that recovers from panics anywhere in the stack chain and handles the control to the centralized [ErrorHandler](https://docs.gofiber.io/error-handling). +Recover middleware for [Fiber](https://github.com/gofiber/fiber) that recovers from panics anywhere in the stack chain and handles the control to the centralized [ErrorHandler](https://docs.gofiber.io/guide/error-handling). ## Signatures From 9c2d214bcfafe89771b0e878f00cb6acc797142d Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 16 Jul 2023 16:06:35 +0200 Subject: [PATCH 151/162] Update app.go prepare release v2.48.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 71b874b7de..e15383a63e 100644 --- a/app.go +++ b/app.go @@ -30,7 +30,7 @@ import ( ) // Version of current fiber package -const Version = "2.47.0" +const Version = "2.48.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From f6446ab0b93d8ba508daa43bd253cfb0631d90aa Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 16 Jul 2023 16:18:23 +0200 Subject: [PATCH 152/162] Update log.md --- docs/api/log.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/api/log.md b/docs/api/log.md index f73ee29870..9b741b13f7 100644 --- a/docs/api/log.md +++ b/docs/api/log.md @@ -1,10 +1,9 @@ --- id: log -title: Log +title: 📃 Log description: Fiber's built-in log package -sidebar_position: 8 +sidebar_position: 6 --- -## Log We can use logs to observe program behavior, diagnose problems, or configure corresponding alarms. And defining a well structured log can improve search efficiency and facilitate handling of problems. From f1ff0f45c8d73442e56a9b5388f2d8ced1a5f633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0tefan=20Baebler?= Date: Tue, 18 Jul 2023 11:57:06 +0200 Subject: [PATCH 153/162] =?UTF-8?q?=F0=9F=93=9A=20Doc:=20Fixed=20link=20to?= =?UTF-8?q?=20slim=20template=20engine=20(#2547)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs: fixed link to slim template engine --- docs/guide/templates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/templates.md b/docs/guide/templates.md index e37bc59b53..cc52c2d9ac 100644 --- a/docs/guide/templates.md +++ b/docs/guide/templates.md @@ -58,7 +58,7 @@ Fiber team maintains [templates](https://docs.gofiber.io/template) package that * [jet](https://docs.gofiber.io/template/jet) * [mustache](https://docs.gofiber.io/template/mustache) * [pug](https://docs.gofiber.io/template/pug) -* [slim](https://docs.gofiber.io/template/pug) +* [slim](https://docs.gofiber.io/template/slim) From 3b08646abf1d85c53f23cfdc56fdbabd1e7397bd Mon Sep 17 00:00:00 2001 From: RW Date: Thu, 20 Jul 2023 16:43:49 +0200 Subject: [PATCH 154/162] Update ctx.md Add additional documentation for GetRespHeaders --- docs/api/ctx.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index f04e34fa2e..3a5efab7d3 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -564,6 +564,9 @@ Returns the HTTP response headers. func (c *Ctx) GetRespHeaders() map[string]string ``` +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + ## GetRouteURL Generates URLs to named routes, with parameters. URLs are relative, for example: "/user/1831" From c0116f445f4f65d3c487e92933a233f4ce535368 Mon Sep 17 00:00:00 2001 From: RW Date: Thu, 20 Jul 2023 16:47:39 +0200 Subject: [PATCH 155/162] Update ctx.md Add hint for references to GetReqHeaders --- docs/api/ctx.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 3a5efab7d3..feebc1ee63 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -532,6 +532,9 @@ Returns the HTTP request headers. func (c *Ctx) GetReqHeaders() map[string]string ``` +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + ## GetRespHeader Returns the HTTP response header specified by the field. From 2c8c4a6545117108f56026460ef59a4e8fe8756b Mon Sep 17 00:00:00 2001 From: Precious Luke Date: Sat, 22 Jul 2023 16:42:48 +0100 Subject: [PATCH 156/162] Update intro.md (#2550) Since we are using port 3000, it ideal to maintain that throughout in order not to confuse beginners --- docs/intro.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/intro.md b/docs/intro.md index 6d1bcec9b5..456035da6b 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -184,9 +184,9 @@ app.Listen(":3000") Now, you can load the files that are in the `./public` directory: ```bash -http://localhost:8080/hello.html -http://localhost:8080/js/jquery.js -http://localhost:8080/css/style.css +http://localhost:3000/hello.html +http://localhost:3000/js/jquery.js +http://localhost:3000/css/style.css ``` ### Note From 8e9d57f5a00cdaa1a0e0ee762de521a9b427518d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jul 2023 16:34:17 +0300 Subject: [PATCH 157/162] Bump github.com/mattn/go-runewidth from 0.0.14 to 0.0.15 (#2551) Bumps [github.com/mattn/go-runewidth](https://github.com/mattn/go-runewidth) from 0.0.14 to 0.0.15. - [Commits](https://github.com/mattn/go-runewidth/compare/v0.0.14...v0.0.15) --- updated-dependencies: - dependency-name: github.com/mattn/go-runewidth dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index bd7ffd15da..b20325a780 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/google/uuid v1.3.0 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.19 - github.com/mattn/go-runewidth v0.0.14 + github.com/mattn/go-runewidth v0.0.15 github.com/tinylib/msgp v1.1.8 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.48.0 diff --git a/go.sum b/go.sum index 6493ee08d9..780c01035e 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ 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.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= From 5d74725a696eb9448232d52d02302242f32946bc Mon Sep 17 00:00:00 2001 From: Lev Saminskij <103888491+Kirari04@users.noreply.github.com> Date: Mon, 24 Jul 2023 15:36:18 +0200 Subject: [PATCH 158/162] =?UTF-8?q?=F0=9F=93=9D=20added=20documentation=20?= =?UTF-8?q?about=20ctx=20Fresh=20(#2549)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit added documentation about ctx Fresh --- docs/api/ctx.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index feebc1ee63..47966a56cc 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -494,7 +494,11 @@ app.Post("/", func(c *fiber.Ctx) error { ## Fresh -[https://expressjs.com/en/4x/api.html\#req.fresh](https://expressjs.com/en/4x/api.html#req.fresh) +When the response is still **fresh** in the client's cache **true** is returned, otherwise **false** is returned to indicate that the client cache is now stale and the full response should be sent. + +When a client sends the Cache-Control: no-cache request header to indicate an end-to-end reload request, `Fresh` will return false to make handling these requests transparent. + +Read more on [https://expressjs.com/en/4x/api.html\#req.fresh](https://expressjs.com/en/4x/api.html#req.fresh) ```go title="Signature" func (c *Ctx) Fresh() bool From 51ea636b60727af37851a28315b7c0a199f79883 Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 24 Jul 2023 16:46:50 +0200 Subject: [PATCH 159/162] =?UTF-8?q?improved=20the=20config=20section=20of?= =?UTF-8?q?=20the=20middleware=20readme=C2=B4s=20(#2552)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api/middleware/adaptor.md | 3 +- docs/api/middleware/basicauth.md | 58 +++------------ docs/api/middleware/cache.md | 78 +++++--------------- docs/api/middleware/compress.md | 34 ++++----- docs/api/middleware/cors.md | 68 ++++-------------- docs/api/middleware/csrf.md | 104 +++++++-------------------- docs/api/middleware/earlydata.md | 35 +++------ docs/api/middleware/encryptcookie.md | 40 +++-------- docs/api/middleware/envvar.md | 17 ++--- docs/api/middleware/etag.md | 26 ++----- docs/api/middleware/expvar.md | 15 ++-- docs/api/middleware/favicon.md | 40 +++-------- docs/api/middleware/filesystem.md | 63 ++++------------ docs/api/middleware/helmet.md | 100 ++++++-------------------- docs/api/middleware/idempotency.md | 50 +++---------- docs/api/middleware/keyauth.md | 51 +++---------- docs/api/middleware/limiter.md | 72 +++++-------------- docs/api/middleware/logger.md | 76 ++++++-------------- docs/api/middleware/monitor.md | 53 +++----------- docs/api/middleware/pprof.md | 23 ++---- docs/api/middleware/proxy.md | 63 ++++------------ docs/api/middleware/recover.md | 27 ++----- docs/api/middleware/redirect.md | 32 ++------- docs/api/middleware/requestid.md | 34 +++------ docs/api/middleware/rewrite.md | 11 ++- docs/api/middleware/session.md | 74 +++++-------------- docs/api/middleware/skip.md | 5 +- docs/api/middleware/timeout.md | 3 +- middleware/session/config.go | 8 +-- 29 files changed, 303 insertions(+), 960 deletions(-) diff --git a/docs/api/middleware/adaptor.md b/docs/api/middleware/adaptor.md index 7e73dd4705..64df229ce2 100644 --- a/docs/api/middleware/adaptor.md +++ b/docs/api/middleware/adaptor.md @@ -1,8 +1,9 @@ --- id: adaptor -title: Adaptor --- +# Adaptor + Converter for net/http handlers to/from Fiber request handlers, special thanks to [@arsmn](https://github.com/arsmn)! ## Signatures diff --git a/docs/api/middleware/basicauth.md b/docs/api/middleware/basicauth.md index 268f5b246e..0e90eafed0 100644 --- a/docs/api/middleware/basicauth.md +++ b/docs/api/middleware/basicauth.md @@ -1,8 +1,9 @@ --- id: basicauth -title: BasicAuth --- +# BasicAuth + Basic Authentication middleware for [Fiber](https://github.com/gofiber/fiber) that provides an HTTP basic authentication. It calls the next handler for valid credentials and [401 Unauthorized](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) or a custom response for missing or invalid credentials. ## Signatures @@ -59,52 +60,15 @@ app.Use(basicauth.New(basicauth.Config{ ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Users defines the allowed credentials - // - // Required. Default: map[string]string{} - Users map[string]string - - // Realm is a string to define realm attribute of BasicAuth. - // the realm identifies the system to authenticate against - // and can be used by clients to save credentials - // - // Optional. Default: "Restricted". - Realm string - - // Authorizer defines a function you can pass - // to check the credentials however you want. - // It will be called with a username and password - // and is expected to return true or false to indicate - // that the credentials were approved or not. - // - // Optional. Default: nil. - Authorizer func(string, string) bool - - // Unauthorized defines the response body for unauthorized responses. - // By default it will return with a 401 Unauthorized and the correct WWW-Auth header - // - // Optional. Default: nil - Unauthorized fiber.Handler - - // ContextUser is the key to store the username in Locals - // - // Optional. Default: "username" - ContextUsername string - - // ContextPass is the key to store the password in Locals - // - // Optional. Default: "password" - ContextPassword string -} -``` +| Property | Type | Description | Default | +|:----------------|:----------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Users | `map[string]string` | Users defines the allowed credentials. | `map[string]string{}` | +| Realm | `string` | Realm is a string to define the realm attribute of BasicAuth. The realm identifies the system to authenticate against and can be used by clients to save credentials. | `"Restricted"` | +| Authorizer | `func(string, string) bool` | Authorizer defines a function to check the credentials. It will be called with a username and password and is expected to return true or false to indicate approval. | `nil` | +| Unauthorized | `fiber.Handler` | Unauthorized defines the response body for unauthorized responses. | `nil` | +| ContextUsername | `string` | ContextUsername is the key to store the username in Locals. | `"username"` | +| ContextPassword | `string` | ContextPassword is the key to store the password in Locals. | `"password"` | ## Default Config diff --git a/docs/api/middleware/cache.md b/docs/api/middleware/cache.md index 99dd833c5f..3a87306d3b 100644 --- a/docs/api/middleware/cache.md +++ b/docs/api/middleware/cache.md @@ -1,8 +1,9 @@ --- id: cache -title: Cache --- +# Cache + Cache middleware for [Fiber](https://github.com/gofiber/fiber) designed to intercept responses and cache them. This middleware will cache the `Body`, `Content-Type` and `StatusCode` using the `c.Path()` as unique identifier. Special thanks to [@codemicro](https://github.com/codemicro/fiber-cache) for creating this middleware for Fiber core! Request Directives
@@ -63,67 +64,20 @@ app.Get("/", func(c *fiber.Ctx) error { ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Expiration is the time that an cached response will live - // - // Optional. Default: 1 * time.Minute - Expiration time.Duration - - // CacheHeader header on response header, indicate cache status, with the following possible return value - // - // hit, miss, unreachable - // - // Optional. Default: X-Cache - CacheHeader string - - // CacheControl enables client side caching if set to true - // - // Optional. Default: false - CacheControl bool - - // Key allows you to generate custom keys, by default c.Path() is used - // - // Default: func(c *fiber.Ctx) string { - // return utils.CopyString(c.Path()) - // } - KeyGenerator func(*fiber.Ctx) string - - // allows you to generate custom Expiration Key By Key, default is Expiration (Optional) - // - // Default: nil - ExpirationGenerator func(*fiber.Ctx, *Config) time.Duration - - // Store is used to store the state of the middleware - // - // Default: an in memory store for this process only - Storage fiber.Storage - - // allows you to store additional headers generated by next middlewares & handler - // - // Default: false - StoreResponseHeaders bool - - // Max number of bytes of response bodies simultaneously stored in cache. When limit is reached, - // entries with the nearest expiration are deleted to make room for new. - // 0 means no limit - // - // Default: 0 - MaxBytes uint - - // You can specify HTTP methods to cache. - // The middleware just caches the routes of its methods in this slice. - // - // Default: []string{fiber.MethodGet, fiber.MethodHead} - Methods []string -} -``` +| Property | Type | Description | Default | +|:---------------------|:------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Expiration | `time.Duration` | Expiration is the time that a cached response will live. | `1 * time.Minute` | +| CacheHeader | `string` | CacheHeader is the header on the response header that indicates the cache status, with the possible return values "hit," "miss," or "unreachable." | `X-Cache` | +| CacheControl | `bool` | CacheControl enables client-side caching if set to true. | `false` | +| KeyGenerator | `func(*fiber.Ctx) string` | Key allows you to generate custom keys. | `func(c *fiber.Ctx) string { return utils.CopyString(c.Path()) }` | +| ExpirationGenerator | `func(*fiber.Ctx, *cache.Config) time.Duration` | ExpirationGenerator allows you to generate custom expiration keys based on the request. | `nil` | +| Storage | `fiber.Storage` | Store is used to store the state of the middleware. | In-memory store | +| Store (Deprecated) | `fiber.Storage` | Deprecated: Use Storage instead. | In-memory store | +| Key (Deprecated) | `func(*fiber.Ctx) string` | Deprecated: Use KeyGenerator instead. | `nil` | +| StoreResponseHeaders | `bool` | StoreResponseHeaders allows you to store additional headers generated by next middlewares & handler. | `false` | +| MaxBytes | `uint` | MaxBytes is the maximum number of bytes of response bodies simultaneously stored in cache. | `0` (No limit) | +| Methods | `[]string` | Methods specifies the HTTP methods to cache. | `[]string{fiber.MethodGet, fiber.MethodHead}` | ## Default Config diff --git a/docs/api/middleware/compress.md b/docs/api/middleware/compress.md index 6066ac7246..f284f5e92c 100644 --- a/docs/api/middleware/compress.md +++ b/docs/api/middleware/compress.md @@ -1,8 +1,9 @@ --- id: compress -title: Compress --- +# Compress + Compression middleware for [Fiber](https://github.com/gofiber/fiber) that will compress the response using `gzip`, `deflate` and `brotli` compression depending on the [Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header. ## Signatures @@ -44,24 +45,19 @@ app.Use(compress.New(compress.Config{ ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Level determines the compression algoritm - // - // Optional. Default: LevelDefault - // LevelDisabled: -1 - // LevelDefault: 0 - // LevelBestSpeed: 1 - // LevelBestCompression: 2 - Level int -} -``` +### Config + +| Property | Type | Description | Default | +|:---------|:------------------------|:--------------------------------------------------------------------|:-------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Level | `Level` | Level determines the compression algorithm. | `LevelDefault (0)` | + +Possible values for the "Level" field are: + +- `LevelDisabled (-1)`: Compression is disabled. +- `LevelDefault (0)`: Default compression level. +- `LevelBestSpeed (1)`: Best compression speed. +- `LevelBestCompression (2)`: Best compression. ## Default Config diff --git a/docs/api/middleware/cors.md b/docs/api/middleware/cors.md index 8a76def1eb..af9b8c5f2b 100644 --- a/docs/api/middleware/cors.md +++ b/docs/api/middleware/cors.md @@ -1,8 +1,9 @@ --- id: cors -title: CORS --- +# CORS + CORS middleware for [Fiber](https://github.com/gofiber/fiber) that can be used to enable [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) with various options. ## Signatures @@ -53,61 +54,16 @@ app.Use(cors.New(cors.Config{ ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // AllowOriginsFunc defines a function that will set the 'access-control-allow-origin' - // response header to the 'origin' request header when returned true. - // - // Note: Using this feature is discouraged in production and it's best practice to explicitly - // set CORS origins via 'AllowOrigins' - // - // Optional. Default: nil - AllowOriginsFunc func(origin string) bool - - // AllowOrigin defines a list of origins that may access the resource. - // - // Optional. Default value "*" - AllowOrigins string - - // AllowMethods defines a list methods allowed when accessing the resource. - // This is used in response to a preflight request. - // - // Optional. Default value "GET,POST,HEAD,PUT,DELETE,PATCH" - AllowMethods string - - // AllowHeaders defines a list of request headers that can be used when - // making the actual request. This is in response to a preflight request. - // - // Optional. Default value "". - AllowHeaders string - - // AllowCredentials indicates whether or not the response to the request - // can be exposed when the credentials flag is true. When used as part of - // a response to a preflight request, this indicates whether or not the - // actual request can be made using credentials. - // - // Optional. Default value false. - AllowCredentials bool - - // ExposeHeaders defines a whitelist headers that clients are allowed to - // access. - // - // Optional. Default value "". - ExposeHeaders string - - // MaxAge indicates how long (in seconds) the results of a preflight request - // can be cached. - // - // Optional. Default value 0. - MaxAge int -} -``` +| Property | Type | Description | Default | +|:-----------------|:---------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| AllowOriginsFunc | `func(origin string) bool` | AllowOriginsFunc defines a function that will set the 'access-control-allow-origin' response header to the 'origin' request header when returned true. | `nil` | +| AllowOrigins | `string` | AllowOrigin defines a list of origins that may access the resource. | `"*"` | +| AllowMethods | `string` | AllowMethods defines a list methods allowed when accessing the resource. This is used in response to a preflight request. | `"GET,POST,HEAD,PUT,DELETE,PATCH"` | +| AllowHeaders | `string` | AllowHeaders defines a list of request headers that can be used when making the actual request. This is in response to a preflight request. | `""` | +| AllowCredentials | `bool` | AllowCredentials indicates whether or not the response to the request can be exposed when the credentials flag is true. | `false` | +| ExposeHeaders | `string` | ExposeHeaders defines a whitelist headers that clients are allowed to access. | `""` | +| MaxAge | `int` | MaxAge indicates how long (in seconds) the results of a preflight request can be cached. | `0` | ## Default Config diff --git a/docs/api/middleware/csrf.md b/docs/api/middleware/csrf.md index d0452a8ece..e588b51762 100644 --- a/docs/api/middleware/csrf.md +++ b/docs/api/middleware/csrf.md @@ -1,8 +1,9 @@ --- id: csrf -title: CSRF --- +# CSRF + CSRF middleware for [Fiber](https://github.com/gofiber/fiber) that provides [Cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) protection by passing a csrf token via cookies. This cookie value will be used to compare against the client csrf token on requests, other than those defined as "safe" by RFC7231 \(GET, HEAD, OPTIONS, or TRACE\). When the csrf token is invalid, this middleware will return the `fiber.ErrForbidden` error. CSRF Tokens are generated on GET requests. You can retrieve the CSRF token with `c.Locals(contextKey)`, where `contextKey` is the string you set in the config (see Custom Config below). @@ -53,85 +54,28 @@ KeyLookup will be ignored if Extractor is explicitly set. ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // KeyLookup is a string in the form of ":" that is used - // to create an Extractor that extracts the token from the request. - // Possible values: - // - "header:" - // - "query:" - // - "param:" - // - "form:" - // - "cookie:" - // - // Ignored if an Extractor is explicitly set. - // - // Optional. Default: "header:X-CSRF-Token" - KeyLookup string - - // Name of the session cookie. This cookie will store session key. - // Optional. Default value "csrf_". - CookieName string - - // Domain of the CSRF cookie. - // Optional. Default value "". - CookieDomain string - - // Path of the CSRF cookie. - // Optional. Default value "". - CookiePath string - - // Indicates if CSRF cookie is secure. - // Optional. Default value false. - CookieSecure bool - - // Indicates if CSRF cookie is HTTP only. - // Optional. Default value false. - CookieHTTPOnly bool - - // Indicates if CSRF cookie is requested by SameSite. - // Optional. Default value "Lax". - CookieSameSite string - - // Decides whether cookie should last for only the browser sesison. - // Ignores Expiration if set to true - CookieSessionOnly bool - - // Expiration is the duration before csrf token will expire - // - // Optional. Default: 1 * time.Hour - Expiration time.Duration - - // Store is used to store the state of the middleware - // - // Optional. Default: memory.New() - Storage fiber.Storage - - // Context key to store generated CSRF token into context. - // If left empty, token will not be stored in context. - // - // Optional. Default: "" - ContextKey string - - // KeyGenerator creates a new CSRF token - // - // Optional. Default: utils.UUID - KeyGenerator func() string - - // Extractor returns the csrf token - // - // If set this will be used in place of an Extractor based on KeyLookup. - // - // Optional. Default will create an Extractor based on KeyLookup. - Extractor func(c *fiber.Ctx) (string, error) -} -``` +### Config + +| Property | Type | Description | Default | +|:------------------|:-----------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| KeyLookup | `string` | KeyLookup is a string in the form of ":" that is used to create an Extractor that extracts the token from the request. Possible values: "header:", "query:", "param:", "form:", "cookie:". Ignored if an Extractor is explicitly set. | "header:X-CSRF-Token" | +| CookieName | `string` | Name of the session cookie. This cookie will store the session key. | "csrf_" | +| CookieDomain | `string` | Domain of the CSRF cookie. | "" | +| CookiePath | `string` | Path of the CSRF cookie. | "" | +| CookieSecure | `bool` | Indicates if the CSRF cookie is secure. | false | +| CookieHTTPOnly | `bool` | Indicates if the CSRF cookie is HTTP-only. | false | +| CookieSameSite | `string` | Value of SameSite cookie. | "Lax" | +| CookieSessionOnly | `bool` | Decides whether the cookie should last for only the browser session. Ignores Expiration if set to true. | false | +| Expiration | `time.Duration` | Expiration is the duration before the CSRF token will expire. | 1 * time.Hour | +| Storage | `fiber.Storage` | Store is used to store the state of the middleware. | memory.New() | +| ContextKey | `string` | Context key to store the generated CSRF token into the context. If left empty, the token will not be stored in the context. | "" | +| KeyGenerator | `func() string` | KeyGenerator creates a new CSRF token. | utils.UUID | +| CookieExpires | `time.Duration` (Deprecated) | Deprecated: Please use Expiration. | 0 | +| Cookie | `*fiber.Cookie` (Deprecated) | Deprecated: Please use Cookie* related fields. | nil | +| TokenLookup | `string` (Deprecated) | Deprecated: Please use KeyLookup. | "" | +| ErrorHandler | `fiber.ErrorHandler` | ErrorHandler is executed when an error is returned from fiber.Handler. | DefaultErrorHandler | +| Extractor | `func(*fiber.Ctx) (string, error)` | Extractor returns the CSRF token. If set, this will be used in place of an Extractor based on KeyLookup. | Extractor based on KeyLookup | ## Default Config diff --git a/docs/api/middleware/earlydata.md b/docs/api/middleware/earlydata.md index 6b83f1214f..50e5bb174c 100644 --- a/docs/api/middleware/earlydata.md +++ b/docs/api/middleware/earlydata.md @@ -1,8 +1,9 @@ --- id: earlydata -title: EarlyData --- +# EarlyData + The Early Data middleware for [Fiber](https://github.com/gofiber/fiber) adds support for TLS 1.3's early data ("0-RTT") feature. Citing [RFC 8446](https://datatracker.ietf.org/doc/html/rfc8446#section-2-3), when a client and server share a PSK, TLS 1.3 allows clients to send data on the first flight ("early data") to speed up the request, effectively reducing the regular 1-RTT request to a 0-RTT request. @@ -48,30 +49,12 @@ app.Use(earlydata.New(earlydata.Config{ ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // IsEarlyData returns whether the request is an early-data request. - // - // Optional. Default: a function which checks if the "Early-Data" request header equals "1". - IsEarlyData func(c *fiber.Ctx) bool - - // AllowEarlyData returns whether the early-data request should be allowed or rejected. - // - // Optional. Default: a function which rejects the request on unsafe and allows the request on safe HTTP request methods. - AllowEarlyData func(c *fiber.Ctx) bool - - // Error is returned in case an early-data request is rejected. - // - // Optional. Default: fiber.ErrTooEarly. - Error error -} -``` +| Property | Type | Description | Default | +|:---------------|:------------------------|:-------------------------------------------------------------------------------------|:-------------------------------------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| IsEarlyData | `func(*fiber.Ctx) bool` | IsEarlyData returns whether the request is an early-data request. | Function checking if "Early-Data" header equals "1" | +| AllowEarlyData | `func(*fiber.Ctx) bool` | AllowEarlyData returns whether the early-data request should be allowed or rejected. | Function rejecting on unsafe and allowing safe methods | +| Error | `error` | Error is returned in case an early-data request is rejected. | `fiber.ErrTooEarly` | ## Default Config @@ -96,4 +79,4 @@ const ( DefaultHeaderName = "Early-Data" DefaultHeaderTrueValue = "1" ) -``` \ No newline at end of file +``` diff --git a/docs/api/middleware/encryptcookie.md b/docs/api/middleware/encryptcookie.md index 594df96389..743578bd87 100644 --- a/docs/api/middleware/encryptcookie.md +++ b/docs/api/middleware/encryptcookie.md @@ -1,8 +1,9 @@ --- id: encryptcookie -title: Encrypt Cookie --- +# Encrypt Cookie + Encrypt middleware for [Fiber](https://github.com/gofiber/fiber) which encrypts cookie values. Note: this middleware does not encrypt cookie names. ## Signatures @@ -54,36 +55,13 @@ app.Post("/", func(c *fiber.Ctx) error { ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Array of cookie keys that should not be encrypted. - // - // Optional. Default: ["csrf_"] - Except []string - - // Base64 encoded unique key to encode & decode cookies. - // - // Required. The key should be 32 bytes of random data in base64-encoded form. - // You may run `openssl rand -base64 32` or use `encryptcookie.GenerateKey()` to generate a new key. - Key string - - // Custom function to encrypt cookies. - // - // Optional. Default: EncryptCookie - Encryptor func(decryptedString, key string) (string, error) - - // Custom function to decrypt cookies. - // - // Optional. Default: DecryptCookie - Decryptor func(encryptedString, key string) (string, error) -} -``` +| Property | Type | Description | Default | +|:----------|:----------------------------------------------------|:----------------------------------------------------------------------------------------------------|:-----------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Except | `[]string` | Array of cookie keys that should not be encrypted. | `[]` | +| Key | `string` | Base64 encoded unique key to encode & decode cookies. Required. Key length should be 32 characters. | (No default, required field) | +| Encryptor | `func(decryptedString, key string) (string, error)` | Custom function to encrypt cookies. | `EncryptCookie` | +| Decryptor | `func(encryptedString, key string) (string, error)` | Custom function to decrypt cookies. | `DecryptCookie` | ## Default Config diff --git a/docs/api/middleware/envvar.md b/docs/api/middleware/envvar.md index 592a502df4..1d9f474297 100644 --- a/docs/api/middleware/envvar.md +++ b/docs/api/middleware/envvar.md @@ -1,8 +1,9 @@ --- id: envvar -title: EnvVar --- +# EnvVar + EnvVar middleware for [Fiber](https://github.com/gofiber/fiber) that can be used to expose environment variables with various options. ## Signatures @@ -56,16 +57,10 @@ Http response contract: ## Config -```go -// Config defines the config for middleware. -type Config struct { - // ExportVars specifies the environment variables that should export - ExportVars map[string]string - // ExcludeVars specifies the environment variables that should not export - ExcludeVars map[string]string -} - -``` +| Property | Type | Description | Default | +|:------------|:--------------------|:-----------------------------------------------------------------------------|:--------| +| ExportVars | `map[string]string` | ExportVars specifies the environment variables that should be exported. | `nil` | +| ExcludeVars | `map[string]string` | ExcludeVars specifies the environment variables that should not be exported. | `nil` | ## Default Config diff --git a/docs/api/middleware/etag.md b/docs/api/middleware/etag.md index b9df4e93fe..24be273021 100644 --- a/docs/api/middleware/etag.md +++ b/docs/api/middleware/etag.md @@ -1,8 +1,9 @@ --- id: etag -title: ETag --- +# ETag + ETag middleware for [Fiber](https://github.com/gofiber/fiber) that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. ## Signatures @@ -46,25 +47,10 @@ app.Get("/", func(c *fiber.Ctx) error { ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Weak indicates that a weak validator is used. Weak etags are easy - // to generate, but are far less useful for comparisons. Strong - // validators are ideal for comparisons but can be very difficult - // to generate efficiently. Weak ETag values of two representations - // of the same resources might be semantically equivalent, but not - // byte-for-byte identical. This means weak etags prevent caching - // when byte range requests are used, but strong etags mean range - // requests can still be cached. - Weak bool -} -``` +| Property | Type | Description | Default | +|:---------|:------------------------|:-------------------------------------------------------------------------------------------------------------------|:--------| +| Weak | `bool` | Weak indicates that a weak validator is used. Weak etags are easy to generate but are less useful for comparisons. | `false` | +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | ## Default Config diff --git a/docs/api/middleware/expvar.md b/docs/api/middleware/expvar.md index c2023fbb33..900850e364 100644 --- a/docs/api/middleware/expvar.md +++ b/docs/api/middleware/expvar.md @@ -1,8 +1,9 @@ --- id: expvar -title: ExpVar --- +# ExpVar + Expvar middleware for [Fiber](https://github.com/gofiber/fiber) that serves via its HTTP server runtime exposed variants in the JSON format. The package is typically only imported for the side effect of registering its HTTP handlers. The handled path is `/debug/vars`. ## Signatures @@ -58,15 +59,9 @@ curl 127.0.0.1:3000/debug/vars?r=c ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool -} -``` +| Property | Type | Description | Default | +|:---------|:------------------------|:--------------------------------------------------------------------|:--------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | ## Default Config diff --git a/docs/api/middleware/favicon.md b/docs/api/middleware/favicon.md index 39432d45b9..3fea8b129a 100644 --- a/docs/api/middleware/favicon.md +++ b/docs/api/middleware/favicon.md @@ -1,8 +1,9 @@ --- id: favicon -title: Favicon --- +# Favicon + Favicon middleware for [Fiber](https://github.com/gofiber/fiber) that ignores favicon requests or caches a provided icon in memory to improve performance by skipping disk access. User agents request favicon.ico frequently and indiscriminately, so you may wish to exclude these requests from your logs by using this middleware before your logger middleware. :::note @@ -41,36 +42,13 @@ app.Use(favicon.New(favicon.Config{ ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // File holds the path to an actual favicon that will be cached - // - // Optional. Default: "" - File string - - // URL for favicon handler - // - // Optional. Default: "/favicon.ico" - URL string - - // FileSystem is an optional alternate filesystem to search for the favicon in. - // An example of this could be an embedded or network filesystem - // - // Optional. Default: nil - FileSystem http.FileSystem - - // CacheControl defines how the Cache-Control header in the response should be set - // - // Optional. Default: "public, max-age=31536000" - CacheControl string -} -``` +| Property | Type | Description | Default | +|:-------------|:------------------------|:---------------------------------------------------------------------------------|:---------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| File | `string` | File holds the path to an actual favicon that will be cached. | "" | +| URL | `string` | URL for favicon handler. | "/favicon.ico" | +| FileSystem | `http.FileSystem` | FileSystem is an optional alternate filesystem to search for the favicon in. | `nil` | +| CacheControl | `string` | CacheControl defines how the Cache-Control header in the response should be set. | "public, max-age=31536000" | ## Default Config diff --git a/docs/api/middleware/filesystem.md b/docs/api/middleware/filesystem.md index 86bc585cfc..38e3622db7 100644 --- a/docs/api/middleware/filesystem.md +++ b/docs/api/middleware/filesystem.md @@ -1,8 +1,9 @@ --- id: filesystem -title: FileSystem --- +# FileSystem + Filesystem middleware for [Fiber](https://github.com/gofiber/fiber) that enables you to serve files from a directory. :::caution @@ -228,56 +229,16 @@ func main() { ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Root is a FileSystem that provides access - // to a collection of files and directories. - // - // Required. Default: nil - Root http.FileSystem `json:"-"` - - // PathPrefix defines a prefix to be added to a filepath when - // reading a file from the FileSystem. - // - // Use when using Go 1.16 embed.FS - // - // Optional. Default "" - PathPrefix string `json:"path_prefix"` - - // Enable directory browsing. - // - // Optional. Default: false - Browse bool `json:"browse"` - - // Index file for serving a directory. - // - // Optional. Default: "index.html" - Index string `json:"index"` - - // The value for the Cache-Control HTTP-header - // that is set on the file response. MaxAge is defined in seconds. - // - // Optional. Default value 0. - MaxAge int `json:"max_age"` - - // File to return if path is not found. Useful for SPA's. - // - // Optional. Default: "" - NotFoundFile string `json:"not_found_file"` - - // The value for the Content-Type HTTP-header - // that is set on the file response - // - // Optional. Default: "" - ContentTypeCharset string `json:"content_type_charset"` -} -``` +| Property | Type | Description | Default | +|:-------------------|:------------------------|:------------------------------------------------------------------------------------------------------------|:-------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Root | `http.FileSystem` | Root is a FileSystem that provides access to a collection of files and directories. | `nil` | +| PathPrefix | `string` | PathPrefix defines a prefix to be added to a filepath when reading a file from the FileSystem. | "" | +| Browse | `bool` | Enable directory browsing. | `false` | +| Index | `string` | Index file for serving a directory. | "index.html" | +| MaxAge | `int` | The value for the Cache-Control HTTP-header that is set on the file response. MaxAge is defined in seconds. | 0 | +| NotFoundFile | `string` | File to return if the path is not found. Useful for SPA's. | "" | +| ContentTypeCharset | `string` | The value for the Content-Type HTTP-header that is set on the file response. | "" | ## Default Config diff --git a/docs/api/middleware/helmet.md b/docs/api/middleware/helmet.md index 1392c5879b..0835f31166 100644 --- a/docs/api/middleware/helmet.md +++ b/docs/api/middleware/helmet.md @@ -1,8 +1,9 @@ --- id: helmet -title: Helmet --- +# Helmet + Helmet middleware helps secure your apps by setting various HTTP headers. ## Signatures @@ -41,83 +42,26 @@ curl -I http://localhost:3000 ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip middleware. - // Optional. Default: nil - Next func(*fiber.Ctx) bool - - // XSSProtection - // Optional. Default value "0". - XSSProtection string - - // ContentTypeNosniff - // Optional. Default value "nosniff". - ContentTypeNosniff string - - // XFrameOptions - // Optional. Default value "SAMEORIGIN". - // Possible values: "SAMEORIGIN", "DENY", "ALLOW-FROM uri" - XFrameOptions string - - // HSTSMaxAge - // Optional. Default value 0. - HSTSMaxAge int - - // HSTSExcludeSubdomains - // Optional. Default value false. - HSTSExcludeSubdomains bool - - // ContentSecurityPolicy - // Optional. Default value "". - ContentSecurityPolicy string - - // CSPReportOnly - // Optional. Default value false. - CSPReportOnly bool - - // HSTSPreloadEnabled - // Optional. Default value false. - HSTSPreloadEnabled bool - - // ReferrerPolicy - // Optional. Default value "ReferrerPolicy". - ReferrerPolicy string - - // Permissions-Policy - // Optional. Default value "". - PermissionPolicy string - - // Cross-Origin-Embedder-Policy - // Optional. Default value "require-corp". - CrossOriginEmbedderPolicy string - - // Cross-Origin-Opener-Policy - // Optional. Default value "same-origin". - CrossOriginOpenerPolicy string - - // Cross-Origin-Resource-Policy - // Optional. Default value "same-origin". - CrossOriginResourcePolicy string - - // Origin-Agent-Cluster - // Optional. Default value "?1". - OriginAgentCluster string - - // X-DNS-Prefetch-Control - // Optional. Default value "off". - XDNSPrefetchControl string - - // X-Download-Options - // Optional. Default value "noopen". - XDownloadOptions string - - // X-Permitted-Cross-Domain-Policies - // Optional. Default value "none". - XPermittedCrossDomain string -} -``` +| Property | Type | Description | Default | +|:--------------------------|:------------------------|:--------------------------------------------|:-----------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip middleware. | `nil` | +| XSSProtection | `string` | XSSProtection | "0" | +| ContentTypeNosniff | `string` | ContentTypeNosniff | "nosniff" | +| XFrameOptions | `string` | XFrameOptions | "SAMEORIGIN" | +| HSTSMaxAge | `int` | HSTSMaxAge | 0 | +| HSTSExcludeSubdomains | `bool` | HSTSExcludeSubdomains | false | +| ContentSecurityPolicy | `string` | ContentSecurityPolicy | "" | +| CSPReportOnly | `bool` | CSPReportOnly | false | +| HSTSPreloadEnabled | `bool` | HSTSPreloadEnabled | false | +| ReferrerPolicy | `string` | ReferrerPolicy | "ReferrerPolicy" | +| PermissionPolicy | `string` | Permissions-Policy | "" | +| CrossOriginEmbedderPolicy | `string` | Cross-Origin-Embedder-Policy | "require-corp" | +| CrossOriginOpenerPolicy | `string` | Cross-Origin-Opener-Policy | "same-origin" | +| CrossOriginResourcePolicy | `string` | Cross-Origin-Resource-Policy | "same-origin" | +| OriginAgentCluster | `string` | Origin-Agent-Cluster | "?1" | +| XDNSPrefetchControl | `string` | X-DNS-Prefetch-Control | "off" | +| XDownloadOptions | `string` | X-Download-Options | "noopen" | +| XPermittedCrossDomain | `string` | X-Permitted-Cross-Domain-Policies | "none" | ## Default Config diff --git a/docs/api/middleware/idempotency.md b/docs/api/middleware/idempotency.md index dc0cda8a15..bab7c0e450 100644 --- a/docs/api/middleware/idempotency.md +++ b/docs/api/middleware/idempotency.md @@ -1,8 +1,9 @@ --- id: idempotency -title: Idempotency --- +# Idempotency + Idempotency middleware for [Fiber](https://github.com/gofiber/fiber) allows for fault-tolerant APIs where duplicate requests — for example due to networking issues on the client-side — do not erroneously cause the same action performed multiple times on the server-side. Refer to https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-02 for a better understanding. @@ -43,44 +44,15 @@ app.Use(idempotency.New(idempotency.Config{ ### Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: a function which skips the middleware on safe HTTP request method. - Next func(c *fiber.Ctx) bool - - // Lifetime is the maximum lifetime of an idempotency key. - // - // Optional. Default: 30 * time.Minute - Lifetime time.Duration - - // KeyHeader is the name of the header that contains the idempotency key. - // - // Optional. Default: X-Idempotency-Key - KeyHeader string - // KeyHeaderValidate defines a function to validate the syntax of the idempotency header. - // - // Optional. Default: a function which ensures the header is 36 characters long (the size of an UUID). - KeyHeaderValidate func(string) error - - // KeepResponseHeaders is a list of headers that should be kept from the original response. - // - // Optional. Default: nil (to keep all headers) - KeepResponseHeaders []string - - // Lock locks an idempotency key. - // - // Optional. Default: an in-memory locker for this process only. - Lock Locker - - // Storage stores response data by idempotency key. - // - // Optional. Default: an in-memory storage for this process only. - Storage fiber.Storage -} -``` +| Property | Type | Description | Default | +|:--------------------|:------------------------|:-----------------------------------------------------------------------------------------|:-------------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | A function for safe methods | +| Lifetime | `time.Duration` | Lifetime is the maximum lifetime of an idempotency key. | 30 * time.Minute | +| KeyHeader | `string` | KeyHeader is the name of the header that contains the idempotency key. | "X-Idempotency-Key" | +| KeyHeaderValidate | `func(string) error` | KeyHeaderValidate defines a function to validate the syntax of the idempotency header. | A function for UUID validation | +| KeepResponseHeaders | `[]string` | KeepResponseHeaders is a list of headers that should be kept from the original response. | nil (keep all headers) | +| Lock | `Locker` | Lock locks an idempotency key. | An in-memory locker | +| Storage | `fiber.Storage` | Storage stores response data by idempotency key. | An in-memory storage | ## Default Config diff --git a/docs/api/middleware/keyauth.md b/docs/api/middleware/keyauth.md index 786d16fd3d..d95f2998e4 100644 --- a/docs/api/middleware/keyauth.md +++ b/docs/api/middleware/keyauth.md @@ -1,8 +1,9 @@ --- id: keyauth -title: Keyauth --- +# Keyauth + Key auth middleware provides a key based authentication. ## Signatures @@ -212,45 +213,15 @@ curl --header "Authorization: Bearer my-super-secret-key" http://localhost:3000 ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip middleware. - // Optional. Default: nil - Next func(*fiber.Ctx) bool - - // SuccessHandler defines a function which is executed for a valid key. - // Optional. Default: nil - SuccessHandler fiber.Handler - - // ErrorHandler defines a function which is executed for an invalid key. - // It may be used to define a custom error. - // Optional. Default: 401 Invalid or expired key - ErrorHandler fiber.ErrorHandler - - // KeyLookup is a string in the form of ":" that is used - // to extract key from the request. - // Optional. Default value "header:Authorization". - // Possible values: - // - "header:" - // - "query:" - // - "form:" - // - "param:" - // - "cookie:" - KeyLookup string - - // AuthScheme to be used in the Authorization header. - // Optional. Default value "Bearer". - AuthScheme string - - // Validator is a function to validate key. - Validator func(*fiber.Ctx, string) (bool, error) - - // Context key to store the bearertoken from the token into context. - // Optional. Default: "token". - ContextKey string -} -``` +| Property | Type | Description | Default | +|:---------------|:-----------------------------------------|:-----------------------------------------------------------------------------------------------------|:------------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| SuccessHandler | `fiber.Handler` | SuccessHandler defines a function which is executed for a valid key. | `nil` | +| ErrorHandler | `fiber.ErrorHandler` | ErrorHandler defines a function which is executed for an invalid key. | `401 Invalid or expired key` | +| KeyLookup | `string` | KeyLookup is a string in the form of ":" that is used to extract key from the request. | "header:Authorization" | +| AuthScheme | `string` | AuthScheme to be used in the Authorization header. | "Bearer" | +| Validator | `func(*fiber.Ctx, string) (bool, error)` | Validator is a function to validate the key. | A function for key validation | +| ContextKey | `string` | Context key to store the bearer token from the token into context. | "token" | ## Default Config diff --git a/docs/api/middleware/limiter.md b/docs/api/middleware/limiter.md index 5a88d69f4a..8a48cbd14b 100644 --- a/docs/api/middleware/limiter.md +++ b/docs/api/middleware/limiter.md @@ -1,8 +1,9 @@ --- id: limiter -title: Limiter --- +# Limiter + Limiter middleware for [Fiber](https://github.com/gofiber/fiber) that is used to limit repeat requests to public APIs and/or endpoints such as password reset. It is also useful for API clients, web crawling, or other tasks that need to be throttled. :::note @@ -75,59 +76,20 @@ rate = weightOfPreviousWindpw + current window's amount request. ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Max number of recent connections during `Duration` seconds before sending a 429 response - // - // Default: 5 - Max int - - // KeyGenerator allows you to generate custom keys, by default c.IP() is used - // - // Default: func(c *fiber.Ctx) string { - // return c.IP() - // } - KeyGenerator func(*fiber.Ctx) string - - // Expiration is the time on how long to keep records of requests in memory - // - // Default: 1 * time.Minute - Expiration time.Duration - - // LimitReached is called when a request hits the limit - // - // Default: func(c *fiber.Ctx) error { - // return c.SendStatus(fiber.StatusTooManyRequests) - // } - LimitReached fiber.Handler - - // When set to true, requests with StatusCode >= 400 won't be counted. - // - // Default: false - SkipFailedRequests bool - - // When set to true, requests with StatusCode < 400 won't be counted. - // - // Default: false - SkipSuccessfulRequests bool - - // Store is used to store the state of the middleware - // - // Default: an in memory store for this process only - Storage fiber.Storage - - // LimiterMiddleware is the struct that implements limiter middleware. - // - // Default: a new Fixed Window Rate Limiter - LimiterMiddleware LimiterHandler -} -``` +| Property | Type | Description | Default | +|:-----------------------|:--------------------------|:--------------------------------------------------------------------------------------------|:-----------------------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Max | `int` | Max number of recent connections during `Expiration` seconds before sending a 429 response. | 5 | +| KeyGenerator | `func(*fiber.Ctx) string` | KeyGenerator allows you to generate custom keys, by default c.IP() is used. | A function using c.IP() as the default | +| Expiration | `time.Duration` | Expiration is the time on how long to keep records of requests in memory. | 1 * time.Minute | +| LimitReached | `fiber.Handler` | LimitReached is called when a request hits the limit. | A function sending 429 response | +| SkipFailedRequests | `bool` | When set to true, requests with StatusCode >= 400 won't be counted. | false | +| SkipSuccessfulRequests | `bool` | When set to true, requests with StatusCode < 400 won't be counted. | false | +| Storage | `fiber.Storage` | Store is used to store the state of the middleware. | An in-memory store for this process only | +| LimiterMiddleware | `LimiterHandler` | LimiterMiddleware is the struct that implements a limiter middleware. | A new Fixed Window Rate Limiter | +| Duration (Deprecated) | `time.Duration` | Deprecated: Use Expiration instead | - | +| Store (Deprecated) | `fiber.Storage` | Deprecated: Use Storage instead | - | +| Key (Deprecated) | `func(*fiber.Ctx) string` | Deprecated: Use KeyGenerator instead | - | :::note A custom store can be used if it implements the `Storage` interface - more details and an example can be found in `store.go`. @@ -160,4 +122,4 @@ storage := sqlite3.New() // From github.com/gofiber/storage/sqlite3 app.Use(limiter.New(limiter.Config{ Storage: storage, })) -``` \ No newline at end of file +``` diff --git a/docs/api/middleware/logger.md b/docs/api/middleware/logger.md index 7b00ff3589..a01e9bd54c 100644 --- a/docs/api/middleware/logger.md +++ b/docs/api/middleware/logger.md @@ -1,8 +1,9 @@ --- id: logger -title: Logger --- +# Logger + Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP request/response details. ## Signatures @@ -88,61 +89,24 @@ app.Use(logger.New(logger.Config{ ``` ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Done is a function that is called after the log string for a request is written to Output, - // and pass the log string as parameter. - // - // Optional. Default: nil - Done func(c *fiber.Ctx, logString []byte) - - // tagFunctions defines the custom tag action - // - // Optional. Default: map[string]LogFunc - CustomTags map[string]LogFunc - - // Format defines the logging tags - // - // Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n - Format string - - // TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html - // - // Optional. Default: 15:04:05 - TimeFormat string - - // TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc - // - // Optional. Default: "Local" - TimeZone string - - // TimeInterval is the delay before the timestamp is updated - // - // Optional. Default: 500 * time.Millisecond - TimeInterval time.Duration - - // Output is a writer where logs are written - // - // Default: os.Stdout - Output io.Writer - - // DisableColors defines if the logs output should be colorized - // - // Default: false - DisableColors bool - - enableColors bool - enableLatency bool - timeZoneLocation *time.Location -} -type LogFunc func(buf logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) -``` + +### Config + +| Property | Type | Description | Default | +|:-----------------|:---------------------------|:---------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Done | `func(*fiber.Ctx, []byte)` | Done is a function that is called after the log string for a request is written to Output, and pass the log string as parameter. | `nil` | +| CustomTags | `map[string]LogFunc` | tagFunctions defines the custom tag action. | `map[string]LogFunc` | +| Format | `string` | Format defines the logging tags. | `[${time}] ${status} - ${latency} ${method} ${path}\n` | +| TimeFormat | `string` | TimeFormat defines the time format for log timestamps. | `15:04:05` | +| TimeZone | `string` | TimeZone can be specified, such as "UTC" and "America/New_York" and "Asia/Chongqing", etc | `"Local"` | +| TimeInterval | `time.Duration` | TimeInterval is the delay before the timestamp is updated. | `500 * time.Millisecond` | +| Output | `io.Writer` | Output is a writer where logs are written. | `os.Stdout` | +| DisableColors | `bool` | DisableColors defines if the logs output should be colorized. | `false` | +| enableColors | `bool` | Internal field for enabling colors in the log output. (This is not a user-configurable field) | - | +| enableLatency | `bool` | Internal field for enabling latency measurement in logs. (This is not a user-configurable field) | - | +| timeZoneLocation | `*time.Location` | Internal field for the time zone location. (This is not a user-configurable field) | - | + ## Default Config ```go var ConfigDefault = Config{ diff --git a/docs/api/middleware/monitor.md b/docs/api/middleware/monitor.md index 0c13509a29..cbac367ce4 100644 --- a/docs/api/middleware/monitor.md +++ b/docs/api/middleware/monitor.md @@ -1,8 +1,9 @@ --- id: monitor -title: Monitor --- +# Monitor + Monitor middleware for [Fiber](https://github.com/gofiber/fiber) that reports server metrics, inspired by [express-status-monitor](https://github.com/RafalWilinski/express-status-monitor) :::caution @@ -48,47 +49,15 @@ You can also access the API endpoint with ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Metrics page title - // - // Optional. Default: "Fiber Monitor" - Title string - - // Refresh period - // - // Optional. Default: 3 seconds - Refresh time.Duration - - // Whether the service should expose only the monitoring API. - // - // Optional. Default: false - APIOnly bool - - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Custom HTML Code to Head Section(Before End) - // - // Optional. Default: empty - CustomHead string - - // FontURL for specify font resource path or URL . also you can use relative path - // - // Optional. Default: https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap - FontURL string - - // ChartJsURL for specify ChartJS library path or URL . also you can use relative path - // - // Optional. Default: https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js - ChartJsURL string - - index string -} -``` +| Property | Type | Description | Default | +|:-----------|:------------------------|:--------------------------------------------------------------------|:----------------------------------------------------------------------------| +| Title | `string` | Metrics page title | "Fiber Monitor" | +| Refresh | `time.Duration` | Refresh period | 3 seconds | +| APIOnly | `bool` | Whether the service should expose only the monitoring API | false | +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| CustomHead | `string` | Custom HTML Code to Head Section(Before End) | empty | +| FontURL | `string` | FontURL for specify font resource path or URL | "https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap" | +| ChartJsURL | `string` | ChartJsURL for specify ChartJS library path or URL | "https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js" | ## Default Config diff --git a/docs/api/middleware/pprof.md b/docs/api/middleware/pprof.md index e716a4f78d..c4808f2c1d 100644 --- a/docs/api/middleware/pprof.md +++ b/docs/api/middleware/pprof.md @@ -1,8 +1,9 @@ --- id: pprof -title: Pprof --- +# Pprof + Pprof middleware for [Fiber](https://github.com/gofiber/fiber) that serves via its HTTP server runtime profiling data in the format expected by the pprof visualization tool. The package is typically only imported for the side effect of registering its HTTP handlers. The handled paths all begin with /debug/pprof/. ## Signatures @@ -38,22 +39,10 @@ app.Use(pprof.New(pprof.Config{Prefix: "/endpoint-prefix"})) ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Prefix defines a URL prefix added before "/debug/pprof". - // Note that it should start with (but not end with) a slash. - // Example: "/federated-fiber" - // - // Optional. Default: "" - Prefix string -} -``` +| Property | Type | Description | Default | +|:---------|:------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------|:--------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Prefix | `string` | Prefix defines a URL prefix added before "/debug/pprof". Note that it should start with (but not end with) a slash. Example: "/federated-fiber" | "" | ## Default Config diff --git a/docs/api/middleware/proxy.md b/docs/api/middleware/proxy.md index 4395a2554b..fd0db609d9 100644 --- a/docs/api/middleware/proxy.md +++ b/docs/api/middleware/proxy.md @@ -1,8 +1,9 @@ --- id: proxy -title: Proxy --- +# Proxy + Proxy middleware for [Fiber](https://github.com/gofiber/fiber) that allows you to proxy requests to multiple servers. ## Signatures @@ -140,55 +141,17 @@ app.Use(proxy.BalancerForward([]string{ ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Servers defines a list of :// HTTP servers, - // - // which are used in a round-robin manner. - // i.e.: "https://foobar.com, http://www.foobar.com" - // - // Required - Servers []string - - // ModifyRequest allows you to alter the request - // - // Optional. Default: nil - ModifyRequest fiber.Handler - - // ModifyResponse allows you to alter the response - // - // Optional. Default: nil - ModifyResponse fiber.Handler - - // Timeout is the request timeout used when calling the proxy client - // - // Optional. Default: 1 second - Timeout time.Duration - - // Per-connection buffer size for requests' reading. - // This also limits the maximum header size. - // Increase this buffer if your clients send multi-KB RequestURIs - // and/or multi-KB headers (for example, BIG cookies). - ReadBufferSize int - - // Per-connection buffer size for responses' writing. - WriteBufferSize int - - // tls config for the http client. - TlsConfig *tls.Config - - // Client is custom client when client config is complex. - // Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig - // will not be used if the client are set. - Client *fasthttp.LBClient -} -``` +| Property | Type | Description | Default | +|:----------------|:-----------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Servers | `[]string` | Servers defines a list of :// HTTP servers, which are used in a round-robin manner. i.e.: "https://foobar.com, http://www.foobar.com" | (Required) | +| ModifyRequest | `fiber.Handler` | ModifyRequest allows you to alter the request. | `nil` | +| ModifyResponse | `fiber.Handler` | ModifyResponse allows you to alter the response. | `nil` | +| Timeout | `time.Duration` | Timeout is the request timeout used when calling the proxy client. | 1 second | +| ReadBufferSize | `int` | Per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers (for example, BIG cookies). | (Not specified) | +| WriteBufferSize | `int` | Per-connection buffer size for responses' writing. | (Not specified) | +| TlsConfig | `*tls.Config` (or `*fasthttp.TLSConfig` in v3) | TLS config for the HTTP client. | `nil` | +| Client | `*fasthttp.LBClient` | Client is a custom client when client config is complex. | `nil` | ## Default Config diff --git a/docs/api/middleware/recover.md b/docs/api/middleware/recover.md index 9388ff2156..81f67fddbc 100644 --- a/docs/api/middleware/recover.md +++ b/docs/api/middleware/recover.md @@ -1,8 +1,9 @@ --- id: recover -title: Recover --- +# Recover + Recover middleware for [Fiber](https://github.com/gofiber/fiber) that recovers from panics anywhere in the stack chain and handles the control to the centralized [ErrorHandler](https://docs.gofiber.io/guide/error-handling). ## Signatures @@ -36,25 +37,11 @@ app.Get("/", func(c *fiber.Ctx) error { ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // EnableStackTrace enables handling stack trace - // - // Optional. Default: false - EnableStackTrace bool - - // StackTraceHandler defines a function to handle stack trace - // - // Optional. Default: defaultStackTraceHandler - StackTraceHandler func(c *fiber.Ctx, e interface{}) -} -``` +| Property | Type | Description | Default | +|:------------------|:--------------------------------|:--------------------------------------------------------------------|:-------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| EnableStackTrace | `bool` | EnableStackTrace enables handling stack trace. | `false` | +| StackTraceHandler | `func(*fiber.Ctx, interface{})` | StackTraceHandler defines a function to handle stack trace. | defaultStackTraceHandler | ## Default Config diff --git a/docs/api/middleware/redirect.md b/docs/api/middleware/redirect.md index ebb47c844b..762aa0b5d3 100644 --- a/docs/api/middleware/redirect.md +++ b/docs/api/middleware/redirect.md @@ -1,8 +1,9 @@ --- id: redirect -title: Redirect --- +# Redirect + Redirection middleware for Fiber. ## Signatures @@ -52,30 +53,11 @@ curl http://localhost:3000/old/hello ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Filter defines a function to skip middleware. - // Optional. Default: nil - Next func(*fiber.Ctx) bool - - // Rules defines the URL path rewrite rules. The values captured in asterisk can be - // retrieved by index e.g. $1, $2 and so on. - // Required. Example: - // "/old": "/new", - // "/api/*": "/$1", - // "/js/*": "/public/javascripts/$1", - // "/users/*/orders/*": "/user/$1/order/$2", - Rules map[string]string - - // The status code when redirecting - // This is ignored if Redirect is disabled - // Optional. Default: 302 (fiber.StatusFound) - StatusCode int - - rulesRegex map[*regexp.Regexp]string -} -``` +| Property | Type | Description | Default | +|:-----------|:------------------------|:---------------------------------------------------------------------------------------------------------------------------|:-----------------------| +| Next | `func(*fiber.Ctx) bool` | Filter defines a function to skip middleware. | `nil` | +| Rules | `map[string]string` | Rules defines the URL path rewrite rules. The values captured in asterisk can be retrieved by index e.g. $1, $2 and so on. | Required | +| StatusCode | `int` | The status code when redirecting. This is ignored if Redirect is disabled. | 302 Temporary Redirect | ## Default Config diff --git a/docs/api/middleware/requestid.md b/docs/api/middleware/requestid.md index aafbd1799e..200ebf4bdd 100644 --- a/docs/api/middleware/requestid.md +++ b/docs/api/middleware/requestid.md @@ -1,8 +1,9 @@ --- id: requestid -title: RequestID --- +# RequestID + RequestID middleware for [Fiber](https://github.com/gofiber/fiber) that adds an indentifier to the response. ## Signatures @@ -39,31 +40,12 @@ app.Use(requestid.New(requestid.Config{ ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Next defines a function to skip this middleware when returned true. - // - // Optional. Default: nil - Next func(c *fiber.Ctx) bool - - // Header is the header key where to get/set the unique request ID - // - // Optional. Default: "X-Request-ID" - Header string - - // Generator defines a function to generate the unique identifier. - // - // Optional. Default: utils.UUID - Generator func() string - - // ContextKey defines the key used when storing the request ID in - // the locals for a specific request. - // - // Optional. Default: requestid - ContextKey interface{} -} -``` +| Property | Type | Description | Default | +|:-----------|:------------------------|:--------------------------------------------------------------------------------------------------|:---------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Header | `string` | Header is the header key where to get/set the unique request ID. | "X-Request-ID" | +| Generator | `func() string` | Generator defines a function to generate the unique identifier. | utils.UUID | +| ContextKey | `interface{}` | ContextKey defines the key used when storing the request ID in the locals for a specific request. | "requestid" | ## Default Config The default config uses a fast UUID generator which will expose the number of diff --git a/docs/api/middleware/rewrite.md b/docs/api/middleware/rewrite.md index 7111cbdde5..fd59595841 100644 --- a/docs/api/middleware/rewrite.md +++ b/docs/api/middleware/rewrite.md @@ -1,10 +1,10 @@ --- id: rewrite -title: Rewrite --- -Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. +# Rewrite +Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. ## Signatures @@ -12,6 +12,13 @@ Rewrite middleware rewrites the URL path based on provided rules. It can be help func New(config ...Config) fiber.Handler ``` +## Config + +| Property | Type | Description | Default | +|:---------|:------------------------|:-----------------------------------------------------------------------------------------------------|:-----------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip middleware. | `nil` | +| Rules | `map[string]string` | Rules defines the URL path rewrite rules. The values captured in asterisk can be retrieved by index. | (Required) | + ### Examples ```go package main diff --git a/docs/api/middleware/session.md b/docs/api/middleware/session.md index f27eaf42bc..22c93feb3b 100644 --- a/docs/api/middleware/session.md +++ b/docs/api/middleware/session.md @@ -1,8 +1,9 @@ --- id: session -title: Session --- +# Session + Session middleware for [Fiber](https://github.com/gofiber/fiber). :::note @@ -86,62 +87,19 @@ app.Get("/", func(c *fiber.Ctx) error { ## Config -```go -// Config defines the config for middleware. -type Config struct { - // Allowed session duration - // Optional. Default value 24 * time.Hour - Expiration time.Duration - - // Storage interface to store the session data - // Optional. Default value memory.New() - Storage fiber.Storage - - // KeyLookup is a string in the form of ":" that is used - // to extract session id from the request. - // Possible values: "header:", "query:" or "cookie:" - // Optional. Default value "cookie:session_id". - KeyLookup string - - // Domain of the CSRF cookie. - // Optional. Default value "". - CookieDomain string - - // Path of the CSRF cookie. - // Optional. Default value "". - CookiePath string - - // Indicates if CSRF cookie is secure. - // Optional. Default value false. - CookieSecure bool - - // Indicates if CSRF cookie is HTTP only. - // Optional. Default value false. - CookieHTTPOnly bool - - // Value of SameSite cookie. - // Optional. Default value "Lax". - CookieSameSite string - - // Decides whether cookie should last for only the browser sesison. - // Ignores Expiration if set to true - // Optional. Default value false. - CookieSessionOnly bool - - // KeyGenerator generates the session key. - // Optional. Default value utils.UUIDv4 - KeyGenerator func() string - - // Deprecated: Please use KeyLookup - CookieName string - - // Source defines where to obtain the session id - source Source - - // The session name - sessionName string -} -``` +| Property | Type | Description | Default | +|:------------------------|:----------------|:------------------------------------------------------------------------------------------------------------|:----------------------| +| Expiration | `time.Duration` | Allowed session duration. | `24 * time.Hour` | +| Storage | `fiber.Storage` | Storage interface to store the session data. | `memory.New()` | +| KeyLookup | `string` | KeyLookup is a string in the form of ":" that is used to extract session id from the request. | `"cookie:session_id"` | +| CookieDomain | `string` | Domain of the cookie. | `""` | +| CookiePath | `string` | Path of the cookie. | `""` | +| CookieSecure | `bool` | Indicates if cookie is secure. | `false` | +| CookieHTTPOnly | `bool` | Indicates if cookie is HTTP only. | `false` | +| CookieSameSite | `string` | Value of SameSite cookie. | `"Lax"` | +| CookieSessionOnly | `bool` | Decides whether cookie should last for only the browser session. Ignores Expiration if set to true. | `false` | +| KeyGenerator | `func() string` | KeyGenerator generates the session key. | `utils.UUIDv4` | +| CookieName (Deprecated) | `string` | Deprecated: Please use KeyLookup. The session name. | `""` | ## Default Config @@ -176,4 +134,4 @@ store := session.New(session.Config{ }) ``` -To use the store, see the [Examples](#examples). \ No newline at end of file +To use the store, see the [Examples](#examples). diff --git a/docs/api/middleware/skip.md b/docs/api/middleware/skip.md index 820c7b2c38..0923bd0ee2 100644 --- a/docs/api/middleware/skip.md +++ b/docs/api/middleware/skip.md @@ -1,8 +1,9 @@ --- id: skip -title: Skip --- +# Skip + Skip middleware for [Fiber](https://github.com/gofiber/fiber) that skips a wrapped handler if a predicate is true. ## Signatures @@ -43,4 +44,4 @@ func BasicHandler(ctx *fiber.Ctx) error { :::tip app.Use will handle requests from any route, and any method. In the example above, it will only skip if the method is GET. -::: \ No newline at end of file +::: diff --git a/docs/api/middleware/timeout.md b/docs/api/middleware/timeout.md index 36db36f3bb..5fdf18d527 100644 --- a/docs/api/middleware/timeout.md +++ b/docs/api/middleware/timeout.md @@ -1,8 +1,9 @@ --- id: timeout -title: Timeout --- +# Timeout + There exist two distinct implementations of timeout middleware [Fiber](https://github.com/gofiber/fiber). **New** diff --git a/middleware/session/config.go b/middleware/session/config.go index 62a80279ff..fe74132f5c 100644 --- a/middleware/session/config.go +++ b/middleware/session/config.go @@ -26,19 +26,19 @@ type Config struct { // Optional. Default value "cookie:session_id". KeyLookup string - // Domain of the CSRF cookie. + // Domain of the cookie. // Optional. Default value "". CookieDomain string - // Path of the CSRF cookie. + // Path of the cookie. // Optional. Default value "". CookiePath string - // Indicates if CSRF cookie is secure. + // Indicates if cookie is secure. // Optional. Default value false. CookieSecure bool - // Indicates if CSRF cookie is HTTP only. + // Indicates if cookie is HTTP only. // Optional. Default value false. CookieHTTPOnly bool From 443804e95da84c526e55bafac24801be6b8975a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Mon, 24 Jul 2023 17:03:11 +0200 Subject: [PATCH 160/162] =?UTF-8?q?improved=20the=20config=20section=20of?= =?UTF-8?q?=20the=20middleware=20readme=C2=B4s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/api/middleware/csrf.md | 40 +++++++++++++++++----------------- docs/api/middleware/keyauth.md | 2 +- docs/api/middleware/proxy.md | 2 +- docs/api/middleware/session.md | 2 +- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/api/middleware/csrf.md b/docs/api/middleware/csrf.md index e588b51762..dbbf6007d9 100644 --- a/docs/api/middleware/csrf.md +++ b/docs/api/middleware/csrf.md @@ -56,26 +56,26 @@ KeyLookup will be ignored if Extractor is explicitly set. ### Config -| Property | Type | Description | Default | -|:------------------|:-----------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | -| KeyLookup | `string` | KeyLookup is a string in the form of ":" that is used to create an Extractor that extracts the token from the request. Possible values: "header:", "query:", "param:", "form:", "cookie:". Ignored if an Extractor is explicitly set. | "header:X-CSRF-Token" | -| CookieName | `string` | Name of the session cookie. This cookie will store the session key. | "csrf_" | -| CookieDomain | `string` | Domain of the CSRF cookie. | "" | -| CookiePath | `string` | Path of the CSRF cookie. | "" | -| CookieSecure | `bool` | Indicates if the CSRF cookie is secure. | false | -| CookieHTTPOnly | `bool` | Indicates if the CSRF cookie is HTTP-only. | false | -| CookieSameSite | `string` | Value of SameSite cookie. | "Lax" | -| CookieSessionOnly | `bool` | Decides whether the cookie should last for only the browser session. Ignores Expiration if set to true. | false | -| Expiration | `time.Duration` | Expiration is the duration before the CSRF token will expire. | 1 * time.Hour | -| Storage | `fiber.Storage` | Store is used to store the state of the middleware. | memory.New() | -| ContextKey | `string` | Context key to store the generated CSRF token into the context. If left empty, the token will not be stored in the context. | "" | -| KeyGenerator | `func() string` | KeyGenerator creates a new CSRF token. | utils.UUID | -| CookieExpires | `time.Duration` (Deprecated) | Deprecated: Please use Expiration. | 0 | -| Cookie | `*fiber.Cookie` (Deprecated) | Deprecated: Please use Cookie* related fields. | nil | -| TokenLookup | `string` (Deprecated) | Deprecated: Please use KeyLookup. | "" | -| ErrorHandler | `fiber.ErrorHandler` | ErrorHandler is executed when an error is returned from fiber.Handler. | DefaultErrorHandler | -| Extractor | `func(*fiber.Ctx) (string, error)` | Extractor returns the CSRF token. If set, this will be used in place of an Extractor based on KeyLookup. | Extractor based on KeyLookup | +| Property | Type | Description | Default | +|:------------------|:-----------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| KeyLookup | `string` | KeyLookup is a string in the form of "`:`" that is used to create an Extractor that extracts the token from the request. Possible values: "`header:`", "`query:`", "`param:`", "`form:`", "`cookie:`". Ignored if an Extractor is explicitly set. | "header:X-CSRF-Token" | +| CookieName | `string` | Name of the session cookie. This cookie will store the session key. | "csrf_" | +| CookieDomain | `string` | Domain of the CSRF cookie. | "" | +| CookiePath | `string` | Path of the CSRF cookie. | "" | +| CookieSecure | `bool` | Indicates if the CSRF cookie is secure. | false | +| CookieHTTPOnly | `bool` | Indicates if the CSRF cookie is HTTP-only. | false | +| CookieSameSite | `string` | Value of SameSite cookie. | "Lax" | +| CookieSessionOnly | `bool` | Decides whether the cookie should last for only the browser session. Ignores Expiration if set to true. | false | +| Expiration | `time.Duration` | Expiration is the duration before the CSRF token will expire. | 1 * time.Hour | +| Storage | `fiber.Storage` | Store is used to store the state of the middleware. | memory.New() | +| ContextKey | `string` | Context key to store the generated CSRF token into the context. If left empty, the token will not be stored in the context. | "" | +| KeyGenerator | `func() string` | KeyGenerator creates a new CSRF token. | utils.UUID | +| CookieExpires | `time.Duration` (Deprecated) | Deprecated: Please use Expiration. | 0 | +| Cookie | `*fiber.Cookie` (Deprecated) | Deprecated: Please use Cookie* related fields. | nil | +| TokenLookup | `string` (Deprecated) | Deprecated: Please use KeyLookup. | "" | +| ErrorHandler | `fiber.ErrorHandler` | ErrorHandler is executed when an error is returned from fiber.Handler. | DefaultErrorHandler | +| Extractor | `func(*fiber.Ctx) (string, error)` | Extractor returns the CSRF token. If set, this will be used in place of an Extractor based on KeyLookup. | Extractor based on KeyLookup | ## Default Config diff --git a/docs/api/middleware/keyauth.md b/docs/api/middleware/keyauth.md index d95f2998e4..ecabe122e7 100644 --- a/docs/api/middleware/keyauth.md +++ b/docs/api/middleware/keyauth.md @@ -218,7 +218,7 @@ curl --header "Authorization: Bearer my-super-secret-key" http://localhost:3000 | Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | SuccessHandler | `fiber.Handler` | SuccessHandler defines a function which is executed for a valid key. | `nil` | | ErrorHandler | `fiber.ErrorHandler` | ErrorHandler defines a function which is executed for an invalid key. | `401 Invalid or expired key` | -| KeyLookup | `string` | KeyLookup is a string in the form of ":" that is used to extract key from the request. | "header:Authorization" | +| KeyLookup | `string` | KeyLookup is a string in the form of "`:`" that is used to extract key from the request. | "header:Authorization" | | AuthScheme | `string` | AuthScheme to be used in the Authorization header. | "Bearer" | | Validator | `func(*fiber.Ctx, string) (bool, error)` | Validator is a function to validate the key. | A function for key validation | | ContextKey | `string` | Context key to store the bearer token from the token into context. | "token" | diff --git a/docs/api/middleware/proxy.md b/docs/api/middleware/proxy.md index fd0db609d9..e36654fe48 100644 --- a/docs/api/middleware/proxy.md +++ b/docs/api/middleware/proxy.md @@ -144,7 +144,7 @@ app.Use(proxy.BalancerForward([]string{ | Property | Type | Description | Default | |:----------------|:-----------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------| | Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | -| Servers | `[]string` | Servers defines a list of :// HTTP servers, which are used in a round-robin manner. i.e.: "https://foobar.com, http://www.foobar.com" | (Required) | +| Servers | `[]string` | Servers defines a list of `://` HTTP servers, which are used in a round-robin manner. i.e.: "https://foobar.com, http://www.foobar.com" | (Required) | | ModifyRequest | `fiber.Handler` | ModifyRequest allows you to alter the request. | `nil` | | ModifyResponse | `fiber.Handler` | ModifyResponse allows you to alter the response. | `nil` | | Timeout | `time.Duration` | Timeout is the request timeout used when calling the proxy client. | 1 second | diff --git a/docs/api/middleware/session.md b/docs/api/middleware/session.md index 22c93feb3b..65d23681e7 100644 --- a/docs/api/middleware/session.md +++ b/docs/api/middleware/session.md @@ -91,7 +91,7 @@ app.Get("/", func(c *fiber.Ctx) error { |:------------------------|:----------------|:------------------------------------------------------------------------------------------------------------|:----------------------| | Expiration | `time.Duration` | Allowed session duration. | `24 * time.Hour` | | Storage | `fiber.Storage` | Storage interface to store the session data. | `memory.New()` | -| KeyLookup | `string` | KeyLookup is a string in the form of ":" that is used to extract session id from the request. | `"cookie:session_id"` | +| KeyLookup | `string` | KeyLookup is a string in the form of "`:`" that is used to extract session id from the request. | `"cookie:session_id"` | | CookieDomain | `string` | Domain of the cookie. | `""` | | CookiePath | `string` | Path of the cookie. | `""` | | CookieSecure | `bool` | Indicates if cookie is secure. | `false` | From 3e5743bcac4b32a3f0410724907a9052b3816c4c Mon Sep 17 00:00:00 2001 From: Renan Bastos Date: Tue, 25 Jul 2023 15:15:23 -0300 Subject: [PATCH 161/162] doc: Improve *fiber.Client section (#2553) * wip * doc: Improve *fiber.Client section Enhanced the documentation for the *fiber.Client section, providing a clear and concise example to help users better understand its usage. * chore: refactor message about example --- docs/api/client.md | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/docs/api/client.md b/docs/api/client.md index 0e17dbbd79..c3ed4b11a6 100644 --- a/docs/api/client.md +++ b/docs/api/client.md @@ -19,8 +19,48 @@ func (c *Client) Patch(url string) *Agent func (c *Client) Delete(url string) *Agent ``` -## ✨ Agent +Here we present a brief example demonstrating the simulation of a proxy using our `*fiber.Agent` methods. +```go +// Get something +func getSomething(c *fiber.Ctx) (err error) { + agent := fiber.Get("") + statusCode, body, errs := agent.Bytes() + if len(errs) > 0 { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "errs": errs, + }) + } + + var something fiber.Map + err = json.Unmarshal(body, &something) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "err": err, + }) + } + + return c.Status(statusCode).JSON(something) +} + +// Post something +func createSomething(c *fiber.Ctx) (err error) { + agent := fiber.Post("") + agent.Body(c.Body()) // set body received by request + statusCode, body, errs := agent.Bytes() + if len(errs) > 0 { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "errs": errs, + }) + } + + // pass status code and body received by the proxy + return c.Status(statusCode).Send(body) +} +``` +Based on this short example, we can perceive that using the `*fiber.Client` is very straightforward and intuitive. + +## ✨ Agent `Agent` is built on top of FastHTTP's [`HostClient`](https://github.com/valyala/fasthttp/blob/master/client.go#L603) which has lots of convenient helper methods such as dedicated methods for request methods. ### Parse From e91b02b34537d02f71f5e398ba6102699959aaea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Wed, 26 Jul 2023 14:27:45 +0300 Subject: [PATCH 162/162] :memo: docs: fix wrong JSON docs (#2554) --- docs/api/ctx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/ctx.md b/docs/api/ctx.md index 47966a56cc..aa48c60929 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -709,7 +709,7 @@ app.Get("/", func(c *fiber.Ctx) error { ## JSON -Converts any **interface** or **string** to JSON using the [goccy/go-json](https://github.com/goccy/go-json) package. +Converts any **interface** or **string** to JSON using the [encoding/json](https://pkg.go.dev/encoding/json) package. :::info JSON also sets the content header to **application/json**.