From 3629947c3da6c595faa314cd801eebdb5105c7fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Nov 2022 19:48:07 +0800 Subject: [PATCH 001/212] Bump github.com/valyala/fasthttp from 1.42.0 to 1.43.0 (#2245) Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.42.0 to 1.43.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.42.0...v1.43.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] 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 634a4674d6..3ef179744c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 github.com/valyala/bytebufferpool v1.0.0 - github.com/valyala/fasthttp v1.42.0 + github.com/valyala/fasthttp v1.43.0 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab ) diff --git a/go.sum b/go.sum index 546eef5258..29fb104d02 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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.42.0 h1:LBMyqvJR8DEBgN79oI8dGbkuj5Lm9jbHESxH131TTN8= -github.com/valyala/fasthttp v1.42.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= +github.com/valyala/fasthttp v1.43.0 h1:Gy4sb32C98fbzVWZlTM1oTMdLWGyvxR03VhM6cBIU4g= +github.com/valyala/fasthttp v1.43.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= From 6d798dbda94c72205881d3354337d18f7247de21 Mon Sep 17 00:00:00 2001 From: Xaver Fischer <33641215+Simerax@users.noreply.github.com> Date: Thu, 1 Dec 2022 08:53:40 +0100 Subject: [PATCH 002/212] :memo: middleware/filesystem does not handle url encoded values on it's own (#2247) * :memo: middleware/filesystem does not handle url encoded values * Update README.md add newline after first warning Co-authored-by: Xaver Fischer --- middleware/filesystem/README.md | 3 ++- middleware/filesystem/filesystem.go | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/middleware/filesystem/README.md b/middleware/filesystem/README.md index e976abf4a7..87caf1c841 100644 --- a/middleware/filesystem/README.md +++ b/middleware/filesystem/README.md @@ -2,7 +2,8 @@ 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!** +⚠️ **`: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 diff --git a/middleware/filesystem/filesystem.go b/middleware/filesystem/filesystem.go index aca5a1bf55..890fdf9824 100644 --- a/middleware/filesystem/filesystem.go +++ b/middleware/filesystem/filesystem.go @@ -64,7 +64,11 @@ var ConfigDefault = Config{ MaxAge: 0, } -// New creates a new middleware handler +// New creates a new middleware handler. +// +// filesystem does not handle url encoded values (for example spaces) +// on it's own. If you need that functionality, set "UnescapePath" +// in fiber.Config func New(config ...Config) fiber.Handler { // Set default config cfg := ConfigDefault From 17dfcc756ba4bb8b492f4916a7da2a666dc97f5a Mon Sep 17 00:00:00 2001 From: leonklingele Date: Thu, 1 Dec 2022 09:11:14 +0100 Subject: [PATCH 003/212] middleware/requestid: mention that the default UUID generator exposes the number of requests made to the server (#2241) --- middleware/requestid/README.md | 10 ++++++---- middleware/requestid/config.go | 3 +++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/middleware/requestid/README.md b/middleware/requestid/README.md index 91019d95a3..ffba06629b 100644 --- a/middleware/requestid/README.md +++ b/middleware/requestid/README.md @@ -64,13 +64,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/middleware/requestid/config.go b/middleware/requestid/config.go index ace51ad4a7..b3b605e590 100644 --- a/middleware/requestid/config.go +++ b/middleware/requestid/config.go @@ -30,6 +30,9 @@ type Config struct { } // ConfigDefault is the default config +// It 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. var ConfigDefault = Config{ Next: nil, Header: fiber.HeaderXRequestID, From f3679167ff296893dd5bcb962b83db135a9311e6 Mon Sep 17 00:00:00 2001 From: Clark Winters <40615752+cwinters8@users.noreply.github.com> Date: Thu, 1 Dec 2022 02:18:23 -0600 Subject: [PATCH 004/212] =?UTF-8?q?=F0=9F=A9=B9=20Fix:=20Unintended=20over?= =?UTF-8?q?written=20bind=20variables=20(#2240)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * bindMap key value should only be set when not already present * add comment Co-authored-by: Clark Winters --- ctx.go | 5 ++++- ctx_test.go | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/ctx.go b/ctx.go index e07ce30da6..92d7fad985 100644 --- a/ctx.go +++ b/ctx.go @@ -1437,7 +1437,10 @@ func (c *Ctx) renderExtensions(bind interface{}) { // Bind view map if c.viewBindMap != nil { for _, v := range c.viewBindMap.D { - bindMap[v.Key] = v.Value + // make sure key does not exist already + if _, ok := bindMap[v.Key]; !ok { + bindMap[v.Key] = v.Value + } } } diff --git a/ctx_test.go b/ctx_test.go index 0d20898065..c2b2c2d9e6 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2851,6 +2851,29 @@ func Test_Ctx_RenderWithBind(t *testing.T) { } +func Test_Ctx_RenderWithOverwrittenBind(t *testing.T) { + t.Parallel() + + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + + err := c.Bind(Map{ + "Title": "Hello, World!", + }) + utils.AssertEqual(t, nil, err) + defer app.ReleaseCtx(c) + err = c.Render("./.github/testdata/index.tmpl", Map{ + "Title": "Hello from Fiber!", + }) + + buf := bytebufferpool.Get() + _, _ = buf.WriteString("overwrite") + defer bytebufferpool.Put(buf) + + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "

Hello from Fiber!

", string(c.Response().Body())) +} + func Test_Ctx_RenderWithBindLocals(t *testing.T) { t.Parallel() From 075dfc5a8ac932cb202823afed5ac414cee8ebb2 Mon Sep 17 00:00:00 2001 From: kinggo Date: Thu, 1 Dec 2022 16:19:26 +0800 Subject: [PATCH 005/212] optimize: set byteSent log to 0 when use SetBodyStreamWriter (#2239) --- middleware/logger/logger_test.go | 41 ++++++++++++++++++++++++++++++++ middleware/logger/tags.go | 3 +++ 2 files changed, 44 insertions(+) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 3ca82c9fa7..ffb9341c52 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -1,6 +1,7 @@ package logger import ( + "bufio" "bytes" "errors" "fmt" @@ -10,6 +11,7 @@ import ( "os" "sync" "testing" + "time" "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" @@ -441,3 +443,42 @@ func Test_CustomTags(t *testing.T) { utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, customTag, buf.String()) } + +// go test -run Test_Logger_ByteSent_Streaming +func Test_Logger_ByteSent_Streaming(t *testing.T) { + app := fiber.New() + + buf := bytebufferpool.Get() + defer bytebufferpool.Put(buf) + + app.Use(New(Config{ + Format: "${bytesReceived} ${bytesSent} ${status}", + Output: buf, + })) + + app.Get("/", func(c *fiber.Ctx) error { + c.Set("Connection", "keep-alive") + c.Set("Transfer-Encoding", "chunked") + c.Context().SetBodyStreamWriter(func(w *bufio.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) + err := w.Flush() + if err != nil { + break + } + if i == 10 { + break + } + } + }) + return nil + }) + + resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + utils.AssertEqual(t, "0 0 200", buf.String()) +} diff --git a/middleware/logger/tags.go b/middleware/logger/tags.go index 854f22e24a..5a02d7f20f 100644 --- a/middleware/logger/tags.go +++ b/middleware/logger/tags.go @@ -89,6 +89,9 @@ func createTagMap(cfg *Config) map[string]LogFunc { return appendInt(output, len(c.Request().Body())) }, TagBytesSent: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + if c.Response().Header.ContentLength() < 0 { + return appendInt(output, 0) + } return appendInt(output, len(c.Response().Body())) }, TagRoute: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { From 6b9601fb9908c56ecff49fae90f6d3f366e86e8b Mon Sep 17 00:00:00 2001 From: V1def <68892674+v1def@users.noreply.github.com> Date: Thu, 1 Dec 2022 09:21:44 +0100 Subject: [PATCH 006/212] Added Ukrainian README translation (#2249) Added ukrainian localization --- .github/README.md | 3 + .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 | 708 ++++++++++++++++++++++++++++++++++++++++ .github/README_zh-CN.md | 3 + .github/README_zh-TW.md | 3 + 19 files changed, 762 insertions(+) create mode 100644 .github/README_uk.md diff --git a/.github/README.md b/.github/README.md index acced73074..35a871eeaf 100644 --- a/.github/README.md +++ b/.github/README.md @@ -58,6 +58,9 @@ + + +
diff --git a/.github/README_ckb.md b/.github/README_ckb.md index 9c5841340a..2beaa178a0 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_de.md b/.github/README_de.md index c3c33942bd..3774dac2fe 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_es.md b/.github/README_es.md index 918207b59f..6edb581c7a 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_fa.md b/.github/README_fa.md index 5cbaba189f..6b150be111 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_fr.md b/.github/README_fr.md index 4ecf6da868..b21028158b 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_he.md b/.github/README_he.md index 5457e55eb3..6387f84c1a 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_id.md b/.github/README_id.md index 98f5fdf409..9658057dca 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_it.md b/.github/README_it.md index 5f8b06c0b0..ac30bef308 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_ja.md b/.github/README_ja.md index 4264f83736..6b432075dd 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_ko.md b/.github/README_ko.md index ca2008d55d..01a8d32412 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_nl.md b/.github/README_nl.md index 85a7888f11..a7bb91f549 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_pt.md b/.github/README_pt.md index 4568b145d3..cd091ee4b0 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_ru.md b/.github/README_ru.md index a87dcb94a4..7618139602 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_sa.md b/.github/README_sa.md index 740c754e20..80069c6c4f 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_tr.md b/.github/README_tr.md index 4e4864c786..cd63f018df 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -52,6 +52,9 @@ + + +
diff --git a/.github/README_uk.md b/.github/README_uk.md new file mode 100644 index 0000000000..9970367b02 --- /dev/null +++ b/.github/README_uk.md @@ -0,0 +1,708 @@ +

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

+ +

+ Fiber — це веб фреймворк, який був натхненний Express + і заснований на Fasthttp, найшвидшому HTTP-двигунові написаному на + Go. Фреймворк розроблено з метою спростити процес швидкої розробки + високопродуктивних веб-додатків з нульовим розподілом пам'яті. +

+ +## ⚡️ Швидкий старт + +```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") +} +``` + +## 🤖 Еталонні показники + +Тестування проводилося за допомогою [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) +та [Go Web](https://github.com/smallnest/go-web-framework-benchmark). Якщо ви хочете побачити всі результати, будь ласка +відвідайте наш [Wiki](https://docs.gofiber.io/extra/benchmarks). + +

+ + +

+ +## ⚙️ Встановлення + +Переконайтеся, що Go встановлено ([завантажити](https://go.dev/dl/)). Потрібна версія `1.14` або вища. + +Ініціалізуйте проект, створивши папку, а потім запустивши `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): + +```bash +go get -u github.com/gofiber/fiber/v2 +``` + +## 🎯 Особливості + +- Надійна [маршрутизація](https://docs.gofiber.io/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) +- [Швидке](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) +- [Обмежувач швидкості](https://docs.gofiber.io/api/middleware/limiter) +- Документація доступна [18 мовами](https://docs.gofiber.io/) +- І багато іншого, [відвідайте наш Wiki](https://docs.gofiber.io/) + +## 💡 Філософія + +Нові програмісти, які переходять із [Node.js](https://nodejs.org/en/about/) на [Go](https://go.dev/doc/), мають справу зі звивистою кривою навчання, перш ніж можуть розпочати створення своїх веб-додатків або мікросервісів. Fiber, як **веб-фреймворк**, було створено з ідеєю **мінімалізму** та слідує **шляху UNIX**, щоб нові програмісти могли швидко увійти у світ Go з теплим та надійним прийомом. + +Fiber **натхненний** Express, найпопулярнішим веб-фреймворком в Інтернеті. Ми поєднали **легкість** Express і **чисту продуктивність** Go. Якщо ви коли-небудь реалізовували веб-додаток у Node.js (_з використанням Express або подібного_), то багато методів і принципів здадуться вам **дуже звичайними**. + +Ми **прислухаємося** до наших користувачів у [issues](https://github.com/gofiber/fiber/issues), Discord [сервері](https://gofiber.io/discord) та в інших місцях Інтернета, щоб створити **швидкий**, **гнучкий** та **доброзичливий** веб фреймворк на Go для **будь-яких** завдань, **дедлайнів** та **рівнів** розробників! Як це робить Express у світі JavaScript. + +## ⚠️ Обмеження + +- Через те, що Fiber використовує unsafe, бібліотека не завжди може бути сумісною з останньою версією Go. Fiber 2.40.0 було протестовано з Go версій 1.16 до 1.19. +- Fiber не сумісний з інтерфейсами net/http. Це означає, що ви не зможете використовувати такі проекти, як gqlgen, go-swagger або будь-які інші, які є частиною екосистеми net/http. + +## 👀 Приклади + +Нижче наведено деякі типові приклади. Якщо ви хочете переглянути більше прикладів коду, відвідайте наше [репозиторій рецептів](https://github.com/gofiber/recipes) або відвідайте нашу розміщену [документацію API](https://docs.gofiber.io). + +#### 📖 [**Основна маршрутизація**](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")) +} +``` + +#### 📖 [**Назви маршруту**](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")) +} +``` + +#### 📖 [**Обслуговування статичних файлів**](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")) +} +``` + +
+ 📚 Показати більше прикладів коду + +### Двигуни перегляду + +📖 [Конфігурація](https://docs.gofiber.io/api/fiber#config) +📖 [Двигуни](https://github.com/gofiber/template) +📖 [Рендер](https://docs.gofiber.io/api/ctx#render) + +Fiber за умовчанням використовує [html/template](https://pkg.go.dev/html/template/), якщо жодного двигуна не було вказано. + +Якщо ви хочете виконати частково або використовувати інший двигун, наприклад [amber](https://github.com/eknkc/amber), [handlebars](https://github.com/aymerick/raymond), [mustache]( https://github.com/cbroglie/mustache) або [jade](https://github.com/Joker/jade), тощо. + +Перегляньте наш пакет [Шаблон](https://github.com/gofiber/template), який підтримує кілька двигунів перегляду. + +```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")) +} +``` + +### Групування маршрутів у ланцюги + +📖 [Група](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 логування + +📖 [Логування](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")) +} +``` + +### Спільне використання ресурсів між джерелами (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")) +} +``` + +Перевірте CORS, передавши будь-який домен у заголовку `Origin`: + +```bash +curl -H "Origin: http://example.com" --verbose http://localhost:3000 +``` + +### Власна відповідь 404 + +📖 [HTTP Методи](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 Відповідь + +📖 [JSON](https://docs.gofiber.io/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 + +📖 [Більше інформації](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")) +} +``` + +
+ +### Використання довіреного проксі + +📖 [Конфігурація](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")) +} +``` + + + +## 🧬 Внутрішні Middleware + +Ось список middleware, яке входить до складу Fiber фреймворку. + +| Middleware | Опис | +|:---------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Middleware який забезпечує базову автентифікацію по HTTP. | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Middleware який перехоплює та кешує відповіді | +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | стиснення для Fiber, воно за замовчуванням підтримує `deflate`, `gzip` і `brotli`. | +| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Middleware який вмикає перехресне використання ресурсів \(CORS\) із різними параметрами. | +| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Захист від експлойтів CSRF. | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Шифрування значень файлів cookie. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Middleware для відкриття змінних середевищ. | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Middleware яке робить кеш-пам’ять більш ефективним і заощаджує пропускну здатність, оскільки веб-серверу не потрібно повторно надсилати повну відповідь, якщо вміст не змінився. | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Middleware який обслуговує доступні варіанти середовища виконання HTTP у форматі JSON. | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ігнорування значка із журналів або обслуговувати з пам’яті, якщо вказано шлях до файлу. | +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Middleware файлової системи, особлива подяка та кредити Alireza Salary. | +| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Ообмеження швидкості для Fiber. Використовуйте для обмеження повторних запитів до загальнодоступних API та/або кінцевих точок, таких як скидання пароля. | +| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | Реєстратор запитів/відповідей HTTP. | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Middleware який повідомляє показники сервера. | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Особлива подяка Метью Лі \(@mthli\) . | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Дозволяє надсилати проксі-запити до кількох серверів. | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Middleware який відновлює паніки будь-де в ланцюжку стека та передає керування централізованому [обробнику помилок](https://docs.gofiber.io/guide/error-handling). | +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | До кожного запиту додає ідентифікатор запиту. | +| [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, якщо його перевищено. | + +## 🧬 Зовнішні Middleware + +Список зовнішніх middleware модулів, які підтримуються [командою Fiber](https://github.com/orgs/gofiber/people). + +| 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 з підтримкою місцевих користувачів! | + +## 🕶️ Чудовий список + +Більше статей, middleware, прикладів або інструментів дивіться у нашому [чудовому списку](https://github.com/gofiber/awesome-fiber). + +## 👍 Внести свій внесок + +Якщо ви хочете сказати **дякую** та/або підтримати активний розвиток `Fiber`: + +1. Додайте [зірку GitHub](https://github.com/gofiber/fiber/stargazers) до проекту. +2. Напишіть про проект [у своєму Twitter](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). +3. Напишіть огляд або підручник на [Medium](https://medium.com/), [Dev.to](https://dev.to/) або особистому блогу. +4. Підтримайте проект, пожертвувавши [чашку кави](https://buymeacoff.ee/fenny). + +## ☕ Прихильники + +Fiber – це проект із відкритим вихідним кодом, який працює за рахунок пожертвувань для оплати рахунків, наприклад наше доменне ім’я, gitbook, netlify і безсерверний хостинг. Якщо ви хочете підтримати Fiber, ви можете ☕ [**купити каву тут**](https://buymeacoff.ee/fenny). + +| | Користувач | Пожертвування | +| :--------------------------------------------------------- | :----------------------------------------------- | :------------ | +| ![](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 | + +## ‎‍💻 Автори коду + +Code Contributors + +## ⭐️ Звіздарі + +Stargazers over time + +## ⚠️ Ліцензія + +Авторське право (c) 2019-дотепер [Fenny](https://github.com/fenny) та [Contributors](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). + +**Ліцензії сторонніх бібліотек** + +- [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_zh-CN.md b/.github/README_zh-CN.md index 0936f09f2f..e36566b9db 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -55,6 +55,9 @@ + + +
diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index 68d16bdd23..a54142786d 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -55,6 +55,9 @@ + + +
From 077a5dc3d4cedd9c63545cb945d9b9bbb943ab27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Mon, 5 Dec 2022 10:27:31 +0300 Subject: [PATCH 007/212] :bug: bug: fix regex constraints that contain comma (#2256) --- path.go | 5 +++-- path_test.go | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/path.go b/path.go index 6ea02951cf..f652f5c678 100644 --- a/path.go +++ b/path.go @@ -271,12 +271,12 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r // Assign constraint if start != -1 && end != -1 { constraint := &Constraint{ - ID: getParamConstraintType(c[:start]), - Data: splitNonEscaped(c[start+1:end], string(parameterConstraintDataSeparatorChars)), + ID: getParamConstraintType(c[:start]), } // remove escapes from data if constraint.ID != regexConstraint { + constraint.Data = splitNonEscaped(c[start+1:end], string(parameterConstraintDataSeparatorChars)) if len(constraint.Data) == 1 { constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) } else if len(constraint.Data) == 2 { @@ -287,6 +287,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r // Precompile regex if has regex constraint if constraint.ID == regexConstraint { + constraint.Data = []string{c[start+1 : end]} constraint.RegexCompiler = regexp.MustCompile(constraint.Data[0]) } diff --git a/path_test.go b/path_test.go index a702eaa207..7ef56eb5c8 100644 --- a/path_test.go +++ b/path_test.go @@ -6,6 +6,7 @@ package fiber import ( "fmt" + "strings" "testing" "github.com/gofiber/fiber/v2/utils" @@ -526,6 +527,12 @@ func Test_Path_matchParams(t *testing.T) { {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}, From d9d2153fcc082d3c035b5eaf3f26a5b9f8da8364 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Mon, 5 Dec 2022 08:27:51 +0100 Subject: [PATCH 008/212] :adhesive_bandage: Fix: Properly handle error of "net.ParseCIDR" in "(*App).handleTrustedProxy" (#2243) * app: do not use empty *net.IPNet in case of an error of "net.ParseCIDR" * app: expose error returned by "net.ParseCIDR" * ctx: do not repeatedly call method in loop * ctx: add test for "IsProxyTrusted" func --- app.go | 7 ++-- ctx.go | 9 ++--- ctx_test.go | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 8 deletions(-) diff --git a/app.go b/app.go index 0c48cebb96..504a1efc50 100644 --- a/app.go +++ b/app.go @@ -585,12 +585,11 @@ func New(config ...Config) *App { func (app *App) handleTrustedProxy(ipAddress string) { if strings.Contains(ipAddress, "/") { _, ipNet, err := net.ParseCIDR(ipAddress) - if err != nil { - fmt.Printf("[Warning] IP range `%s` could not be parsed. \n", ipAddress) + fmt.Printf("[Warning] IP range %q could not be parsed: %v\n", ipAddress, err) + } else { + app.config.trustedProxyRanges = append(app.config.trustedProxyRanges, ipNet) } - - app.config.trustedProxyRanges = append(app.config.trustedProxyRanges, ipNet) } else { app.config.trustedProxiesMap[ipAddress] = struct{}{} } diff --git a/ctx.go b/ctx.go index 92d7fad985..50cdc06a41 100644 --- a/ctx.go +++ b/ctx.go @@ -1747,13 +1747,14 @@ func (c *Ctx) IsProxyTrusted() bool { return true } - _, trusted := c.app.config.trustedProxiesMap[c.fasthttp.RemoteIP().String()] - if trusted { - return trusted + ip := c.fasthttp.RemoteIP() + + if _, trusted := c.app.config.trustedProxiesMap[ip.String()]; trusted { + return true } for _, ipNet := range c.app.config.trustedProxyRanges { - if ipNet.Contains(c.fasthttp.RemoteIP()) { + if ipNet.Contains(ip) { return true } } diff --git a/ctx_test.go b/ctx_test.go index c2b2c2d9e6..ab6bbfb418 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -1023,6 +1023,105 @@ func Test_Ctx_Get(t *testing.T) { utils.AssertEqual(t, "default", c.Get("unknown", "default")) } +// go test -run Test_Ctx_IsProxyTrusted +func Test_Ctx_IsProxyTrusted(t *testing.T) { + t.Parallel() + + { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + utils.AssertEqual(t, true, c.IsProxyTrusted()) + } + { + app := New(Config{ + EnableTrustedProxyCheck: false, + }) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + utils.AssertEqual(t, true, c.IsProxyTrusted()) + } + + { + app := New(Config{ + EnableTrustedProxyCheck: true, + }) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + utils.AssertEqual(t, false, c.IsProxyTrusted()) + } + { + app := New(Config{ + EnableTrustedProxyCheck: true, + + TrustedProxies: []string{}, + }) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + utils.AssertEqual(t, false, c.IsProxyTrusted()) + } + { + app := New(Config{ + EnableTrustedProxyCheck: true, + + TrustedProxies: []string{ + "127.0.0.1", + }, + }) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + utils.AssertEqual(t, false, c.IsProxyTrusted()) + } + { + app := New(Config{ + EnableTrustedProxyCheck: true, + + TrustedProxies: []string{ + "127.0.0.1/8", + }, + }) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + utils.AssertEqual(t, false, c.IsProxyTrusted()) + } + { + app := New(Config{ + EnableTrustedProxyCheck: true, + + TrustedProxies: []string{ + "0.0.0.0", + }, + }) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + utils.AssertEqual(t, true, c.IsProxyTrusted()) + } + { + app := New(Config{ + EnableTrustedProxyCheck: true, + + TrustedProxies: []string{ + "0.0.0.1/31", + }, + }) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + utils.AssertEqual(t, true, c.IsProxyTrusted()) + } + { + app := New(Config{ + EnableTrustedProxyCheck: true, + + TrustedProxies: []string{ + "0.0.0.1/31junk", + }, + }) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + utils.AssertEqual(t, false, c.IsProxyTrusted()) + } +} + // go test -run Test_Ctx_Hostname func Test_Ctx_Hostname(t *testing.T) { t.Parallel() From 356448cb66ea04b28e6429d5be5948121b5d7c82 Mon Sep 17 00:00:00 2001 From: Angel ILIEV Date: Mon, 5 Dec 2022 16:02:18 +0200 Subject: [PATCH 009/212] Update docstring for FormValue() (#2262) Include the other places that are searched for the provided form value key --- ctx.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ctx.go b/ctx.go index 50cdc06a41..88e54fa774 100644 --- a/ctx.go +++ b/ctx.go @@ -553,6 +553,7 @@ func (c *Ctx) FormFile(key string) (*multipart.FileHeader, error) { } // FormValue returns the first value by key from a MultipartForm. +// Search is performed in QueryArgs, PostArgs, MultipartForm and FormFile in this particular order. // Defaults to the empty string "" if the form value doesn't exist. // If a default value is given, it will return that value if the form value does not exist. // Returned value is only valid within the handler. Do not store any references. From affa7594771cd57193f4d17070bb3c3ed15487c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Thu, 8 Dec 2022 19:07:32 +0300 Subject: [PATCH 010/212] :memo: docs: replace `1.14` with `1.16` in READMEs (#2265) --- .github/README.md | 2 +- .github/README_ckb.md | 2 +- .github/README_de.md | 2 +- .github/README_es.md | 2 +- .github/README_fa.md | 2 +- .github/README_fr.md | 2 +- .github/README_he.md | 2 +- .github/README_id.md | 2 +- .github/README_it.md | 2 +- .github/README_ja.md | 2 +- .github/README_ko.md | 2 +- .github/README_nl.md | 2 +- .github/README_pt.md | 2 +- .github/README_ru.md | 2 +- .github/README_sa.md | 2 +- .github/README_tr.md | 6 +++--- .github/README_uk.md | 2 +- .github/README_zh-CN.md | 2 +- .github/README_zh-TW.md | 2 +- 19 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/README.md b/.github/README.md index 35a871eeaf..206f8a94c3 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.14` or higher is required. +Make sure you have Go installed ([download](https://go.dev/dl/)). Version `1.16` 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: diff --git a/.github/README_ckb.md b/.github/README_ckb.md index 2beaa178a0..1fc1104d87 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -114,7 +114,7 @@ func main() { ## ⚙️ دامەزراندن -دڵنیا بە لەوەی کە لەناو ئامێرەکەت Go دامەزراوە ([دای بگرە](https://go.dev/dl/)). دەبێت وەشانەکەشی `1.14` یان سەرووتر بێت. +دڵنیا بە لەوەی کە لەناو ئامێرەکەت Go دامەزراوە ([دای بگرە](https://go.dev/dl/)). دەبێت وەشانەکەشی `1.16` یان سەرووتر بێت. پڕۆژەکەت دەست پێ بکە بە دروستکردنی بوخچەیەک و کار پێ کردنی فەرمانی `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 3774dac2fe..7b79b82b73 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.14` oder neuer wird zu der Nutzung Fibers benötigt. +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. 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: diff --git a/.github/README_es.md b/.github/README_es.md index 6edb581c7a..d85239dc88 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.14` o superior. +Asegúrese de tener instalado Go ([descargar](https://go.dev/dl/)). Versión `1.16` 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): diff --git a/.github/README_fa.md b/.github/README_fa.md index 6b150be111..f0f7e44e58 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -133,7 +133,7 @@ func main() {

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

diff --git a/.github/README_fr.md b/.github/README_fr.md index b21028158b..fd4ed56409 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.14` or higher is required. +Make sure you have Go installed ([download](https://go.dev/dl/)). Version `1.16` 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: diff --git a/.github/README_he.md b/.github/README_he.md index 6387f84c1a..81c9abbcd4 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.14` or higher is required. +Make sure you have Go installed ([download](https://go.dev/dl/)). Version `1.16` 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: diff --git a/.github/README_id.md b/.github/README_id.md index 9658057dca..55cf1d7085 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.14` atau lebih tinggi [ Direkomendasikan ]. +Pastikan kamu sudah menginstalasi Golang ([unduh](https://go.dev/dl/)). Dengan versi `1.16` 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): diff --git a/.github/README_it.md b/.github/README_it.md index ac30bef308..a8cf511dc9 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.14` o superiore. +Assicurati di avere Go ([per scaricalro](https://go.dev/dl/)) installato. Devi avere la versione `1.16` 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): diff --git a/.github/README_ja.md b/.github/README_ja.md index 6b432075dd..f1303cb39e 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -115,7 +115,7 @@ func main() { ## ⚙️ インストール -Go がインストールされていることを確認してください ([ダウンロード](https://go.dev/dl/)). バージョン `1.14` またはそれ以上であることが必要です。 +Go がインストールされていることを確認してください ([ダウンロード](https://go.dev/dl/)). バージョン `1.16` またはそれ以上であることが必要です。 フォルダを作成し、フォルダ内で `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) コマンドでインストールしてください。 diff --git a/.github/README_ko.md b/.github/README_ko.md index 01a8d32412..13af80fdb8 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -114,7 +114,7 @@ func main() { ## ⚙️ 설치 -Go가 설치되어 있는 것을 확인해 주세요 ([download](https://go.dev/dl/)). 버전 1.14 또는 그 이상이어야 합니다. +Go가 설치되어 있는 것을 확인해 주세요 ([download](https://go.dev/dl/)). 버전 1.16 또는 그 이상이어야 합니다. 폴더를 생성하여 당신의 프로젝트를 초기화하고, 폴더 안에서 `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를 설치하세요: diff --git a/.github/README_nl.md b/.github/README_nl.md index a7bb91f549..32f6a6599a 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.14` or higher is required. +Make sure you have Go installed ([download](https://go.dev/dl/)). Version `1.16` 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: diff --git a/.github/README_pt.md b/.github/README_pt.md index cd091ee4b0..86dfc0ddff 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.14` ou superior é obrigatória. +Certifique-se de ter o Go instalado ([download](https://go.dev/dl/)). Versão `1.16` 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): diff --git a/.github/README_ru.md b/.github/README_ru.md index 7618139602..f38e82a136 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -114,7 +114,7 @@ func main() { ## ⚙️ Установка -Убедитесь, что Go установлен ([скачать](https://go.dev/dl/)). Требуется версия `1.14` или выше. +Убедитесь, что Go установлен ([скачать](https://go.dev/dl/)). Требуется версия `1.16` или выше. Инициализируйте проект, создав папку, а затем запустив `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): diff --git a/.github/README_sa.md b/.github/README_sa.md index 80069c6c4f..eaa66c52b0 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -120,7 +120,7 @@ func main() { ## ⚙️ تثبيت -تأكد من تثبيت Go ([تحميل](https://go.dev/dl/)). الإصدار `1.14` أو أعلى مطلوب. +تأكد من تثبيت Go ([تحميل](https://go.dev/dl/)). الإصدار `1.16` أو أعلى مطلوب. ابدأ مشروعك بإنشاء مجلد ثم تشغيله `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) أمر: diff --git a/.github/README_tr.md b/.github/README_tr.md index cd63f018df..b018096d1c 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.14` sürümü ([indir](https://go.dev/dl/)) veya daha yüksek bir sürüm gerekli. +Go'nun `1.16` 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.14 ile 1.19 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.16 ile 1.19 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.14 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.16 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 9970367b02..7c2d1c4d61 100644 --- a/.github/README_uk.md +++ b/.github/README_uk.md @@ -124,7 +124,7 @@ func main() { ## ⚙️ Встановлення -Переконайтеся, що Go встановлено ([завантажити](https://go.dev/dl/)). Потрібна версія `1.14` або вища. +Переконайтеся, що Go встановлено ([завантажити](https://go.dev/dl/)). Потрібна версія `1.16` або вища. Ініціалізуйте проект, створивши папку, а потім запустивши `go mod init github.com/your/repo` ([детальніше](https://go.dev/blog/using-go-modules)) всередині цієї папки. Далі встановіть Fiber за допомогою diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index e36566b9db..01fa113a91 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -116,7 +116,7 @@ func main() { ## ⚙️ 安装 -确保已安装 `1.14` 或更高版本的 Go ([下载](https://go.dev/dl/))。 +确保已安装 `1.16` 或更高版本的 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: diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index a54142786d..63e96ac248 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -114,7 +114,7 @@ func main() { ## ⚙️ 安裝 -確保已安裝 Go 版本 `1.14` 或以上 ([下載](https://go.dev/dl/))。 +確保已安裝 Go 版本 `1.16` 或以上 ([下載](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 : From 2e3f73cb4ddad7defe23cde508075c9432c34efa Mon Sep 17 00:00:00 2001 From: kinggo Date: Sat, 10 Dec 2022 20:58:18 +0800 Subject: [PATCH 011/212] optimize: use fasthttp.AddMissingPort (#2268) --- client.go | 16 +--------------- client_test.go | 5 ----- 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/client.go b/client.go index 02816db228..f2fbec41a0 100644 --- a/client.go +++ b/client.go @@ -8,11 +8,9 @@ import ( "fmt" "io" "mime/multipart" - "net" "os" "path/filepath" "strconv" - "strings" "sync" "time" @@ -202,7 +200,7 @@ func (a *Agent) Parse() error { } a.HostClient = &fasthttp.HostClient{ - Addr: addMissingPort(string(uri.Host()), isTLS), + Addr: fasthttp.AddMissingPort(string(uri.Host()), isTLS), Name: name, NoDefaultUserAgentHeader: a.NoDefaultUserAgentHeader, IsTLS: isTLS, @@ -211,18 +209,6 @@ func (a *Agent) Parse() error { return nil } -func addMissingPort(addr string, isTLS bool) string { - n := strings.Index(addr, ":") - if n >= 0 { - return addr - } - port := 80 - if isTLS { - port = 443 - } - return net.JoinHostPort(addr, strconv.Itoa(port)) -} - /************************** Header Setting **************************/ // Set sets the given 'key: value' header. diff --git a/client_test.go b/client_test.go index c8e93c5e85..323881127a 100644 --- a/client_test.go +++ b/client_test.go @@ -1134,11 +1134,6 @@ func Test_Client_Agent_Parse(t *testing.T) { utils.AssertEqual(t, nil, a.Parse()) } -func Test_AddMissingPort_TLS(t *testing.T) { - addr := addMissingPort("example.com", true) - utils.AssertEqual(t, "example.com:443", addr) -} - func testAgent(t *testing.T, handler Handler, wrapAgent func(agent *Agent), excepted string, count ...int) { t.Parallel() From a9ddef7a208f32351a15399198378382a6022558 Mon Sep 17 00:00:00 2001 From: kinggo Date: Sun, 11 Dec 2022 02:28:39 +0800 Subject: [PATCH 012/212] optimize: add more detail error message in serverErrorHandler (#2267) --- app.go | 2 +- app_test.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app.go b/app.go index 504a1efc50..9b6312a043 100644 --- a/app.go +++ b/app.go @@ -1031,7 +1031,7 @@ func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) { } else if strings.Contains(err.Error(), "timeout") { err = ErrRequestTimeout } else { - err = ErrBadRequest + err = NewError(StatusBadRequest, err.Error()) } if catch := app.ErrorHandler(c, err); catch != nil { diff --git a/app_test.go b/app_test.go index 7d532f3143..d414bd27bf 100644 --- a/app_test.go +++ b/app_test.go @@ -245,6 +245,16 @@ func Test_App_ErrorHandler_RouteStack(t *testing.T) { utils.AssertEqual(t, "1: USE error", string(body)) } +func Test_App_serverErrorHandler_Internal_Error(t *testing.T) { + app := New() + msg := "test err" + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + app.serverErrorHandler(c.fasthttp, errors.New(msg)) + utils.AssertEqual(t, string(c.fasthttp.Response.Body()), msg) + utils.AssertEqual(t, c.fasthttp.Response.StatusCode(), StatusBadRequest) +} + func Test_App_Nested_Params(t *testing.T) { app := New() From efeea7a4b5f9a5422376379c9b293f57e8fc558e Mon Sep 17 00:00:00 2001 From: kinggo Date: Sun, 11 Dec 2022 04:56:43 +0800 Subject: [PATCH 013/212] optimize: latency use lowest time unit in logger middleware (#2261) --- middleware/logger/logger_test.go | 5 ++--- middleware/logger/tags.go | 3 +-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index ffb9341c52..d58feb262d 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -13,12 +13,11 @@ import ( "testing" "time" - "github.com/valyala/bytebufferpool" - "github.com/valyala/fasthttp" - "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/requestid" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" + "github.com/valyala/fasthttp" ) // go test -run Test_Logger diff --git a/middleware/logger/tags.go b/middleware/logger/tags.go index 5a02d7f20f..7eb67582bc 100644 --- a/middleware/logger/tags.go +++ b/middleware/logger/tags.go @@ -3,7 +3,6 @@ package logger import ( "fmt" "strings" - "time" "github.com/gofiber/fiber/v2" ) @@ -192,7 +191,7 @@ func createTagMap(cfg *Config) map[string]LogFunc { return output.WriteString(data.Pid) }, TagLatency: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - latency := data.Stop.Sub(data.Start).Round(time.Millisecond) + latency := data.Stop.Sub(data.Start) return output.WriteString(fmt.Sprintf("%7v", latency)) }, TagTime: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { From f13c948e360ca5b06c0b2adee28d570233b3b81e Mon Sep 17 00:00:00 2001 From: pj Date: Mon, 12 Dec 2022 03:37:35 +1100 Subject: [PATCH 014/212] Match function (#2142) Add matching function Co-authored-by: rocketlaunchr-cto --- path.go | 59 +++++ path_test.go | 676 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 735 insertions(+) diff --git a/path.go b/path.go index f652f5c678..2bc041d3d1 100644 --- a/path.go +++ b/path.go @@ -113,6 +113,65 @@ var ( parameterConstraintDataSeparatorChars = []byte{paramConstraintDataSeparator} ) +// RoutePatternMatch checks if a given path matches a Fiber route pattern. +func RoutePatternMatch(path, pattern string, cfg ...Config) bool { + // See logic in (*Route).match and (*App).register + var ctxParams [maxParams]string + + config := Config{} + if len(cfg) > 0 { + config = cfg[0] + } + + if path == "" { + path = "/" + } + + // Cannot have an empty pattern + if pattern == "" { + pattern = "/" + } + // Pattern always start with a '/' + if pattern[0] != '/' { + pattern = "/" + pattern + } + + patternPretty := pattern + + // Case sensitive routing, all to lowercase + if !config.CaseSensitive { + patternPretty = utils.ToLower(patternPretty) + path = utils.ToLower(path) + } + // Strict routing, remove trailing slashes + if !config.StrictRouting && len(patternPretty) > 1 { + patternPretty = utils.TrimRight(patternPretty, '/') + } + + parser := parseRoute(patternPretty) + + if patternPretty == "/" && path == "/" { + return true + // '*' wildcard matches any path + } else if patternPretty == "/*" { + return true + } + + // Does this route have parameters + if len(parser.params) > 0 { + if match := parser.getMatch(path, path, &ctxParams, false); match { + return true + } + } + // Check for a simple match + patternPretty = RemoveEscapeChar(patternPretty) + if len(patternPretty) == len(path) && patternPretty == path { + return true + } + // No match + return false +} + // parseRoute analyzes the route and divides it into segments for constant areas and parameters, // this information is needed later when assigning the requests to the declared routes func parseRoute(pattern string) routeParser { diff --git a/path_test.go b/path_test.go index 7ef56eb5c8..8d269be97b 100644 --- a/path_test.go +++ b/path_test.go @@ -612,6 +612,459 @@ func Test_Path_matchParams(t *testing.T) { }) } +// 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) { + for _, c := range cases { + 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}, + }) +} + func Test_Utils_GetTrimmedParam(t *testing.T) { t.Parallel() res := GetTrimmedParam("") @@ -871,3 +1324,226 @@ func Benchmark_Path_matchParams(t *testing.B) { {url: "/api/v1/", params: []string{""}, match: true}, }) } + +// 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 { + var matchRes bool + state := "match" + if !c.match { + state = "not match" + } + t.Run(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 + matchRes = true + } + } + utils.AssertEqual(t, c.match, matchRes, fmt.Sprintf("route: '%s', url: '%s'", 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}, + }) +} From 8889cea116a8f62a1f87185fcdb851c8774d403c Mon Sep 17 00:00:00 2001 From: pj Date: Tue, 20 Dec 2022 02:40:43 +1100 Subject: [PATCH 015/212] feat: add ShutdownWithTimeout function (#2228) * add ShutdownWithTimeout function * no message * fix func documentation * test: add Test_App_ShutdownWithTimeout Co-authored-by: rocketlaunchr-cto Co-authored-by: kinggo --- app.go | 23 +++++++++++++++++++++-- app_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/app.go b/app.go index 9b6312a043..21df0fe3cb 100644 --- a/app.go +++ b/app.go @@ -9,6 +9,7 @@ package fiber import ( "bufio" + "context" "encoding/json" "encoding/xml" "errors" @@ -838,12 +839,30 @@ func (app *App) HandlersCount() uint32 { } // Shutdown gracefully shuts down the server without interrupting any active connections. -// Shutdown works by first closing all open listeners and then waiting indefinitely for all connections to return to idle and then shut down. +// Shutdown works by first closing all open listeners and then waiting indefinitely for all connections to return to idle before shutting down. // // Make sure the program doesn't exit and waits instead for Shutdown to return. // // 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()) +} + +// ShutdownWithTimeout gracefully shuts down the server without interrupting any active connections. However, if the timeout is exceeded, +// ShutdownWithTimeout will forcefully close any active connections. +// ShutdownWithTimeout works by first closing all open listeners and then waiting for all connections to return to idle before shutting down. +// +// Make sure the program doesn't exit and waits instead for ShutdownWithTimeout to return. +// +// ShutdownWithTimeout does not close keepalive connections so its recommended to set ReadTimeout to something else than 0. +func (app *App) ShutdownWithTimeout(timeout time.Duration) error { + ctx, cancelFunc := context.WithTimeout(context.Background(), timeout) + defer cancelFunc() + 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 { if app.hooks != nil { defer app.hooks.executeOnShutdownHooks() } @@ -853,7 +872,7 @@ func (app *App) Shutdown() error { if app.server == nil { return fmt.Errorf("shutdown: server is not running") } - return app.server.Shutdown() + return app.server.ShutdownWithContext(ctx) } // Server returns the underlying fasthttp server diff --git a/app_test.go b/app_test.go index d414bd27bf..cfd491b2b2 100644 --- a/app_test.go +++ b/app_test.go @@ -6,6 +6,7 @@ package fiber import ( "bytes" + "context" "crypto/tls" "errors" "fmt" @@ -23,6 +24,7 @@ import ( "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" + "github.com/valyala/fasthttp/fasthttputil" ) var testEmptyHandler = func(c *Ctx) error { @@ -717,6 +719,45 @@ func Test_App_Shutdown(t *testing.T) { }) } +func Test_App_ShutdownWithTimeout(t *testing.T) { + 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() { + shutdownErr <- app.ShutdownWithTimeout(1 * time.Second) + }() + + timer := time.NewTimer(time.Second * 5) + select { + case <-timer.C: + t.Fatal("idle connections not closed on shutdown") + case err := <-shutdownErr: + if err == nil || 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) { app := New() From 4d43db0c79d54d367700ef1a3301e2712c15e13b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Dec 2022 19:40:42 +0300 Subject: [PATCH 016/212] Bump github.com/mattn/go-isatty from 0.0.16 to 0.0.17 (#2279) Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.16 to 0.0.17. - [Release notes](https://github.com/mattn/go-isatty/releases) - [Commits](https://github.com/mattn/go-isatty/compare/v0.0.16...v0.0.17) --- updated-dependencies: - dependency-name: github.com/mattn/go-isatty dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] 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 3ef179744c..0728329bfb 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/mattn/go-colorable v0.1.13 - github.com/mattn/go-isatty v0.0.16 + github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-runewidth v0.0.14 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.43.0 diff --git a/go.sum b/go.sum index 29fb104d02..057b1bcdf4 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,9 @@ github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQan github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= 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 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= 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-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= From 9dfdea45c174cc8bb95af39636db22f9e211f7ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serhat=20=C5=9Eevki=20Din=C3=A7er?= Date: Sun, 1 Jan 2023 23:16:38 +1100 Subject: [PATCH 017/212] :broom: :bug: fix some warnings, go-ole on mac os (#2280) * :broom: fix some warnings * :bug: fix go-ole on mac os --- .../go-ole/{variant_386.go => variant32.go} | 4 +- .../go-ole/{variant_amd64.go => variant64.go} | 4 +- internal/go-ole/variant_ppc64le.go | 13 ------- internal/go-ole/variant_s390x.go | 13 ------- internal/gopsutil/common/common_unix.go | 3 +- internal/gopsutil/mem/mem.go | 2 +- internal/gopsutil/net/net.go | 2 +- internal/gopsutil/net/net_darwin.go | 2 +- internal/gopsutil/process/process.go | 4 +- internal/gopsutil/process/process_darwin.go | 7 ++-- internal/memory/memory.go | 37 +++++++++---------- internal/msgp/elsize.go | 16 ++++---- internal/msgp/write.go | 4 +- internal/schema/encoder.go | 4 +- 14 files changed, 43 insertions(+), 72 deletions(-) rename internal/go-ole/{variant_386.go => variant32.go} (80%) rename internal/go-ole/{variant_amd64.go => variant64.go} (70%) delete mode 100644 internal/go-ole/variant_ppc64le.go delete mode 100644 internal/go-ole/variant_s390x.go diff --git a/internal/go-ole/variant_386.go b/internal/go-ole/variant32.go similarity index 80% rename from internal/go-ole/variant_386.go rename to internal/go-ole/variant32.go index 2e3d3aa4aa..9d17d1f09e 100644 --- a/internal/go-ole/variant_386.go +++ b/internal/go-ole/variant32.go @@ -1,5 +1,5 @@ -//go:build 386 -// +build 386 +//go:build 386 || arm +// +build 386 arm package ole diff --git a/internal/go-ole/variant_amd64.go b/internal/go-ole/variant64.go similarity index 70% rename from internal/go-ole/variant_amd64.go rename to internal/go-ole/variant64.go index b48c3ce6fc..474adaad8e 100644 --- a/internal/go-ole/variant_amd64.go +++ b/internal/go-ole/variant64.go @@ -1,5 +1,5 @@ -//go:build amd64 -// +build amd64 +//go:build amd64 || arm64 || ppc64le || s390x +// +build amd64 arm64 ppc64le s390x package ole diff --git a/internal/go-ole/variant_ppc64le.go b/internal/go-ole/variant_ppc64le.go deleted file mode 100644 index 4ce060d8fc..0000000000 --- a/internal/go-ole/variant_ppc64le.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build ppc64le -// +build ppc64le - -package ole - -type VARIANT struct { - VT VT // 2 - wReserved1 uint16 // 4 - wReserved2 uint16 // 6 - wReserved3 uint16 // 8 - Val int64 // 16 - _ [8]byte // 24 -} diff --git a/internal/go-ole/variant_s390x.go b/internal/go-ole/variant_s390x.go deleted file mode 100644 index f83731098a..0000000000 --- a/internal/go-ole/variant_s390x.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build s390x -// +build s390x - -package ole - -type VARIANT struct { - VT VT // 2 - wReserved1 uint16 // 4 - wReserved2 uint16 // 6 - wReserved3 uint16 // 8 - Val int64 // 16 - _ [8]byte // 24 -} diff --git a/internal/gopsutil/common/common_unix.go b/internal/gopsutil/common/common_unix.go index a02f053e17..a4a953a2f5 100644 --- a/internal/gopsutil/common/common_unix.go +++ b/internal/gopsutil/common/common_unix.go @@ -42,8 +42,7 @@ func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args .. } func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) { - var cmd []string - cmd = []string{"-P", strconv.Itoa(int(pid))} + cmd := []string{"-P", strconv.Itoa(int(pid))} pgrep, err := exec.LookPath("pgrep") if err != nil { return []int32{}, err diff --git a/internal/gopsutil/mem/mem.go b/internal/gopsutil/mem/mem.go index f3aea58519..69cb881010 100644 --- a/internal/gopsutil/mem/mem.go +++ b/internal/gopsutil/mem/mem.go @@ -6,7 +6,7 @@ import ( "github.com/gofiber/fiber/v2/internal/gopsutil/common" ) -var invoke common.Invoker = common.Invoke{} +var _ common.Invoke // Memory usage statistics. Total, Available and Used contain numbers of bytes // for human consumption. diff --git a/internal/gopsutil/net/net.go b/internal/gopsutil/net/net.go index 0fccecd3e7..53f2f7dd63 100644 --- a/internal/gopsutil/net/net.go +++ b/internal/gopsutil/net/net.go @@ -125,7 +125,7 @@ func (l *ConntrackStatList) Append(c *ConntrackStat) { } func (l *ConntrackStatList) Items() []ConntrackStat { - items := make([]ConntrackStat, len(l.items), len(l.items)) + items := make([]ConntrackStat, len(l.items)) for i, el := range l.items { items[i] = *el } diff --git a/internal/gopsutil/net/net_darwin.go b/internal/gopsutil/net/net_darwin.go index 32d67c0d51..a7e382dcbb 100644 --- a/internal/gopsutil/net/net_darwin.go +++ b/internal/gopsutil/net/net_darwin.go @@ -251,7 +251,7 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, } } - if pernic == false { + if !pernic { return getIOCountersAll(ret) } return ret, nil diff --git a/internal/gopsutil/process/process.go b/internal/gopsutil/process/process.go index 1710446304..bbf0fdf148 100644 --- a/internal/gopsutil/process/process.go +++ b/internal/gopsutil/process/process.go @@ -164,8 +164,8 @@ func NewProcess(pid int32) (*Process, error) { if !exists { return p, ErrorProcessNotRunning } - p.CreateTime() - return p, nil + _, err = p.CreateTime() + return p, err } func PidExists(pid int32) (bool, error) { diff --git a/internal/gopsutil/process/process_darwin.go b/internal/gopsutil/process/process_darwin.go index fb42b23c32..432b5175d9 100644 --- a/internal/gopsutil/process/process_darwin.go +++ b/internal/gopsutil/process/process_darwin.go @@ -417,9 +417,9 @@ func convertCPUTimes(s string) (ret float64, err error) { if err != nil { return ret, err } - h, err := strconv.Atoi(_t[0]) + h, _ := strconv.Atoi(_t[0]) t += h * ClockTicks - h, err = strconv.Atoi(_t[1]) + h, _ = strconv.Atoi(_t[1]) t += h return float64(t) / ClockTicks, nil } @@ -608,8 +608,7 @@ func (p *Process) getKProc() (*KinfoProc, error) { func (p *Process) getKProcWithContext(ctx context.Context) (*KinfoProc, error) { mib := []int32{CTLKern, KernProc, KernProcPID, p.Pid} - procK := KinfoProc{} - length := uint64(unsafe.Sizeof(procK)) + length := uint64(unsafe.Sizeof(KinfoProc{})) buf := make([]byte, length) _, _, syserr := unix.Syscall6( 202, // unix.SYS___SYSCTL https://github.com/golang/sys/blob/76b94024e4b621e672466e8db3d7f084e7ddcad2/unix/zsysnum_darwin_amd64.go#L146 diff --git a/internal/memory/memory.go b/internal/memory/memory.go index ff2202847b..d7b053de46 100644 --- a/internal/memory/memory.go +++ b/internal/memory/memory.go @@ -73,28 +73,25 @@ func (s *Storage) gc(sleep time.Duration) { defer ticker.Stop() var expired []string - for { - select { - case <-ticker.C: - ts := atomic.LoadUint32(&utils.Timestamp) - expired = expired[:0] - s.RLock() - for key, v := range s.data { - if v.e != 0 && v.e <= ts { - expired = append(expired, key) - } + for range ticker.C { + ts := atomic.LoadUint32(&utils.Timestamp) + expired = expired[:0] + s.RLock() + for key, v := range s.data { + if v.e != 0 && v.e <= ts { + expired = append(expired, key) } - s.RUnlock() - s.Lock() - // Double-checked locking. - // We might have replaced the item in the meantime. - for i := range expired { - v := s.data[expired[i]] - if v.e != 0 && v.e <= ts { - delete(s.data, expired[i]) - } + } + s.RUnlock() + s.Lock() + // Double-checked locking. + // We might have replaced the item in the meantime. + for i := range expired { + v := s.data[expired[i]] + if v.e != 0 && v.e <= ts { + delete(s.data, expired[i]) } - s.Unlock() } + s.Unlock() } } diff --git a/internal/msgp/elsize.go b/internal/msgp/elsize.go index 0bf2b9fb05..601d388636 100644 --- a/internal/msgp/elsize.go +++ b/internal/msgp/elsize.go @@ -83,14 +83,14 @@ type bytespec struct { type varmode int8 const ( - constsize varmode = 0 // constant size (size bytes + uint8(varmode) objects) - extra8 = -1 // has uint8(p[1]) extra bytes - extra16 = -2 // has be16(p[1:]) extra bytes - extra32 = -3 // has be32(p[1:]) extra bytes - map16v = -4 // use map16 - map32v = -5 // use map32 - array16v = -6 // use array16 - array32v = -7 // use array32 + constsize varmode = -iota // constant size (size bytes + uint8(varmode) objects) + extra8 // has uint8(p[1]) extra bytes + extra16 // has be16(p[1:]) extra bytes + extra32 // has be32(p[1:]) extra bytes + map16v // use map16 + map32v // use map32 + array16v // use array16 + array32v // use array32 ) func getType(v byte) Type { diff --git a/internal/msgp/write.go b/internal/msgp/write.go index 8b3e6cb2d0..24fee4ba15 100644 --- a/internal/msgp/write.go +++ b/internal/msgp/write.go @@ -778,7 +778,9 @@ func (mw *Writer) writeVal(v reflect.Value) error { case reflect.Interface, reflect.Ptr: if v.IsNil() { - mw.WriteNil() + if err := mw.WriteNil(); err != nil { + return err + } } return mw.writeVal(v.Elem()) diff --git a/internal/schema/encoder.go b/internal/schema/encoder.go index f0ed631210..c01de00dd7 100644 --- a/internal/schema/encoder.go +++ b/internal/schema/encoder.go @@ -94,7 +94,7 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error { // Encode struct pointer types if the field is a valid pointer and a struct. if isValidStructPointer(v.Field(i)) { - e.encode(v.Field(i).Elem(), dst) + _ = e.encode(v.Field(i).Elem(), dst) continue } @@ -112,7 +112,7 @@ func (e *Encoder) encode(v reflect.Value, dst map[string][]string) error { } if v.Field(i).Type().Kind() == reflect.Struct { - e.encode(v.Field(i), dst) + _ = e.encode(v.Field(i), dst) continue } From f6b0a07db0899c6e2b371ce56be6123064c67f61 Mon Sep 17 00:00:00 2001 From: TAKAHASHI Shuuji Date: Tue, 3 Jan 2023 02:54:48 +0900 Subject: [PATCH 018/212] =?UTF-8?q?=F0=9F=93=9D=20docs:=20fix=20ci=20badge?= =?UTF-8?q?=20errors=20(#2282)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :memo: docs: fix ci badge errors * :memo: docs: fix ci badge errors on translated README files --- .github/README.md | 4 ++-- .github/README_ckb.md | 4 ++-- .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 | 4 ++-- .github/README_uk.md | 4 ++-- .github/README_zh-CN.md | 4 ++-- .github/README_zh-TW.md | 4 ++-- 19 files changed, 38 insertions(+), 38 deletions(-) diff --git a/.github/README.md b/.github/README.md index 206f8a94c3..aec54dc70b 100644 --- a/.github/README.md +++ b/.github/README.md @@ -72,10 +72,10 @@ - + - + diff --git a/.github/README_ckb.md b/.github/README_ckb.md index 1fc1104d87..9d4ea3aa1e 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_de.md b/.github/README_de.md index 7b79b82b73..445baa0a1f 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_es.md b/.github/README_es.md index d85239dc88..d12421fcb7 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_fa.md b/.github/README_fa.md index f0f7e44e58..834b390e80 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_fr.md b/.github/README_fr.md index fd4ed56409..cd65599140 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_he.md b/.github/README_he.md index 81c9abbcd4..f22d5c1e3d 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_id.md b/.github/README_id.md index 55cf1d7085..49d98dc1ad 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_it.md b/.github/README_it.md index a8cf511dc9..17b81eef16 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_ja.md b/.github/README_ja.md index f1303cb39e..675e53e413 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_ko.md b/.github/README_ko.md index 13af80fdb8..2a7a16a960 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_nl.md b/.github/README_nl.md index 32f6a6599a..98071f8563 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_pt.md b/.github/README_pt.md index 86dfc0ddff..9f4e08ae62 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_ru.md b/.github/README_ru.md index f38e82a136..3032a6fee6 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_sa.md b/.github/README_sa.md index eaa66c52b0..ac3d99d38d 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_tr.md b/.github/README_tr.md index b018096d1c..36a2ba3010 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -66,10 +66,10 @@ - + - + diff --git a/.github/README_uk.md b/.github/README_uk.md index 7c2d1c4d61..f7b89c2840 100644 --- a/.github/README_uk.md +++ b/.github/README_uk.md @@ -72,10 +72,10 @@ - + - + diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index 01fa113a91..84de35bfd7 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -69,10 +69,10 @@ - + - + diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index 63e96ac248..8ac8802487 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -69,10 +69,10 @@ - + - + From c13a7e39180be408e8b8a266fc612f206bfeeca2 Mon Sep 17 00:00:00 2001 From: RW Date: Tue, 3 Jan 2023 15:31:22 +0100 Subject: [PATCH 019/212] prepare release v2.41.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 21df0fe3cb..e299075b37 100644 --- a/app.go +++ b/app.go @@ -28,7 +28,7 @@ import ( ) // Version of current fiber package -const Version = "2.40.1" +const Version = "2.41.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From ad5250a6fff979894cf32a6d2d5e553d5e48be17 Mon Sep 17 00:00:00 2001 From: TwiN Date: Wed, 4 Jan 2023 23:32:28 -0500 Subject: [PATCH 020/212] =?UTF-8?q?=F0=9F=93=9Ddocs(middleware):=20fix=20a?= =?UTF-8?q?lignment=20in=20limiter=20example=20(#2283)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs(middleware): Fix indentation in limiter example Noticed a small formatting issue while going through the docs. --- 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 8682dd11ad..068b623f0f 100644 --- a/middleware/limiter/README.md +++ b/middleware/limiter/README.md @@ -52,8 +52,8 @@ 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, + Max: 20, + Expiration: 30 * time.Second, KeyGenerator: func(c *fiber.Ctx) string{ return "key" } From 9c5dfdbe5dc426ec9b98e586f293a14f30d20e06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Jan 2023 13:47:15 +0800 Subject: [PATCH 021/212] Bump github.com/valyala/fasthttp from 1.43.0 to 1.44.0 (#2292) Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.43.0 to 1.44.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.43.0...v1.44.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] 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 0728329bfb..35d8a95af4 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/mattn/go-isatty v0.0.17 github.com/mattn/go-runewidth v0.0.14 github.com/valyala/bytebufferpool v1.0.0 - github.com/valyala/fasthttp v1.43.0 + github.com/valyala/fasthttp v1.44.0 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab ) diff --git a/go.sum b/go.sum index 057b1bcdf4..fcae1f2638 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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.43.0 h1:Gy4sb32C98fbzVWZlTM1oTMdLWGyvxR03VhM6cBIU4g= -github.com/valyala/fasthttp v1.43.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= +github.com/valyala/fasthttp v1.44.0 h1:R+gLUhldIsfg1HokMuQjdQ5bh9nuXHPIfvkYUu9eR5Q= +github.com/valyala/fasthttp v1.44.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= From adcf92dec1f1299d029fcdb962dfec582f10f52d Mon Sep 17 00:00:00 2001 From: leonklingele Date: Fri, 13 Jan 2023 08:38:50 +0100 Subject: [PATCH 022/212] =?UTF-8?q?=F0=9F=9A=80=20Feature:=20Add=20idempot?= =?UTF-8?q?ency=20middleware=20(v2=20backport)=20(#2288)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🚀 Feature: Add idempotency middleware (#2253) * middleware: add idempotency middleware * middleware/idempotency: use fiber.Storage instead of custom storage * middleware/idempotency: only allocate data if really required * middleware/idempotency: marshal response using msgp * middleware/idempotency: add msgp tests * middleware/idempotency: do not export response * middleware/idempotency: disable msgp's -io option to disable generating unused methods * middleware/idempotency: switch to time.Duration based app.Test * middleware/idempotency: only create closure once * middleware/idempotency: add benchmarks * middleware/idempotency: optimize strings.ToLower when making comparison The real "strings.ToLower" still needs to be used when storing the data. * middleware/idempotency: safe-copy body * middleware/idempotency: backport to v2 --- helpers.go | 30 ++++ middleware/idempotency/README.md | 118 +++++++++++++ middleware/idempotency/config.go | 120 +++++++++++++ middleware/idempotency/idempotency.go | 149 ++++++++++++++++ middleware/idempotency/idempotency_test.go | 176 +++++++++++++++++++ middleware/idempotency/locker.go | 53 ++++++ middleware/idempotency/locker_test.go | 59 +++++++ middleware/idempotency/response.go | 10 ++ middleware/idempotency/response_msgp.go | 112 ++++++++++++ middleware/idempotency/response_msgp_test.go | 67 +++++++ 10 files changed, 894 insertions(+) create mode 100644 middleware/idempotency/README.md create mode 100644 middleware/idempotency/config.go create mode 100644 middleware/idempotency/idempotency.go create mode 100644 middleware/idempotency/idempotency_test.go create mode 100644 middleware/idempotency/locker.go create mode 100644 middleware/idempotency/locker_test.go create mode 100644 middleware/idempotency/response.go create mode 100644 middleware/idempotency/response_msgp.go create mode 100644 middleware/idempotency/response_msgp_test.go diff --git a/helpers.go b/helpers.go index 8df138787c..e9dca2ead1 100644 --- a/helpers.go +++ b/helpers.go @@ -369,6 +369,36 @@ func (app *App) methodInt(s string) int { return -1 } +// IsMethodSafe reports whether the HTTP method is considered safe. +// See https://datatracker.ietf.org/doc/html/rfc9110#section-9.2.1 +func IsMethodSafe(m string) bool { + switch m { + case MethodGet, + MethodHead, + MethodOptions, + MethodTrace: + return true + default: + return false + } +} + +// IsMethodIdempotent reports whether the HTTP method is considered idempotent. +// See https://datatracker.ietf.org/doc/html/rfc9110#section-9.2.2 +func IsMethodIdempotent(m string) bool { + if IsMethodSafe(m) { + return true + } + + switch m { + case MethodPut, + MethodDelete: + return true + default: + return false + } +} + // HTTP methods were copied from net/http. const ( MethodGet = "GET" // RFC 7231, 4.3.1 diff --git a/middleware/idempotency/README.md b/middleware/idempotency/README.md new file mode 100644 index 0000000000..f95295fd07 --- /dev/null +++ b/middleware/idempotency/README.md @@ -0,0 +1,118 @@ +# 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/idempotency/config.go b/middleware/idempotency/config.go new file mode 100644 index 0000000000..c8f249a074 --- /dev/null +++ b/middleware/idempotency/config.go @@ -0,0 +1,120 @@ +package idempotency + +import ( + "errors" + "fmt" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/internal/storage/memory" +) + +var ( + ErrInvalidIdempotencyKey = errors.New("invalid idempotency key") +) + +// 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 +} + +// ConfigDefault is the default config +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. +} + +// 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.Next == nil { + cfg.Next = ConfigDefault.Next + } + + if cfg.Lifetime.Nanoseconds() == 0 { + cfg.Lifetime = ConfigDefault.Lifetime + } + + if cfg.KeyHeader == "" { + cfg.KeyHeader = ConfigDefault.KeyHeader + } + if cfg.KeyHeaderValidate == nil { + cfg.KeyHeaderValidate = ConfigDefault.KeyHeaderValidate + } + + if cfg.KeepResponseHeaders != nil && len(cfg.KeepResponseHeaders) == 0 { + cfg.KeepResponseHeaders = ConfigDefault.KeepResponseHeaders + } + + if cfg.Lock == nil { + cfg.Lock = NewMemoryLock() + } + + if cfg.Storage == nil { + cfg.Storage = memory.New(memory.Config{ + GCInterval: cfg.Lifetime / 2, + }) + } + + return cfg +} diff --git a/middleware/idempotency/idempotency.go b/middleware/idempotency/idempotency.go new file mode 100644 index 0000000000..3ad2b9dedc --- /dev/null +++ b/middleware/idempotency/idempotency.go @@ -0,0 +1,149 @@ +package idempotency + +import ( + "fmt" + "log" + "strings" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" +) + +// Inspired by https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-idempotency-key-header-02 +// and https://github.com/penguin-statistics/backend-next/blob/f2f7d5ba54fc8a58f168d153baa17b2ad4a14e45/internal/pkg/middlewares/idempotency.go + +const ( + localsKeyIsFromCache = "idempotency_isfromcache" + localsKeyWasPutToCache = "idempotency_wasputtocache" +) + +func IsFromCache(c *fiber.Ctx) bool { + return c.Locals(localsKeyIsFromCache) != nil +} + +func WasPutToCache(c *fiber.Ctx) bool { + return c.Locals(localsKeyWasPutToCache) != nil +} + +func New(config ...Config) fiber.Handler { + // Set default config + cfg := configDefault(config...) + + keepResponseHeadersMap := make(map[string]struct{}, len(cfg.KeepResponseHeaders)) + for _, h := range cfg.KeepResponseHeaders { + keepResponseHeadersMap[strings.ToLower(h)] = struct{}{} + } + + maybeWriteCachedResponse := func(c *fiber.Ctx, key string) (bool, error) { + if val, err := cfg.Storage.Get(key); err != nil { + return false, fmt.Errorf("failed to read response: %w", err) + } else if val != nil { + var res response + if _, err := res.UnmarshalMsg(val); err != nil { + return false, fmt.Errorf("failed to unmarshal response: %w", err) + } + + _ = c.Status(res.StatusCode) + + for header, val := range res.Headers { + c.Set(header, val) + } + + if len(res.Body) != 0 { + if err := c.Send(res.Body); err != nil { + return true, err + } + } + + _ = c.Locals(localsKeyIsFromCache, true) + + return true, nil + } + + return false, nil + } + + return func(c *fiber.Ctx) error { + // Don't execute middleware if Next returns true + if cfg.Next != nil && cfg.Next(c) { + return c.Next() + } + + // Don't execute middleware if the idempotency key is empty + key := utils.CopyString(c.Get(cfg.KeyHeader)) + if key == "" { + return c.Next() + } + + // Validate key + if err := cfg.KeyHeaderValidate(key); err != nil { + return err + } + + // First-pass: if the idempotency key is in the storage, get and return the response + if ok, err := maybeWriteCachedResponse(c, key); err != nil { + return fmt.Errorf("failed to write cached response at fastpath: %w", err) + } else if ok { + return nil + } + + if err := cfg.Lock.Lock(key); err != nil { + return fmt.Errorf("failed to lock: %w", err) + } + defer func() { + if err := cfg.Lock.Unlock(key); err != nil { + log.Printf("middleware/idempotency: failed to unlock key %q: %v", key, err) + } + }() + + // Lock acquired. If the idempotency key now is in the storage, get and return the response + if ok, err := maybeWriteCachedResponse(c, key); err != nil { + return fmt.Errorf("failed to write cached response while locked: %w", err) + } else if ok { + return nil + } + + // Execute the request handler + if err := c.Next(); err != nil { + // If the request handler returned an error, return it and skip idempotency + return err + } + + // Construct response + res := &response{ + StatusCode: c.Response().StatusCode(), + + Body: utils.CopyBytes(c.Response().Body()), + } + { + headers := c.GetRespHeaders() + if cfg.KeepResponseHeaders == nil { + // Keep all + res.Headers = headers + } else { + // Filter + res.Headers = make(map[string]string) + for h := range headers { + if _, ok := keepResponseHeadersMap[utils.ToLower(h)]; ok { + res.Headers[h] = headers[h] + } + } + } + } + + // Marshal response + bs, err := res.MarshalMsg(nil) + if err != nil { + return fmt.Errorf("failed to marshal response: %w", err) + } + + // Store response + if err := cfg.Storage.Set(key, bs, cfg.Lifetime); err != nil { + return fmt.Errorf("failed to save response: %w", err) + } + + _ = c.Locals(localsKeyWasPutToCache, true) + + return nil + } +} diff --git a/middleware/idempotency/idempotency_test.go b/middleware/idempotency/idempotency_test.go new file mode 100644 index 0000000000..e6fdbcd8a3 --- /dev/null +++ b/middleware/idempotency/idempotency_test.go @@ -0,0 +1,176 @@ +package idempotency_test + +import ( + "errors" + "io" + "net/http" + "net/http/httptest" + "strconv" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/idempotency" + "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" +) + +// go test -run Test_Idempotency +func Test_Idempotency(t *testing.T) { + t.Parallel() + + app := fiber.New() + + app.Use(func(c *fiber.Ctx) error { + if err := c.Next(); err != nil { + return err + } + + isMethodSafe := fiber.IsMethodSafe(c.Method()) + isIdempotent := idempotency.IsFromCache(c) || idempotency.WasPutToCache(c) + hasReqHeader := c.Get("X-Idempotency-Key") != "" + + if isMethodSafe { + if isIdempotent { + return errors.New("request with safe HTTP method should not be idempotent") + } + } else { + // Unsafe + if hasReqHeader { + if !isIdempotent { + return errors.New("request with unsafe HTTP method should be idempotent if X-Idempotency-Key request header is set") + } + } else { + // No request header + if isIdempotent { + return errors.New("request with unsafe HTTP method should not be idempotent if X-Idempotency-Key request header is not set") + } + } + } + + return nil + }) + + // Needs to be at least a second as the memory storage doesn't support shorter durations. + const lifetime = 1 * time.Second + + app.Use(idempotency.New(idempotency.Config{ + Lifetime: lifetime, + })) + + nextCount := func() func() int { + var count int32 + return func() int { + return int(atomic.AddInt32(&count, 1)) + } + }() + + { + handler := func(c *fiber.Ctx) error { + return c.SendString(strconv.Itoa(nextCount())) + } + + app.Get("/", handler) + app.Post("/", handler) + } + + app.Post("/slow", func(c *fiber.Ctx) error { + time.Sleep(2 * lifetime) + + return c.SendString(strconv.Itoa(nextCount())) + }) + + doReq := func(method, route, idempotencyKey string) string { + req := httptest.NewRequest(method, route, http.NoBody) + if idempotencyKey != "" { + req.Header.Set("X-Idempotency-Key", idempotencyKey) + } + resp, err := app.Test(req, 3*int(lifetime.Milliseconds())) + utils.AssertEqual(t, nil, err) + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, string(body)) + return string(body) + } + + utils.AssertEqual(t, "1", doReq(fiber.MethodGet, "/", "")) + utils.AssertEqual(t, "2", doReq(fiber.MethodGet, "/", "")) + + utils.AssertEqual(t, "3", doReq(fiber.MethodPost, "/", "")) + utils.AssertEqual(t, "4", doReq(fiber.MethodPost, "/", "")) + + utils.AssertEqual(t, "5", doReq(fiber.MethodGet, "/", "00000000-0000-0000-0000-000000000000")) + utils.AssertEqual(t, "6", doReq(fiber.MethodGet, "/", "00000000-0000-0000-0000-000000000000")) + + utils.AssertEqual(t, "7", doReq(fiber.MethodPost, "/", "00000000-0000-0000-0000-000000000000")) + utils.AssertEqual(t, "7", doReq(fiber.MethodPost, "/", "00000000-0000-0000-0000-000000000000")) + utils.AssertEqual(t, "8", doReq(fiber.MethodPost, "/", "")) + utils.AssertEqual(t, "9", doReq(fiber.MethodPost, "/", "11111111-1111-1111-1111-111111111111")) + + utils.AssertEqual(t, "7", doReq(fiber.MethodPost, "/", "00000000-0000-0000-0000-000000000000")) + time.Sleep(2 * lifetime) + utils.AssertEqual(t, "10", doReq(fiber.MethodPost, "/", "00000000-0000-0000-0000-000000000000")) + utils.AssertEqual(t, "10", doReq(fiber.MethodPost, "/", "00000000-0000-0000-0000-000000000000")) + + // Test raciness + { + var wg sync.WaitGroup + for i := 0; i < 100; i++ { + wg.Add(1) + go func() { + defer wg.Done() + utils.AssertEqual(t, "11", doReq(fiber.MethodPost, "/slow", "22222222-2222-2222-2222-222222222222")) + }() + } + wg.Wait() + utils.AssertEqual(t, "11", doReq(fiber.MethodPost, "/slow", "22222222-2222-2222-2222-222222222222")) + } + time.Sleep(2 * lifetime) + utils.AssertEqual(t, "12", doReq(fiber.MethodPost, "/slow", "22222222-2222-2222-2222-222222222222")) +} + +// go test -v -run=^$ -bench=Benchmark_Idempotency -benchmem -count=4 +func Benchmark_Idempotency(b *testing.B) { + app := fiber.New() + + // Needs to be at least a second as the memory storage doesn't support shorter durations. + const lifetime = 1 * time.Second + + app.Use(idempotency.New(idempotency.Config{ + Lifetime: lifetime, + })) + + app.Post("/", func(c *fiber.Ctx) error { + return nil + }) + + h := app.Handler() + + b.Run("hit", func(b *testing.B) { + c := &fasthttp.RequestCtx{} + c.Request.Header.SetMethod(fiber.MethodPost) + c.Request.SetRequestURI("/") + c.Request.Header.Set("X-Idempotency-Key", "00000000-0000-0000-0000-000000000000") + + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + h(c) + } + }) + + b.Run("skip", func(b *testing.B) { + c := &fasthttp.RequestCtx{} + c.Request.Header.SetMethod(fiber.MethodPost) + c.Request.SetRequestURI("/") + + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + h(c) + } + }) + +} diff --git a/middleware/idempotency/locker.go b/middleware/idempotency/locker.go new file mode 100644 index 0000000000..bf8bf0e065 --- /dev/null +++ b/middleware/idempotency/locker.go @@ -0,0 +1,53 @@ +package idempotency + +import ( + "sync" +) + +// Locker implements a spinlock for a string key. +type Locker interface { + Lock(key string) error + Unlock(key string) error +} + +type MemoryLock struct { + mu sync.Mutex + + keys map[string]*sync.Mutex +} + +func (l *MemoryLock) Lock(key string) error { + l.mu.Lock() + mu, ok := l.keys[key] + if !ok { + mu = new(sync.Mutex) + l.keys[key] = mu + } + l.mu.Unlock() + + mu.Lock() + + return nil +} + +func (l *MemoryLock) Unlock(key string) error { + l.mu.Lock() + mu, ok := l.keys[key] + l.mu.Unlock() + if !ok { + // This happens if we try to unlock an unknown key + return nil + } + + mu.Unlock() + + return nil +} + +func NewMemoryLock() *MemoryLock { + return &MemoryLock{ + keys: make(map[string]*sync.Mutex), + } +} + +var _ Locker = (*MemoryLock)(nil) diff --git a/middleware/idempotency/locker_test.go b/middleware/idempotency/locker_test.go new file mode 100644 index 0000000000..3063b2b67b --- /dev/null +++ b/middleware/idempotency/locker_test.go @@ -0,0 +1,59 @@ +package idempotency_test + +import ( + "testing" + "time" + + "github.com/gofiber/fiber/v2/middleware/idempotency" + "github.com/gofiber/fiber/v2/utils" +) + +// go test -run Test_MemoryLock +func Test_MemoryLock(t *testing.T) { + t.Parallel() + + l := idempotency.NewMemoryLock() + + { + err := l.Lock("a") + utils.AssertEqual(t, nil, err) + } + { + done := make(chan struct{}) + go func() { + defer close(done) + + err := l.Lock("a") + utils.AssertEqual(t, nil, err) + }() + + select { + case <-done: + t.Fatal("lock acquired again") + case <-time.After(time.Second): + } + } + + { + err := l.Lock("b") + utils.AssertEqual(t, nil, err) + } + { + err := l.Unlock("b") + utils.AssertEqual(t, nil, err) + } + { + err := l.Lock("b") + utils.AssertEqual(t, nil, err) + } + + { + err := l.Unlock("c") + utils.AssertEqual(t, nil, err) + } + + { + err := l.Lock("d") + utils.AssertEqual(t, nil, err) + } +} diff --git a/middleware/idempotency/response.go b/middleware/idempotency/response.go new file mode 100644 index 0000000000..ca06bcb452 --- /dev/null +++ b/middleware/idempotency/response.go @@ -0,0 +1,10 @@ +package idempotency + +//go:generate msgp -o=response_msgp.go -io=false -unexported +type response struct { + StatusCode int `msg:"sc"` + + Headers map[string]string `msg:"hs"` + + Body []byte `msg:"b"` +} diff --git a/middleware/idempotency/response_msgp.go b/middleware/idempotency/response_msgp.go new file mode 100644 index 0000000000..255d96f06d --- /dev/null +++ b/middleware/idempotency/response_msgp.go @@ -0,0 +1,112 @@ +package idempotency + +// Code generated by github.com/tinylib/msgp DO NOT EDIT. + +import ( + "github.com/gofiber/fiber/v2/internal/msgp" +) + +// MarshalMsg implements msgp.Marshaler +func (z *response) MarshalMsg(b []byte) (o []byte, err error) { + o = msgp.Require(b, z.Msgsize()) + // map header, size 3 + // string "sc" + o = append(o, 0x83, 0xa2, 0x73, 0x63) + o = msgp.AppendInt(o, z.StatusCode) + // string "hs" + o = append(o, 0xa2, 0x68, 0x73) + o = msgp.AppendMapHeader(o, uint32(len(z.Headers))) + for za0001, za0002 := range z.Headers { + o = msgp.AppendString(o, za0001) + o = msgp.AppendString(o, za0002) + } + // string "b" + o = append(o, 0xa1, 0x62) + o = msgp.AppendBytes(o, z.Body) + return +} + +// UnmarshalMsg implements msgp.Unmarshaler +func (z *response) UnmarshalMsg(bts []byte) (o []byte, err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, bts, err = msgp.ReadMapKeyZC(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "sc": + z.StatusCode, bts, err = msgp.ReadIntBytes(bts) + if err != nil { + err = msgp.WrapError(err, "StatusCode") + return + } + case "hs": + var zb0002 uint32 + zb0002, bts, err = msgp.ReadMapHeaderBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Headers") + return + } + if z.Headers == nil { + z.Headers = make(map[string]string, zb0002) + } else if len(z.Headers) > 0 { + for key := range z.Headers { + delete(z.Headers, key) + } + } + for zb0002 > 0 { + var za0001 string + var za0002 string + zb0002-- + za0001, bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Headers") + return + } + za0002, bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Headers", za0001) + return + } + z.Headers[za0001] = za0002 + } + case "b": + z.Body, bts, err = msgp.ReadBytesBytes(bts, z.Body) + if err != nil { + err = msgp.WrapError(err, "Body") + return + } + default: + bts, err = msgp.Skip(bts) + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + o = bts + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z *response) Msgsize() (s int) { + s = 1 + 3 + msgp.IntSize + 3 + msgp.MapHeaderSize + if z.Headers != nil { + for za0001, za0002 := range z.Headers { + _ = za0002 + s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002) + } + } + s += 2 + msgp.BytesPrefixSize + len(z.Body) + return +} diff --git a/middleware/idempotency/response_msgp_test.go b/middleware/idempotency/response_msgp_test.go new file mode 100644 index 0000000000..a932efb866 --- /dev/null +++ b/middleware/idempotency/response_msgp_test.go @@ -0,0 +1,67 @@ +package idempotency + +// Code generated by github.com/tinylib/msgp DO NOT EDIT. + +import ( + "testing" + + "github.com/gofiber/fiber/v2/internal/msgp" +) + +func TestMarshalUnmarshalresponse(t *testing.T) { + v := response{} + bts, err := v.MarshalMsg(nil) + if err != nil { + t.Fatal(err) + } + left, err := v.UnmarshalMsg(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after UnmarshalMsg(): %q", len(left), left) + } + + left, err = msgp.Skip(bts) + if err != nil { + t.Fatal(err) + } + if len(left) > 0 { + t.Errorf("%d bytes left over after Skip(): %q", len(left), left) + } +} + +func BenchmarkMarshalMsgresponse(b *testing.B) { + v := response{} + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + v.MarshalMsg(nil) + } +} + +func BenchmarkAppendMsgresponse(b *testing.B) { + v := response{} + bts := make([]byte, 0, v.Msgsize()) + bts, _ = v.MarshalMsg(bts[0:0]) + b.SetBytes(int64(len(bts))) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + bts, _ = v.MarshalMsg(bts[0:0]) + } +} + +func BenchmarkUnmarshalresponse(b *testing.B) { + v := response{} + bts, _ := v.MarshalMsg(nil) + b.ReportAllocs() + b.SetBytes(int64(len(bts))) + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := v.UnmarshalMsg(bts) + if err != nil { + b.Fatal(err) + } + } +} From 0628b95a3de3e1b8c454fd8eb994fc1121c0b211 Mon Sep 17 00:00:00 2001 From: cloudwindy <49033775+cloudwindy@users.noreply.github.com> Date: Sat, 14 Jan 2023 20:18:57 +0800 Subject: [PATCH 023/212] Fix: logger color output (#2296) --- middleware/logger/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/logger/config.go b/middleware/logger/config.go index e9766db6fc..21f34aad7c 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -136,7 +136,7 @@ func configDefault(config ...Config) Config { } // Enable colors if no custom format or output is given - if cfg.Output == nil && checkColorEnable(cfg.Format) { + if cfg.Output == ConfigDefault.Output && checkColorEnable(cfg.Format) { cfg.enableColors = true } From 1898c1f9a70725325a7d4af84d5e84602ddfb229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Sun, 15 Jan 2023 15:39:04 +0300 Subject: [PATCH 024/212] :bug: bug: fix gopsutil compilation (#2298) * :bug: bug: fix gopsutil compilation * nolint --- internal/gopsutil/mem/mem.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/gopsutil/mem/mem.go b/internal/gopsutil/mem/mem.go index 69cb881010..b039c4ce4e 100644 --- a/internal/gopsutil/mem/mem.go +++ b/internal/gopsutil/mem/mem.go @@ -6,7 +6,8 @@ import ( "github.com/gofiber/fiber/v2/internal/gopsutil/common" ) -var _ common.Invoke +//lint:ignore U1000 we need this elsewhere +var invoke common.Invoker = common.Invoke{} //nolint:all // Memory usage statistics. Total, Available and Used contain numbers of bytes // for human consumption. From 8a605c67d1313ad10a8dd22f9ab8c661c352645a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Sun, 15 Jan 2023 15:56:53 +0300 Subject: [PATCH 025/212] :adhesive_bandage: bug: use `app.getString, app.GetBytes` instead of `utils.UnsafeString, utils.UnsafeBytes` in `ctx.go` (#2297) * :adhesive_bandage: bug: use `app.getString, app.GetBytes` instead of `utils.UnsafeString, utils.UnsafeBytes` in `ctx.go` * fix Test_Client_Debug --- client_test.go | 2 +- ctx.go | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/client_test.go b/client_test.go index 323881127a..19584e1c4c 100644 --- a/client_test.go +++ b/client_test.go @@ -870,7 +870,7 @@ func Test_Client_Debug(t *testing.T) { str := output.String() - utils.AssertEqual(t, true, strings.Contains(str, "Connected to example.com(pipe)")) + utils.AssertEqual(t, true, strings.Contains(str, "Connected to example.com(InmemoryListener)")) utils.AssertEqual(t, true, strings.Contains(str, "GET / HTTP/1.1")) utils.AssertEqual(t, true, strings.Contains(str, "User-Agent: fiber")) utils.AssertEqual(t, true, strings.Contains(str, "Host: example.com\r\n\r\n")) diff --git a/ctx.go b/ctx.go index 88e54fa774..589dc07459 100644 --- a/ctx.go +++ b/ctx.go @@ -314,8 +314,8 @@ func (c *Ctx) Body() []byte { var body []byte // faster than peek c.Request().Header.VisitAll(func(key, value []byte) { - if utils.UnsafeString(key) == HeaderContentEncoding { - encoding = utils.UnsafeString(value) + if c.app.getString(key) == HeaderContentEncoding { + encoding = c.app.getString(value) } }) @@ -356,7 +356,7 @@ func decoderBuilder(parserConfig ParserConfig) interface{} { // If none of the content types above are matched, it will return a ErrUnprocessableEntity error func (c *Ctx) BodyParser(out interface{}) error { // Get content-type - ctype := utils.ToLower(utils.UnsafeString(c.fasthttp.Request.Header.ContentType())) + ctype := utils.ToLower(c.app.getString(c.fasthttp.Request.Header.ContentType())) ctype = utils.ParseVendorSpecificContentType(ctype) @@ -373,8 +373,8 @@ func (c *Ctx) BodyParser(out interface{}) error { return } - k := utils.UnsafeString(key) - v := utils.UnsafeString(val) + k := c.app.getString(key) + v := c.app.getString(val) if strings.Contains(k, "[") { k, err = parseParamSquareBrackets(k) @@ -813,7 +813,7 @@ func (c *Ctx) Is(extension string) bool { } return strings.HasPrefix( - utils.TrimLeft(utils.UnsafeString(c.fasthttp.Request.Header.ContentType()), ' '), + utils.TrimLeft(c.app.getString(c.fasthttp.Request.Header.ContentType()), ' '), extensionHeader, ) } @@ -1096,8 +1096,8 @@ func (c *Ctx) QueryParser(out interface{}) error { return } - k := utils.UnsafeString(key) - v := utils.UnsafeString(val) + k := c.app.getString(key) + v := c.app.getString(val) if strings.Contains(k, "[") { k, err = parseParamSquareBrackets(k) @@ -1151,8 +1151,8 @@ func parseParamSquareBrackets(k string) (string, error) { func (c *Ctx) ReqHeaderParser(out interface{}) error { data := make(map[string][]string) c.fasthttp.Request.Header.VisitAll(func(key, val []byte) { - k := utils.UnsafeString(key) - v := utils.UnsafeString(val) + k := c.app.getString(key) + v := c.app.getString(val) if strings.Contains(v, ",") && equalFieldType(out, reflect.Slice, k) { values := strings.Split(v, ",") @@ -1450,9 +1450,9 @@ func (c *Ctx) renderExtensions(bind interface{}) { // Loop through each local and set it in the map c.fasthttp.VisitUserValues(func(key []byte, val interface{}) { // check if bindMap doesn't contain the key - if _, ok := bindMap[utils.UnsafeString(key)]; !ok { + if _, ok := bindMap[c.app.getString(key)]; !ok { // Set the key and value in the bindMap - bindMap[utils.UnsafeString(key)] = val + bindMap[c.app.getString(key)] = val } }) } @@ -1627,7 +1627,7 @@ func (c *Ctx) Set(key string, val string) { } func (c *Ctx) setCanonical(key string, val string) { - c.fasthttp.Response.Header.SetCanonical(utils.UnsafeBytes(key), utils.UnsafeBytes(val)) + c.fasthttp.Response.Header.SetCanonical(c.app.getBytes(key), c.app.getBytes(val)) } // Subdomains returns a string slice of subdomains in the domain name of the request. @@ -1709,7 +1709,7 @@ func (c *Ctx) WriteString(s string) (int, error) { // XHR returns a Boolean property, that is true, if the request's X-Requested-With header field is XMLHttpRequest, // indicating that the request was issued by a client library (such as jQuery). func (c *Ctx) XHR() bool { - return utils.EqualFoldBytes(utils.UnsafeBytes(c.Get(HeaderXRequestedWith)), []byte("xmlhttprequest")) + return utils.EqualFoldBytes(c.app.getBytes(c.Get(HeaderXRequestedWith)), []byte("xmlhttprequest")) } // configDependentPaths set paths for route recognition and prepared paths for the user, From 5406560033b64097eecd4116d0fb2bc1b79300ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Sun, 15 Jan 2023 18:21:37 +0300 Subject: [PATCH 026/212] :broom: chore: make most tests parallel (#2299) * :broom: chore: make most tests parallel * revert some tests * revert some tests * revert some tests --- app_test.go | 66 +++++++++++++++++++ client_test.go | 27 +++++++- ctx_test.go | 35 +++++++--- error_test.go | 10 +++ helpers.go | 9 +++ helpers_test.go | 23 ++++--- hooks_test.go | 15 +---- internal/gopsutil/mem/mem_linux.go | 4 -- internal/memory/memory_test.go | 1 + internal/storage/memory/memory_test.go | 9 +++ listen_test.go | 10 +++ middleware/basicauth/basicauth_test.go | 1 - middleware/compress/compress_test.go | 8 +++ middleware/cors/cors_test.go | 6 ++ middleware/csrf/csrf_test.go | 11 ++++ .../encryptcookie/encryptcookie_test.go | 4 ++ middleware/etag/etag_test.go | 17 +++++ middleware/expvar/expvar_test.go | 5 +- middleware/favicon/favicon_test.go | 6 ++ middleware/filesystem/filesystem_test.go | 8 +++ middleware/limiter/limiter_test.go | 8 +++ middleware/logger/logger_test.go | 16 +++++ middleware/monitor/config_test.go | 9 +++ middleware/monitor/monitor_test.go | 2 - middleware/pprof/pprof_test.go | 12 +++- middleware/proxy/proxy_test.go | 2 +- middleware/recover/recover_test.go | 3 + middleware/requestid/requestid_test.go | 3 + middleware/session/session_test.go | 4 ++ middleware/session/store_test.go | 6 ++ middleware/skip/skip_test.go | 3 + middleware/timeout/timeout_test.go | 2 + mount_test.go | 11 ++++ path_test.go | 1 + prefork_test.go | 1 + router_test.go | 32 ++++++++- 36 files changed, 345 insertions(+), 45 deletions(-) diff --git a/app_test.go b/app_test.go index cfd491b2b2..7182ee6c8a 100644 --- a/app_test.go +++ b/app_test.go @@ -51,6 +51,7 @@ func testErrorResponse(t *testing.T, err error, resp *http.Response, expectedBod } func Test_App_MethodNotAllowed(t *testing.T) { + t.Parallel() app := New() app.Use(func(c *Ctx) error { @@ -100,6 +101,7 @@ func Test_App_MethodNotAllowed(t *testing.T) { } func Test_App_Custom_Middleware_404_Should_Not_SetMethodNotAllowed(t *testing.T) { + t.Parallel() app := New() app.Use(func(c *Ctx) error { @@ -124,6 +126,7 @@ func Test_App_Custom_Middleware_404_Should_Not_SetMethodNotAllowed(t *testing.T) } func Test_App_ServerErrorHandler_SmallReadBuffer(t *testing.T) { + t.Parallel() expectedError := regexp.MustCompile( `error when reading request headers: small read buffer\. Increase ReadBufferSize\. Buffer size=4096, contents: "GET / HTTP/1.1\\r\\nHost: example\.com\\r\\nVery-Long-Header: -+`, ) @@ -151,6 +154,7 @@ func Test_App_ServerErrorHandler_SmallReadBuffer(t *testing.T) { } func Test_App_Errors(t *testing.T) { + t.Parallel() app := New(Config{ BodyLimit: 4, }) @@ -174,6 +178,7 @@ func Test_App_Errors(t *testing.T) { } func Test_App_ErrorHandler_Custom(t *testing.T) { + t.Parallel() app := New(Config{ ErrorHandler: func(c *Ctx, err error) error { return c.Status(200).SendString("hi, i'm an custom error") @@ -194,6 +199,7 @@ func Test_App_ErrorHandler_Custom(t *testing.T) { } func Test_App_ErrorHandler_HandlerStack(t *testing.T) { + t.Parallel() app := New(Config{ ErrorHandler: func(c *Ctx, err error) error { utils.AssertEqual(t, "1: USE error", err.Error()) @@ -223,6 +229,7 @@ func Test_App_ErrorHandler_HandlerStack(t *testing.T) { } func Test_App_ErrorHandler_RouteStack(t *testing.T) { + t.Parallel() app := New(Config{ ErrorHandler: func(c *Ctx, err error) error { utils.AssertEqual(t, "1: USE error", err.Error()) @@ -248,6 +255,7 @@ func Test_App_ErrorHandler_RouteStack(t *testing.T) { } func Test_App_serverErrorHandler_Internal_Error(t *testing.T) { + t.Parallel() app := New() msg := "test err" c := app.AcquireCtx(&fasthttp.RequestCtx{}) @@ -258,6 +266,7 @@ func Test_App_serverErrorHandler_Internal_Error(t *testing.T) { } func Test_App_Nested_Params(t *testing.T) { + t.Parallel() app := New() app.Get("/test", func(c *Ctx) error { @@ -281,6 +290,7 @@ func Test_App_Nested_Params(t *testing.T) { } func Test_App_Use_Params(t *testing.T) { + t.Parallel() app := New() app.Use("/prefix/:param", func(c *Ctx) error { @@ -323,6 +333,7 @@ func Test_App_Use_Params(t *testing.T) { } func Test_App_Use_UnescapedPath(t *testing.T) { + t.Parallel() app := New(Config{UnescapePath: true, CaseSensitive: true}) app.Use("/cRéeR/:param", func(c *Ctx) error { @@ -351,6 +362,7 @@ func Test_App_Use_UnescapedPath(t *testing.T) { } func Test_App_Use_CaseSensitive(t *testing.T) { + t.Parallel() app := New(Config{CaseSensitive: true}) app.Use("/abc", func(c *Ctx) error { @@ -381,6 +393,7 @@ func Test_App_Use_CaseSensitive(t *testing.T) { } func Test_App_Not_Use_StrictRouting(t *testing.T) { + t.Parallel() app := New() app.Use("/abc", func(c *Ctx) error { @@ -414,6 +427,7 @@ func Test_App_Not_Use_StrictRouting(t *testing.T) { } func Test_App_Use_MultiplePrefix(t *testing.T) { + t.Parallel() app := New() app.Use([]string{"/john", "/doe"}, func(c *Ctx) error { @@ -460,6 +474,7 @@ func Test_App_Use_MultiplePrefix(t *testing.T) { } func Test_App_Use_StrictRouting(t *testing.T) { + t.Parallel() app := New(Config{StrictRouting: true}) app.Get("/abc", func(c *Ctx) error { @@ -493,6 +508,7 @@ func Test_App_Use_StrictRouting(t *testing.T) { } func Test_App_Add_Method_Test(t *testing.T) { + t.Parallel() defer func() { if err := recover(); err != nil { utils.AssertEqual(t, "add: invalid http method JANE\n", fmt.Sprintf("%v", err)) @@ -523,6 +539,7 @@ func Test_App_Add_Method_Test(t *testing.T) { // go test -run Test_App_GETOnly func Test_App_GETOnly(t *testing.T) { + t.Parallel() app := New(Config{ GETOnly: true, }) @@ -538,6 +555,7 @@ func Test_App_GETOnly(t *testing.T) { } func Test_App_Use_Params_Group(t *testing.T) { + t.Parallel() app := New() group := app.Group("/prefix/:param/*") @@ -556,6 +574,7 @@ func Test_App_Use_Params_Group(t *testing.T) { } func Test_App_Chaining(t *testing.T) { + t.Parallel() n := func(c *Ctx) error { return c.Next() } @@ -584,6 +603,7 @@ func Test_App_Chaining(t *testing.T) { } func Test_App_Order(t *testing.T) { + t.Parallel() app := New() app.Get("/test", func(c *Ctx) error { @@ -616,6 +636,7 @@ func Test_App_Order(t *testing.T) { } func Test_App_Methods(t *testing.T) { + t.Parallel() dummyHandler := testEmptyHandler app := New() @@ -655,6 +676,7 @@ func Test_App_Methods(t *testing.T) { } func Test_App_Route_Naming(t *testing.T) { + t.Parallel() app := New() handler := func(c *Ctx) error { return c.SendStatus(StatusOK) @@ -685,6 +707,7 @@ func Test_App_Route_Naming(t *testing.T) { } func Test_App_New(t *testing.T) { + t.Parallel() app := New() app.Get("/", testEmptyHandler) @@ -695,6 +718,7 @@ func Test_App_New(t *testing.T) { } func Test_App_Config(t *testing.T) { + t.Parallel() app := New(Config{ DisableStartupMessage: true, }) @@ -702,7 +726,9 @@ func Test_App_Config(t *testing.T) { } func Test_App_Shutdown(t *testing.T) { + t.Parallel() t.Run("success", func(t *testing.T) { + t.Parallel() app := New(Config{ DisableStartupMessage: true, }) @@ -710,6 +736,7 @@ func Test_App_Shutdown(t *testing.T) { }) t.Run("no server", func(t *testing.T) { + t.Parallel() app := &App{} if err := app.Shutdown(); err != nil { if err.Error() != "shutdown: server is not running" { @@ -720,6 +747,7 @@ func Test_App_Shutdown(t *testing.T) { } func Test_App_ShutdownWithTimeout(t *testing.T) { + t.Parallel() app := New() app.Get("/", func(ctx *Ctx) error { time.Sleep(5 * time.Second) @@ -760,6 +788,7 @@ func Test_App_ShutdownWithTimeout(t *testing.T) { // go test -run Test_App_Static_Index_Default func Test_App_Static_Index_Default(t *testing.T) { + t.Parallel() app := New() app.Static("/prefix", "./.github/workflows") @@ -789,6 +818,7 @@ func Test_App_Static_Index_Default(t *testing.T) { // go test -run Test_App_Static_Index func Test_App_Static_Direct(t *testing.T) { + t.Parallel() app := New() app.Static("/", "./.github") @@ -817,6 +847,7 @@ func Test_App_Static_Direct(t *testing.T) { // go test -run Test_App_Static_MaxAge func Test_App_Static_MaxAge(t *testing.T) { + t.Parallel() app := New() app.Static("/", "./.github", Static{MaxAge: 100}) @@ -831,6 +862,7 @@ func Test_App_Static_MaxAge(t *testing.T) { // go test -run Test_App_Static_Custom_CacheControl func Test_App_Static_Custom_CacheControl(t *testing.T) { + t.Parallel() app := New() app.Static("/", "./.github", Static{ModifyResponse: func(c *Ctx) error { @@ -851,6 +883,7 @@ func Test_App_Static_Custom_CacheControl(t *testing.T) { // go test -run Test_App_Static_Download func Test_App_Static_Download(t *testing.T) { + t.Parallel() app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) @@ -867,6 +900,7 @@ func Test_App_Static_Download(t *testing.T) { // go test -run Test_App_Static_Group func Test_App_Static_Group(t *testing.T) { + t.Parallel() app := New() grp := app.Group("/v1", func(c *Ctx) error { @@ -896,6 +930,7 @@ func Test_App_Static_Group(t *testing.T) { } func Test_App_Static_Wildcard(t *testing.T) { + t.Parallel() app := New() app.Static("*", "./.github/index.html") @@ -913,6 +948,7 @@ func Test_App_Static_Wildcard(t *testing.T) { } func Test_App_Static_Prefix_Wildcard(t *testing.T) { + t.Parallel() app := New() app.Static("/test/*", "./.github/index.html") @@ -938,6 +974,7 @@ func Test_App_Static_Prefix_Wildcard(t *testing.T) { } func Test_App_Static_Prefix(t *testing.T) { + t.Parallel() app := New() app.Static("/john", "./.github") @@ -968,6 +1005,7 @@ func Test_App_Static_Prefix(t *testing.T) { } func Test_App_Static_Trailing_Slash(t *testing.T) { + t.Parallel() app := New() app.Static("/john", "./.github") @@ -1014,6 +1052,7 @@ func Test_App_Static_Trailing_Slash(t *testing.T) { } func Test_App_Static_Next(t *testing.T) { + t.Parallel() app := New() app.Static("/", ".github", Static{ Next: func(c *Ctx) bool { @@ -1027,6 +1066,7 @@ func Test_App_Static_Next(t *testing.T) { }) t.Run("app.Static is skipped: invoking Get handler", func(t *testing.T) { + t.Parallel() req := httptest.NewRequest("GET", "/", nil) req.Header.Set("X-Custom-Header", "skip") resp, err := app.Test(req) @@ -1041,6 +1081,7 @@ func Test_App_Static_Next(t *testing.T) { }) t.Run("app.Static is not skipped: serving index.html", func(t *testing.T) { + t.Parallel() req := httptest.NewRequest("GET", "/", nil) req.Header.Set("X-Custom-Header", "don't skip") resp, err := app.Test(req) @@ -1057,6 +1098,7 @@ func Test_App_Static_Next(t *testing.T) { // go test -run Test_App_Mixed_Routes_WithSameLen func Test_App_Mixed_Routes_WithSameLen(t *testing.T) { + t.Parallel() app := New() // middleware @@ -1100,6 +1142,7 @@ func Test_App_Mixed_Routes_WithSameLen(t *testing.T) { } func Test_App_Group_Invalid(t *testing.T) { + t.Parallel() defer func() { if err := recover(); err != nil { utils.AssertEqual(t, "use: invalid handler int\n", fmt.Sprintf("%v", err)) @@ -1109,6 +1152,7 @@ func Test_App_Group_Invalid(t *testing.T) { } func Test_App_Group(t *testing.T) { + t.Parallel() dummyHandler := testEmptyHandler app := New() @@ -1169,6 +1213,7 @@ func Test_App_Group(t *testing.T) { } func Test_App_Route(t *testing.T) { + t.Parallel() dummyHandler := testEmptyHandler app := New() @@ -1218,6 +1263,7 @@ func Test_App_Route(t *testing.T) { } func Test_App_Deep_Group(t *testing.T) { + t.Parallel() runThroughCount := 0 dummyHandler := func(c *Ctx) error { runThroughCount++ @@ -1238,6 +1284,7 @@ func Test_App_Deep_Group(t *testing.T) { // go test -run Test_App_Next_Method func Test_App_Next_Method(t *testing.T) { + t.Parallel() app := New() app.config.DisableStartupMessage = true @@ -1289,6 +1336,7 @@ func Benchmark_App_ETag_Weak(b *testing.B) { // go test -run Test_NewError func Test_NewError(t *testing.T) { + t.Parallel() err := NewError(StatusForbidden, "permission denied") utils.AssertEqual(t, StatusForbidden, err.Code) utils.AssertEqual(t, "permission denied", err.Message) @@ -1296,6 +1344,7 @@ func Test_NewError(t *testing.T) { // go test -run Test_Test_Timeout func Test_Test_Timeout(t *testing.T) { + t.Parallel() app := New() app.config.DisableStartupMessage = true @@ -1322,6 +1371,7 @@ func (errorReader) Read([]byte) (int, error) { // go test -run Test_Test_DumpError func Test_Test_DumpError(t *testing.T) { + t.Parallel() app := New() app.config.DisableStartupMessage = true @@ -1334,6 +1384,7 @@ func Test_Test_DumpError(t *testing.T) { // go test -run Test_App_Handler func Test_App_Handler(t *testing.T) { + t.Parallel() h := New().Handler() utils.AssertEqual(t, "fasthttp.RequestHandler", reflect.TypeOf(h).String()) } @@ -1346,6 +1397,7 @@ func (i invalidView) Render(io.Writer, string, interface{}, ...string) error { p // go test -run Test_App_Init_Error_View func Test_App_Init_Error_View(t *testing.T) { + t.Parallel() app := New(Config{Views: invalidView{}}) defer func() { @@ -1358,6 +1410,7 @@ func Test_App_Init_Error_View(t *testing.T) { // go test -run Test_App_Stack func Test_App_Stack(t *testing.T) { + t.Parallel() app := New() app.Use("/path0", testEmptyHandler) @@ -1381,6 +1434,7 @@ func Test_App_Stack(t *testing.T) { // go test -run Test_App_HandlersCount func Test_App_HandlersCount(t *testing.T) { + t.Parallel() app := New() app.Use("/path0", testEmptyHandler) @@ -1393,6 +1447,7 @@ func Test_App_HandlersCount(t *testing.T) { // go test -run Test_App_ReadTimeout func Test_App_ReadTimeout(t *testing.T) { + t.Parallel() app := New(Config{ ReadTimeout: time.Nanosecond, IdleTimeout: time.Minute, @@ -1432,6 +1487,7 @@ func Test_App_ReadTimeout(t *testing.T) { // go test -run Test_App_BadRequest func Test_App_BadRequest(t *testing.T) { + t.Parallel() app := New(Config{ DisableStartupMessage: true, }) @@ -1467,6 +1523,7 @@ func Test_App_BadRequest(t *testing.T) { // go test -run Test_App_SmallReadBuffer func Test_App_SmallReadBuffer(t *testing.T) { + t.Parallel() app := New(Config{ ReadBufferSize: 1, DisableStartupMessage: true, @@ -1490,12 +1547,14 @@ func Test_App_SmallReadBuffer(t *testing.T) { } func Test_App_Server(t *testing.T) { + t.Parallel() app := New() utils.AssertEqual(t, false, app.Server() == nil) } func Test_App_Error_In_Fasthttp_Server(t *testing.T) { + t.Parallel() app := New() app.config.ErrorHandler = func(ctx *Ctx, err error) error { return errors.New("fake error") @@ -1509,6 +1568,7 @@ func Test_App_Error_In_Fasthttp_Server(t *testing.T) { // go test -race -run Test_App_New_Test_Parallel func Test_App_New_Test_Parallel(t *testing.T) { + t.Parallel() t.Run("Test_App_New_Test_Parallel_1", func(t *testing.T) { t.Parallel() app := New(Config{Immutable: true}) @@ -1524,6 +1584,7 @@ func Test_App_New_Test_Parallel(t *testing.T) { } func Test_App_ReadBodyStream(t *testing.T) { + t.Parallel() app := New(Config{StreamRequestBody: true}) app.Post("/", func(c *Ctx) error { // Calling c.Body() automatically reads the entire stream. @@ -1538,6 +1599,7 @@ func Test_App_ReadBodyStream(t *testing.T) { } func Test_App_DisablePreParseMultipartForm(t *testing.T) { + t.Parallel() // Must be used with both otherwise there is no point. testString := "this is a test" @@ -1585,6 +1647,7 @@ func Test_App_DisablePreParseMultipartForm(t *testing.T) { } func Test_App_Test_no_timeout_infinitely(t *testing.T) { + t.Parallel() var err error c := make(chan int) @@ -1617,6 +1680,7 @@ func Test_App_Test_no_timeout_infinitely(t *testing.T) { } func Test_App_SetTLSHandler(t *testing.T) { + t.Parallel() tlsHandler := &TLSHandler{clientHelloInfo: &tls.ClientHelloInfo{ ServerName: "example.golang", }} @@ -1631,6 +1695,7 @@ func Test_App_SetTLSHandler(t *testing.T) { } func Test_App_AddCustomRequestMethod(t *testing.T) { + t.Parallel() methods := append(DefaultMethods, "TEST") app := New(Config{ RequestMethods: methods, @@ -1644,6 +1709,7 @@ func Test_App_AddCustomRequestMethod(t *testing.T) { } func TestApp_GetRoutes(t *testing.T) { + t.Parallel() app := New() app.Use(func(c *Ctx) error { return c.Next() diff --git a/client_test.go b/client_test.go index 19584e1c4c..d9fd61a893 100644 --- a/client_test.go +++ b/client_test.go @@ -256,6 +256,7 @@ func Test_Client_UserAgent(t *testing.T) { go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() t.Run("default", func(t *testing.T) { + t.Parallel() for i := 0; i < 5; i++ { a := Get("http://example.com") @@ -270,6 +271,7 @@ func Test_Client_UserAgent(t *testing.T) { }) t.Run("custom", func(t *testing.T) { + t.Parallel() for i := 0; i < 5; i++ { c := AcquireClient() c.UserAgent = "ua" @@ -289,6 +291,7 @@ func Test_Client_UserAgent(t *testing.T) { } func Test_Client_Agent_Set_Or_Add_Headers(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { c.Request().Header.VisitAll(func(key, value []byte) { if k := string(key); k == "K1" || k == "K2" { @@ -314,6 +317,7 @@ func Test_Client_Agent_Set_Or_Add_Headers(t *testing.T) { } func Test_Client_Agent_Connection_Close(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { if c.Request().Header.ConnectionClose() { return c.SendString("close") @@ -329,6 +333,7 @@ func Test_Client_Agent_Connection_Close(t *testing.T) { } func Test_Client_Agent_UserAgent(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { return c.Send(c.Request().Header.UserAgent()) } @@ -342,6 +347,7 @@ func Test_Client_Agent_UserAgent(t *testing.T) { } func Test_Client_Agent_Cookie(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { return c.SendString( c.Cookies("k1") + c.Cookies("k2") + c.Cookies("k3") + c.Cookies("k4")) @@ -359,6 +365,7 @@ func Test_Client_Agent_Cookie(t *testing.T) { } func Test_Client_Agent_Referer(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { return c.Send(c.Request().Header.Referer()) } @@ -372,6 +379,7 @@ func Test_Client_Agent_Referer(t *testing.T) { } func Test_Client_Agent_ContentType(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { return c.Send(c.Request().Header.ContentType()) } @@ -413,6 +421,7 @@ func Test_Client_Agent_Host(t *testing.T) { } func Test_Client_Agent_QueryString(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { return c.Send(c.Request().URI().QueryString()) } @@ -426,6 +435,7 @@ func Test_Client_Agent_QueryString(t *testing.T) { } func Test_Client_Agent_BasicAuth(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { // Get authorization header auth := c.Get(HeaderAuthorization) @@ -445,6 +455,7 @@ func Test_Client_Agent_BasicAuth(t *testing.T) { } func Test_Client_Agent_BodyString(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { return c.Send(c.Request().Body()) } @@ -457,6 +468,7 @@ func Test_Client_Agent_BodyString(t *testing.T) { } func Test_Client_Agent_Body(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { return c.Send(c.Request().Body()) } @@ -469,6 +481,7 @@ func Test_Client_Agent_Body(t *testing.T) { } func Test_Client_Agent_BodyStream(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { return c.Send(c.Request().Body()) } @@ -531,6 +544,7 @@ func Test_Client_Agent_Dest(t *testing.T) { go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() t.Run("small dest", func(t *testing.T) { + t.Parallel() dest := []byte("de") a := Get("http://example.com") @@ -546,6 +560,7 @@ func Test_Client_Agent_Dest(t *testing.T) { }) t.Run("enough dest", func(t *testing.T) { + t.Parallel() dest := []byte("foobar") a := Get("http://example.com") @@ -622,6 +637,7 @@ func Test_Client_Agent_RetryIf(t *testing.T) { } func Test_Client_Agent_Json(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { utils.AssertEqual(t, MIMEApplicationJSON, string(c.Request().Header.ContentType())) @@ -636,6 +652,7 @@ func Test_Client_Agent_Json(t *testing.T) { } func Test_Client_Agent_Json_Error(t *testing.T) { + t.Parallel() a := Get("http://example.com"). JSONEncoder(json.Marshal). JSON(complex(1, 1)) @@ -648,6 +665,7 @@ func Test_Client_Agent_Json_Error(t *testing.T) { } func Test_Client_Agent_XML(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { utils.AssertEqual(t, MIMEApplicationXML, string(c.Request().Header.ContentType())) @@ -662,6 +680,7 @@ func Test_Client_Agent_XML(t *testing.T) { } func Test_Client_Agent_XML_Error(t *testing.T) { + t.Parallel() a := Get("http://example.com"). XML(complex(1, 1)) @@ -673,6 +692,7 @@ func Test_Client_Agent_XML_Error(t *testing.T) { } func Test_Client_Agent_Form(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { utils.AssertEqual(t, MIMEApplicationForm, string(c.Request().Header.ContentType())) @@ -856,6 +876,7 @@ func Test_Client_Agent_SendFile_Error(t *testing.T) { } func Test_Client_Debug(t *testing.T) { + t.Parallel() handler := func(c *Ctx) error { return c.SendString("debug") } @@ -1016,6 +1037,7 @@ func Test_Client_Agent_MaxRedirectsCount(t *testing.T) { go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() t.Run("success", func(t *testing.T) { + t.Parallel() a := Get("http://example.com?foo"). MaxRedirectsCount(1) @@ -1029,6 +1051,7 @@ func Test_Client_Agent_MaxRedirectsCount(t *testing.T) { }) t.Run("error", func(t *testing.T) { + t.Parallel() a := Get("http://example.com"). MaxRedirectsCount(1) @@ -1093,6 +1116,7 @@ func Test_Client_Agent_Struct(t *testing.T) { }) t.Run("error", func(t *testing.T) { + t.Parallel() a := Get("http://example.com/error") a.HostClient.Dial = func(addr string) (net.Conn, error) { return ln.Dial() } @@ -1108,6 +1132,7 @@ func Test_Client_Agent_Struct(t *testing.T) { }) t.Run("nil jsonDecoder", func(t *testing.T) { + t.Parallel() a := AcquireAgent() defer ReleaseAgent(a) defer a.ConnectionClose() @@ -1135,7 +1160,7 @@ func Test_Client_Agent_Parse(t *testing.T) { } func testAgent(t *testing.T, handler Handler, wrapAgent func(agent *Agent), excepted string, count ...int) { - t.Parallel() + t.Helper() ln := fasthttputil.NewInmemoryListener() diff --git a/ctx_test.go b/ctx_test.go index ab6bbfb418..14f41375e1 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -4,9 +4,6 @@ package fiber -// go test -v -run=^$ -bench=Benchmark_Ctx_Accepts -benchmem -count=4 -// go test -run Test_Ctx - import ( "bufio" "bytes" @@ -453,6 +450,7 @@ func Test_Ctx_ParamParser(t *testing.T) { // go test -run Test_Ctx_BodyParser_WithSetParserDecoder func Test_Ctx_BodyParser_WithSetParserDecoder(t *testing.T) { + t.Parallel() type CustomTime time.Time timeConverter := func(value string) reflect.Value { @@ -630,6 +628,7 @@ func Test_Ctx_UserContext(t *testing.T) { // go test -run Test_Ctx_SetUserContext func Test_Ctx_SetUserContext(t *testing.T) { + t.Parallel() app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) @@ -643,6 +642,7 @@ func Test_Ctx_SetUserContext(t *testing.T) { // go test -run Test_Ctx_UserContext_Multiple_Requests func Test_Ctx_UserContext_Multiple_Requests(t *testing.T) { + t.Parallel() testKey := struct{}{} testValue := "foobar-value" @@ -1564,6 +1564,7 @@ func Benchmark_Ctx_Is(b *testing.B) { // go test -run Test_Ctx_Locals func Test_Ctx_Locals(t *testing.T) { + t.Parallel() app := New() app.Use(func(c *Ctx) error { c.Locals("john", "doe") @@ -1955,6 +1956,7 @@ func Test_Ctx_Path(t *testing.T) { // go test -run Test_Ctx_Protocol func Test_Ctx_Protocol(t *testing.T) { + t.Parallel() app := New() freq := &fasthttp.RequestCtx{} @@ -2691,6 +2693,7 @@ func Test_Ctx_Location(t *testing.T) { // go test -run Test_Ctx_Next func Test_Ctx_Next(t *testing.T) { + t.Parallel() app := New() app.Use("/", func(c *Ctx) error { return c.Next() @@ -2707,6 +2710,7 @@ func Test_Ctx_Next(t *testing.T) { // go test -run Test_Ctx_Next_Error func Test_Ctx_Next_Error(t *testing.T) { + t.Parallel() app := New() app.Use("/", func(c *Ctx) error { c.Set("X-Next-Result", "Works") @@ -2952,7 +2956,6 @@ func Test_Ctx_RenderWithBind(t *testing.T) { func Test_Ctx_RenderWithOverwrittenBind(t *testing.T) { t.Parallel() - app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) @@ -2975,7 +2978,6 @@ func Test_Ctx_RenderWithOverwrittenBind(t *testing.T) { func Test_Ctx_RenderWithBindLocals(t *testing.T) { t.Parallel() - app := New(Config{ PassLocalsToViews: true, }) @@ -3154,6 +3156,7 @@ func Benchmark_Ctx_RenderBind(b *testing.B) { // go test -run Test_Ctx_RestartRouting func Test_Ctx_RestartRouting(t *testing.T) { + t.Parallel() app := New() calls := 0 app.Get("/", func(c *Ctx) error { @@ -3171,6 +3174,7 @@ func Test_Ctx_RestartRouting(t *testing.T) { // go test -run Test_Ctx_RestartRoutingWithChangedPath func Test_Ctx_RestartRoutingWithChangedPath(t *testing.T) { + t.Parallel() app := New() executedOldHandler := false executedNewHandler := false @@ -3197,6 +3201,7 @@ func Test_Ctx_RestartRoutingWithChangedPath(t *testing.T) { // go test -run Test_Ctx_RestartRoutingWithChangedPathAnd404 func Test_Ctx_RestartRoutingWithChangedPathAndCatchAll(t *testing.T) { + t.Parallel() app := New() app.Get("/new", func(c *Ctx) error { return nil @@ -3238,6 +3243,7 @@ func (t *testTemplateEngine) Load() error { // go test -run Test_Ctx_Render_Engine func Test_Ctx_Render_Engine(t *testing.T) { + t.Parallel() engine := &testTemplateEngine{} utils.AssertEqual(t, nil, engine.Load()) app := New() @@ -3253,6 +3259,7 @@ func Test_Ctx_Render_Engine(t *testing.T) { // go test -run Test_Ctx_Render_Engine_With_View_Layout func Test_Ctx_Render_Engine_With_View_Layout(t *testing.T) { + t.Parallel() engine := &testTemplateEngine{} utils.AssertEqual(t, nil, engine.Load()) app := New(Config{ViewsLayout: "main.tmpl"}) @@ -3307,7 +3314,10 @@ func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) { // go test -run Test_Ctx_Get_Location_From_Route_name func Test_Ctx_Get_Location_From_Route_name(t *testing.T) { + t.Parallel() + t.Run("case insensitive", func(t *testing.T) { + t.Parallel() app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) @@ -3325,6 +3335,7 @@ func Test_Ctx_Get_Location_From_Route_name(t *testing.T) { }) t.Run("case sensitive", func(t *testing.T) { + t.Parallel() app := New(Config{CaseSensitive: true}) c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) @@ -3344,6 +3355,7 @@ func Test_Ctx_Get_Location_From_Route_name(t *testing.T) { // go test -run Test_Ctx_Get_Location_From_Route_name_Optional_greedy func Test_Ctx_Get_Location_From_Route_name_Optional_greedy(t *testing.T) { + t.Parallel() app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) @@ -3362,6 +3374,7 @@ func Test_Ctx_Get_Location_From_Route_name_Optional_greedy(t *testing.T) { // go test -run Test_Ctx_Get_Location_From_Route_name_Optional_greedy_one_param func Test_Ctx_Get_Location_From_Route_name_Optional_greedy_one_param(t *testing.T) { + t.Parallel() app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) @@ -3387,6 +3400,7 @@ func (t errorTemplateEngine) Load() error { return nil } // go test -run Test_Ctx_Render_Engine_Error func Test_Ctx_Render_Engine_Error(t *testing.T) { + t.Parallel() app := New() app.config.Views = errorTemplateEngine{} c := app.AcquireCtx(&fasthttp.RequestCtx{}) @@ -3398,7 +3412,6 @@ func Test_Ctx_Render_Engine_Error(t *testing.T) { // go test -run Test_Ctx_Render_Go_Template func Test_Ctx_Render_Go_Template(t *testing.T) { t.Parallel() - file, err := os.CreateTemp(os.TempDir(), "fiber") utils.AssertEqual(t, nil, err) defer os.Remove(file.Name()) @@ -3806,6 +3819,7 @@ func Test_Ctx_QueryParser(t *testing.T) { // go test -run Test_Ctx_QueryParser_WithSetParserDecoder -v func Test_Ctx_QueryParser_WithSetParserDecoder(t *testing.T) { + t.Parallel() type NonRFCTime time.Time NonRFCConverter := func(value string) reflect.Value { @@ -4033,6 +4047,7 @@ func Test_Ctx_ReqHeaderParser(t *testing.T) { // go test -run Test_Ctx_ReqHeaderParser_WithSetParserDecoder -v func Test_Ctx_ReqHeaderParser_WithSetParserDecoder(t *testing.T) { + t.Parallel() type NonRFCTime time.Time NonRFCConverter := func(value string) reflect.Value { @@ -4174,6 +4189,7 @@ func Test_Ctx_ReqHeaderParser_Schema(t *testing.T) { } func Test_Ctx_EqualFieldType(t *testing.T) { + t.Parallel() var out int utils.AssertEqual(t, false, equalFieldType(&out, reflect.Int, "key")) @@ -4310,7 +4326,6 @@ func Benchmark_Ctx_ReqHeaderParser(b *testing.B) { // go test -run Test_Ctx_BodyStreamWriter func Test_Ctx_BodyStreamWriter(t *testing.T) { t.Parallel() - ctx := &fasthttp.RequestCtx{} ctx.SetBodyStreamWriter(func(w *bufio.Writer) { @@ -4361,7 +4376,6 @@ func Benchmark_Ctx_BodyStreamWriter(b *testing.B) { func Test_Ctx_String(t *testing.T) { t.Parallel() - app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) @@ -4371,8 +4385,8 @@ func Test_Ctx_String(t *testing.T) { func TestCtx_ParamsInt(t *testing.T) { // Create a test context and set some strings (or params) - // create a fake app to be used within this test + t.Parallel() app := New() // Create some test endpoints @@ -4472,6 +4486,7 @@ func TestCtx_ParamsInt(t *testing.T) { // go test -run Test_Ctx_GetRespHeader func Test_Ctx_GetRespHeader(t *testing.T) { + t.Parallel() app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) @@ -4484,6 +4499,7 @@ func Test_Ctx_GetRespHeader(t *testing.T) { // go test -run Test_Ctx_GetRespHeaders func Test_Ctx_GetRespHeaders(t *testing.T) { + t.Parallel() app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) @@ -4501,6 +4517,7 @@ func Test_Ctx_GetRespHeaders(t *testing.T) { // go test -run Test_Ctx_GetReqHeaders func Test_Ctx_GetReqHeaders(t *testing.T) { + t.Parallel() app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) diff --git a/error_test.go b/error_test.go index 14e2a322e6..fe0ec36605 100644 --- a/error_test.go +++ b/error_test.go @@ -11,56 +11,66 @@ import ( ) func TestConversionError(t *testing.T) { + t.Parallel() ok := errors.As(ConversionError{}, &schema.ConversionError{}) utils.AssertEqual(t, true, ok) } func TestUnknownKeyError(t *testing.T) { + t.Parallel() ok := errors.As(UnknownKeyError{}, &schema.UnknownKeyError{}) utils.AssertEqual(t, true, ok) } func TestEmptyFieldError(t *testing.T) { + t.Parallel() ok := errors.As(EmptyFieldError{}, &schema.EmptyFieldError{}) utils.AssertEqual(t, true, ok) } func TestMultiError(t *testing.T) { + t.Parallel() ok := errors.As(MultiError{}, &schema.MultiError{}) utils.AssertEqual(t, true, ok) } func TestInvalidUnmarshalError(t *testing.T) { + t.Parallel() var e *jerrors.InvalidUnmarshalError ok := errors.As(&InvalidUnmarshalError{}, &e) utils.AssertEqual(t, true, ok) } func TestMarshalerError(t *testing.T) { + t.Parallel() var e *jerrors.MarshalerError ok := errors.As(&MarshalerError{}, &e) utils.AssertEqual(t, true, ok) } func TestSyntaxError(t *testing.T) { + t.Parallel() var e *jerrors.SyntaxError ok := errors.As(&SyntaxError{}, &e) utils.AssertEqual(t, true, ok) } func TestUnmarshalTypeError(t *testing.T) { + t.Parallel() var e *jerrors.UnmarshalTypeError ok := errors.As(&UnmarshalTypeError{}, &e) utils.AssertEqual(t, true, ok) } func TestUnsupportedTypeError(t *testing.T) { + t.Parallel() var e *jerrors.UnsupportedTypeError ok := errors.As(&UnsupportedTypeError{}, &e) utils.AssertEqual(t, true, ok) } func TestUnsupportedValeError(t *testing.T) { + t.Parallel() var e *jerrors.UnsupportedValueError ok := errors.As(&UnsupportedValueError{}, &e) utils.AssertEqual(t, true, ok) diff --git a/helpers.go b/helpers.go index e9dca2ead1..b074c6ca8e 100644 --- a/helpers.go +++ b/helpers.go @@ -728,3 +728,12 @@ const ( ConstraintDatetime = "datetime" ConstraintRegex = "regex" ) + +func IndexRune(str string, needle int32) bool { + for _, b := range str { + if b == needle { + return true + } + } + return false +} diff --git a/helpers_test.go b/helpers_test.go index b56dcc5c51..fefb8f2527 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -16,8 +16,10 @@ import ( // go test -v -run=Test_Utils_ -count=3 func Test_Utils_ETag(t *testing.T) { + t.Parallel() app := New() t.Run("Not Status OK", func(t *testing.T) { + t.Parallel() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) err := c.SendString("Hello, World!") @@ -28,6 +30,7 @@ func Test_Utils_ETag(t *testing.T) { }) t.Run("No Body", func(t *testing.T) { + t.Parallel() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) setETag(c, false) @@ -35,6 +38,7 @@ func Test_Utils_ETag(t *testing.T) { }) t.Run("Has HeaderIfNoneMatch", func(t *testing.T) { + t.Parallel() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) err := c.SendString("Hello, World!") @@ -47,6 +51,7 @@ func Test_Utils_ETag(t *testing.T) { }) t.Run("No HeaderIfNoneMatch", func(t *testing.T) { + t.Parallel() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) err := c.SendString("Hello, World!") @@ -71,8 +76,10 @@ func Benchmark_Utils_ETag(b *testing.B) { // go test -v -run=Test_Utils_ETag_Weak -count=1 func Test_Utils_ETag_Weak(t *testing.T) { + t.Parallel() app := New() t.Run("Set Weak", func(t *testing.T) { + t.Parallel() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) err := c.SendString("Hello, World!") @@ -82,6 +89,7 @@ func Test_Utils_ETag_Weak(t *testing.T) { }) t.Run("Match Weak ETag", func(t *testing.T) { + t.Parallel() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) err := c.SendString("Hello, World!") @@ -94,6 +102,7 @@ func Test_Utils_ETag_Weak(t *testing.T) { }) t.Run("Not Match Weak ETag", func(t *testing.T) { + t.Parallel() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) err := c.SendString("Hello, World!") @@ -105,6 +114,7 @@ func Test_Utils_ETag_Weak(t *testing.T) { } func Test_Utils_UniqueRouteStack(t *testing.T) { + t.Parallel() route1 := &Route{} route2 := &Route{} route3 := &Route{} @@ -194,6 +204,7 @@ func Benchmark_Utils_Unescape(b *testing.B) { } func Test_Utils_Parse_Address(t *testing.T) { + t.Parallel() testCases := []struct { addr, host, port string }{ @@ -210,12 +221,14 @@ 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")) } func Test_Utils_TestConn_Deadline(t *testing.T) { + t.Parallel() conn := &testConn{} utils.AssertEqual(t, nil, conn.SetDeadline(time.Time{})) utils.AssertEqual(t, nil, conn.SetReadDeadline(time.Time{})) @@ -223,6 +236,7 @@ func Test_Utils_TestConn_Deadline(t *testing.T) { } func Test_Utils_IsNoCache(t *testing.T) { + t.Parallel() testCases := []struct { string bool @@ -293,12 +307,3 @@ func Benchmark_SlashRecognition(b *testing.B) { utils.AssertEqual(b, true, result) }) } - -func IndexRune(str string, needle int32) bool { - for _, b := range str { - if b == needle { - return true - } - } - return false -} diff --git a/hooks_test.go b/hooks_test.go index 626a1a4b64..df3c2aed96 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -16,7 +16,6 @@ var testSimpleHandler = func(c *Ctx) error { func Test_Hook_OnRoute(t *testing.T) { t.Parallel() - app := New() app.Hooks().OnRoute(func(r Route) error { @@ -35,7 +34,6 @@ func Test_Hook_OnRoute(t *testing.T) { func Test_Hook_OnRoute_Mount(t *testing.T) { t.Parallel() - app := New() subApp := New() app.Mount("/sub", subApp) @@ -58,7 +56,6 @@ func Test_Hook_OnRoute_Mount(t *testing.T) { func Test_Hook_OnName(t *testing.T) { t.Parallel() - app := New() buf := bytebufferpool.Get() @@ -84,7 +81,6 @@ func Test_Hook_OnName(t *testing.T) { func Test_Hook_OnName_Error(t *testing.T) { t.Parallel() - app := New() defer func() { if err := recover(); err != nil { @@ -101,7 +97,6 @@ func Test_Hook_OnName_Error(t *testing.T) { func Test_Hook_OnGroup(t *testing.T) { t.Parallel() - app := New() buf := bytebufferpool.Get() @@ -121,7 +116,6 @@ func Test_Hook_OnGroup(t *testing.T) { func Test_Hook_OnGroup_Mount(t *testing.T) { t.Parallel() - app := New() micro := New() micro.Mount("/john", app) @@ -139,7 +133,6 @@ func Test_Hook_OnGroup_Mount(t *testing.T) { func Test_Hook_OnGroupName(t *testing.T) { t.Parallel() - app := New() buf := bytebufferpool.Get() @@ -161,7 +154,6 @@ func Test_Hook_OnGroupName(t *testing.T) { func Test_Hook_OnGroupName_Error(t *testing.T) { t.Parallel() - app := New() defer func() { if err := recover(); err != nil { @@ -179,7 +171,6 @@ func Test_Hook_OnGroupName_Error(t *testing.T) { func Test_Hook_OnShutdown(t *testing.T) { t.Parallel() - app := New() buf := bytebufferpool.Get() @@ -198,7 +189,6 @@ func Test_Hook_OnShutdown(t *testing.T) { func Test_Hook_OnListen(t *testing.T) { t.Parallel() - app := New(Config{ DisableStartupMessage: true, }) @@ -223,12 +213,12 @@ func Test_Hook_OnListen(t *testing.T) { } func Test_Hook_OnHook(t *testing.T) { + app := New() + // Reset test var testPreforkMaster = true testOnPrefork = true - app := New() - go func() { time.Sleep(1000 * time.Millisecond) utils.AssertEqual(t, nil, app.Shutdown()) @@ -244,7 +234,6 @@ func Test_Hook_OnHook(t *testing.T) { func Test_Hook_OnMount(t *testing.T) { t.Parallel() - app := New() app.Get("/", testSimpleHandler).Name("x") diff --git a/internal/gopsutil/mem/mem_linux.go b/internal/gopsutil/mem/mem_linux.go index 18b347cfe9..a0fc7fd44c 100644 --- a/internal/gopsutil/mem/mem_linux.go +++ b/internal/gopsutil/mem/mem_linux.go @@ -281,9 +281,5 @@ func calcuateAvailVmem(ret *VirtualMemoryStat, retEx *VirtualMemoryExStat) uint6 availMemory += pageCache availMemory += ret.SReclaimable - uint64(math.Min(float64(ret.SReclaimable/2.0), float64(watermarkLow))) - if availMemory < 0 { - availMemory = 0 - } - return availMemory } diff --git a/internal/memory/memory_test.go b/internal/memory/memory_test.go index 12bcf3884c..a28b9e63e2 100644 --- a/internal/memory/memory_test.go +++ b/internal/memory/memory_test.go @@ -10,6 +10,7 @@ import ( // go test -run Test_Memory -v -race func Test_Memory(t *testing.T) { + t.Parallel() var store = New() var ( key = "john" diff --git a/internal/storage/memory/memory_test.go b/internal/storage/memory/memory_test.go index 1fc527f2cd..fb2b88a0e5 100644 --- a/internal/storage/memory/memory_test.go +++ b/internal/storage/memory/memory_test.go @@ -10,6 +10,7 @@ import ( var testStore = New() func Test_Storage_Memory_Set(t *testing.T) { + t.Parallel() var ( key = "john" val = []byte("doe") @@ -20,6 +21,7 @@ func Test_Storage_Memory_Set(t *testing.T) { } func Test_Storage_Memory_Set_Override(t *testing.T) { + t.Parallel() var ( key = "john" val = []byte("doe") @@ -33,6 +35,7 @@ func Test_Storage_Memory_Set_Override(t *testing.T) { } func Test_Storage_Memory_Get(t *testing.T) { + t.Parallel() var ( key = "john" val = []byte("doe") @@ -47,6 +50,7 @@ func Test_Storage_Memory_Get(t *testing.T) { } func Test_Storage_Memory_Set_Expiration(t *testing.T) { + t.Parallel() var ( key = "john" val = []byte("doe") @@ -70,6 +74,7 @@ func Test_Storage_Memory_Get_Expired(t *testing.T) { } func Test_Storage_Memory_Get_NotExist(t *testing.T) { + t.Parallel() result, err := testStore.Get("notexist") utils.AssertEqual(t, nil, err) @@ -77,6 +82,7 @@ func Test_Storage_Memory_Get_NotExist(t *testing.T) { } func Test_Storage_Memory_Delete(t *testing.T) { + t.Parallel() var ( key = "john" val = []byte("doe") @@ -94,6 +100,7 @@ func Test_Storage_Memory_Delete(t *testing.T) { } func Test_Storage_Memory_Reset(t *testing.T) { + t.Parallel() var ( val = []byte("doe") ) @@ -117,10 +124,12 @@ func Test_Storage_Memory_Reset(t *testing.T) { } func Test_Storage_Memory_Close(t *testing.T) { + t.Parallel() utils.AssertEqual(t, nil, testStore.Close()) } func Test_Storage_Memory_Conn(t *testing.T) { + t.Parallel() utils.AssertEqual(t, true, testStore.Conn() != nil) } diff --git a/listen_test.go b/listen_test.go index 8e4691d65f..0dd2cb2937 100644 --- a/listen_test.go +++ b/listen_test.go @@ -22,6 +22,7 @@ import ( // go test -run Test_App_Listen func Test_App_Listen(t *testing.T) { + t.Parallel() app := New(Config{DisableStartupMessage: true}) utils.AssertEqual(t, false, app.Listen(":99999") == nil) @@ -45,6 +46,7 @@ func Test_App_Listen_Prefork(t *testing.T) { // go test -run Test_App_ListenTLS func Test_App_ListenTLS(t *testing.T) { + t.Parallel() app := New() // invalid port @@ -74,6 +76,7 @@ func Test_App_ListenTLS_Prefork(t *testing.T) { // go test -run Test_App_ListenMutualTLS func Test_App_ListenMutualTLS(t *testing.T) { + t.Parallel() app := New() // invalid port @@ -103,6 +106,7 @@ func Test_App_ListenMutualTLS_Prefork(t *testing.T) { // go test -run Test_App_Listener func Test_App_Listener(t *testing.T) { + t.Parallel() app := New() go func() { @@ -115,6 +119,7 @@ func Test_App_Listener(t *testing.T) { } func Test_App_Listener_TLS_Listener(t *testing.T) { + t.Parallel() // Create tls certificate cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") if err != nil { @@ -172,6 +177,7 @@ 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)) @@ -185,6 +191,7 @@ 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)) @@ -195,6 +202,7 @@ 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() { @@ -205,6 +213,7 @@ func Test_App_Master_Process_Show_Startup_MessageWithAppNameNonAscii(t *testing. } func Test_App_print_Route(t *testing.T) { + t.Parallel() app := New(Config{EnablePrintRoutes: true}) app.Get("/", emptyHandler).Name("routeName") printRoutesMessage := captureOutput(func() { @@ -218,6 +227,7 @@ func Test_App_print_Route(t *testing.T) { } func Test_App_print_Route_with_group(t *testing.T) { + t.Parallel() app := New(Config{EnablePrintRoutes: true}) app.Get("/", emptyHandler) diff --git a/middleware/basicauth/basicauth_test.go b/middleware/basicauth/basicauth_test.go index 9046059e8d..20f103e6b6 100644 --- a/middleware/basicauth/basicauth_test.go +++ b/middleware/basicauth/basicauth_test.go @@ -16,7 +16,6 @@ import ( // go test -run Test_BasicAuth_Next func Test_BasicAuth_Next(t *testing.T) { t.Parallel() - app := fiber.New() app.Use(New(Config{ Next: func(_ *fiber.Ctx) bool { diff --git a/middleware/compress/compress_test.go b/middleware/compress/compress_test.go index e2ad5acc4f..371a755976 100644 --- a/middleware/compress/compress_test.go +++ b/middleware/compress/compress_test.go @@ -24,6 +24,7 @@ func init() { // go test -run Test_Compress_Gzip func Test_Compress_Gzip(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -49,9 +50,11 @@ func Test_Compress_Gzip(t *testing.T) { // go test -run Test_Compress_Different_Level func Test_Compress_Different_Level(t *testing.T) { + t.Parallel() levels := []Level{LevelBestSpeed, LevelBestCompression} for _, level := range levels { t.Run(fmt.Sprintf("level %d", level), func(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{Level: level})) @@ -78,6 +81,7 @@ func Test_Compress_Different_Level(t *testing.T) { } func Test_Compress_Deflate(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -101,6 +105,7 @@ func Test_Compress_Deflate(t *testing.T) { } func Test_Compress_Brotli(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -124,6 +129,7 @@ func Test_Compress_Brotli(t *testing.T) { } func Test_Compress_Disabled(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{Level: LevelDisabled})) @@ -147,6 +153,7 @@ func Test_Compress_Disabled(t *testing.T) { } func Test_Compress_Next_Error(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -170,6 +177,7 @@ func Test_Compress_Next_Error(t *testing.T) { // go test -run Test_Compress_Next func Test_Compress_Next(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ Next: func(_ *fiber.Ctx) bool { diff --git a/middleware/cors/cors_test.go b/middleware/cors/cors_test.go index fc6d1e53e8..7d42c3c318 100644 --- a/middleware/cors/cors_test.go +++ b/middleware/cors/cors_test.go @@ -10,6 +10,7 @@ import ( ) func Test_CORS_Defaults(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -17,6 +18,7 @@ func Test_CORS_Defaults(t *testing.T) { } func Test_CORS_Empty_Config(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{})) @@ -49,6 +51,7 @@ func testDefaultOrEmptyConfig(t *testing.T, app *fiber.App) { // go test -run -v Test_CORS_Wildcard func Test_CORS_Wildcard(t *testing.T) { + t.Parallel() // New fiber instance app := fiber.New() // OPTIONS (preflight) response headers when AllowOrigins is * @@ -88,6 +91,7 @@ func Test_CORS_Wildcard(t *testing.T) { // go test -run -v Test_CORS_Subdomain func Test_CORS_Subdomain(t *testing.T) { + t.Parallel() // New fiber instance app := fiber.New() // OPTIONS (preflight) response headers when AllowOrigins is set to a subdomain @@ -122,6 +126,7 @@ func Test_CORS_Subdomain(t *testing.T) { } func Test_CORS_AllowOriginScheme(t *testing.T) { + t.Parallel() tests := []struct { reqOrigin, pattern string shouldAllowOrigin bool @@ -224,6 +229,7 @@ func Test_CORS_AllowOriginScheme(t *testing.T) { // go test -run Test_CORS_Next func Test_CORS_Next(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ Next: func(_ *fiber.Ctx) bool { diff --git a/middleware/csrf/csrf_test.go b/middleware/csrf/csrf_test.go index dcdabc4628..ffa6af3e9f 100644 --- a/middleware/csrf/csrf_test.go +++ b/middleware/csrf/csrf_test.go @@ -11,6 +11,7 @@ import ( ) func Test_CSRF(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -63,6 +64,7 @@ func Test_CSRF(t *testing.T) { // go test -run Test_CSRF_Next func Test_CSRF_Next(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ Next: func(_ *fiber.Ctx) bool { @@ -76,6 +78,7 @@ func Test_CSRF_Next(t *testing.T) { } func Test_CSRF_Invalid_KeyLookup(t *testing.T) { + t.Parallel() defer func() { utils.AssertEqual(t, "[CSRF] KeyLookup must in the form of :", recover()) }() @@ -94,6 +97,7 @@ func Test_CSRF_Invalid_KeyLookup(t *testing.T) { } func Test_CSRF_From_Form(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{KeyLookup: "form:_csrf"})) @@ -127,6 +131,7 @@ func Test_CSRF_From_Form(t *testing.T) { } func Test_CSRF_From_Query(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{KeyLookup: "query:_csrf"})) @@ -163,6 +168,7 @@ func Test_CSRF_From_Query(t *testing.T) { } func Test_CSRF_From_Param(t *testing.T) { + t.Parallel() app := fiber.New() csrfGroup := app.Group("/:csrf", New(Config{KeyLookup: "param:csrf"})) @@ -199,6 +205,7 @@ func Test_CSRF_From_Param(t *testing.T) { } func Test_CSRF_From_Cookie(t *testing.T) { + t.Parallel() app := fiber.New() csrfGroup := app.Group("/", New(Config{KeyLookup: "cookie:csrf"})) @@ -237,6 +244,7 @@ func Test_CSRF_From_Cookie(t *testing.T) { } func Test_CSRF_From_Custom(t *testing.T) { + t.Parallel() app := fiber.New() extractor := func(c *fiber.Ctx) (string, error) { @@ -281,6 +289,7 @@ func Test_CSRF_From_Custom(t *testing.T) { } func Test_CSRF_ErrorHandler_InvalidToken(t *testing.T) { + t.Parallel() app := fiber.New() errHandler := func(ctx *fiber.Ctx, err error) error { @@ -312,6 +321,7 @@ func Test_CSRF_ErrorHandler_InvalidToken(t *testing.T) { } func Test_CSRF_ErrorHandler_EmptyToken(t *testing.T) { + t.Parallel() app := fiber.New() errHandler := func(ctx *fiber.Ctx, err error) error { @@ -343,6 +353,7 @@ func Test_CSRF_ErrorHandler_EmptyToken(t *testing.T) { // TODO: use this test case and make the unsafe header value bug from https://github.com/gofiber/fiber/issues/2045 reproducible and permanently fixed/tested by this testcase //func Test_CSRF_UnsafeHeaderValue(t *testing.T) { +// t.Parallel() // app := fiber.New() // // app.Use(New()) diff --git a/middleware/encryptcookie/encryptcookie_test.go b/middleware/encryptcookie/encryptcookie_test.go index 74188209bf..8241d6133c 100644 --- a/middleware/encryptcookie/encryptcookie_test.go +++ b/middleware/encryptcookie/encryptcookie_test.go @@ -13,6 +13,7 @@ import ( var testKey = GenerateKey() func Test_Middleware_Encrypt_Cookie(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ @@ -72,6 +73,7 @@ func Test_Middleware_Encrypt_Cookie(t *testing.T) { } func Test_Encrypt_Cookie_Next(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ @@ -95,6 +97,7 @@ func Test_Encrypt_Cookie_Next(t *testing.T) { } func Test_Encrypt_Cookie_Except(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ @@ -137,6 +140,7 @@ func Test_Encrypt_Cookie_Except(t *testing.T) { } func Test_Encrypt_Cookie_Custom_Encryptor(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ diff --git a/middleware/etag/etag_test.go b/middleware/etag/etag_test.go index 3b01cc89fe..567e1e2e94 100644 --- a/middleware/etag/etag_test.go +++ b/middleware/etag/etag_test.go @@ -13,6 +13,7 @@ import ( // go test -run Test_ETag_Next func Test_ETag_Next(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ Next: func(_ *fiber.Ctx) bool { @@ -27,6 +28,7 @@ func Test_ETag_Next(t *testing.T) { // go test -run Test_ETag_SkipError func Test_ETag_SkipError(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -42,6 +44,7 @@ func Test_ETag_SkipError(t *testing.T) { // go test -run Test_ETag_NotStatusOK func Test_ETag_NotStatusOK(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -57,6 +60,7 @@ func Test_ETag_NotStatusOK(t *testing.T) { // go test -run Test_ETag_NoBody func Test_ETag_NoBody(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -72,13 +76,17 @@ func Test_ETag_NoBody(t *testing.T) { // go test -run Test_ETag_NewEtag func Test_ETag_NewEtag(t *testing.T) { + t.Parallel() t.Run("without HeaderIfNoneMatch", func(t *testing.T) { + t.Parallel() testETagNewEtag(t, false, false) }) t.Run("with HeaderIfNoneMatch and not matched", func(t *testing.T) { + t.Parallel() testETagNewEtag(t, true, false) }) t.Run("with HeaderIfNoneMatch and matched", func(t *testing.T) { + t.Parallel() testETagNewEtag(t, true, true) }) } @@ -122,13 +130,17 @@ func testETagNewEtag(t *testing.T, headerIfNoneMatch, matched bool) { // go test -run Test_ETag_WeakEtag func Test_ETag_WeakEtag(t *testing.T) { + t.Parallel() t.Run("without HeaderIfNoneMatch", func(t *testing.T) { + t.Parallel() testETagWeakEtag(t, false, false) }) t.Run("with HeaderIfNoneMatch and not matched", func(t *testing.T) { + t.Parallel() testETagWeakEtag(t, true, false) }) t.Run("with HeaderIfNoneMatch and matched", func(t *testing.T) { + t.Parallel() testETagWeakEtag(t, true, true) }) } @@ -172,13 +184,17 @@ func testETagWeakEtag(t *testing.T, headerIfNoneMatch, matched bool) { // go test -run Test_ETag_CustomEtag func Test_ETag_CustomEtag(t *testing.T) { + t.Parallel() t.Run("without HeaderIfNoneMatch", func(t *testing.T) { + t.Parallel() testETagCustomEtag(t, false, false) }) t.Run("with HeaderIfNoneMatch and not matched", func(t *testing.T) { + t.Parallel() testETagCustomEtag(t, true, false) }) t.Run("with HeaderIfNoneMatch and matched", func(t *testing.T) { + t.Parallel() testETagCustomEtag(t, true, true) }) } @@ -226,6 +242,7 @@ func testETagCustomEtag(t *testing.T, headerIfNoneMatch, matched bool) { // go test -run Test_ETag_CustomEtagPut func Test_ETag_CustomEtagPut(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) diff --git a/middleware/expvar/expvar_test.go b/middleware/expvar/expvar_test.go index 7240c93a62..3306952efa 100644 --- a/middleware/expvar/expvar_test.go +++ b/middleware/expvar/expvar_test.go @@ -11,6 +11,7 @@ import ( ) func Test_Non_Expvar_Path(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -29,6 +30,7 @@ func Test_Non_Expvar_Path(t *testing.T) { } func Test_Expvar_Index(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -49,6 +51,7 @@ func Test_Expvar_Index(t *testing.T) { } func Test_Expvar_Filter(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -69,6 +72,7 @@ func Test_Expvar_Filter(t *testing.T) { } func Test_Expvar_Other_Path(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -85,7 +89,6 @@ func Test_Expvar_Other_Path(t *testing.T) { // go test -run Test_Expvar_Next func Test_Expvar_Next(t *testing.T) { t.Parallel() - app := fiber.New() app.Use(New(Config{ diff --git a/middleware/favicon/favicon_test.go b/middleware/favicon/favicon_test.go index bcd6a0a849..f53311e3ae 100644 --- a/middleware/favicon/favicon_test.go +++ b/middleware/favicon/favicon_test.go @@ -15,6 +15,7 @@ import ( // go test -run Test_Middleware_Favicon func Test_Middleware_Favicon(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -44,6 +45,7 @@ func Test_Middleware_Favicon(t *testing.T) { // go test -run Test_Middleware_Favicon_Not_Found func Test_Middleware_Favicon_Not_Found(t *testing.T) { + t.Parallel() defer func() { if err := recover(); err == nil { t.Fatal("should cache panic") @@ -57,6 +59,7 @@ func Test_Middleware_Favicon_Not_Found(t *testing.T) { // go test -run Test_Middleware_Favicon_Found func Test_Middleware_Favicon_Found(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ @@ -95,6 +98,7 @@ func (m mockFS) Open(name string) (http.File, error) { // go test -run Test_Middleware_Favicon_FileSystem func Test_Middleware_Favicon_FileSystem(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ @@ -111,6 +115,7 @@ func Test_Middleware_Favicon_FileSystem(t *testing.T) { // go test -run Test_Middleware_Favicon_CacheControl func Test_Middleware_Favicon_CacheControl(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ @@ -146,6 +151,7 @@ func Benchmark_Middleware_Favicon(b *testing.B) { // go test -run Test_Favicon_Next func Test_Favicon_Next(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ Next: func(_ *fiber.Ctx) bool { diff --git a/middleware/filesystem/filesystem_test.go b/middleware/filesystem/filesystem_test.go index 454811e71a..dcbfcace75 100644 --- a/middleware/filesystem/filesystem_test.go +++ b/middleware/filesystem/filesystem_test.go @@ -11,6 +11,7 @@ import ( // go test -run Test_FileSystem func Test_FileSystem(t *testing.T) { + t.Parallel() app := fiber.New() app.Use("/test", New(Config{ @@ -117,6 +118,7 @@ func Test_FileSystem(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + t.Parallel() resp, err := app.Test(httptest.NewRequest("GET", tt.url, nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, tt.statusCode, resp.StatusCode) @@ -131,6 +133,7 @@ func Test_FileSystem(t *testing.T) { // go test -run Test_FileSystem_Next func Test_FileSystem_Next(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ Root: http.Dir("../../.github/testdata/fs"), @@ -145,6 +148,7 @@ func Test_FileSystem_Next(t *testing.T) { } func Test_FileSystem_NonGetAndHead(t *testing.T) { + t.Parallel() app := fiber.New() app.Use("/test", New(Config{ @@ -157,6 +161,7 @@ func Test_FileSystem_NonGetAndHead(t *testing.T) { } func Test_FileSystem_Head(t *testing.T) { + t.Parallel() app := fiber.New() app.Use("/test", New(Config{ @@ -170,6 +175,7 @@ func Test_FileSystem_Head(t *testing.T) { } func Test_FileSystem_NoRoot(t *testing.T) { + t.Parallel() defer func() { utils.AssertEqual(t, "filesystem: Root cannot be nil", recover()) }() @@ -180,6 +186,7 @@ func Test_FileSystem_NoRoot(t *testing.T) { } func Test_FileSystem_UsingParam(t *testing.T) { + t.Parallel() app := fiber.New() app.Use("/:path", func(c *fiber.Ctx) error { @@ -193,6 +200,7 @@ func Test_FileSystem_UsingParam(t *testing.T) { } func Test_FileSystem_UsingParam_NonFile(t *testing.T) { + t.Parallel() app := fiber.New() app.Use("/:path", func(c *fiber.Ctx) error { diff --git a/middleware/limiter/limiter_test.go b/middleware/limiter/limiter_test.go index acf0f0026b..847aba354d 100644 --- a/middleware/limiter/limiter_test.go +++ b/middleware/limiter/limiter_test.go @@ -16,6 +16,7 @@ import ( // go test -run Test_Limiter_Concurrency_Store -race -v func Test_Limiter_Concurrency_Store(t *testing.T) { + t.Parallel() // Test concurrency using a custom store app := fiber.New() @@ -62,6 +63,7 @@ func Test_Limiter_Concurrency_Store(t *testing.T) { // go test -run Test_Limiter_Concurrency -race -v func Test_Limiter_Concurrency(t *testing.T) { + t.Parallel() // Test concurrency using a default store app := fiber.New() @@ -107,6 +109,7 @@ func Test_Limiter_Concurrency(t *testing.T) { // go test -run Test_Limiter_No_Skip_Choices -v func Test_Limiter_No_Skip_Choices(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ @@ -138,6 +141,7 @@ func Test_Limiter_No_Skip_Choices(t *testing.T) { // go test -run Test_Limiter_Skip_Failed_Requests -v func Test_Limiter_Skip_Failed_Requests(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ @@ -174,6 +178,7 @@ func Test_Limiter_Skip_Failed_Requests(t *testing.T) { // go test -run Test_Limiter_Skip_Successful_Requests -v func Test_Limiter_Skip_Successful_Requests(t *testing.T) { + t.Parallel() // Test concurrency using a default store app := fiber.New() @@ -239,6 +244,7 @@ func Benchmark_Limiter_Custom_Store(b *testing.B) { // go test -run Test_Limiter_Next func Test_Limiter_Next(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ Next: func(_ *fiber.Ctx) bool { @@ -252,6 +258,7 @@ func Test_Limiter_Next(t *testing.T) { } func Test_Limiter_Headers(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ @@ -306,6 +313,7 @@ func Benchmark_Limiter(b *testing.B) { // go test -run Test_Sliding_Window -race -v func Test_Sliding_Window(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ Max: 10, diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index d58feb262d..2295e8a4f5 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -22,6 +22,7 @@ import ( // go test -run Test_Logger func Test_Logger(t *testing.T) { + t.Parallel() app := fiber.New() buf := bytebufferpool.Get() @@ -44,6 +45,7 @@ func Test_Logger(t *testing.T) { // go test -run Test_Logger_locals func Test_Logger_locals(t *testing.T) { + t.Parallel() app := fiber.New() buf := bytebufferpool.Get() @@ -90,6 +92,7 @@ func Test_Logger_locals(t *testing.T) { // go test -run Test_Logger_Next func Test_Logger_Next(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ Next: func(_ *fiber.Ctx) bool { @@ -104,6 +107,7 @@ func Test_Logger_Next(t *testing.T) { // go test -run Test_Logger_Done func Test_Logger_Done(t *testing.T) { + t.Parallel() buf := bytes.NewBuffer(nil) app := fiber.New() app.Use(New(Config{ @@ -125,6 +129,7 @@ func Test_Logger_Done(t *testing.T) { // go test -run Test_Logger_ErrorTimeZone func Test_Logger_ErrorTimeZone(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ TimeZone: "invalid", @@ -144,6 +149,7 @@ func (o *fakeOutput) Write([]byte) (int, error) { // go test -run Test_Logger_ErrorOutput func Test_Logger_ErrorOutput(t *testing.T) { + t.Parallel() o := new(fakeOutput) app := fiber.New() app.Use(New(Config{ @@ -159,6 +165,7 @@ func Test_Logger_ErrorOutput(t *testing.T) { // go test -run Test_Logger_All func Test_Logger_All(t *testing.T) { + t.Parallel() buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) @@ -181,6 +188,7 @@ func Test_Logger_All(t *testing.T) { // go test -run Test_Query_Params func Test_Query_Params(t *testing.T) { + t.Parallel() buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) @@ -200,6 +208,7 @@ func Test_Query_Params(t *testing.T) { // go test -run Test_Response_Body func Test_Response_Body(t *testing.T) { + t.Parallel() buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) @@ -234,6 +243,7 @@ func Test_Response_Body(t *testing.T) { // go test -run Test_Logger_AppendUint func Test_Logger_AppendUint(t *testing.T) { + t.Parallel() app := fiber.New() buf := bytebufferpool.Get() @@ -256,6 +266,7 @@ func Test_Logger_AppendUint(t *testing.T) { // go test -run Test_Logger_Data_Race -race func Test_Logger_Data_Race(t *testing.T) { + t.Parallel() app := fiber.New() buf := bytebufferpool.Get() @@ -345,6 +356,7 @@ func Benchmark_Logger(b *testing.B) { // go test -run Test_Response_Header func Test_Response_Header(t *testing.T) { + t.Parallel() buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) @@ -372,6 +384,7 @@ func Test_Response_Header(t *testing.T) { // go test -run Test_Req_Header func Test_Req_Header(t *testing.T) { + t.Parallel() buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) @@ -394,6 +407,7 @@ func Test_Req_Header(t *testing.T) { // go test -run Test_ReqHeader_Header func Test_ReqHeader_Header(t *testing.T) { + t.Parallel() buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) @@ -416,6 +430,7 @@ func Test_ReqHeader_Header(t *testing.T) { // go test -run Test_CustomTags func Test_CustomTags(t *testing.T) { + t.Parallel() customTag := "it is a custom tag" buf := bytebufferpool.Get() @@ -445,6 +460,7 @@ func Test_CustomTags(t *testing.T) { // go test -run Test_Logger_ByteSent_Streaming func Test_Logger_ByteSent_Streaming(t *testing.T) { + t.Parallel() app := fiber.New() buf := bytebufferpool.Get() diff --git a/middleware/monitor/config_test.go b/middleware/monitor/config_test.go index 0759624c62..062d1e4070 100644 --- a/middleware/monitor/config_test.go +++ b/middleware/monitor/config_test.go @@ -12,6 +12,7 @@ func Test_Config_Default(t *testing.T) { t.Parallel() t.Run("use default", func(t *testing.T) { + t.Parallel() cfg := configDefault() utils.AssertEqual(t, defaultTitle, cfg.Title) @@ -25,6 +26,7 @@ func Test_Config_Default(t *testing.T) { }) t.Run("set title", func(t *testing.T) { + t.Parallel() title := "title" cfg := configDefault(Config{ Title: title, @@ -41,6 +43,7 @@ func Test_Config_Default(t *testing.T) { }) t.Run("set refresh less than default", func(t *testing.T) { + t.Parallel() cfg := configDefault(Config{ Refresh: 100 * time.Millisecond, }) @@ -56,6 +59,7 @@ func Test_Config_Default(t *testing.T) { }) t.Run("set refresh", func(t *testing.T) { + t.Parallel() refresh := time.Second cfg := configDefault(Config{ Refresh: refresh, @@ -72,6 +76,7 @@ func Test_Config_Default(t *testing.T) { }) t.Run("set font url", func(t *testing.T) { + t.Parallel() fontUrl := "https://example.com" cfg := configDefault(Config{ FontURL: fontUrl, @@ -88,6 +93,7 @@ func Test_Config_Default(t *testing.T) { }) t.Run("set chart js url", func(t *testing.T) { + t.Parallel() chartUrl := "http://example.com" cfg := configDefault(Config{ ChartJsURL: chartUrl, @@ -104,6 +110,7 @@ func Test_Config_Default(t *testing.T) { }) t.Run("set custom head", func(t *testing.T) { + t.Parallel() head := "head" cfg := configDefault(Config{ CustomHead: head, @@ -120,6 +127,7 @@ func Test_Config_Default(t *testing.T) { }) t.Run("set api only", func(t *testing.T) { + t.Parallel() cfg := configDefault(Config{ APIOnly: true, }) @@ -135,6 +143,7 @@ func Test_Config_Default(t *testing.T) { }) t.Run("set next", func(t *testing.T) { + t.Parallel() f := func(c *fiber.Ctx) bool { return true } diff --git a/middleware/monitor/monitor_test.go b/middleware/monitor/monitor_test.go index 661bed8373..d069bba08d 100644 --- a/middleware/monitor/monitor_test.go +++ b/middleware/monitor/monitor_test.go @@ -174,8 +174,6 @@ func Test_Monitor_Next(t *testing.T) { // go test -run Test_Monitor_APIOnly -race func Test_Monitor_APIOnly(t *testing.T) { - //t.Parallel() - app := fiber.New() app.Get("/", New(Config{ diff --git a/middleware/pprof/pprof_test.go b/middleware/pprof/pprof_test.go index 09d7206dc0..b76a6f1c65 100644 --- a/middleware/pprof/pprof_test.go +++ b/middleware/pprof/pprof_test.go @@ -11,6 +11,7 @@ import ( ) func Test_Non_Pprof_Path(t *testing.T) { + t.Parallel() app := fiber.New(fiber.Config{DisableStartupMessage: true}) app.Use(New()) @@ -29,6 +30,7 @@ func Test_Non_Pprof_Path(t *testing.T) { } func Test_Non_Pprof_Path_WithPrefix(t *testing.T) { + t.Parallel() app := fiber.New(fiber.Config{DisableStartupMessage: true}) app.Use(New(Config{Prefix: "/federated-fiber"})) @@ -47,6 +49,7 @@ func Test_Non_Pprof_Path_WithPrefix(t *testing.T) { } func Test_Pprof_Index(t *testing.T) { + t.Parallel() app := fiber.New(fiber.Config{DisableStartupMessage: true}) app.Use(New()) @@ -66,6 +69,7 @@ func Test_Pprof_Index(t *testing.T) { } func Test_Pprof_Index_WithPrefix(t *testing.T) { + t.Parallel() app := fiber.New(fiber.Config{DisableStartupMessage: true}) app.Use(New(Config{Prefix: "/federated-fiber"})) @@ -85,6 +89,7 @@ func Test_Pprof_Index_WithPrefix(t *testing.T) { } func Test_Pprof_Subs(t *testing.T) { + t.Parallel() app := fiber.New(fiber.Config{DisableStartupMessage: true}) app.Use(New()) @@ -100,6 +105,7 @@ func Test_Pprof_Subs(t *testing.T) { for _, sub := range subs { t.Run(sub, func(t *testing.T) { + t.Parallel() target := "/debug/pprof/" + sub if sub == "profile" { target += "?seconds=1" @@ -112,6 +118,7 @@ func Test_Pprof_Subs(t *testing.T) { } func Test_Pprof_Subs_WithPrefix(t *testing.T) { + t.Parallel() app := fiber.New(fiber.Config{DisableStartupMessage: true}) app.Use(New(Config{Prefix: "/federated-fiber"})) @@ -127,6 +134,7 @@ func Test_Pprof_Subs_WithPrefix(t *testing.T) { for _, sub := range subs { t.Run(sub, func(t *testing.T) { + t.Parallel() target := "/federated-fiber/debug/pprof/" + sub if sub == "profile" { target += "?seconds=1" @@ -139,6 +147,7 @@ func Test_Pprof_Subs_WithPrefix(t *testing.T) { } func Test_Pprof_Other(t *testing.T) { + t.Parallel() app := fiber.New(fiber.Config{DisableStartupMessage: true}) app.Use(New()) @@ -153,6 +162,7 @@ func Test_Pprof_Other(t *testing.T) { } func Test_Pprof_Other_WithPrefix(t *testing.T) { + t.Parallel() app := fiber.New(fiber.Config{DisableStartupMessage: true}) app.Use(New(Config{Prefix: "/federated-fiber"})) @@ -169,7 +179,6 @@ func Test_Pprof_Other_WithPrefix(t *testing.T) { // go test -run Test_Pprof_Next func Test_Pprof_Next(t *testing.T) { t.Parallel() - app := fiber.New() app.Use(New(Config{ @@ -186,7 +195,6 @@ func Test_Pprof_Next(t *testing.T) { // go test -run Test_Pprof_Next_WithPrefix func Test_Pprof_Next_WithPrefix(t *testing.T) { t.Parallel() - app := fiber.New() app.Use(New(Config{ diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index 595a46ff66..d7626da34e 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -126,7 +126,7 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) { // go test -run Test_Proxy_Forward_WithTlsConfig_To_Http func Test_Proxy_Forward_WithTlsConfig_To_Http(t *testing.T) { - //t.Parallel() + t.Parallel() _, targetAddr := createProxyTestServer(func(c *fiber.Ctx) error { return c.SendString("hello from target") diff --git a/middleware/recover/recover_test.go b/middleware/recover/recover_test.go index b08cb4b0fe..16bf164c12 100644 --- a/middleware/recover/recover_test.go +++ b/middleware/recover/recover_test.go @@ -10,6 +10,7 @@ import ( // go test -run Test_Recover func Test_Recover(t *testing.T) { + t.Parallel() app := fiber.New(fiber.Config{ ErrorHandler: func(c *fiber.Ctx, err error) error { utils.AssertEqual(t, "Hi, I'm an error!", err.Error()) @@ -30,6 +31,7 @@ func Test_Recover(t *testing.T) { // go test -run Test_Recover_Next func Test_Recover_Next(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ Next: func(_ *fiber.Ctx) bool { @@ -43,6 +45,7 @@ func Test_Recover_Next(t *testing.T) { } func Test_Recover_EnableStackTrace(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ EnableStackTrace: true, diff --git a/middleware/requestid/requestid_test.go b/middleware/requestid/requestid_test.go index ff9baef7dd..eddafff01b 100644 --- a/middleware/requestid/requestid_test.go +++ b/middleware/requestid/requestid_test.go @@ -10,6 +10,7 @@ import ( // go test -run Test_RequestID func Test_RequestID(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New()) @@ -36,6 +37,7 @@ func Test_RequestID(t *testing.T) { // go test -run Test_RequestID_Next func Test_RequestID_Next(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(New(Config{ Next: func(_ *fiber.Ctx) bool { @@ -51,6 +53,7 @@ func Test_RequestID_Next(t *testing.T) { // go test -run Test_RequestID_Locals func Test_RequestID_Locals(t *testing.T) { + t.Parallel() reqId := "ThisIsARequestId" ctxKey := "ThisIsAContextKey" diff --git a/middleware/session/session_test.go b/middleware/session/session_test.go index ff7ec15432..489ea46edf 100644 --- a/middleware/session/session_test.go +++ b/middleware/session/session_test.go @@ -278,6 +278,7 @@ func Test_Session_Save_Expiration(t *testing.T) { t.Parallel() t.Run("save to cookie", func(t *testing.T) { + t.Parallel() // session store store := New() // fiber instance @@ -315,6 +316,7 @@ func Test_Session_Reset(t *testing.T) { t.Parallel() t.Run("reset from cookie", func(t *testing.T) { + t.Parallel() // session store store := New() // fiber instance @@ -332,6 +334,7 @@ func Test_Session_Reset(t *testing.T) { }) t.Run("reset from header", func(t *testing.T) { + t.Parallel() // session store store := New(Config{ KeyLookup: "header:session_id", @@ -442,6 +445,7 @@ func Test_Session_Deletes_Single_Key(t *testing.T) { // go test -run Test_Session_Regenerate // Regression: https://github.com/gofiber/fiber/issues/1395 func Test_Session_Regenerate(t *testing.T) { + t.Parallel() // fiber instance app := fiber.New() t.Run("set fresh to be true when regenerating a session", func(t *testing.T) { diff --git a/middleware/session/store_test.go b/middleware/session/store_test.go index fb8ef1c450..4c755c3290 100644 --- a/middleware/session/store_test.go +++ b/middleware/session/store_test.go @@ -11,12 +11,14 @@ import ( // go test -run TestStore_getSessionID func TestStore_getSessionID(t *testing.T) { + t.Parallel() expectedID := "test-session-id" // fiber instance app := fiber.New() t.Run("from cookie", func(t *testing.T) { + t.Parallel() // session store store := New() // fiber context @@ -29,6 +31,7 @@ func TestStore_getSessionID(t *testing.T) { }) t.Run("from header", func(t *testing.T) { + t.Parallel() // session store store := New(Config{ KeyLookup: "header:session_id", @@ -43,6 +46,7 @@ func TestStore_getSessionID(t *testing.T) { }) t.Run("from url query", func(t *testing.T) { + t.Parallel() // session store store := New(Config{ KeyLookup: "query:session_id", @@ -60,10 +64,12 @@ func TestStore_getSessionID(t *testing.T) { // go test -run TestStore_Get // Regression: https://github.com/gofiber/fiber/issues/1408 func TestStore_Get(t *testing.T) { + t.Parallel() unexpectedID := "test-session-id" // fiber instance app := fiber.New() t.Run("session should persisted even session is invalid", func(t *testing.T) { + t.Parallel() // session store store := New() // fiber context diff --git a/middleware/skip/skip_test.go b/middleware/skip/skip_test.go index 7a334872dc..cfbbec9d8d 100644 --- a/middleware/skip/skip_test.go +++ b/middleware/skip/skip_test.go @@ -11,6 +11,7 @@ import ( // go test -run Test_Skip func Test_Skip(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(skip.New(errTeapotHandler, func(*fiber.Ctx) bool { return true })) @@ -23,6 +24,7 @@ func Test_Skip(t *testing.T) { // go test -run Test_SkipFalse func Test_SkipFalse(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(skip.New(errTeapotHandler, func(*fiber.Ctx) bool { return false })) @@ -35,6 +37,7 @@ func Test_SkipFalse(t *testing.T) { // go test -run Test_SkipNilFunc func Test_SkipNilFunc(t *testing.T) { + t.Parallel() app := fiber.New() app.Use(skip.New(errTeapotHandler, nil)) diff --git a/middleware/timeout/timeout_test.go b/middleware/timeout/timeout_test.go index 225eabc152..aa60a3504c 100644 --- a/middleware/timeout/timeout_test.go +++ b/middleware/timeout/timeout_test.go @@ -14,6 +14,7 @@ import ( // go test -run Test_Timeout func Test_Timeout(t *testing.T) { + t.Parallel() // fiber instance app := fiber.New() h := New(func(c *fiber.Ctx) error { @@ -44,6 +45,7 @@ var ErrFooTimeOut = errors.New("foo context canceled") // go test -run Test_TimeoutWithCustomError func Test_TimeoutWithCustomError(t *testing.T) { + t.Parallel() // fiber instance app := fiber.New() h := New(func(c *fiber.Ctx) error { diff --git a/mount_test.go b/mount_test.go index d53ea06214..580366e69d 100644 --- a/mount_test.go +++ b/mount_test.go @@ -16,6 +16,7 @@ import ( // go test -run Test_App_Mount func Test_App_Mount(t *testing.T) { + t.Parallel() micro := New() micro.Get("/doe", func(c *Ctx) error { return c.SendStatus(StatusOK) @@ -30,6 +31,7 @@ func Test_App_Mount(t *testing.T) { } func Test_App_Mount_RootPath_Nested(t *testing.T) { + t.Parallel() app := New() dynamic := New() apiserver := New() @@ -50,6 +52,7 @@ func Test_App_Mount_RootPath_Nested(t *testing.T) { // go test -run Test_App_Mount_Nested func Test_App_Mount_Nested(t *testing.T) { + t.Parallel() app := New() one := New() two := New() @@ -88,6 +91,7 @@ func Test_App_Mount_Nested(t *testing.T) { // go test -run Test_App_MountPath func Test_App_MountPath(t *testing.T) { + t.Parallel() app := New() one := New() two := New() @@ -104,6 +108,7 @@ func Test_App_MountPath(t *testing.T) { } func Test_App_ErrorHandler_GroupMount(t *testing.T) { + t.Parallel() micro := New(Config{ ErrorHandler: func(c *Ctx, err error) error { utils.AssertEqual(t, "0: GET error", err.Error()) @@ -123,6 +128,7 @@ func Test_App_ErrorHandler_GroupMount(t *testing.T) { } func Test_App_ErrorHandler_GroupMountRootLevel(t *testing.T) { + t.Parallel() micro := New(Config{ ErrorHandler: func(c *Ctx, err error) error { utils.AssertEqual(t, "0: GET error", err.Error()) @@ -143,6 +149,7 @@ func Test_App_ErrorHandler_GroupMountRootLevel(t *testing.T) { // go test -run Test_App_Group_Mount func Test_App_Group_Mount(t *testing.T) { + t.Parallel() micro := New() micro.Get("/doe", func(c *Ctx) error { return c.SendStatus(StatusOK) @@ -159,6 +166,7 @@ func Test_App_Group_Mount(t *testing.T) { } func Test_App_UseParentErrorHandler(t *testing.T) { + t.Parallel() app := New(Config{ ErrorHandler: func(ctx *Ctx, err error) error { return ctx.Status(500).SendString("hi, i'm a custom error") @@ -177,6 +185,7 @@ func Test_App_UseParentErrorHandler(t *testing.T) { } func Test_App_UseMountedErrorHandler(t *testing.T) { + t.Parallel() app := New() fiber := New(Config{ @@ -195,6 +204,7 @@ func Test_App_UseMountedErrorHandler(t *testing.T) { } func Test_App_UseMountedErrorHandlerRootLevel(t *testing.T) { + t.Parallel() app := New() fiber := New(Config{ @@ -213,6 +223,7 @@ func Test_App_UseMountedErrorHandlerRootLevel(t *testing.T) { } func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { + t.Parallel() app := New() tsf := func(ctx *Ctx, err error) error { diff --git a/path_test.go b/path_test.go index 8d269be97b..656b82e12a 100644 --- a/path_test.go +++ b/path_test.go @@ -14,6 +14,7 @@ import ( // go test -race -run Test_Path_parseRoute func Test_Path_parseRoute(t *testing.T) { + t.Parallel() var rp routeParser rp = parseRoute("/shop/product/::filter/color::color/size::size") diff --git a/prefork_test.go b/prefork_test.go index e61780f2cd..4842774f26 100644 --- a/prefork_test.go +++ b/prefork_test.go @@ -68,6 +68,7 @@ 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) diff --git a/router_test.go b/router_test.go index fbd82e4bfd..26407ce10e 100644 --- a/router_test.go +++ b/router_test.go @@ -4,8 +4,6 @@ package fiber -// go test -v ./... -run=^$ -bench=Benchmark_Router -benchmem -count=2 - import ( "encoding/json" "errors" @@ -33,6 +31,8 @@ func init() { } func Test_Route_Match_SameLength(t *testing.T) { + t.Parallel() + app := New() app.Get("/:param", func(c *Ctx) error { @@ -58,6 +58,8 @@ func Test_Route_Match_SameLength(t *testing.T) { } func Test_Route_Match_Star(t *testing.T) { + t.Parallel() + app := New() app.Get("/*", func(c *Ctx) error { @@ -104,6 +106,8 @@ func Test_Route_Match_Star(t *testing.T) { } func Test_Route_Match_Root(t *testing.T) { + t.Parallel() + app := New() app.Get("/", func(c *Ctx) error { @@ -120,6 +124,8 @@ func Test_Route_Match_Root(t *testing.T) { } func Test_Route_Match_Parser(t *testing.T) { + t.Parallel() + app := New() app.Get("/foo/:ParamName", func(c *Ctx) error { @@ -147,6 +153,8 @@ func Test_Route_Match_Parser(t *testing.T) { } func Test_Route_Match_Middleware(t *testing.T) { + t.Parallel() + app := New() app.Use("/foo/*", func(c *Ctx) error { @@ -172,6 +180,8 @@ func Test_Route_Match_Middleware(t *testing.T) { } func Test_Route_Match_UnescapedPath(t *testing.T) { + t.Parallel() + app := New(Config{UnescapePath: true}) app.Use("/créer", func(c *Ctx) error { @@ -198,6 +208,8 @@ func Test_Route_Match_UnescapedPath(t *testing.T) { } func Test_Route_Match_WithEscapeChar(t *testing.T) { + t.Parallel() + app := New() // static route and escaped part app.Get("/v1/some/resource/name\\:customVerb", func(c *Ctx) error { @@ -242,6 +254,8 @@ func Test_Route_Match_WithEscapeChar(t *testing.T) { } func Test_Route_Match_Middleware_HasPrefix(t *testing.T) { + t.Parallel() + app := New() app.Use("/foo", func(c *Ctx) error { @@ -258,6 +272,8 @@ func Test_Route_Match_Middleware_HasPrefix(t *testing.T) { } func Test_Route_Match_Middleware_Root(t *testing.T) { + t.Parallel() + app := New() app.Use("/", func(c *Ctx) error { @@ -274,6 +290,8 @@ func Test_Route_Match_Middleware_Root(t *testing.T) { } func Test_Router_Register_Missing_Handler(t *testing.T) { + t.Parallel() + app := New() defer func() { if err := recover(); err != nil { @@ -284,6 +302,8 @@ func Test_Router_Register_Missing_Handler(t *testing.T) { } func Test_Ensure_Router_Interface_Implementation(t *testing.T) { + t.Parallel() + var app interface{} = (*App)(nil) _, ok := app.(Router) utils.AssertEqual(t, true, ok) @@ -294,6 +314,8 @@ func Test_Ensure_Router_Interface_Implementation(t *testing.T) { } func Test_Router_Handler_SetETag(t *testing.T) { + t.Parallel() + app := New() app.config.ETag = true @@ -309,6 +331,8 @@ func Test_Router_Handler_SetETag(t *testing.T) { } func Test_Router_Handler_Catch_Error(t *testing.T) { + t.Parallel() + app := New() app.config.ErrorHandler = func(ctx *Ctx, err error) error { return errors.New("fake error") @@ -326,6 +350,8 @@ func Test_Router_Handler_Catch_Error(t *testing.T) { } func Test_Route_Static_Root(t *testing.T) { + t.Parallel() + dir := "./.github/testdata/fs/css" app := New() app.Static("/", dir, Static{ @@ -361,6 +387,8 @@ func Test_Route_Static_Root(t *testing.T) { } func Test_Route_Static_HasPrefix(t *testing.T) { + t.Parallel() + dir := "./.github/testdata/fs/css" app := New() app.Static("/static", dir, Static{ From 07ab88278bff1ee8c82ac1256a2239708294feff Mon Sep 17 00:00:00 2001 From: Xiaoyue Lin <36526527+100gle@users.noreply.github.com> Date: Wed, 18 Jan 2023 15:23:19 +0800 Subject: [PATCH 027/212] =?UTF-8?q?=F0=9F=93=9D=20docs(filesystem):=20fix?= =?UTF-8?q?=20statik=20filesystem=20middleware=20example=20typo=20(#2302)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs(filesystem): fix statik filesystem middleware example typo --- middleware/filesystem/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/middleware/filesystem/README.md b/middleware/filesystem/README.md index 87caf1c841..95e4aa7e91 100644 --- a/middleware/filesystem/README.md +++ b/middleware/filesystem/README.md @@ -216,12 +216,13 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/filesystem" - "/statik" + // Use blank to invoke init function and register data to statik + _ "/statik" fs "github.com/rakyll/statik/fs" ) func main() { - statik, err := fs.New() + statikFS, err := fs.New() if err != nil { panic(err) } From 1c3140c02125be10c2e30b7cd623f32f46192f71 Mon Sep 17 00:00:00 2001 From: Rendi Putra Pradana <34341857+rendiputra@users.noreply.github.com> Date: Thu, 19 Jan 2023 19:54:56 +0700 Subject: [PATCH 028/212] =?UTF-8?q?=F0=9F=93=9D=20Docs:=20Add=20discord=20?= =?UTF-8?q?channel=20link=20(ID)=20(#2303)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: translate some words to bahasa Translate: download -> unduh WebSocket support -> Mendukung WebSocket Route Naming -> Penamaan Route default -> bawaan * 📝 docs: translate some words to bahasa(ID) * 📝 Docs: Add discord channel link --- .github/README_id.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README_id.md b/.github/README_id.md index 49d98dc1ad..ac62971ab7 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -144,7 +144,7 @@ Bagi yang baru yang beralih dari [Node.js](https://nodejs.org/en/about/) ke [Go] Fiber terinspirasi dari Express, salah satu kerangka kerja web yang paling terkenal di Internet. Kami menggabungkan **kemudahan** dari Express dan **kinerja luar biasa** dari Go. Apabila anda pernah membuat aplikasi dengan Node.js (_dengan Express atau yang lainnya_), maka banyak metode dan prinsip yang akan terasa **sangat umum** bagi anda. -Kami **mendengarkan** para pengguna di [GitHub Issues](https://github.com/gofiber/fiber/issues) (_dan berbagai platform lainnya_) untuk menciptakan kerangka kerja web yang **cepat**, **fleksibel** dan **bersahabat** untuk berbagai macam keperluan, **tenggat waktu** dan **keahlian** para pengguna! Sama halnya seperti yang dilakukan Express di dunia JavaScript. +Kami **mendengarkan** para pengguna di [GitHub Issues](https://github.com/gofiber/fiber/issues), Discord [channel](https://gofiber.io/discord), _dan berbagai platform lainnya_ untuk menciptakan kerangka kerja web yang **cepat**, **fleksibel** dan **bersahabat** untuk berbagai macam keperluan, **tenggat waktu** dan **keahlian** para pengguna! Sama halnya seperti yang dilakukan Express di dunia JavaScript. ## ⚠️ Limitasi From c5691c7de55701400c4636793225058f5192f1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Thu, 19 Jan 2023 21:41:33 +0100 Subject: [PATCH 029/212] change output folder for the benchmarks result (gh-pages) --- .github/workflows/benchmark.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 0adeb1782b..d375be446a 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -23,6 +23,7 @@ jobs: tool: 'go' output-file-path: output.txt github-token: ${{ secrets.BENCHMARK_TOKEN }} + benchmark-data-dir-path: 'benchmarks' fail-on-alert: true comment-on-alert: true auto-push: true From a0004cf8a82d57a00d7078b8cdbdbbe356934957 Mon Sep 17 00:00:00 2001 From: Iliya Date: Mon, 23 Jan 2023 21:36:12 +0330 Subject: [PATCH 030/212] =?UTF-8?q?=F0=9F=94=A5=20write=20integer=20Query?= =?UTF-8?q?=20Parser.=20(#2306)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feature: write integer Query Parser. * request changes on https://github.com/gofiber/fiber/pull/2306#discussion_r1082171003 * ref(test): separate test cases for QueryInt --- ctx.go | 22 ++++++++++++++++++++++ ctx_test.go | 17 ++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/ctx.go b/ctx.go index 589dc07459..4d91d311b0 100644 --- a/ctx.go +++ b/ctx.go @@ -1086,6 +1086,28 @@ func (c *Ctx) Query(key string, defaultValue ...string) string { return defaultString(c.app.getString(c.fasthttp.QueryArgs().Peek(key)), defaultValue) } +// QueryInt returns integer value of key string parameter in the url. +// Default to empty or invalid key is 0. +// +// GET /?name=alex&wanna_cake=2&id= +// QueryInt("wanna_cake", 1) == 2 +// QueryInt("name", 1) == 1 +// QueryInt("id", 1) == 1 +// QueryInt("id") == 0 +func (c *Ctx) QueryInt(key string, defaultValue ...int) int { + // Use Atoi to convert the param to an int or return zero and an error + value, err := strconv.Atoi(c.app.getString(c.fasthttp.QueryArgs().Peek(key))) + if err != nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } else { + 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 14f41375e1..b8ae027bd5 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2115,12 +2115,27 @@ func Test_Ctx_Query(t *testing.T) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Request().URI().SetQueryString("search=john&age=20") + c.Request().URI().SetQueryString("search=john&age=20&id=") utils.AssertEqual(t, "john", c.Query("search")) utils.AssertEqual(t, "20", c.Query("age")) utils.AssertEqual(t, "default", c.Query("unknown", "default")) } +func Test_Ctx_QueryInt(t *testing.T) { + t.Parallel() + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Request().URI().SetQueryString("search=john&age=20&id=") + + utils.AssertEqual(t, 0, c.QueryInt("foo")) + utils.AssertEqual(t, 20, c.QueryInt("age", 12)) + utils.AssertEqual(t, 0, c.QueryInt("search")) + utils.AssertEqual(t, 1, c.QueryInt("search", 1)) + utils.AssertEqual(t, 0, c.QueryInt("id")) + utils.AssertEqual(t, 2, c.QueryInt("id", 2)) +} + // go test -run Test_Ctx_Range func Test_Ctx_Range(t *testing.T) { t.Parallel() From 66cc869b1f76905368696ba6c53397bbd6f45ca4 Mon Sep 17 00:00:00 2001 From: pan93412 Date: Thu, 26 Jan 2023 03:35:00 +0800 Subject: [PATCH 031/212] Doc: Remove the redundant space beside a comma (#2309) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 📚 Doc: Remove the redundant space beside a comma --- .github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index aec54dc70b..41625d5366 100644 --- a/.github/README.md +++ b/.github/README.md @@ -156,7 +156,7 @@ We **listen** to our users in [issues](https://github.com/gofiber/fiber/issues), ## 👀 Examples -Listed below are some of the common examples. If you want to see more code examples , please visit our [Recipes repository](https://github.com/gofiber/recipes) or visit our hosted [API documentation](https://docs.gofiber.io). +Listed below are some of the common examples. If you want to see more code examples, please visit our [Recipes repository](https://github.com/gofiber/recipes) or visit our hosted [API documentation](https://docs.gofiber.io). #### 📖 [**Basic Routing**](https://docs.gofiber.io/#basic-routing) From e2cb81ddd31c76689deb351b3da665f84e8795e7 Mon Sep 17 00:00:00 2001 From: Limux <66315042+rhabichl@users.noreply.github.com> Date: Wed, 25 Jan 2023 20:38:29 +0100 Subject: [PATCH 032/212] =?UTF-8?q?=F0=9F=9A=A8=20added=20testcases=20and?= =?UTF-8?q?=20minor=20algorithm=20improvment=20(#2308)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Deleted redundant check for an ipv4 address octet block that is bigger than 255 in utils/ip.go. Also added a testcase for octetblocks that are bigger than 255. * Added extra testcases --- utils/ips.go | 6 ++++-- utils/ips_test.go | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/utils/ips.go b/utils/ips.go index 6d2446470a..4886c117f7 100644 --- a/utils/ips.go +++ b/utils/ips.go @@ -1,6 +1,8 @@ package utils -import "net" +import ( + "net" +) // IsIPv4 works the same way as net.ParseIP, // but without check for IPv6 case and without returning net.IP slice, whereby IsIPv4 makes no allocations. @@ -26,7 +28,7 @@ func IsIPv4(s string) bool { } } - if ci == 0 || n > 0xFF || (ci > 1 && s[0] == '0') { + if ci == 0 || (ci > 1 && s[0] == '0') { return false } diff --git a/utils/ips_test.go b/utils/ips_test.go index 49c258e604..862319f3fd 100644 --- a/utils/ips_test.go +++ b/utils/ips_test.go @@ -25,6 +25,11 @@ func Test_IsIPv4(t *testing.T) { AssertEqual(t, false, IsIPv4("")) AssertEqual(t, false, IsIPv4("2345:0425:2CA1::0567:5673:23b5")) AssertEqual(t, false, IsIPv4("invalid")) + AssertEqual(t, false, IsIPv4("189.12.34.260")) + AssertEqual(t, false, IsIPv4("189.12.260.260")) + AssertEqual(t, false, IsIPv4("189.260.260.260")) + AssertEqual(t, false, IsIPv4("999.999.999.999")) + AssertEqual(t, false, IsIPv4("9999.9999.9999.9999")) } // go test -v -run=^$ -bench=UnsafeString -benchmem -count=2 From b564f944013ca239f90250c050b2ab2eed6daa99 Mon Sep 17 00:00:00 2001 From: pan93412 Date: Thu, 26 Jan 2023 16:57:25 +0800 Subject: [PATCH 033/212] =?UTF-8?q?=F0=9F=93=9A=20Doc:=20Correct=20the=20f?= =?UTF-8?q?igure=20link=20in=20READMEs=20(#2312)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/README.md | 10 +++++----- .github/README_ckb.md | 8 ++++---- .github/README_de.md | 8 ++++---- .github/README_es.md | 8 ++++---- .github/README_fa.md | 8 ++++---- .github/README_fr.md | 8 ++++---- .github/README_he.md | 8 ++++---- .github/README_id.md | 8 ++++---- .github/README_it.md | 8 ++++---- .github/README_ja.md | 6 +++--- .github/README_ko.md | 8 ++++---- .github/README_nl.md | 8 ++++---- .github/README_pt.md | 8 ++++---- .github/README_ru.md | 8 ++++---- .github/README_sa.md | 8 ++++---- .github/README_tr.md | 10 +++++----- .github/README_uk.md | 10 +++++----- .github/README_zh-CN.md | 10 +++++----- .github/README_zh-TW.md | 8 ++++---- 19 files changed, 79 insertions(+), 79 deletions(-) diff --git a/.github/README.md b/.github/README.md index 41625d5366..ccb9d28a86 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -83,7 +83,7 @@ - +

Fiber is an Express inspired web framework built on top of Fasthttp, the fastest HTTP engine for Go. Designed to ease things up for fast development with zero memory allocation and performance in mind. @@ -112,8 +112,8 @@ func main() { These tests are performed by [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) and [Go Web](https://github.com/smallnest/go-web-framework-benchmark). If you want to see all the results, please visit our [Wiki](https://docs.gofiber.io/extra/benchmarks).

- - + +

## ⚙️ Installation @@ -524,7 +524,7 @@ func main() { 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()) diff --git a/.github/README_ckb.md b/.github/README_ckb.md index 9d4ea3aa1e..72196ddc0a 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -108,8 +108,8 @@ func main() { ئەم تاقیکردنەوانە لەلایەن [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) و [Go Web](https://github.com/smallnest/go-web-framework-benchmark) ئەنجام دراون. دەتوانیت هەموو ئەنجامەکان [لێرە](https://docs.gofiber.io/extra/benchmarks) ببینیت.

- - + +

## ⚙️ دامەزراندن @@ -523,7 +523,7 @@ func main() { 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()) diff --git a/.github/README_de.md b/.github/README_de.md index 445baa0a1f..f5a6ec8ca0 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -108,8 +108,8 @@ func main() { Diese Tests wurden von [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) und [Go Web](https://github.com/smallnest/go-web-framework-benchmark) ausgeführt. Falls du alle Resultate sehen möchtest, besuche bitte unser [Wiki](https://docs.gofiber.io/extra/benchmarks).

- - + +

## ⚙️ Installation @@ -518,7 +518,7 @@ func main() { 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()) diff --git a/.github/README_es.md b/.github/README_es.md index d12421fcb7..686b9f6c23 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -108,8 +108,8 @@ func main() { Estas pruebas son realizadas por [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) y [Go Web](https://github.com/smallnest/go-web-framework-benchmark). Si desea ver todos los resultados, visite nuestra [Wiki](https://docs.gofiber.io/extra/benchmarks).

- - + +

## ⚙️ Instalación @@ -518,7 +518,7 @@ func main() { 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()) diff --git a/.github/README_fa.md b/.github/README_fa.md index 834b390e80..c07124da49 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -123,8 +123,8 @@ func main() {

- - + +


@@ -620,7 +620,7 @@ func main() { 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()) diff --git a/.github/README_fr.md b/.github/README_fr.md index cd65599140..a2fff61d34 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -108,8 +108,8 @@ func main() { Ces tests sont effectués par [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) et [Go Web](https://github.com/smallnest/go-web-framework-benchmark). Si vous voulez voir tous les résultats, n'hésitez pas à consulter notre [Wiki](https://docs.gofiber.io/extra/benchmarks).

- - + +

## ⚙️ Installation @@ -520,7 +520,7 @@ func main() { 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()) diff --git a/.github/README_he.md b/.github/README_he.md index f22d5c1e3d..fe952f4045 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -125,8 +125,8 @@ func main() {

- - + +

@@ -620,7 +620,7 @@ func main() { 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()) diff --git a/.github/README_id.md b/.github/README_id.md index ac62971ab7..3ca1fb4d7c 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -108,8 +108,8 @@ func main() { Pengukuran ini dilakukan oleh [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) dan [Go Web](https://github.com/smallnest/go-web-framework-benchmark). Apabila anda ingin melihat hasil lengkapnya, silahkan kunjungi halaman [Wiki](https://docs.gofiber.io/extra/benchmarks) kami.

- - + +

## ⚙️ Instalasi @@ -521,7 +521,7 @@ func main() { 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()) diff --git a/.github/README_it.md b/.github/README_it.md index 17b81eef16..a9371d5f49 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -108,8 +108,8 @@ func main() { Questi test sono stati eseguiti da [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) e [Go Web](https://github.com/smallnest/go-web-framework-benchmark). Se vuoi vedere tutti i risultati, visita la nostra [Wiki](https://docs.gofiber.io/extra/benchmarks).

- - + +

## ⚙️ Installazione @@ -519,7 +519,7 @@ func main() { 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()) diff --git a/.github/README_ja.md b/.github/README_ja.md index 675e53e413..137768c04b 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -109,8 +109,8 @@ func main() { これらのテストは[TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext)および[Go Web](https://github.com/smallnest/go-web-framework-benchmark)によって計測を行っています 。すべての結果を表示するには、 [Wiki](https://docs.gofiber.io/extra/benchmarks)にアクセスしてください。

- - + +

## ⚙️ インストール diff --git a/.github/README_ko.md b/.github/README_ko.md index 2a7a16a960..240a660ffb 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -108,8 +108,8 @@ func main() { 이 테스트들은 [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext)와 [Go Web](https://github.com/smallnest/go-web-framework-benchmark)을 통해 측정되었습니다. 만약 모든 결과를 보고 싶다면, [Wiki](https://docs.gofiber.io/extra/benchmarks)를 확인해 주세요.

- - + +

## ⚙️ 설치 @@ -524,7 +524,7 @@ func main() { 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()) diff --git a/.github/README_nl.md b/.github/README_nl.md index 98071f8563..c67bf32d92 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -108,8 +108,8 @@ func main() { Deze tests zijn uitgevoerd door [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) en [Go Web](https://github.com/smallnest/go-web-framework-benchmark). Bezoek onze [Wiki](https://fiber.wiki/benchmarks) voor alle benchmark resultaten.

- - + +

## ⚙️ Installatie @@ -524,7 +524,7 @@ func main() { 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()) diff --git a/.github/README_pt.md b/.github/README_pt.md index 9f4e08ae62..37c38d7137 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -108,8 +108,8 @@ func main() { Esses testes são realizados pelo [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) e [Go Web](https://github.com/smallnest/go-web-framework-benchmark). Se você quiser ver todos os resultados, visite nosso [Wiki](https://docs.gofiber.io/extra/benchmarks) .

- - + +

## ⚙️ Instalação @@ -518,7 +518,7 @@ func main() { 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()) diff --git a/.github/README_ru.md b/.github/README_ru.md index 3032a6fee6..0fd1725975 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -108,8 +108,8 @@ func main() { Тестирование проводилось с помощью [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) и [Go Web](https://github.com/smallnest/go-web-framework-benchmark). Если вы хотите увидеть все результаты, пожалуйста, посетите наш [Wiki](https://docs.gofiber.io/extra/benchmarks).

- - + +

## ⚙️ Установка @@ -524,7 +524,7 @@ func main() { 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()) diff --git a/.github/README_sa.md b/.github/README_sa.md index ac3d99d38d..fec84d47c5 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -114,8 +114,8 @@ func main() { يتم تنفيذ هذه الاختبارات من قبل [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) و [Go Web](https://github.com/smallnest/go-web-framework-benchmark). إذا كنت تريد رؤية جميع النتائج ، يرجى زيارة موقعنا [Wiki](https://docs.gofiber.io/extra/benchmarks).

- - + +

## ⚙️ تثبيت @@ -584,7 +584,7 @@ func main() { 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()) diff --git a/.github/README_tr.md b/.github/README_tr.md index 36a2ba3010..f1b0bea488 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -108,8 +108,8 @@ func main() { Bu testler [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) ve [Go Web](https://github.com/smallnest/go-web-framework-benchmark) tarafından gerçekleştirildi. Bütün sonuçları görmek için lütfen [Wiki](https://docs.gofiber.io/extra/benchmarks) sayfasını ziyaret ediniz.

- - + +

## ⚙️ Kurulum @@ -257,7 +257,7 @@ func main() { fmt.Println("🥇 İlk handler") return c.Next() }) - + // /api ile başlayan bütün routelara etki eder. app.Use("/api", func(c *fiber.Ctx) error { fmt.Println("🥈 İkinci handler") @@ -518,7 +518,7 @@ func main() { 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()) diff --git a/.github/README_uk.md b/.github/README_uk.md index f7b89c2840..f19388490a 100644 --- a/.github/README_uk.md +++ b/.github/README_uk.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -83,7 +83,7 @@ - +

@@ -118,8 +118,8 @@ func main() { відвідайте наш [Wiki](https://docs.gofiber.io/extra/benchmarks).

- - + +

## ⚙️ Встановлення @@ -528,7 +528,7 @@ func main() { 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()) diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index 84de35bfd7..9d4e4e1cfe 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -110,8 +110,8 @@ func main() { 这些测试由 [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) 和 [Go Web](https://github.com/smallnest/go-web-framework-benchmark) 完成。如果您想查看所有结果,请访问我们的 [Wiki](https://docs.gofiber.io/extra/benchmarks) 。

- - + +

## ⚙️ 安装 @@ -142,7 +142,7 @@ go get -u github.com/gofiber/fiber/v2 ## 💡 哲学 -从 [Node.js](https://nodejs.org/en/about/) 切换到 [Go](https://go.dev/doc/) 的新 `gopher` 在开始构建 `Web` +从 [Node.js](https://nodejs.org/en/about/) 切换到 [Go](https://go.dev/doc/) 的新 `gopher` 在开始构建 `Web` 应用程序或微服务之前需要经历一段艰难的学习过程。 而 `Fiber`,一个基于**极简主义**并且遵循 **UNIX 方式**创建的 **Web 框架**, 使新的 `gopher` 可以在热烈和可信赖的欢迎中迅速进入 `Go` 的世界。 @@ -526,7 +526,7 @@ func main() { 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()) diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index 8ac8802487..fae2b4b190 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -1,6 +1,6 @@

- Fiber + Fiber
@@ -108,8 +108,8 @@ func main() { 本測試使用[TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext)和[Go Web 框架效能測試](https://github.com/smallnest/go-web-framework-benchmark)。如果要看全部的執行結果,請到[Wiki](https://docs.gofiber.io/extra/benchmarks) 。

- - + +

## ⚙️ 安裝 @@ -521,7 +521,7 @@ func main() { 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()) From 7327a17951228f6ea8e36b160e78218f7dbd46ed Mon Sep 17 00:00:00 2001 From: pan93412 Date: Fri, 27 Jan 2023 00:30:49 +0800 Subject: [PATCH 034/212] =?UTF-8?q?=F0=9F=93=9A=20Docs:=20Rework=20Chinese?= =?UTF-8?q?=20(Taiwan)=20translation=20of=20documentation=20(#2310)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 📚 Doc: Reset zh_TW translation * 📚 Doc: 1st revision of Chinese (Taiwan) translation * 📚 Doc: Language Flag * 📚 Doc: 2nd rev. of Chinese (Taiwan) translation * 📚 Doc: Translated the middlewares list * Docs: Fixup space * 📚 Doc: Correct the figure link in READMEs * 📚 Doc: Update according to review suggestions * 📚 Doc: Update according to review suggestions --- .github/README_zh-TW.md | 372 ++++++++++++++++++++++------------------ 1 file changed, 201 insertions(+), 171 deletions(-) diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index fae2b4b190..bb29d21e03 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -55,6 +55,9 @@ + + + @@ -80,9 +83,10 @@ +

- Fiber是移植NodeJS的Express框架改以Go語言編寫。本套件基於Fasthttp,Fasthttp有不分配記憶體空間Request Pool的特性,在網路效能方面有著顯著的效能。 + Fiber 是款啟發自 ExpressWeb 框架,建基於 Fasthttp——Go最快的 HTTP 引擎。設計旨在 減輕 快速開發的負擔,兼顧 零記憶體分配效能

## ⚡️ 快速入門 @@ -103,9 +107,9 @@ func main() { } ``` -## 🤖 效能 +## 🤖 效能評定結果 -本測試使用[TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext)和[Go Web 框架效能測試](https://github.com/smallnest/go-web-framework-benchmark)。如果要看全部的執行結果,請到[Wiki](https://docs.gofiber.io/extra/benchmarks) 。 +這些測試由 [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) 和 [Go Web 框架效能測試](https://github.com/smallnest/go-web-framework-benchmark) 完成。若需參閱所有結果,請參閱我們的 [Wiki](https://docs.gofiber.io/extra/benchmarks) 資訊。

@@ -114,49 +118,48 @@ func main() { ## ⚙️ 安裝 -確保已安裝 Go 版本 `1.16` 或以上 ([下載](https://go.dev/dl/))。 +先確定您已經安裝 `1.16` 或更新版本的 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 : +要初始化專案,首先建立檔案夾,然後在檔案夾中執行 `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: ```bash go get -u github.com/gofiber/fiber/v2 ``` -## 🎯 功能 +## 🎯 特色 -- 強大的[路由](https://docs.gofiber.io/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://docs.gofiber.io/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://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) -- 支援[限速](https://docs.gofiber.io/api/middleware/limiter) -- 被翻譯成[18種語言](https://docs.gofiber.io/) -- 豐富的[文件](https://docs.gofiber.io/) +- [支援 WebSocket](https://github.com/gofiber/websocket) +- [Server-Sent Events](https://github.com/gofiber/recipes/tree/master/sse) +- 支援[速率限制](https://docs.gofiber.io/api/middleware/limiter) +- 有 [18 門語言](https://docs.gofiber.io/)的翻譯 +- 還有很多功能,[開始探索 Fiber](https://docs.gofiber.io/) -## 💡 理念 +## 💡 設計哲學 -不少[Node.js](https://nodejs.org/en/about/)的工程師跳到[Go](https://go.dev/doc/)必須學習一些知識,因此做了一個跟 Express 一樣的 Fiber 省這些麻煩。設計還是照原本的**極簡主義**還有遵循**UNIX 慣例**,因此新手們可以**無痛**迅速進入 Go 的世界。 +從 [Node.js](https://nodejs.org/en/about/) 轉到 [Go](https://go.dev/doc/) 的新進 Go 開發者,得先面對 Go 的各種知識點,才能開始建構自己的 Web 應用程式或微服務。Fiber 作為一款 **Web 框架**,設計之初便以 **極簡主義** 為理念,並遵循 **UNIX 之道**,讓新進 Go 開發者能夠快速隨著友善且值得信賴的社群,進入 Go 的世界。 -Fiber **受到** 網路上最流行的 Web 框架 ExpressJS**啟發**,結合 Express 的**易用性**和 Go 的**高效能**。若你之前用過 Node.js 寫 Web 應用(_使用 ExpressJS/Koa 或類似工具_),那你已經**上手**了。 +Fiber **啟發自** Express——網際網路上最知名的 Web 框架,我們將 Express 的 **易用性** 和 Go 的 **原始效能** 結合在一起。如果您曾經在 Node.js(使用 Express 或類似框架)實作過 Web 應用程式,那麼許多方法和開發準則,將讓您感到 **無比熟悉**。 -有什麼問題請發[issues](https://github.com/gofiber/fiber/issues)或加入 Discord [channel](https://gofiber.io/discord)討論,我們想要創造**快速**、**彈性**、**友善**的社群給**任何人**使用!就像 Express 那樣。 +我們 **傾聽** 使用者在 [Issues](https://github.com/gofiber/fiber/issues)、Discord [群組](https://gofiber.io/discord) 和 **網路上任何角落** 的意見和建議,製作出 **快速**、**靈活** 且 **易於上手** 的 Go Web 框架,來應對**任何**工作、**繳件期限**以及開發者的**能力區間**——如同 Express 在 JavaScript 世界所扮演的角色一樣! -## 限制 -* 由於 Fiber 使用了 unsafe,該庫可能並不總是與最新的 Go 版本兼容。 Fiber 2.40.0 已經用 Go 版本 1.16 到 1.19 進行了測試。 -* Fiber 與 net/http 接口不兼容。 這意味著您將無法使用 gqlgen、go-swagger 或任何其他屬於 net/http 生態系統的項目。 +## ⚠️ 限制 -## 👀 範例 +- 由於 Fiber 有用到 Unsafe,本函式庫有時可能無法相容最新版的 Go 語言。Fiber 2.40.0 已在 Go 1.16 至 1.19 的版本測試過。 +- Fiber 不相容 net/http 的介面,意味著您無法使用像是 gqlgen、go-swagger 或其他任何屬於 net/http 生態系統的專案。 -以下是一些常見範例。 +## 👀 範例 -> 更多程式碼在[範例專案](https://github.com/gofiber/recipes)中或直接看[API 文件](https://docs.gofiber.io)。 +下方列出一些常見範例。如果您想查看更多程式碼範例,請參閱我們的 [Recipes 儲存庫](https://github.com/gofiber/recipes),或前往我們提供的 [API 文件](https://docs.gofiber.io)。 -#### 📖 [**Basic Routing**](https://docs.gofiber.io/#basic-routing) +#### 📖 [**基礎路由**](https://docs.gofiber.io/#basic-routing) ```go func main() { @@ -170,8 +173,8 @@ func main() { // 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 + msg := fmt.Sprintf("💸 從:%s,到:%s", c.Params("from"), c.Params("to")) + return c.SendString(msg) // => 💸 從:LAX,到:SFO }) // GET /dictionary.txt @@ -182,14 +185,14 @@ func main() { // 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 + msg := fmt.Sprintf("👴 %s 已經 %s 歲了", c.Params("name"), c.Params("age")) + return c.SendString(msg) // => 👴 john 已經 75 歲了 }) // GET /john app.Get("/:name", func(c *fiber.Ctx) error { - msg := fmt.Sprintf("Hello, %s 👋!", c.Params("name")) - return c.SendString(msg) // => Hello john 👋! + msg := fmt.Sprintf("哈囉,%s 👋!", c.Params("name")) + return c.SendString(msg) // => 哈囉,john 👋! }) log.Fatal(app.Listen(":3000")) @@ -197,7 +200,7 @@ func main() { ``` -#### 📖 [**路線命名**](https://docs.gofiber.io/api/app#name) +#### 📖 [**路由命名**](https://docs.gofiber.io/api/app#name) ```go func main() { @@ -211,7 +214,7 @@ func main() { data, _ := json.MarshalIndent(app.GetRoute("api"), "", " ") fmt.Print(string(data)) - // Prints: + // 會輸出: // { // "method": "GET", // "name": "api", @@ -227,7 +230,7 @@ func main() { ``` -#### 📖 [**提供靜態文件**](https://docs.gofiber.io/api/app#static) +#### 📖 [**寄存靜態檔案**](https://docs.gofiber.io/api/app#static) ```go func main() { @@ -249,47 +252,49 @@ func main() { ``` -#### 📖 [**Middleware & Next**](https://docs.gofiber.io/api/ctx#next) +#### 📖 [**中介模組和接續函式 (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/register - app.Get("/api/list", func(c *fiber.Ctx) error { - fmt.Println("🥉 Last handler") - return c.SendString("Hello, World 👋!") - }) - - log.Fatal(app.Listen(":3000")) + app := fiber.New() + + // 符合任何路由 + app.Use(func(c *fiber.Ctx) error { + fmt.Println("🥇 第一個處理常式") + return c.Next() + }) + + // 符合所有 /api 開頭的路由 + app.Use("/api", func(c *fiber.Ctx) error { + fmt.Println("🥈 第二個處理常式") + return c.Next() + }) + + // GET /api/list + app.Get("/api/list", func(c *fiber.Ctx) error { + fmt.Println("🥉 最後一個處理常式") + return c.SendString("Hello, World 👋!") + }) + + log.Fatal(app.Listen(":3000")) } ```

- 📚 顯示更多範例 + 📚 展示更多程式碼範例 -### 界面引擎 +### 檢視引擎 -📖 [設定](https://docs.gofiber.io/api/fiber#config) +📖 [組態設定](https://docs.gofiber.io/api/fiber#config) 📖 [引擎](https://github.com/gofiber/template) -📖 [渲染](https://docs.gofiber.io/api/ctx#render) +📖 [轉譯 (render)](https://docs.gofiber.io/api/ctx#render) + +若未指定檢視引擎,Fiber 預設採用 [html/template](https://pkg.go.dev/html/template/)。 -當不指定樣板引擎時 Fiber 預設用[html/template](https://pkg.go.dev/html/template/)。 +如果您想執行部分檢視 (partials),或者是使用如 [amber](https://github.com/eknkc/amber)、[handlebars](https://github.com/aymerick/raymond)、[mustache](https://github.com/cbroglie/mustache) 或 [pug](https://github.com/Joker/jade) 等等不同的引擎…… -如果你想要執行部份或用別的樣板引擎[amber](https://github.com/eknkc/amber)、[handlebars](https://github.com/aymerick/raymond)、[mustache](https://github.com/cbroglie/mustache)、[pug](https://github.com/Joker/jade)之類…請參考符合多樣板引擎的[樣板](https://github.com/gofiber/template)套件。 +請參考我們的 [Template](https://github.com/gofiber/template) 套件——它支援多種檢視引擎。 ```go package main @@ -300,12 +305,12 @@ import ( ) func main() { - // You can setup Views engine before initiation app: + // 您可以在 app 初始化前設定檢視 (Views) 引擎: app := fiber.New(fiber.Config{ Views: pug.New("./views", ".pug"), }) - // And now, you can call template `./views/home.pug` like this: + // 現在,您可以用下面這種方式呼叫 `./views/home.pug` 樣板: app.Get("/", func(c *fiber.Ctx) error { return c.Render("home", fiber.Map{ "title": "Homepage", @@ -315,16 +320,15 @@ func main() { log.Fatal(app.Listen(":3000")) } - ``` -### 鏈式路線分組 +### 組合路由鏈 -📖 [Group](https://docs.gofiber.io/api/app#group) +📖 [分組](https://docs.gofiber.io/api/app#group) ```go func middleware(c *fiber.Ctx) error { - fmt.Println("Don't mind me!") + fmt.Println("不要理我!") return c.Next() } @@ -335,15 +339,15 @@ func handler(c *fiber.Ctx) error { func main() { app := fiber.New() - // Root API route + // 根 API 路由 api := app.Group("/api", middleware) // /api - // API v1 routes + // API v1 路由 v1 := api.Group("/v1", middleware) // /api/v1 v1.Get("/list", handler) // /api/v1/list v1.Get("/user", handler) // /api/v1/user - // API v2 routes + // API v2 路由 v2 := api.Group("/v2", middleware) // /api/v2 v2.Get("/list", handler) // /api/v2/list v2.Get("/user", handler) // /api/v2/user @@ -353,9 +357,9 @@ func main() { ``` -### 中介器 logger +### 中介模組記錄器 -📖 [Logger](https://docs.gofiber.io/api/middleware/logger) +📖 [記錄器](https://docs.gofiber.io/api/middleware/logger) ```go package main @@ -378,7 +382,7 @@ func main() { } ``` -### 跨網域資源共享 (CORS) +### 跨原始來源資源共用 (CORS) 📖 [CORS](https://docs.gofiber.io/api/middleware/cors) @@ -401,15 +405,15 @@ func main() { } ``` -在`Origin` header 中放網域來檢查 CORS: +在 `Origin` 標頭傳入任何網域來檢查 CORS 的效果: ```bash curl -H "Origin: http://example.com" --verbose http://localhost:3000 ``` -### 客制 404 回應 +### 自訂 404 回應 -📖 [HTTP Methods](https://docs.gofiber.io/api/ctx#status) +📖 [HTTP 方法](https://docs.gofiber.io/api/ctx#status) ```go func main() { @@ -425,7 +429,7 @@ func main() { return c.SendString("Welcome!") }) - // Last middleware to match anything + // 最後一個中介模組,符合所有條件 app.Use(func(c *fiber.Ctx) error { return c.SendStatus(404) // => 404 "Not Found" @@ -465,7 +469,7 @@ func main() { } ``` -### WebSocket 升級 +### WebSocket 升級 (Upgrade) 📖 [Websocket](https://github.com/gofiber/websocket) @@ -501,7 +505,7 @@ func main() { ### Server-Sent Events -📖 [More Info](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) +📖 [更多資訊](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) ```go import ( @@ -524,8 +528,8 @@ func main() { for { i++ - msg := fmt.Sprintf("%d - the time is %v", i, time.Now()) - fmt.Fprintf(w, "data: Message: %s\n\n", msg) + msg := fmt.Sprintf("%d - 目前時間為 %v", i, time.Now()) + fmt.Fprintf(w, "data: 訊息: %s\n\n", msg) fmt.Println(msg) w.Flush() @@ -540,125 +544,151 @@ func main() { } ``` -### Recover 中介器 +### Recover 中介模組 📖 [Recover](https://docs.gofiber.io/api/middleware/recover) ```go import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/recover" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/recover" ) func main() { - app := fiber.New() + app := fiber.New() - app.Use(recover.New()) + app.Use(recover.New()) - app.Get("/", func(c *fiber.Ctx) error { - panic("normally this would crash your app") - }) + app.Get("/", func(c *fiber.Ctx) error { + panic("正常來說,這會導致 app 當機") + }) - log.Fatal(app.Listen(":3000")) + log.Fatal(app.Listen(":3000")) +} +``` + +
+ +### 使用信任的代理伺服器 + +📖 [組態設定](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 地址或 IP 地址區間 + ProxyHeader: fiber.HeaderXForwardedFor}, + }) + + // ... + + log.Fatal(app.Listen(":3000")) } ``` -## 🧬 内部中間件 -以下為包含在Fiber框架中的中間件列表. - -| 中間件 | 描述 | -| :------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | 基本身份驗證中間件提供 HTTP 基本身份驗證。 它為有效憑據調用下一個處理程序,並為丟失或無效憑據調用 401 Unauthorized。 | -| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | 攔截和緩存響應 | -| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | CFiber的壓縮中間件,默認支持`deflate`、`gzip`和`brotli`。 | -| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | 使用各種選項啟用跨域資源共享 \(CORS\)。 | -| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | 防止 CSRF 漏洞利用。 | -| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | -| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | -| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware 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. | -| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | -| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | 如果提供了文件路徑,則忽略日誌中的網站圖標或從內存中提供服務。 | -| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | 用於 Fiber 的 FileSystem 中間件,特別感謝 Alireza Salary | -| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Fiber 的限速中間件。 用於限制對公共 API 和/或端點的重複請求,例如密碼重置。 | -| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP 請求/響應 logger. | -| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Monitor middleware that reports server metrics, inspired by express-status-monitor | -| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | 特別感謝 Matthew Lee \(@mthli\) | -| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | 允許您將請求代理到多個服務器 | -| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | 恢復中間件從堆棧鏈中任何地方的恐慌中恢復,並將控制權交給集中式[ ErrorHandler](https://docs.gofiber.io/guide/error-handling). | -| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | 為每個請求添加一個 requestid。 | -| [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) | 添加請求的最大時間,如果超過則轉發給 ErrorHandler。 | - -## 🧬 外部中間件 - -由 [Fiber 團隊] (https://github.com/orgs/gofiber/people) 維護的外部託管中間件模塊列表。 - -| Middleware | Description | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [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) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | 重寫中間件根據提供的規則重寫 URL 路徑。 它有助於向後兼容或只是創建更清晰和更具描述性的鏈接。 | -| [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) |該軟件包包含 8 個模板引擎,可用於 Fiber `v1.10.x` Go 版本 1.13 或更高版本。 | -| [websocket](https://github.com/gofiber/websocket) | 基於 Fasthttp WebSocket for Fiber,支持 Locals! | +## 🧬 內建中介模組 + +這裡列出了 Fiber 框架內建的中介模組。 + +| 中介模組 | 描述 | +| :------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | 提供 HTTP Basic 認證的基本認證中介模組。如果憑證有效,則會呼叫接續函式 (next);如果沒有憑證或失效,則回傳 401 Unauthorized。 | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | 攔截並快取回應。 | +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | 適用於 Fiber 的壓縮中介模組。預設支援 `deflate`、`gzip` 和 `brotli`。 | +| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | 啟用跨來源資源共用 (CORS),可調整多種選項。 | +| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | 保護資源防止 CSRF 利用。 | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | 加密中介模組,會將 Cookie 的值進行加密。 | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | 公開環境變數,並提供可調整設定。 | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag 中介模組,讓快取更高效,同時因為 Web 伺服器不需要在內容未更動時重新傳送完整請求,因此可以減少流量使用。 | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar 中介模組,透過其 HTTP 伺服器執行階段,提供 JSON 格式的公用變數。 | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | 不輸出 Favicons 請求記錄;若有提供檔案路徑,則從記憶體提供圖示。 | +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | 適用於 Fiber 的檔案系統中介模組。特別銘謝 Alireza Salary! | +| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | 適用於 Fiber 的速率限制中介模組。用來限制傳入公開 API 或者(以及)端點(如密碼重設)的重複請求。 | +| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP 請求/回應記錄工具。 | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | 監控中介模組,用來回報伺服器指標。啟發自 express-status-monitor。 | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | 特別感謝 Matthew Lee \(@mthli\) | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | 讓您可以將請求代理 (proxy) 至多台伺服器。 | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Recover 中介模組:可以從呼叫堆疊鏈中任何部分的當機 (panic) 中復原,並將控制權交由中央的 [錯誤處理常式 (ErrorHandler)](https://docs.gofiber.io/guide/error-handling) 處理。 | +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | 為每個請求加上 requestid。 | +| [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)。 | + +## 🧬 外掛中介模組 + +這裡列出由 [Fiber 團隊](https://github.com/orgs/gofiber/people) 維護、存放在外部的中介模組。 + +| 中介模組 | 描述 | +| :------------------------------------------------ | :----------------------------------------------------------------------------------------------------- | +| [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)! | ## 🕶️ 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)。 ## 👍 貢獻 -如果您要說聲**謝謝**或支援`Fiber`的積極發展: +如果您想和我們 **道謝**,或者是支持 `Fiber` 繼續積極開發下去(也可以兩個都做): -1. 點擊[GitHub Star](https://github.com/gofiber/fiber/stargazers)關注本專案。 -2. 在[Twitter](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)轉[推](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)。 -3. 在[Medium](https://medium.com/)、[Dev.to](https://dev.to/)、部落格上發表意見或教學。 -4. 贊助我們[一杯咖啡](https://buymeacoff.ee/fenny)。 +1. 送給專案一顆 [GitHub 星星](https://github.com/gofiber/fiber/stargazers)。 +2. [在您的 Twitter 上](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)發出關於本專案的推文。 +3. 在 [Medium](https://medium.com/)、[Dev.to](https://dev.to/) 或者是個人部落格上寫下評論或教學。 +4. 捐專案 [一杯咖啡的費用](https://buymeacoff.ee/fenny) 以示支持。 ## ☕ 支持者 -Fiber 是一個以贊助維生的開源專案,像是: 網域、gitbook、netlify、serverless 伺服器。如果你想贊助也可以 ☕ [**買杯咖啡**](https://buymeacoff.ee/fenny) - -| | User | Donation | -| :--------------------------------------------------------- | :----------------------------------------------- | :------- | -| ![](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/thomasvvugt) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/31921460?s=25) | [@toishy](https://github.com/toishy) | ☕ x 1 | - -## ‎‍💻 貢獻者 +Fiber 是個仰賴捐款的開放原始碼專案——用來支付如域名、Gitbook、Netlify 和無服務器運算服務的費用。如果您想支持 Fiber,可以在 ☕ [**這裡捐杯咖啡**](https://buymeacoff.ee/fenny)。 + +| | 使用者 | 捐款 | +| :--------------------------------------------------------- | :----------------------------------------------- | :------ | +| ![](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 | + +## ‎‍💻 程式碼貢獻者 Code Contributors -## ⭐️ Stargazers +## ⭐️ 收藏數 Stargazers over time -## ⚠️ 授權 +## ⚠️ 授權條款 -版權所有 (c) 2019 年至今 [Fenny](https://github.com/fenny) 和 [貢獻者](https://github.com/gofiber/fiber/graphs/contributors)。 `Fiber` 是根據 [MIT 許可證] (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 國際)。 +著作權所有 (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) 授權,免費且開放原始碼的軟體。官方圖示 (logo) 由 [Vic Shóstak](https://github.com/koddr) 製作,並依據 [創用 CC](https://creativecommons.org/licenses/by-sa/4.0/) 授權條款散佈 (CC BY-SA 4.0 International)。 -**Third-party library licenses** +**第三方函式庫的授權條款** - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) - [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) From 167a8b5e9421e0ab51fbf44c5621632f4a1a90c5 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Fri, 27 Jan 2023 09:01:37 +0100 Subject: [PATCH 035/212] =?UTF-8?q?=F0=9F=9A=80=20Feature:=20Add=20and=20a?= =?UTF-8?q?pply=20more=20stricter=20golangci-lint=20linting=20rules=20(#22?= =?UTF-8?q?86)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * golangci-lint: add and apply more stricter linting rules * github: drop security workflow now that we use gosec linter inside golangci-lint * github: use official golangci-lint CI linter * Add editorconfig and gitattributes file --- .editorconfig | 8 + .gitattributes | 12 + .github/workflows/linter.yml | 35 +- .github/workflows/security.yml | 17 - .golangci.yml | 258 ++++++++++++++ app.go | 58 ++-- app_test.go | 65 ++-- client.go | 83 +++-- client_test.go | 42 ++- color.go | 2 + ctx.go | 188 ++++++----- ctx_test.go | 314 ++++++++++-------- error_test.go | 15 +- group.go | 1 - helpers.go | 78 +++-- helpers_test.go | 1 + hooks.go | 30 +- hooks_test.go | 3 +- internal/dictpool/pool.go | 2 +- internal/gopsutil/common/common.go | 2 +- internal/gopsutil/mem/mem.go | 3 +- internal/gopsutil/mem/mem_freebsd.go | 1 - internal/gopsutil/mem/mem_linux.go | 10 +- internal/msgp/json.go | 2 +- internal/schema/cache.go | 2 +- internal/storage/memory/memory_test.go | 4 +- internal/template/html/html.go | 1 - internal/template/utils/utils.go | 1 - listen.go | 62 ++-- listen_test.go | 14 +- middleware/basicauth/basicauth_test.go | 13 +- middleware/basicauth/config.go | 2 + middleware/cache/cache.go | 13 +- middleware/cache/cache_test.go | 159 ++++----- middleware/cache/config.go | 12 +- middleware/cache/heap.go | 6 +- middleware/cache/manager.go | 39 ++- middleware/compress/compress.go | 1 + middleware/compress/compress_test.go | 16 +- middleware/compress/config.go | 2 + middleware/cors/cors.go | 5 +- middleware/cors/cors_test.go | 3 +- middleware/cors/utils.go | 12 +- middleware/csrf/config.go | 21 +- middleware/csrf/csrf.go | 8 +- middleware/csrf/csrf_test.go | 155 ++++----- middleware/csrf/manager.go | 63 +--- middleware/encryptcookie/config.go | 6 +- middleware/encryptcookie/encryptcookie.go | 1 + .../encryptcookie/encryptcookie_test.go | 27 +- middleware/encryptcookie/utils.go | 34 +- middleware/envvar/envvar.go | 7 +- middleware/envvar/envvar_test.go | 76 +++-- middleware/etag/config.go | 2 + middleware/etag/etag.go | 40 +-- middleware/etag/etag_test.go | 25 +- middleware/expvar/config.go | 5 +- middleware/expvar/expvar.go | 3 +- middleware/favicon/favicon.go | 2 + middleware/favicon/favicon_test.go | 31 +- middleware/filesystem/filesystem.go | 40 +-- middleware/filesystem/filesystem_test.go | 18 +- middleware/filesystem/utils.go | 20 +- middleware/idempotency/config.go | 10 +- middleware/idempotency/idempotency_test.go | 3 +- middleware/limiter/config.go | 18 +- middleware/limiter/limiter_test.go | 48 +-- middleware/limiter/manager.go | 50 +-- middleware/logger/README.md | 4 +- middleware/logger/config.go | 4 +- middleware/logger/data.go | 3 - middleware/logger/logger.go | 39 ++- middleware/logger/logger_test.go | 68 ++-- middleware/logger/tags.go | 4 +- middleware/logger/template_chain.go | 17 +- middleware/monitor/config.go | 38 ++- middleware/monitor/config_test.go | 46 +-- middleware/monitor/index.go | 13 +- middleware/monitor/monitor.go | 77 +++-- middleware/monitor/monitor_test.go | 10 +- middleware/pprof/config.go | 5 +- middleware/pprof/pprof.go | 31 +- middleware/proxy/config.go | 5 +- middleware/proxy/proxy.go | 19 +- middleware/proxy/proxy_test.go | 89 +++-- middleware/recover/config.go | 4 +- middleware/recover/recover.go | 6 +- middleware/recover/recover_test.go | 8 +- middleware/requestid/config.go | 2 + middleware/requestid/requestid_test.go | 16 +- middleware/session/README.md | 4 +- middleware/session/config.go | 12 +- middleware/session/data.go | 3 +- middleware/session/session.go | 7 +- middleware/session/session_test.go | 94 +++--- middleware/session/store.go | 9 +- middleware/session/store_test.go | 1 + middleware/skip/skip.go | 4 +- middleware/skip/skip_test.go | 6 +- middleware/timeout/timeout_test.go | 14 +- mount.go | 1 - mount_test.go | 2 +- path.go | 21 +- path_test.go | 2 - prefork.go | 60 ++-- prefork_test.go | 5 +- router.go | 43 ++- router_test.go | 14 +- utils/assertions.go | 17 +- utils/assertions_test.go | 4 +- utils/common.go | 12 +- utils/common_test.go | 7 +- utils/convert.go | 10 +- utils/convert_test.go | 4 +- utils/deprecated.go | 8 +- utils/http.go | 14 +- utils/ips.go | 4 + utils/time.go | 1 + utils/time_test.go | 7 +- utils/xml_test.go | 2 +- 120 files changed, 1889 insertions(+), 1321 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes delete mode 100644 .github/workflows/security.yml create mode 100644 .golangci.yml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..6a4ec76843 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +; This file is for unifying the coding style for different editors and IDEs. +; More information at http://editorconfig.org +; This style originates from https://github.com/fewagency/best-practices +root = true + +[*] +charset = utf-8 +end_of_line = lf diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..963a68ec2d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# Handle line endings automatically for files detected as text +# and leave all files detected as binary untouched. +* text=auto eol=lf + +# Force batch scripts to always use CRLF line endings so that if a repo is accessed +# in Windows via a file share from Linux, the scripts will work. +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Force bash scripts to always use LF line endings so that if a repo is accessed +# in Unix via a file share from Windows, the scripts will work. +*.sh text eol=lf diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 4397113a31..b259fccfd7 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -1,17 +1,28 @@ +# Adapted from https://github.com/golangci/golangci-lint-action/blob/b56f6f529003f1c81d4d759be6bd5f10bf9a0fa0/README.md#how-to-use + +name: golangci-lint on: - push: - branches: - - master - - main - pull_request: -name: Linter + push: + tags: + - v* + branches: + - master + - main + pull_request: +permissions: + contents: read jobs: - Golint: + golangci: + name: lint runs-on: ubuntu-latest steps: - - name: Fetch Repository - uses: actions/checkout@v3 - - name: Run Golint - uses: reviewdog/action-golangci-lint@v2 + - uses: actions/setup-go@v3 with: - golangci_lint_flags: "--tests=false" + # NOTE: Keep this in sync with the version from go.mod + go-version: 1.19 + - uses: actions/checkout@v3 + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + # NOTE: Keep this in sync with the version from .golangci.yml + version: v1.50.1 diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml deleted file mode 100644 index e7d8bade55..0000000000 --- a/.github/workflows/security.yml +++ /dev/null @@ -1,17 +0,0 @@ -on: - push: - branches: - - master - - main - pull_request: -name: Security -jobs: - Gosec: - runs-on: ubuntu-latest - steps: - - name: Fetch Repository - uses: actions/checkout@v3 - - name: Run Gosec - uses: securego/gosec@master - with: - args: -exclude-dir=internal/*/ ./... diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000000..87e975e036 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,258 @@ +# Created based on v1.50.1 +# NOTE: Keep this in sync with the version in .github/workflows/linter.yml + +run: + modules-download-mode: readonly + skip-dirs-use-default: false + skip-dirs: + - internal # TODO: Also apply proper linting for internal dir + +output: + sort-results: true + +linters-settings: + # TODO: Eventually enable these checks + # depguard: + # include-go-root: true + # packages: + # - flag + # - io/ioutil + # - reflect + # - unsafe + # packages-with-error-message: + # - flag: '`flag` package is only allowed in main.go' + # - io/ioutil: '`io/ioutil` package is deprecated, use the `io` and `os` package instead' + # - reflect: '`reflect` package is dangerous to use' + # - unsafe: '`unsafe` package is dangerous to use' + + errcheck: + check-type-assertions: true + check-blank: true + disable-default-exclusions: true + + errchkjson: + report-no-exported: true + + exhaustive: + default-signifies-exhaustive: true + + forbidigo: + forbid: + - ^(fmt\.Print(|f|ln)|print|println)$ + - 'http\.Default(Client|Transport)' + # TODO: Eventually enable these patterns + # - 'time\.Sleep' + # - 'panic' + + gci: + sections: + - standard + - prefix(github.com/gofiber/fiber) + - default + - blank + - dot + custom-order: true + + gocritic: + disabled-checks: + - ifElseChain + + gofumpt: + module-path: github.com/gofiber/fiber + extra-rules: true + + gosec: + config: + global: + audit: true + + govet: + check-shadowing: true + enable-all: true + disable: + - shadow + - fieldalignment + + grouper: + import-require-single-import: true + import-require-grouping: true + + misspell: + locale: US + + nolintlint: + require-explanation: true + require-specific: true + + nonamedreturns: + report-error-in-defer: true + + predeclared: + q: true + + promlinter: + strict: true + + revive: + enable-all-rules: true + rules: + # Provided by gomnd linter + - name: add-constant + disabled: true + - name: argument-limit + disabled: true + # Provided by bidichk + - name: banned-characters + disabled: true + - name: cognitive-complexity + disabled: true + - name: cyclomatic + disabled: true + - name: exported + disabled: true + - name: file-header + disabled: true + - name: function-result-limit + disabled: true + - name: function-length + disabled: true + - name: line-length-limit + disabled: true + - name: max-public-structs + disabled: true + - name: modifies-parameter + disabled: true + - name: nested-structs + disabled: true + - name: package-comments + disabled: true + + stylecheck: + checks: + - all + - -ST1000 + - -ST1020 + - -ST1021 + - -ST1022 + + tagliatelle: + case: + rules: + json: snake + + #tenv: + # all: true + + #unparam: + # check-exported: true + + wrapcheck: + ignorePackageGlobs: + - github.com/gofiber/fiber/* + - github.com/valyala/fasthttp + +issues: + exclude-use-default: false + +linters: + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - containedctx + - contextcheck + # - cyclop + # - deadcode + # - decorder + - depguard + - dogsled + # - dupl + # - dupword + - durationcheck + - errcheck + - errchkjson + - errname + - errorlint + - execinquery + - exhaustive + # - exhaustivestruct + # - exhaustruct + - exportloopref + - forbidigo + - forcetypeassert + # - funlen + - gci + - gochecknoglobals + - gochecknoinits + # - gocognit + - goconst + - gocritic + # - gocyclo + # - godot + # - godox + # - goerr113 + - gofmt + - gofumpt + # - goheader + - goimports + # - golint + - gomnd + - gomoddirectives + # - gomodguard + - goprintffuncname + - gosec + - gosimple + - govet + - grouper + # - ifshort + # - importas + - ineffassign + # - interfacebloat + # - interfacer + # - ireturn + # - lll + - loggercheck + # - maintidx + # - makezero + # - maligned + - misspell + - nakedret + # - nestif + - nilerr + - nilnil + # - nlreturn + - noctx + - nolintlint + - nonamedreturns + # - nosnakecase + - nosprintfhostport + # - paralleltest # TODO: Enable once https://github.com/gofiber/fiber/issues/2254 is implemented + # - prealloc + - predeclared + - promlinter + - reassign + - revive + - rowserrcheck + # - scopelint + - sqlclosecheck + - staticcheck + # - structcheck + - stylecheck + - tagliatelle + # - tenv # TODO: Enable once we drop support for Go 1.16 + # - testableexamples + # - testpackage # TODO: Enable once https://github.com/gofiber/fiber/issues/2252 is implemented + - thelper + # - tparallel # TODO: Enable once https://github.com/gofiber/fiber/issues/2254 is implemented + - typecheck + - unconvert + - unparam + - unused + - usestdlibvars + # - varcheck + # - varnamelen + - wastedassign + - whitespace + - wrapcheck + # - wsl diff --git a/app.go b/app.go index e299075b37..002da62c4b 100644 --- a/app.go +++ b/app.go @@ -14,6 +14,7 @@ import ( "encoding/xml" "errors" "fmt" + "log" "net" "net/http" "net/http/httputil" @@ -24,6 +25,7 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -306,7 +308,7 @@ type Config struct { // FEATURE: v2.3.x // The router executes the same handler by default if StrictRouting or CaseSensitive is disabled. - // Enabling RedirectFixedPath will change this behaviour into a client redirect to the original route path. + // Enabling RedirectFixedPath will change this behavior into a client redirect to the original route path. // Using the status code 301 for GET requests and 308 for all other request methods. // // Default: false @@ -454,6 +456,8 @@ const ( ) // HTTP methods enabled by default +// +//nolint:gochecknoglobals // Using a global var is fine here var DefaultMethods = []string{ MethodGet, MethodHead, @@ -467,7 +471,7 @@ var DefaultMethods = []string{ } // DefaultErrorHandler that process return errors from handlers -var DefaultErrorHandler = func(c *Ctx, err error) error { +func DefaultErrorHandler(c *Ctx, err error) error { code := StatusInternalServerError var e *Error if errors.As(err, &e) { @@ -519,7 +523,7 @@ func New(config ...Config) *App { if app.config.ETag { if !IsChild() { - fmt.Println("[Warning] Config.ETag is deprecated since v2.0.6, please use 'middleware/etag'.") + log.Printf("[Warning] Config.ETag is deprecated since v2.0.6, please use 'middleware/etag'.\n") } } @@ -587,7 +591,7 @@ func (app *App) handleTrustedProxy(ipAddress string) { if strings.Contains(ipAddress, "/") { _, ipNet, err := net.ParseCIDR(ipAddress) if err != nil { - fmt.Printf("[Warning] IP range %q could not be parsed: %v\n", ipAddress, err) + log.Printf("[Warning] IP range %q could not be parsed: %v\n", ipAddress, err) } else { app.config.trustedProxyRanges = append(app.config.trustedProxyRanges, ipNet) } @@ -822,7 +826,7 @@ func (app *App) Config() Config { } // Handler returns the server handler. -func (app *App) Handler() fasthttp.RequestHandler { +func (app *App) Handler() fasthttp.RequestHandler { //revive:disable-line:confusing-naming // Having both a Handler() (uppercase) and a handler() (lowercase) is fine. TODO: Use nolint:revive directive instead. See https://github.com/golangci/golangci-lint/issues/3476 // prepare the server for the start app.startupProcess() return app.handler @@ -887,7 +891,7 @@ func (app *App) Hooks() *Hooks { // Test is used for internal debugging by passing a *http.Request. // Timeout is optional and defaults to 1s, -1 will disable it completely. -func (app *App) Test(req *http.Request, msTimeout ...int) (resp *http.Response, err error) { +func (app *App) Test(req *http.Request, msTimeout ...int) (*http.Response, error) { // Set timeout timeout := 1000 if len(msTimeout) > 0 { @@ -902,15 +906,15 @@ func (app *App) Test(req *http.Request, msTimeout ...int) (resp *http.Response, // Dump raw http request dump, err := httputil.DumpRequest(req, true) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to dump request: %w", err) } // Create test connection conn := new(testConn) // Write raw http request - if _, err = conn.r.Write(dump); err != nil { - return nil, err + if _, err := conn.r.Write(dump); err != nil { + return nil, fmt.Errorf("failed to write: %w", err) } // prepare the server for the start app.startupProcess() @@ -943,7 +947,7 @@ func (app *App) Test(req *http.Request, msTimeout ...int) (resp *http.Response, } // Check for errors - if err != nil && err != fasthttp.ErrGetOnly { + if err != nil && !errors.Is(err, fasthttp.ErrGetOnly) { return nil, err } @@ -951,12 +955,17 @@ func (app *App) Test(req *http.Request, msTimeout ...int) (resp *http.Response, buffer := bufio.NewReader(&conn.w) // Convert raw http response to *http.Response - return http.ReadResponse(buffer, req) + res, err := http.ReadResponse(buffer, req) + if err != nil { + return nil, fmt.Errorf("failed to read response: %w", err) + } + + return res, nil } type disableLogger struct{} -func (dl *disableLogger) Printf(_ string, _ ...interface{}) { +func (*disableLogger) Printf(_ string, _ ...interface{}) { // fmt.Println(fmt.Sprintf(format, args...)) } @@ -967,7 +976,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 { - fmt.Printf("views: %v\n", err) + log.Printf("[Warning]: failed to load views: %v\n", err) } } @@ -1039,25 +1048,30 @@ func (app *App) ErrorHandler(ctx *Ctx, err error) error { // errors before calling the application's error handler method. func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) { c := app.AcquireCtx(fctx) - if _, ok := err.(*fasthttp.ErrSmallBuffer); ok { + defer app.ReleaseCtx(c) + + var errNetOP *net.OpError + + switch { + case errors.As(err, new(*fasthttp.ErrSmallBuffer)): err = ErrRequestHeaderFieldsTooLarge - } else if netErr, ok := err.(*net.OpError); ok && netErr.Timeout() { + case errors.As(err, &errNetOP) && errNetOP.Timeout(): err = ErrRequestTimeout - } else if err == fasthttp.ErrBodyTooLarge { + case errors.Is(err, fasthttp.ErrBodyTooLarge): err = ErrRequestEntityTooLarge - } else if err == fasthttp.ErrGetOnly { + case errors.Is(err, fasthttp.ErrGetOnly): err = ErrMethodNotAllowed - } else if strings.Contains(err.Error(), "timeout") { + case strings.Contains(err.Error(), "timeout"): err = ErrRequestTimeout - } else { + default: err = NewError(StatusBadRequest, err.Error()) } if catch := app.ErrorHandler(c, err); catch != nil { - _ = c.SendStatus(StatusInternalServerError) + log.Printf("serverErrorHandler: failed to call ErrorHandler: %v\n", catch) + _ = c.SendStatus(StatusInternalServerError) //nolint:errcheck // It is fine to ignore the error here + return } - - app.ReleaseCtx(c) } // startupProcess Is the method which executes all the necessary processes just before the start of the server. diff --git a/app_test.go b/app_test.go index 7182ee6c8a..5f4fbc8767 100644 --- a/app_test.go +++ b/app_test.go @@ -2,6 +2,7 @@ // 🤖 Github Repository: https://github.com/gofiber/fiber // 📌 API Documentation: https://docs.gofiber.io +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package fiber import ( @@ -23,15 +24,16 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" "github.com/valyala/fasthttp/fasthttputil" ) -var testEmptyHandler = func(c *Ctx) error { +func testEmptyHandler(_ *Ctx) error { return nil } -func testStatus200(t *testing.T, app *App, url string, method string) { +func testStatus200(t *testing.T, app *App, url, method string) { t.Helper() req := httptest.NewRequest(method, url, nil) @@ -42,6 +44,8 @@ func testStatus200(t *testing.T, app *App, url string, method string) { } func testErrorResponse(t *testing.T, err error, resp *http.Response, expectedBodyError string) { + t.Helper() + utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 500, resp.StatusCode, "Status code") @@ -140,7 +144,6 @@ func Test_App_ServerErrorHandler_SmallReadBuffer(t *testing.T) { logHeaderSlice := make([]string, 5000) request.Header.Set("Very-Long-Header", strings.Join(logHeaderSlice, "-")) _, err := app.Test(request) - if err == nil { t.Error("Expect an error at app.Test(request)") } @@ -470,7 +473,6 @@ func Test_App_Use_MultiplePrefix(t *testing.T) { body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "/test/doe", string(body)) - } func Test_App_Use_StrictRouting(t *testing.T) { @@ -515,7 +517,7 @@ func Test_App_Add_Method_Test(t *testing.T) { } }() - methods := append(DefaultMethods, "JOHN") + methods := append(DefaultMethods, "JOHN") //nolint:gocritic // We want a new slice here app := New(Config{ RequestMethods: methods, }) @@ -780,7 +782,7 @@ func Test_App_ShutdownWithTimeout(t *testing.T) { case <-timer.C: t.Fatal("idle connections not closed on shutdown") case err := <-shutdownErr: - if err == nil || err != context.DeadlineExceeded { + if err == nil || !errors.Is(err, context.DeadlineExceeded) { t.Fatalf("unexpected err %v. Expecting %v", err, context.DeadlineExceeded) } } @@ -852,7 +854,7 @@ func Test_App_Static_MaxAge(t *testing.T) { app.Static("/", "./.github", Static{MaxAge: 100}) - resp, err := app.Test(httptest.NewRequest("GET", "/index.html", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/index.html", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") @@ -866,19 +868,19 @@ func Test_App_Static_Custom_CacheControl(t *testing.T) { app := New() app.Static("/", "./.github", Static{ModifyResponse: func(c *Ctx) error { - if strings.Contains(string(c.GetRespHeader("Content-Type")), "text/html") { + if strings.Contains(c.GetRespHeader("Content-Type"), "text/html") { c.Response().Header.Set("Cache-Control", "no-cache, no-store, must-revalidate") } return nil }}) - resp, err := app.Test(httptest.NewRequest("GET", "/index.html", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/index.html", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "no-cache, no-store, must-revalidate", resp.Header.Get(HeaderCacheControl), "CacheControl Control") - normal_resp, normal_err := app.Test(httptest.NewRequest("GET", "/config.yml", nil)) - utils.AssertEqual(t, nil, normal_err, "app.Test(req)") - utils.AssertEqual(t, "", normal_resp.Header.Get(HeaderCacheControl), "CacheControl Control") + respNormal, errNormal := app.Test(httptest.NewRequest(MethodGet, "/config.yml", nil)) + utils.AssertEqual(t, nil, errNormal, "app.Test(req)") + utils.AssertEqual(t, "", respNormal.Header.Get(HeaderCacheControl), "CacheControl Control") } // go test -run Test_App_Static_Download @@ -890,7 +892,7 @@ func Test_App_Static_Download(t *testing.T) { app.Static("/fiber.png", "./.github/testdata/fs/img/fiber.png", Static{Download: true}) - resp, err := app.Test(httptest.NewRequest("GET", "/fiber.png", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/fiber.png", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") @@ -1067,7 +1069,7 @@ func Test_App_Static_Next(t *testing.T) { t.Run("app.Static is skipped: invoking Get handler", func(t *testing.T) { t.Parallel() - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(MethodGet, "/", nil) req.Header.Set("X-Custom-Header", "skip") resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -1082,7 +1084,7 @@ func Test_App_Static_Next(t *testing.T) { t.Run("app.Static is not skipped: serving index.html", func(t *testing.T) { t.Parallel() - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(MethodGet, "/", nil) req.Header.Set("X-Custom-Header", "don't skip") resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -1379,7 +1381,7 @@ func Test_Test_DumpError(t *testing.T) { resp, err := app.Test(httptest.NewRequest(MethodGet, "/", errorReader(0))) utils.AssertEqual(t, true, resp == nil) - utils.AssertEqual(t, "errorReader", err.Error()) + utils.AssertEqual(t, "failed to dump request: errorReader", err.Error()) } // go test -run Test_App_Handler @@ -1393,7 +1395,7 @@ type invalidView struct{} func (invalidView) Load() error { return errors.New("invalid view") } -func (i invalidView) Render(io.Writer, string, interface{}, ...string) error { panic("implement me") } +func (invalidView) Render(io.Writer, string, interface{}, ...string) error { panic("implement me") } // go test -run Test_App_Init_Error_View func Test_App_Init_Error_View(t *testing.T) { @@ -1405,7 +1407,9 @@ func Test_App_Init_Error_View(t *testing.T) { utils.AssertEqual(t, "implement me", fmt.Sprintf("%v", err)) } }() - _ = app.config.Views.Render(nil, "", nil) + + err := app.config.Views.Render(nil, "", nil) + utils.AssertEqual(t, nil, err) } // go test -run Test_App_Stack @@ -1535,11 +1539,12 @@ func Test_App_SmallReadBuffer(t *testing.T) { go func() { time.Sleep(500 * time.Millisecond) - resp, err := http.Get("http://127.0.0.1:4006/small-read-buffer") - if resp != nil { - utils.AssertEqual(t, 431, resp.StatusCode) - } + req, err := http.NewRequestWithContext(context.Background(), MethodGet, "http://127.0.0.1:4006/small-read-buffer", http.NoBody) utils.AssertEqual(t, nil, err) + var client http.Client + resp, err := client.Do(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 431, resp.StatusCode) utils.AssertEqual(t, nil, app.Shutdown()) }() @@ -1572,13 +1577,13 @@ func Test_App_New_Test_Parallel(t *testing.T) { t.Run("Test_App_New_Test_Parallel_1", func(t *testing.T) { t.Parallel() app := New(Config{Immutable: true}) - _, err := app.Test(httptest.NewRequest("GET", "/", nil)) + _, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) }) t.Run("Test_App_New_Test_Parallel_2", func(t *testing.T) { t.Parallel() app := New(Config{Immutable: true}) - _, err := app.Test(httptest.NewRequest("GET", "/", nil)) + _, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) }) } @@ -1591,7 +1596,7 @@ func Test_App_ReadBodyStream(t *testing.T) { return c.SendString(fmt.Sprintf("%v %s", c.Request().IsBodyStream(), c.Body())) }) testString := "this is a test" - resp, err := app.Test(httptest.NewRequest("POST", "/", bytes.NewBufferString(testString))) + resp, err := app.Test(httptest.NewRequest(MethodPost, "/", bytes.NewBufferString(testString))) utils.AssertEqual(t, nil, err, "app.Test(req)") body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "io.ReadAll(resp.Body)") @@ -1615,12 +1620,12 @@ func Test_App_DisablePreParseMultipartForm(t *testing.T) { } file, err := mpf.File["test"][0].Open() if err != nil { - return err + return fmt.Errorf("failed to open: %w", err) } buffer := make([]byte, len(testString)) n, err := file.Read(buffer) if err != nil { - return err + return fmt.Errorf("failed to read: %w", err) } if n != len(testString) { return fmt.Errorf("bad read length") @@ -1636,7 +1641,7 @@ func Test_App_DisablePreParseMultipartForm(t *testing.T) { utils.AssertEqual(t, len(testString), n, "writer n") utils.AssertEqual(t, nil, w.Close(), "w.Close()") - req := httptest.NewRequest("POST", "/", b) + req := httptest.NewRequest(MethodPost, "/", b) req.Header.Set("Content-Type", w.FormDataContentType()) resp, err := app.Test(req) utils.AssertEqual(t, nil, err, "app.Test(req)") @@ -1659,7 +1664,7 @@ func Test_App_Test_no_timeout_infinitely(t *testing.T) { return nil }) - req := httptest.NewRequest(http.MethodGet, "/", http.NoBody) + req := httptest.NewRequest(MethodGet, "/", http.NoBody) _, err = app.Test(req, -1) }() @@ -1696,7 +1701,7 @@ func Test_App_SetTLSHandler(t *testing.T) { func Test_App_AddCustomRequestMethod(t *testing.T) { t.Parallel() - methods := append(DefaultMethods, "TEST") + methods := append(DefaultMethods, "TEST") //nolint:gocritic // We want a new slice here app := New(Config{ RequestMethods: methods, }) diff --git a/client.go b/client.go index f2fbec41a0..334b99a890 100644 --- a/client.go +++ b/client.go @@ -15,6 +15,7 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -50,6 +51,7 @@ type Args = fasthttp.Args // Copy from fasthttp type RetryIfFunc = fasthttp.RetryIfFunc +//nolint:gochecknoglobals // TODO: Do not use a global var here var defaultClient Client // Client implements http client. @@ -186,11 +188,11 @@ func (a *Agent) Parse() error { uri := a.req.URI() - isTLS := false + var isTLS bool scheme := uri.Scheme() - if bytes.Equal(scheme, strHTTPS) { + if bytes.Equal(scheme, []byte(schemeHTTPS)) { isTLS = true - } else if !bytes.Equal(scheme, strHTTP) { + } else if !bytes.Equal(scheme, []byte(schemeHTTP)) { return fmt.Errorf("unsupported protocol %q. http and https are supported", scheme) } @@ -241,7 +243,7 @@ func (a *Agent) SetBytesV(k string, v []byte) *Agent { // SetBytesKV sets the given 'key: value' header. // // Use AddBytesKV for setting multiple header values under the same key. -func (a *Agent) SetBytesKV(k []byte, v []byte) *Agent { +func (a *Agent) SetBytesKV(k, v []byte) *Agent { a.req.Header.SetBytesKV(k, v) return a @@ -281,7 +283,7 @@ func (a *Agent) AddBytesV(k string, v []byte) *Agent { // // Multiple headers with the same key may be added with this function. // Use SetBytesKV for setting a single header for the given key. -func (a *Agent) AddBytesKV(k []byte, v []byte) *Agent { +func (a *Agent) AddBytesKV(k, v []byte) *Agent { a.req.Header.AddBytesKV(k, v) return a @@ -652,10 +654,8 @@ func (a *Agent) Reuse() *Agent { // certificate chain and host name. func (a *Agent) InsecureSkipVerify() *Agent { if a.HostClient.TLSConfig == nil { - /* #nosec G402 */ - a.HostClient.TLSConfig = &tls.Config{InsecureSkipVerify: true} // #nosec G402 + a.HostClient.TLSConfig = &tls.Config{InsecureSkipVerify: true} //nolint:gosec // We explicitly let the user set insecure mode here } else { - /* #nosec G402 */ a.HostClient.TLSConfig.InsecureSkipVerify = true } @@ -728,14 +728,14 @@ func (a *Agent) RetryIf(retryIf RetryIfFunc) *Agent { // Bytes returns the status code, bytes body and errors of url. // // it's not safe to use Agent after calling [Agent.Bytes] -func (a *Agent) Bytes() (code int, body []byte, errs []error) { +func (a *Agent) Bytes() (int, []byte, []error) { defer a.release() return a.bytes() } -func (a *Agent) bytes() (code int, body []byte, errs []error) { +func (a *Agent) bytes() (code int, body []byte, errs []error) { //nolint:nonamedreturns,revive // We want to overwrite the body in a deferred func. TODO: Check if we really need to do this. We eventually want to get rid of all named returns. if errs = append(errs, a.errs...); len(errs) > 0 { - return + return code, body, errs } var ( @@ -760,7 +760,7 @@ func (a *Agent) bytes() (code int, body []byte, errs []error) { code = resp.StatusCode() } - body = append(a.dest, resp.Body()...) + body = append(a.dest, resp.Body()...) //nolint:gocritic // We want to append to the returned slice here if nilResp { ReleaseResponse(resp) @@ -770,25 +770,25 @@ func (a *Agent) bytes() (code int, body []byte, errs []error) { if a.timeout > 0 { if err := a.HostClient.DoTimeout(req, resp, a.timeout); err != nil { errs = append(errs, err) - return + return code, body, errs } } else if a.maxRedirectsCount > 0 && (string(req.Header.Method()) == MethodGet || string(req.Header.Method()) == MethodHead) { if err := a.HostClient.DoRedirects(req, resp, a.maxRedirectsCount); err != nil { errs = append(errs, err) - return + return code, body, errs } } else if err := a.HostClient.Do(req, resp); err != nil { errs = append(errs, err) } - return + return code, body, errs } func printDebugInfo(req *Request, resp *Response, w io.Writer) { msg := fmt.Sprintf("Connected to %s(%s)\r\n\r\n", req.URI().Host(), resp.RemoteAddr()) - _, _ = w.Write(utils.UnsafeBytes(msg)) - _, _ = req.WriteTo(w) - _, _ = resp.WriteTo(w) + _, _ = w.Write(utils.UnsafeBytes(msg)) //nolint:errcheck // This will never fail + _, _ = req.WriteTo(w) //nolint:errcheck // This will never fail + _, _ = resp.WriteTo(w) //nolint:errcheck // This will never fail } // String returns the status code, string body and errors of url. @@ -797,6 +797,7 @@ func printDebugInfo(req *Request, resp *Response, w io.Writer) { func (a *Agent) String() (int, string, []error) { defer a.release() code, body, errs := a.bytes() + // TODO: There might be a data race here on body. Maybe use utils.CopyBytes on it? return code, utils.UnsafeString(body), errs } @@ -805,12 +806,15 @@ func (a *Agent) String() (int, string, []error) { // And bytes body will be unmarshalled to given v. // // it's not safe to use Agent after calling [Agent.Struct] -func (a *Agent) Struct(v interface{}) (code int, body []byte, errs []error) { +func (a *Agent) Struct(v interface{}) (int, []byte, []error) { defer a.release() - if code, body, errs = a.bytes(); len(errs) > 0 { - return + + code, body, errs := a.bytes() + if len(errs) > 0 { + return code, body, errs } + // TODO: This should only be done once if a.jsonDecoder == nil { a.jsonDecoder = json.Unmarshal } @@ -819,7 +823,7 @@ func (a *Agent) Struct(v interface{}) (code int, body []byte, errs []error) { errs = append(errs, err) } - return + return code, body, errs } func (a *Agent) release() { @@ -855,6 +859,7 @@ func (a *Agent) reset() { a.formFiles = a.formFiles[:0] } +//nolint:gochecknoglobals // TODO: Do not use global vars here var ( clientPool sync.Pool agentPool = sync.Pool{ @@ -877,7 +882,11 @@ func AcquireClient() *Client { if v == nil { return &Client{} } - return v.(*Client) + c, ok := v.(*Client) + if !ok { + panic(fmt.Errorf("failed to type-assert to *Client")) + } + return c } // ReleaseClient returns c acquired via AcquireClient to client pool. @@ -899,7 +908,11 @@ func ReleaseClient(c *Client) { // no longer needed. This allows Agent recycling, reduces GC pressure // and usually improves performance. func AcquireAgent() *Agent { - return agentPool.Get().(*Agent) + a, ok := agentPool.Get().(*Agent) + if !ok { + panic(fmt.Errorf("failed to type-assert to *Agent")) + } + return a } // ReleaseAgent returns a acquired via AcquireAgent to Agent pool. @@ -922,7 +935,11 @@ func AcquireResponse() *Response { if v == nil { return &Response{} } - return v.(*Response) + r, ok := v.(*Response) + if !ok { + panic(fmt.Errorf("failed to type-assert to *Response")) + } + return r } // ReleaseResponse return resp acquired via AcquireResponse to response pool. @@ -945,7 +962,11 @@ func AcquireArgs() *Args { if v == nil { return &Args{} } - return v.(*Args) + a, ok := v.(*Args) + if !ok { + panic(fmt.Errorf("failed to type-assert to *Args")) + } + return a } // ReleaseArgs returns the object acquired via AcquireArgs to the pool. @@ -966,7 +987,11 @@ func AcquireFormFile() *FormFile { if v == nil { return &FormFile{} } - return v.(*FormFile) + ff, ok := v.(*FormFile) + if !ok { + panic(fmt.Errorf("failed to type-assert to *FormFile")) + } + return ff } // ReleaseFormFile returns the object acquired via AcquireFormFile to the pool. @@ -981,9 +1006,7 @@ func ReleaseFormFile(ff *FormFile) { formFilePool.Put(ff) } -var ( - strHTTP = []byte("http") - strHTTPS = []byte("https") +const ( defaultUserAgent = "fiber" ) diff --git a/client_test.go b/client_test.go index d9fd61a893..8df0562d61 100644 --- a/client_test.go +++ b/client_test.go @@ -1,3 +1,4 @@ +//nolint:wrapcheck // We must not wrap errors in tests package fiber import ( @@ -19,6 +20,7 @@ import ( "github.com/gofiber/fiber/v2/internal/tlstest" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp/fasthttputil" ) @@ -295,8 +297,10 @@ func Test_Client_Agent_Set_Or_Add_Headers(t *testing.T) { handler := func(c *Ctx) error { c.Request().Header.VisitAll(func(key, value []byte) { if k := string(key); k == "K1" || k == "K2" { - _, _ = c.Write(key) - _, _ = c.Write(value) + _, err := c.Write(key) + utils.AssertEqual(t, nil, err) + _, err = c.Write(value) + utils.AssertEqual(t, nil, err) } }) return nil @@ -581,25 +585,26 @@ type readErrorConn struct { net.Conn } -func (r *readErrorConn) Read(p []byte) (int, error) { +func (*readErrorConn) Read(_ []byte) (int, error) { return 0, fmt.Errorf("error") } -func (r *readErrorConn) Write(p []byte) (int, error) { +func (*readErrorConn) Write(p []byte) (int, error) { return len(p), nil } -func (r *readErrorConn) Close() error { +func (*readErrorConn) Close() error { return nil } -func (r *readErrorConn) LocalAddr() net.Addr { +func (*readErrorConn) LocalAddr() net.Addr { return nil } -func (r *readErrorConn) RemoteAddr() net.Addr { +func (*readErrorConn) RemoteAddr() net.Addr { return nil } + func Test_Client_Agent_RetryIf(t *testing.T) { t.Parallel() @@ -783,7 +788,10 @@ func Test_Client_Agent_MultipartForm_SendFiles(t *testing.T) { buf := make([]byte, fh1.Size) f, err := fh1.Open() utils.AssertEqual(t, nil, err) - defer func() { _ = f.Close() }() + defer func() { + err := f.Close() + utils.AssertEqual(t, nil, err) + }() _, err = f.Read(buf) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "form file", string(buf)) @@ -831,13 +839,16 @@ func checkFormFile(t *testing.T, fh *multipart.FileHeader, filename string) { basename := filepath.Base(filename) utils.AssertEqual(t, fh.Filename, basename) - b1, err := os.ReadFile(filename) + b1, err := os.ReadFile(filename) //nolint:gosec // We're in a test so reading user-provided files by name is fine utils.AssertEqual(t, nil, err) b2 := make([]byte, fh.Size) f, err := fh.Open() utils.AssertEqual(t, nil, err) - defer func() { _ = f.Close() }() + defer func() { + err := f.Close() + utils.AssertEqual(t, nil, err) + }() _, err = f.Read(b2) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, b1, b2) @@ -962,6 +973,7 @@ func Test_Client_Agent_InsecureSkipVerify(t *testing.T) { cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") utils.AssertEqual(t, nil, err) + //nolint:gosec // We're in a test so using old ciphers is fine serverTLSConf := &tls.Config{ Certificates: []tls.Certificate{cer}, } @@ -1137,7 +1149,7 @@ func Test_Client_Agent_Struct(t *testing.T) { defer ReleaseAgent(a) defer a.ConnectionClose() request := a.Request() - request.Header.SetMethod("GET") + request.Header.SetMethod(MethodGet) request.SetRequestURI("http://example.com") err := a.Parse() utils.AssertEqual(t, nil, err) @@ -1198,8 +1210,8 @@ type errorMultipartWriter struct { count int } -func (e *errorMultipartWriter) Boundary() string { return "myBoundary" } -func (e *errorMultipartWriter) SetBoundary(_ string) error { return nil } +func (*errorMultipartWriter) Boundary() string { return "myBoundary" } +func (*errorMultipartWriter) SetBoundary(_ string) error { return nil } func (e *errorMultipartWriter) CreateFormFile(_, _ string) (io.Writer, error) { if e.count == 0 { e.count++ @@ -1207,8 +1219,8 @@ func (e *errorMultipartWriter) CreateFormFile(_, _ string) (io.Writer, error) { } return errorWriter{}, nil } -func (e *errorMultipartWriter) WriteField(_, _ string) error { return errors.New("WriteField error") } -func (e *errorMultipartWriter) Close() error { return errors.New("Close error") } +func (*errorMultipartWriter) WriteField(_, _ string) error { return errors.New("WriteField error") } +func (*errorMultipartWriter) Close() error { return errors.New("Close error") } type errorWriter struct{} diff --git a/color.go b/color.go index cbccd2ebee..944a9b3695 100644 --- a/color.go +++ b/color.go @@ -53,6 +53,8 @@ type Colors struct { } // DefaultColors Default color codes +// +//nolint:gochecknoglobals // Using a global var is fine here var DefaultColors = Colors{ Black: "\u001b[90m", Red: "\u001b[91m", diff --git a/ctx.go b/ctx.go index 4d91d311b0..2b648fe8d7 100644 --- a/ctx.go +++ b/ctx.go @@ -27,10 +27,16 @@ import ( "github.com/gofiber/fiber/v2/internal/dictpool" "github.com/gofiber/fiber/v2/internal/schema" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) +const ( + schemeHTTP = "http" + schemeHTTPS = "https" +) + // maxParams defines the maximum number of parameters per route. const maxParams = 30 @@ -45,6 +51,7 @@ const ( // userContextKey define the key name for storing context.Context in *fasthttp.RequestCtx const userContextKey = "__local_user_context__" +//nolint:gochecknoglobals // TODO: Do not use global vars here var ( // decoderPoolMap helps to improve BodyParser's, QueryParser's and ReqHeaderParser's performance decoderPoolMap = map[string]*sync.Pool{} @@ -52,6 +59,7 @@ var ( tags = []string{queryTag, bodyTag, reqHeaderTag, paramsTag} ) +//nolint:gochecknoinits // init() is used to initialize a global map variable func init() { for _, tag := range tags { decoderPoolMap[tag] = &sync.Pool{New: func() interface{} { @@ -100,9 +108,10 @@ type TLSHandler struct { } // GetClientInfo Callback function to set CHI +// TODO: Why is this a getter which sets stuff? func (t *TLSHandler) GetClientInfo(info *tls.ClientHelloInfo) (*tls.Certificate, error) { t.clientHelloInfo = info - return nil, nil + return nil, nil //nolint:nilnil // Not returning anything useful here is probably fine } // Range data for c.Range @@ -151,7 +160,10 @@ type ParserConfig struct { // AcquireCtx retrieves a new Ctx from the pool. func (app *App) AcquireCtx(fctx *fasthttp.RequestCtx) *Ctx { - c := app.pool.Get().(*Ctx) + c, ok := app.pool.Get().(*Ctx) + if !ok { + panic(fmt.Errorf("failed to type-assert to *Ctx")) + } // Set app reference c.app = app // Reset route and handler index @@ -388,7 +400,6 @@ func (c *Ctx) BodyParser(out interface{}) error { } else { data[k] = append(data[k], v) } - }) return c.parseToStruct(bodyTag, out, data) @@ -401,7 +412,10 @@ func (c *Ctx) BodyParser(out interface{}) error { return c.parseToStruct(bodyTag, out, data.Value) } if strings.HasPrefix(ctype, MIMETextXML) || strings.HasPrefix(ctype, MIMEApplicationXML) { - return xml.Unmarshal(c.Body(), out) + if err := xml.Unmarshal(c.Body(), out); err != nil { + return fmt.Errorf("failed to unmarshal: %w", err) + } + return nil } // No suitable content type found return ErrUnprocessableEntity @@ -673,8 +687,11 @@ func (c *Ctx) Hostname() string { // Port returns the remote port of the request. func (c *Ctx) Port() string { - port := c.fasthttp.RemoteAddr().(*net.TCPAddr).Port - return strconv.Itoa(port) + tcpaddr, ok := c.fasthttp.RemoteAddr().(*net.TCPAddr) + if !ok { + panic(fmt.Errorf("failed to type-assert to *net.TCPAddr")) + } + return strconv.Itoa(tcpaddr.Port) } // IP returns the remote IP address of the request. @@ -691,13 +708,16 @@ func (c *Ctx) IP() string { // extractIPsFromHeader will return a slice of IPs it found given a header name in the order they appear. // When IP validation is enabled, any invalid IPs will be omitted. func (c *Ctx) extractIPsFromHeader(header string) []string { + // TODO: Reuse the c.extractIPFromHeader func somehow in here + headerValue := c.Get(header) // We can't know how many IPs we will return, but we will try to guess with this constant division. // Counting ',' makes function slower for about 50ns in general case. - estimatedCount := len(headerValue) / 8 - if estimatedCount > 8 { - estimatedCount = 8 // Avoid big allocation on big header + const maxEstimatedCount = 8 + estimatedCount := len(headerValue) / maxEstimatedCount + if estimatedCount > maxEstimatedCount { + estimatedCount = maxEstimatedCount // Avoid big allocation on big header } ipsFound := make([]string, 0, estimatedCount) @@ -707,11 +727,10 @@ func (c *Ctx) extractIPsFromHeader(header string) []string { iploop: for { - v4 := false - v6 := false + var v4, v6 bool // Manually splitting string without allocating slice, working with parts directly - i, j = j+1, j+2 + i, j = j+1, j+2 //nolint:gomnd // Using these values is fine if j > len(headerValue) { break @@ -758,9 +777,10 @@ func (c *Ctx) extractIPFromHeader(header string) string { iploop: for { - v4 := false - v6 := false - i, j = j+1, j+2 + var v4, v6 bool + + // Manually splitting string without allocating slice, working with parts directly + i, j = j+1, j+2 //nolint:gomnd // Using these values is fine if j > len(headerValue) { break @@ -793,14 +813,14 @@ func (c *Ctx) extractIPFromHeader(header string) string { return c.fasthttp.RemoteIP().String() } - // default behaviour if IP validation is not enabled is just to return whatever value is + // default behavior if IP validation is not enabled is just to return whatever value is // in the proxy header. Even if it is empty or invalid return c.Get(c.app.config.ProxyHeader) } // IPs returns a string slice of IP addresses specified in the X-Forwarded-For request header. // When IP validation is enabled, only valid IPs are returned. -func (c *Ctx) IPs() (ips []string) { +func (c *Ctx) IPs() []string { return c.extractIPsFromHeader(HeaderXForwardedFor) } @@ -839,7 +859,7 @@ func (c *Ctx) JSON(data interface{}) error { func (c *Ctx) JSONP(data interface{}, callback ...string) error { raw, err := json.Marshal(data) if err != nil { - return err + return fmt.Errorf("failed to marshal: %w", err) } var result, cb string @@ -877,11 +897,11 @@ func (c *Ctx) Links(link ...string) { bb := bytebufferpool.Get() for i := range link { if i%2 == 0 { - _ = bb.WriteByte('<') - _, _ = bb.WriteString(link[i]) - _ = bb.WriteByte('>') + _ = bb.WriteByte('<') //nolint:errcheck // This will never fail + _, _ = bb.WriteString(link[i]) //nolint:errcheck // This will never fail + _ = bb.WriteByte('>') //nolint:errcheck // This will never fail } else { - _, _ = bb.WriteString(`; rel="` + link[i] + `",`) + _, _ = bb.WriteString(`; rel="` + link[i] + `",`) //nolint:errcheck // This will never fail } } c.setCanonical(HeaderLink, utils.TrimRight(c.app.getString(bb.Bytes()), ',')) @@ -890,7 +910,7 @@ func (c *Ctx) Links(link ...string) { // Locals makes it possible to pass interface{} values under keys scoped to the request // and therefore available to all following routes that match the request. -func (c *Ctx) Locals(key interface{}, value ...interface{}) (val interface{}) { +func (c *Ctx) Locals(key interface{}, value ...interface{}) interface{} { if len(value) == 0 { return c.fasthttp.UserValue(key) } @@ -933,9 +953,10 @@ func (c *Ctx) ClientHelloInfo() *tls.ClientHelloInfo { } // Next executes the next method in the stack that matches the current route. -func (c *Ctx) Next() (err error) { +func (c *Ctx) Next() error { // Increment handler index c.indexHandler++ + var err error // Did we executed all route handlers? if c.indexHandler < len(c.route.Handlers) { // Continue route stack @@ -947,7 +968,7 @@ func (c *Ctx) Next() (err error) { return err } -// RestartRouting instead of going to the next handler. This may be usefull after +// RestartRouting instead of going to the next handler. This may be useful after // changing the request path. Note that handlers might be executed again. func (c *Ctx) RestartRouting() error { c.indexRoute = -1 @@ -1017,9 +1038,8 @@ func (c *Ctx) ParamsInt(key string, defaultValue ...int) (int, error) { if err != nil { if len(defaultValue) > 0 { return defaultValue[0], nil - } else { - return 0, err } + return 0, fmt.Errorf("failed to convert: %w", err) } return value, nil @@ -1044,15 +1064,16 @@ func (c *Ctx) Path(override ...string) string { // Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. func (c *Ctx) Protocol() string { if c.fasthttp.IsTLS() { - return "https" + return schemeHTTPS } if !c.IsProxyTrusted() { - return "http" + return schemeHTTP } - scheme := "http" + scheme := schemeHTTP + const lenXHeaderName = 12 c.fasthttp.Request.Header.VisitAll(func(key, val []byte) { - if len(key) < 12 { + if len(key) < lenXHeaderName { return // Neither "X-Forwarded-" nor "X-Url-Scheme" } switch { @@ -1067,7 +1088,7 @@ func (c *Ctx) Protocol() string { scheme = v } } else if bytes.Equal(key, []byte(HeaderXForwardedSsl)) && bytes.Equal(val, []byte("on")) { - scheme = "https" + scheme = schemeHTTPS } case bytes.Equal(key, []byte(HeaderXUrlScheme)): @@ -1100,9 +1121,8 @@ func (c *Ctx) QueryInt(key string, defaultValue ...int) int { if err != nil { if len(defaultValue) > 0 { return defaultValue[0] - } else { - return 0 } + return 0 } return value @@ -1133,7 +1153,6 @@ func (c *Ctx) QueryParser(out interface{}) error { } else { data[k] = append(data[k], v) } - }) if err != nil { @@ -1150,10 +1169,9 @@ func parseParamSquareBrackets(k string) (string, error) { kbytes := []byte(k) for i, b := range kbytes { - if b == '[' && kbytes[i+1] != ']' { if err := bb.WriteByte('.'); err != nil { - return "", err + return "", fmt.Errorf("failed to write: %w", err) } } @@ -1162,7 +1180,7 @@ func parseParamSquareBrackets(k string) (string, error) { } if err := bb.WriteByte(b); err != nil { - return "", err + return "", fmt.Errorf("failed to write: %w", err) } } @@ -1184,21 +1202,27 @@ func (c *Ctx) ReqHeaderParser(out interface{}) error { } else { data[k] = append(data[k], v) } - }) return c.parseToStruct(reqHeaderTag, out, data) } -func (c *Ctx) parseToStruct(aliasTag string, out interface{}, data map[string][]string) error { +func (*Ctx) parseToStruct(aliasTag string, out interface{}, data map[string][]string) error { // Get decoder from pool - schemaDecoder := decoderPoolMap[aliasTag].Get().(*schema.Decoder) + schemaDecoder, ok := decoderPoolMap[aliasTag].Get().(*schema.Decoder) + if !ok { + panic(fmt.Errorf("failed to type-assert to *schema.Decoder")) + } defer decoderPoolMap[aliasTag].Put(schemaDecoder) // Set alias tag schemaDecoder.SetAliasTag(aliasTag) - return schemaDecoder.Decode(out, data) + if err := schemaDecoder.Decode(out, data); err != nil { + return fmt.Errorf("failed to decode: %w", err) + } + + return nil } func equalFieldType(out interface{}, kind reflect.Kind, key string) bool { @@ -1248,24 +1272,23 @@ var ( ) // Range returns a struct containing the type and a slice of ranges. -func (c *Ctx) Range(size int) (rangeData Range, err error) { +func (c *Ctx) Range(size int) (Range, error) { + var rangeData Range rangeStr := c.Get(HeaderRange) if rangeStr == "" || !strings.Contains(rangeStr, "=") { - err = ErrRangeMalformed - return + return rangeData, ErrRangeMalformed } data := strings.Split(rangeStr, "=") - if len(data) != 2 { - err = ErrRangeMalformed - return + const expectedDataParts = 2 + if len(data) != expectedDataParts { + return rangeData, ErrRangeMalformed } rangeData.Type = data[0] arr := strings.Split(data[1], ",") for i := 0; i < len(arr); i++ { item := strings.Split(arr[i], "-") if len(item) == 1 { - err = ErrRangeMalformed - return + return rangeData, ErrRangeMalformed } start, startErr := strconv.Atoi(item[0]) end, endErr := strconv.Atoi(item[1]) @@ -1290,11 +1313,10 @@ func (c *Ctx) Range(size int) (rangeData Range, err error) { }) } if len(rangeData.Ranges) < 1 { - err = ErrRangeUnsatisfiable - return + return rangeData, ErrRangeUnsatisfiable } - return + return rangeData, nil } // Redirect to the URL derived from the specified path, with specified status. @@ -1330,7 +1352,7 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { if !segment.IsParam { _, err := buf.WriteString(segment.Const) if err != nil { - return "", err + return "", fmt.Errorf("failed to write string: %w", err) } continue } @@ -1341,7 +1363,7 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { if isSame || isGreedy { _, err := buf.WriteString(utils.ToString(val)) if err != nil { - return "", err + return "", fmt.Errorf("failed to write string: %w", err) } } } @@ -1373,10 +1395,10 @@ func (c *Ctx) RedirectToRoute(routeName string, params Map, status ...int) error i := 1 for k, v := range queries { - _, _ = queryText.WriteString(k + "=" + v) + _, _ = queryText.WriteString(k + "=" + v) //nolint:errcheck // This will never fail if i != len(queries) { - _, _ = queryText.WriteString("&") + _, _ = queryText.WriteString("&") //nolint:errcheck // This will never fail } i++ } @@ -1399,7 +1421,6 @@ func (c *Ctx) RedirectBack(fallback string, status ...int) error { // Render a template with data and sends a text/html response. // We support the following engines: html, amber, handlebars, mustache, pug func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error { - var err error // Get new buffer from pool buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) @@ -1421,7 +1442,7 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error { // Render template from Views if app.config.Views != nil { if err := app.config.Views.Render(buf, name, bind, layouts...); err != nil { - return err + return fmt.Errorf("failed to render: %w", err) } rendered = true @@ -1433,17 +1454,18 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error { if !rendered { // Render raw template using 'name' as filepath if no engine is set var tmpl *template.Template - if _, err = readContent(buf, name); err != nil { + if _, err := readContent(buf, name); err != nil { return err } // Parse template - if tmpl, err = template.New("").Parse(c.app.getString(buf.Bytes())); err != nil { - return err + tmpl, err := template.New("").Parse(c.app.getString(buf.Bytes())) + if err != nil { + return fmt.Errorf("failed to parse: %w", err) } buf.Reset() // Render template - if err = tmpl.Execute(buf, bind); err != nil { - return err + if err := tmpl.Execute(buf, bind); err != nil { + return fmt.Errorf("failed to execute: %w", err) } } @@ -1451,8 +1473,8 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error { c.fasthttp.Response.Header.SetContentType(MIMETextHTMLCharsetUTF8) // Set rendered template to body c.fasthttp.Response.SetBody(buf.Bytes()) - // Return err if exist - return err + + return nil } func (c *Ctx) renderExtensions(bind interface{}) { @@ -1501,28 +1523,32 @@ func (c *Ctx) Route() *Route { } // SaveFile saves any multipart file to disk. -func (c *Ctx) SaveFile(fileheader *multipart.FileHeader, path string) error { +func (*Ctx) SaveFile(fileheader *multipart.FileHeader, path string) error { return fasthttp.SaveMultipartFile(fileheader, path) } // SaveFileToStorage saves any multipart file to an external storage system. -func (c *Ctx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, storage Storage) error { +func (*Ctx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, storage Storage) error { file, err := fileheader.Open() if err != nil { - return err + return fmt.Errorf("failed to open: %w", err) } content, err := io.ReadAll(file) if err != nil { - return err + return fmt.Errorf("failed to read: %w", err) + } + + if err := storage.Set(path, content, 0); err != nil { + return fmt.Errorf("failed to store: %w", err) } - return storage.Set(path, content, 0) + return nil } // Secure returns whether a secure connection was established. func (c *Ctx) Secure() bool { - return c.Protocol() == "https" + return c.Protocol() == schemeHTTPS } // Send sets the HTTP response body without copying it. @@ -1533,6 +1559,7 @@ func (c *Ctx) Send(body []byte) error { return nil } +//nolint:gochecknoglobals // TODO: Do not use global vars here var ( sendFileOnce sync.Once sendFileFS *fasthttp.FS @@ -1548,6 +1575,7 @@ func (c *Ctx) SendFile(file string, compress ...bool) error { // https://github.com/valyala/fasthttp/blob/c7576cc10cabfc9c993317a2d3f8355497bea156/fs.go#L129-L134 sendFileOnce.Do(func() { + const cacheDuration = 10 * time.Second sendFileFS = &fasthttp.FS{ Root: "", AllowEmptyRoot: true, @@ -1555,7 +1583,7 @@ func (c *Ctx) SendFile(file string, compress ...bool) error { AcceptByteRange: true, Compress: true, CompressedFileSuffix: c.app.config.CompressedFileSuffix, - CacheDuration: 10 * time.Second, + CacheDuration: cacheDuration, IndexNames: []string{"index.html"}, PathNotFound: func(ctx *fasthttp.RequestCtx) { ctx.Response.SetStatusCode(StatusNotFound) @@ -1579,7 +1607,7 @@ func (c *Ctx) SendFile(file string, compress ...bool) error { var err error file = filepath.FromSlash(file) if file, err = filepath.Abs(file); err != nil { - return err + return fmt.Errorf("failed to determine abs file path: %w", err) } if hasTrailingSlash { file += "/" @@ -1644,11 +1672,11 @@ func (c *Ctx) SendStream(stream io.Reader, size ...int) error { } // Set sets the response's HTTP header field to the specified key, value. -func (c *Ctx) Set(key string, val string) { +func (c *Ctx) Set(key, val string) { c.fasthttp.Response.Header.Set(key, val) } -func (c *Ctx) setCanonical(key string, val string) { +func (c *Ctx) setCanonical(key, val string) { c.fasthttp.Response.Header.SetCanonical(c.app.getBytes(key), c.app.getBytes(val)) } @@ -1719,6 +1747,7 @@ func (c *Ctx) Write(p []byte) (int, error) { // Writef appends f & a into response body writer. func (c *Ctx) Writef(f string, a ...interface{}) (int, error) { + //nolint:wrapcheck // This must not be wrapped return fmt.Fprintf(c.fasthttp.Response.BodyWriter(), f, a...) } @@ -1760,8 +1789,9 @@ func (c *Ctx) configDependentPaths() { // Define the path for dividing routes into areas for fast tree detection, so that fewer routes need to be traversed, // since the first three characters area select a list of routes c.treePath = c.treePath[0:0] - if len(c.detectionPath) >= 3 { - c.treePath = c.detectionPath[:3] + const maxDetectionPaths = 3 + if len(c.detectionPath) >= maxDetectionPaths { + c.treePath = c.detectionPath[:maxDetectionPaths] } } @@ -1786,7 +1816,7 @@ func (c *Ctx) IsProxyTrusted() bool { } // IsLocalHost will return true if address is a localhost address. -func (c *Ctx) isLocalHost(address string) bool { +func (*Ctx) isLocalHost(address string) bool { localHosts := []string{"127.0.0.1", "0.0.0.0", "::1"} for _, h := range localHosts { if strings.Contains(address, h) { diff --git a/ctx_test.go b/ctx_test.go index b8ae027bd5..7fc59dc89c 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2,6 +2,7 @@ // 🤖 Github Repository: https://github.com/gofiber/fiber // 📌 API Documentation: https://docs.gofiber.io +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package fiber import ( @@ -28,6 +29,7 @@ import ( "github.com/gofiber/fiber/v2/internal/storage/memory" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) @@ -361,8 +363,10 @@ func Test_Ctx_BodyParser(t *testing.T) { { var gzipJSON bytes.Buffer w := gzip.NewWriter(&gzipJSON) - _, _ = w.Write([]byte(`{"name":"john"}`)) - _ = w.Close() + _, err := w.Write([]byte(`{"name":"john"}`)) + utils.AssertEqual(t, nil, err) + err = w.Close() + utils.AssertEqual(t, nil, err) c.Request().Header.SetContentType(MIMEApplicationJSON) c.Request().Header.Set(HeaderContentEncoding, "gzip") @@ -431,9 +435,7 @@ func Test_Ctx_ParamParser(t *testing.T) { UserID uint `params:"userId"` RoleID uint `params:"roleId"` } - var ( - d = new(Demo) - ) + d := new(Demo) if err := ctx.ParamsParser(d); err != nil { t.Fatal(err) } @@ -519,7 +521,7 @@ func Benchmark_Ctx_BodyParser_JSON(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.BodyParser(d) + _ = c.BodyParser(d) //nolint:errcheck // It is fine to ignore the error here as we check it once further below } utils.AssertEqual(b, nil, c.BodyParser(d)) utils.AssertEqual(b, "john", d.Name) @@ -543,7 +545,7 @@ func Benchmark_Ctx_BodyParser_XML(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.BodyParser(d) + _ = c.BodyParser(d) //nolint:errcheck // It is fine to ignore the error here as we check it once further below } utils.AssertEqual(b, nil, c.BodyParser(d)) utils.AssertEqual(b, "john", d.Name) @@ -567,7 +569,7 @@ func Benchmark_Ctx_BodyParser_Form(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.BodyParser(d) + _ = c.BodyParser(d) //nolint:errcheck // It is fine to ignore the error here as we check it once further below } utils.AssertEqual(b, nil, c.BodyParser(d)) utils.AssertEqual(b, "john", d.Name) @@ -592,7 +594,7 @@ func Benchmark_Ctx_BodyParser_MultipartForm(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.BodyParser(d) + _ = c.BodyParser(d) //nolint:errcheck // It is fine to ignore the error here as we check it once further below } utils.AssertEqual(b, nil, c.BodyParser(d)) utils.AssertEqual(b, "john", d.Name) @@ -879,12 +881,13 @@ func Test_Ctx_FormFile(t *testing.T) { f, err := fh.Open() utils.AssertEqual(t, nil, err) + defer func() { + utils.AssertEqual(t, nil, f.Close()) + }() b := new(bytes.Buffer) _, err = io.Copy(b, f) utils.AssertEqual(t, nil, err) - - f.Close() utils.AssertEqual(t, "hello world", b.String()) return nil }) @@ -897,8 +900,7 @@ func Test_Ctx_FormFile(t *testing.T) { _, err = ioWriter.Write([]byte("hello world")) utils.AssertEqual(t, nil, err) - - writer.Close() + utils.AssertEqual(t, nil, writer.Close()) req := httptest.NewRequest(MethodPost, "/test", body) req.Header.Set(HeaderContentType, writer.FormDataContentType()) @@ -921,10 +923,9 @@ func Test_Ctx_FormValue(t *testing.T) { body := &bytes.Buffer{} writer := multipart.NewWriter(body) - utils.AssertEqual(t, nil, writer.WriteField("name", "john")) + utils.AssertEqual(t, nil, writer.Close()) - writer.Close() req := httptest.NewRequest(MethodPost, "/test", body) req.Header.Set("Content-Type", fmt.Sprintf("multipart/form-data; boundary=%s", writer.Boundary())) req.Header.Set("Content-Length", strconv.Itoa(len(body.Bytes()))) @@ -1240,7 +1241,7 @@ func Test_Ctx_IP(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - // default behaviour will return the remote IP from the stack + // default behavior will return the remote IP from the stack utils.AssertEqual(t, "0.0.0.0", c.IP()) // X-Forwarded-For is set, but it is ignored because proxyHeader is not set @@ -1252,7 +1253,7 @@ func Test_Ctx_IP(t *testing.T) { func Test_Ctx_IP_ProxyHeader(t *testing.T) { t.Parallel() - // make sure that the same behaviour exists for different proxy header names + // make sure that the same behavior exists for different proxy header names proxyHeaderNames := []string{"Real-Ip", HeaderXForwardedFor} for _, proxyHeaderName := range proxyHeaderNames { @@ -1286,7 +1287,7 @@ func Test_Ctx_IP_ProxyHeader(t *testing.T) { func Test_Ctx_IP_ProxyHeader_With_IP_Validation(t *testing.T) { t.Parallel() - // make sure that the same behaviour exists for different proxy header names + // make sure that the same behavior exists for different proxy header names proxyHeaderNames := []string{"Real-Ip", HeaderXForwardedFor} for _, proxyHeaderName := range proxyHeaderNames { @@ -1625,35 +1626,43 @@ func Test_Ctx_ClientHelloInfo(t *testing.T) { }) // Test without TLS handler - resp, _ := app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) - body, _ := io.ReadAll(resp.Body) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) + utils.AssertEqual(t, nil, err) + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, []byte("ClientHelloInfo is nil"), body) // Test with TLS Handler const ( - PSSWithSHA256 = 0x0804 - VersionTLS13 = 0x0304 + pssWithSHA256 = 0x0804 + versionTLS13 = 0x0304 ) app.tlsHandler = &TLSHandler{clientHelloInfo: &tls.ClientHelloInfo{ ServerName: "example.golang", - SignatureSchemes: []tls.SignatureScheme{PSSWithSHA256}, - SupportedVersions: []uint16{VersionTLS13}, + SignatureSchemes: []tls.SignatureScheme{pssWithSHA256}, + SupportedVersions: []uint16{versionTLS13}, }} // Test ServerName - resp, _ = app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) - body, _ = io.ReadAll(resp.Body) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) + utils.AssertEqual(t, nil, err) + body, err = io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, []byte("example.golang"), body) // Test SignatureSchemes - resp, _ = app.Test(httptest.NewRequest(MethodGet, "/SignatureSchemes", nil)) - body, _ = io.ReadAll(resp.Body) - utils.AssertEqual(t, "["+strconv.Itoa(PSSWithSHA256)+"]", string(body)) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/SignatureSchemes", nil)) + utils.AssertEqual(t, nil, err) + body, err = io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "["+strconv.Itoa(pssWithSHA256)+"]", string(body)) // Test SupportedVersions - resp, _ = app.Test(httptest.NewRequest(MethodGet, "/SupportedVersions", nil)) - body, _ = io.ReadAll(resp.Body) - utils.AssertEqual(t, "["+strconv.Itoa(VersionTLS13)+"]", string(body)) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/SupportedVersions", nil)) + utils.AssertEqual(t, nil, err) + body, err = io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "["+strconv.Itoa(versionTLS13)+"]", string(body)) } // go test -run Test_Ctx_InvalidMethod @@ -1688,10 +1697,9 @@ func Test_Ctx_MultipartForm(t *testing.T) { body := &bytes.Buffer{} writer := multipart.NewWriter(body) - utils.AssertEqual(t, nil, writer.WriteField("name", "john")) + utils.AssertEqual(t, nil, writer.Close()) - writer.Close() req := httptest.NewRequest(MethodPost, "/test", body) req.Header.Set(HeaderContentType, fmt.Sprintf("multipart/form-data; boundary=%s", writer.Boundary())) req.Header.Set(HeaderContentLength, strconv.Itoa(len(body.Bytes()))) @@ -1706,8 +1714,8 @@ func Benchmark_Ctx_MultipartForm(b *testing.B) { app := New() app.Post("/", func(c *Ctx) error { - _, _ = c.MultipartForm() - return nil + _, err := c.MultipartForm() + return err }) c := &fasthttp.RequestCtx{} @@ -1889,11 +1897,16 @@ func Benchmark_Ctx_AllParams(b *testing.B) { for n := 0; n < b.N; n++ { res = c.AllParams() } - utils.AssertEqual(b, map[string]string{"param1": "john", - "param2": "doe", - "param3": "is", - "param4": "awesome"}, - res) + utils.AssertEqual( + b, + map[string]string{ + "param1": "john", + "param2": "doe", + "param3": "is", + "param4": "awesome", + }, + res, + ) } // go test -v -run=^$ -bench=Benchmark_Ctx_ParamsParse -benchmem -count=4 @@ -1964,31 +1977,31 @@ func Test_Ctx_Protocol(t *testing.T) { c := app.AcquireCtx(freq) defer app.ReleaseCtx(c) - c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedProto, "https, http") - utils.AssertEqual(t, "https", c.Protocol()) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedProtocol, "https, http") - utils.AssertEqual(t, "https", c.Protocol()) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "https", c.Protocol()) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) } // go test -v -run=^$ -bench=Benchmark_Ctx_Protocol -benchmem -count=4 @@ -2002,7 +2015,7 @@ func Benchmark_Ctx_Protocol(b *testing.B) { for n := 0; n < b.N; n++ { res = c.Protocol() } - utils.AssertEqual(b, "http", res) + utils.AssertEqual(b, schemeHTTP, res) } // go test -run Test_Ctx_Protocol_TrustedProxy @@ -2012,23 +2025,23 @@ func Test_Ctx_Protocol_TrustedProxy(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "https", c.Protocol()) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) } // go test -run Test_Ctx_Protocol_TrustedProxyRange @@ -2038,23 +2051,23 @@ func Test_Ctx_Protocol_TrustedProxyRange(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "https", c.Protocol()) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) + utils.AssertEqual(t, schemeHTTPS, c.Protocol()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) } // go test -run Test_Ctx_Protocol_UntrustedProxyRange @@ -2064,23 +2077,23 @@ func Test_Ctx_Protocol_UntrustedProxyRange(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "http", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "http", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "http", c.Protocol()) + c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) } // go test -run Test_Ctx_Protocol_UnTrustedProxy @@ -2090,23 +2103,23 @@ func Test_Ctx_Protocol_UnTrustedProxy(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "http", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "http", c.Protocol()) + c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() - c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "http", c.Protocol()) + c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Protocol()) + utils.AssertEqual(t, schemeHTTP, c.Protocol()) } // go test -run Test_Ctx_Query @@ -2224,7 +2237,12 @@ func Test_Ctx_SaveFile(t *testing.T) { tempFile, err := os.CreateTemp(os.TempDir(), "test-") utils.AssertEqual(t, nil, err) - defer os.Remove(tempFile.Name()) + defer func(file *os.File) { + err := file.Close() + utils.AssertEqual(t, nil, err) + err = os.Remove(file.Name()) + utils.AssertEqual(t, nil, err) + }(tempFile) err = c.SaveFile(fh, tempFile.Name()) utils.AssertEqual(t, nil, err) @@ -2242,7 +2260,7 @@ func Test_Ctx_SaveFile(t *testing.T) { _, err = ioWriter.Write([]byte("hello world")) utils.AssertEqual(t, nil, err) - writer.Close() + utils.AssertEqual(t, nil, writer.Close()) req := httptest.NewRequest(MethodPost, "/test", body) req.Header.Set("Content-Type", writer.FormDataContentType()) @@ -2284,7 +2302,7 @@ func Test_Ctx_SaveFileToStorage(t *testing.T) { _, err = ioWriter.Write([]byte("hello world")) utils.AssertEqual(t, nil, err) - writer.Close() + utils.AssertEqual(t, nil, writer.Close()) req := httptest.NewRequest(MethodPost, "/test", body) req.Header.Set("Content-Type", writer.FormDataContentType()) @@ -2370,7 +2388,9 @@ func Test_Ctx_Download(t *testing.T) { f, err := os.Open("./ctx.go") utils.AssertEqual(t, nil, err) - defer f.Close() + defer func() { + utils.AssertEqual(t, nil, f.Close()) + }() expect, err := io.ReadAll(f) utils.AssertEqual(t, nil, err) @@ -2389,7 +2409,9 @@ func Test_Ctx_SendFile(t *testing.T) { // fetch file content f, err := os.Open("./ctx.go") utils.AssertEqual(t, nil, err) - defer f.Close() + defer func() { + utils.AssertEqual(t, nil, f.Close()) + }() expectFileContent, err := io.ReadAll(f) utils.AssertEqual(t, nil, err) // fetch file info for the not modified test case @@ -2435,7 +2457,7 @@ func Test_Ctx_SendFile_404(t *testing.T) { return err }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, StatusNotFound, resp.StatusCode) } @@ -2473,11 +2495,11 @@ func Test_Ctx_SendFile_Immutable(t *testing.T) { for _, endpoint := range endpointsForTest { t.Run(endpoint, func(t *testing.T) { // 1st try - resp, err := app.Test(httptest.NewRequest("GET", endpoint, nil)) + resp, err := app.Test(httptest.NewRequest(MethodGet, endpoint, nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, StatusOK, resp.StatusCode) // 2nd try - resp, err = app.Test(httptest.NewRequest("GET", endpoint, nil)) + resp, err = app.Test(httptest.NewRequest(MethodGet, endpoint, nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, StatusOK, resp.StatusCode) }) @@ -2495,9 +2517,9 @@ func Test_Ctx_SendFile_RestoreOriginalURL(t *testing.T) { return err }) - _, err1 := app.Test(httptest.NewRequest("GET", "/?test=true", nil)) + _, err1 := app.Test(httptest.NewRequest(MethodGet, "/?test=true", nil)) // second request required to confirm with zero allocation - _, err2 := app.Test(httptest.NewRequest("GET", "/?test=true", nil)) + _, err2 := app.Test(httptest.NewRequest(MethodGet, "/?test=true", nil)) utils.AssertEqual(t, nil, err1) utils.AssertEqual(t, nil, err2) @@ -2893,12 +2915,12 @@ func Test_Ctx_Render(t *testing.T) { err := c.Render("./.github/testdata/index.tmpl", Map{ "Title": "Hello, World!", }) + utils.AssertEqual(t, nil, err) buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") + _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

Hello, World!

", string(c.Response().Body())) err = c.Render("./.github/testdata/template-non-exists.html", nil) @@ -2918,12 +2940,12 @@ func Test_Ctx_RenderWithoutLocals(t *testing.T) { c.Locals("Title", "Hello, World!") defer app.ReleaseCtx(c) err := c.Render("./.github/testdata/index.tmpl", Map{}) + utils.AssertEqual(t, nil, err) buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") + _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

", string(c.Response().Body())) } @@ -2937,14 +2959,13 @@ func Test_Ctx_RenderWithLocals(t *testing.T) { c.Locals("Title", "Hello, World!") defer app.ReleaseCtx(c) err := c.Render("./.github/testdata/index.tmpl", Map{}) + utils.AssertEqual(t, nil, err) buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") + _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

Hello, World!

", string(c.Response().Body())) - } func Test_Ctx_RenderWithBind(t *testing.T) { @@ -2959,14 +2980,13 @@ func Test_Ctx_RenderWithBind(t *testing.T) { utils.AssertEqual(t, nil, err) defer app.ReleaseCtx(c) err = c.Render("./.github/testdata/index.tmpl", Map{}) + utils.AssertEqual(t, nil, err) buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") + _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

Hello, World!

", string(c.Response().Body())) - } func Test_Ctx_RenderWithOverwrittenBind(t *testing.T) { @@ -2982,12 +3002,12 @@ func Test_Ctx_RenderWithOverwrittenBind(t *testing.T) { err = c.Render("./.github/testdata/index.tmpl", Map{ "Title": "Hello from Fiber!", }) + utils.AssertEqual(t, nil, err) buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") + _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

Hello from Fiber!

", string(c.Response().Body())) } @@ -3005,13 +3025,12 @@ func Test_Ctx_RenderWithBindLocals(t *testing.T) { utils.AssertEqual(t, nil, err) c.Locals("Summary", "Test") - defer app.ReleaseCtx(c) - err = c.Render("./.github/testdata/template.tmpl", Map{}) + err = c.Render("./.github/testdata/template.tmpl", Map{}) utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

Hello, World! Test

", string(c.Response().Body())) + utils.AssertEqual(t, "

Hello, World! Test

", string(c.Response().Body())) } func Test_Ctx_RenderWithLocalsAndBinding(t *testing.T) { @@ -3027,11 +3046,12 @@ func Test_Ctx_RenderWithLocalsAndBinding(t *testing.T) { c.Locals("Title", "This is a test.") defer app.ReleaseCtx(c) + err = c.Render("index.tmpl", Map{ "Title": "Hello, World!", }) - utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "

Hello, World!

", string(c.Response().Body())) } @@ -3060,8 +3080,8 @@ func Benchmark_Ctx_RenderWithLocalsAndBinding(b *testing.B) { for n := 0; n < b.N; n++ { err = c.Render("template.tmpl", Map{}) } - utils.AssertEqual(b, nil, err) + utils.AssertEqual(b, "

Hello, World! Test

", string(c.Response().Body())) } @@ -3083,8 +3103,8 @@ func Benchmark_Ctx_RedirectToRoute(b *testing.B) { "name": "fiber", }) } - utils.AssertEqual(b, nil, err) + utils.AssertEqual(b, 302, c.Response().StatusCode()) utils.AssertEqual(b, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } @@ -3108,8 +3128,8 @@ func Benchmark_Ctx_RedirectToRouteWithQueries(b *testing.B) { "queries": map[string]string{"a": "a", "b": "b"}, }) } - utils.AssertEqual(b, nil, err) + utils.AssertEqual(b, 302, c.Response().StatusCode()) // analysis of query parameters with url parsing, since a map pass is always randomly ordered location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) @@ -3138,8 +3158,8 @@ func Benchmark_Ctx_RenderLocals(b *testing.B) { for n := 0; n < b.N; n++ { err = c.Render("index.tmpl", Map{}) } - utils.AssertEqual(b, nil, err) + utils.AssertEqual(b, "

Hello, World!

", string(c.Response().Body())) } @@ -3164,8 +3184,8 @@ func Benchmark_Ctx_RenderBind(b *testing.B) { for n := 0; n < b.N; n++ { err = c.Render("index.tmpl", Map{}) } - utils.AssertEqual(b, nil, err) + utils.AssertEqual(b, "

Hello, World!

", string(c.Response().Body())) } @@ -3191,8 +3211,7 @@ func Test_Ctx_RestartRouting(t *testing.T) { func Test_Ctx_RestartRoutingWithChangedPath(t *testing.T) { t.Parallel() app := New() - executedOldHandler := false - executedNewHandler := false + var executedOldHandler, executedNewHandler bool app.Get("/old", func(c *Ctx) error { c.Path("/new") @@ -3242,10 +3261,18 @@ type testTemplateEngine struct { func (t *testTemplateEngine) Render(w io.Writer, name string, bind interface{}, layout ...string) error { if len(layout) == 0 { - return t.templates.ExecuteTemplate(w, name, bind) + if err := t.templates.ExecuteTemplate(w, name, bind); err != nil { + return fmt.Errorf("failed to execute template without layout: %w", err) + } + return nil + } + if err := t.templates.ExecuteTemplate(w, name, bind); err != nil { + return fmt.Errorf("failed to execute template: %w", err) } - _ = t.templates.ExecuteTemplate(w, name, bind) - return t.templates.ExecuteTemplate(w, layout[0], bind) + if err := t.templates.ExecuteTemplate(w, layout[0], bind); err != nil { + return fmt.Errorf("failed to execute template with layout: %w", err) + } + return nil } func (t *testTemplateEngine) Load() error { @@ -3324,7 +3351,6 @@ func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) { } utils.AssertEqual(b, "/user/fiber", location) utils.AssertEqual(b, nil, err) - } // go test -run Test_Ctx_Get_Location_From_Route_name @@ -3407,11 +3433,11 @@ func Test_Ctx_Get_Location_From_Route_name_Optional_greedy_one_param(t *testing. type errorTemplateEngine struct{} -func (t errorTemplateEngine) Render(w io.Writer, name string, bind interface{}, layout ...string) error { +func (errorTemplateEngine) Render(_ io.Writer, _ string, _ interface{}, _ ...string) error { return errors.New("errorTemplateEngine") } -func (t errorTemplateEngine) Load() error { return nil } +func (errorTemplateEngine) Load() error { return nil } // go test -run Test_Ctx_Render_Engine_Error func Test_Ctx_Render_Engine_Error(t *testing.T) { @@ -3429,7 +3455,10 @@ func Test_Ctx_Render_Go_Template(t *testing.T) { t.Parallel() file, err := os.CreateTemp(os.TempDir(), "fiber") utils.AssertEqual(t, nil, err) - defer os.Remove(file.Name()) + defer func() { + err := os.Remove(file.Name()) + utils.AssertEqual(t, nil, err) + }() _, err = file.Write([]byte("template")) utils.AssertEqual(t, nil, err) @@ -3821,7 +3850,7 @@ func Test_Ctx_QueryParser(t *testing.T) { } rq := new(RequiredQuery) c.Request().URI().SetQueryString("") - utils.AssertEqual(t, "name is empty", c.QueryParser(rq).Error()) + utils.AssertEqual(t, "failed to decode: name is empty", c.QueryParser(rq).Error()) type ArrayQuery struct { Data []string @@ -3837,7 +3866,7 @@ func Test_Ctx_QueryParser_WithSetParserDecoder(t *testing.T) { t.Parallel() type NonRFCTime time.Time - NonRFCConverter := func(value string) reflect.Value { + nonRFCConverter := func(value string) reflect.Value { if v, err := time.Parse("2006-01-02", value); err == nil { return reflect.ValueOf(v) } @@ -3846,7 +3875,7 @@ func Test_Ctx_QueryParser_WithSetParserDecoder(t *testing.T) { nonRFCTime := ParserType{ Customtype: NonRFCTime{}, - Converter: NonRFCConverter, + Converter: nonRFCConverter, } SetParserDecoder(ParserConfig{ @@ -3872,7 +3901,6 @@ func Test_Ctx_QueryParser_WithSetParserDecoder(t *testing.T) { c.Request().URI().SetQueryString("date=2021-04-10&title=CustomDateTest&Body=October") utils.AssertEqual(t, nil, c.QueryParser(q)) - fmt.Println(q.Date, "q.Date") utils.AssertEqual(t, "CustomDateTest", q.Title) date := fmt.Sprintf("%v", q.Date) utils.AssertEqual(t, "{0 63753609600 }", date) @@ -3907,7 +3935,7 @@ func Test_Ctx_QueryParser_Schema(t *testing.T) { c.Request().URI().SetQueryString("namex=tom&nested.age=10") q = new(Query1) - utils.AssertEqual(t, "name is empty", c.QueryParser(q).Error()) + utils.AssertEqual(t, "failed to decode: name is empty", c.QueryParser(q).Error()) c.Request().URI().SetQueryString("name=tom&nested.agex=10") q = new(Query1) @@ -3915,7 +3943,7 @@ func Test_Ctx_QueryParser_Schema(t *testing.T) { c.Request().URI().SetQueryString("name=tom&test.age=10") q = new(Query1) - utils.AssertEqual(t, "nested is empty", c.QueryParser(q).Error()) + utils.AssertEqual(t, "failed to decode: nested is empty", c.QueryParser(q).Error()) type Query2 struct { Name string `query:"name"` @@ -3933,11 +3961,11 @@ func Test_Ctx_QueryParser_Schema(t *testing.T) { c.Request().URI().SetQueryString("nested.agex=10") q2 = new(Query2) - utils.AssertEqual(t, "nested.age is empty", c.QueryParser(q2).Error()) + utils.AssertEqual(t, "failed to decode: nested.age is empty", c.QueryParser(q2).Error()) c.Request().URI().SetQueryString("nested.agex=10") q2 = new(Query2) - utils.AssertEqual(t, "nested.age is empty", c.QueryParser(q2).Error()) + utils.AssertEqual(t, "failed to decode: nested.age is empty", c.QueryParser(q2).Error()) type Node struct { Value int `query:"val,required"` @@ -3951,7 +3979,7 @@ func Test_Ctx_QueryParser_Schema(t *testing.T) { c.Request().URI().SetQueryString("next.val=2") n = new(Node) - utils.AssertEqual(t, "val is empty", c.QueryParser(n).Error()) + utils.AssertEqual(t, "failed to decode: val is empty", c.QueryParser(n).Error()) c.Request().URI().SetQueryString("val=3&next.value=2") n = new(Node) @@ -4057,7 +4085,7 @@ func Test_Ctx_ReqHeaderParser(t *testing.T) { } rh := new(RequiredHeader) c.Request().Header.Del("name") - utils.AssertEqual(t, "name is empty", c.ReqHeaderParser(rh).Error()) + utils.AssertEqual(t, "failed to decode: name is empty", c.ReqHeaderParser(rh).Error()) } // go test -run Test_Ctx_ReqHeaderParser_WithSetParserDecoder -v @@ -4065,7 +4093,7 @@ func Test_Ctx_ReqHeaderParser_WithSetParserDecoder(t *testing.T) { t.Parallel() type NonRFCTime time.Time - NonRFCConverter := func(value string) reflect.Value { + nonRFCConverter := func(value string) reflect.Value { if v, err := time.Parse("2006-01-02", value); err == nil { return reflect.ValueOf(v) } @@ -4074,7 +4102,7 @@ func Test_Ctx_ReqHeaderParser_WithSetParserDecoder(t *testing.T) { nonRFCTime := ParserType{ Customtype: NonRFCTime{}, - Converter: NonRFCConverter, + Converter: nonRFCConverter, } SetParserDecoder(ParserConfig{ @@ -4103,7 +4131,6 @@ func Test_Ctx_ReqHeaderParser_WithSetParserDecoder(t *testing.T) { c.Request().Header.Add("Body", "October") utils.AssertEqual(t, nil, c.ReqHeaderParser(r)) - fmt.Println(r.Date, "q.Date") utils.AssertEqual(t, "CustomDateTest", r.Title) date := fmt.Sprintf("%v", r.Date) utils.AssertEqual(t, "{0 63753609600 }", date) @@ -4140,7 +4167,7 @@ func Test_Ctx_ReqHeaderParser_Schema(t *testing.T) { c.Request().Header.Del("Name") q = new(Header1) - utils.AssertEqual(t, "Name is empty", c.ReqHeaderParser(q).Error()) + utils.AssertEqual(t, "failed to decode: Name is empty", c.ReqHeaderParser(q).Error()) c.Request().Header.Add("Name", "tom") c.Request().Header.Del("Nested.Age") @@ -4150,7 +4177,7 @@ func Test_Ctx_ReqHeaderParser_Schema(t *testing.T) { c.Request().Header.Del("Nested.Agex") q = new(Header1) - utils.AssertEqual(t, "Nested is empty", c.ReqHeaderParser(q).Error()) + utils.AssertEqual(t, "failed to decode: Nested is empty", c.ReqHeaderParser(q).Error()) c.Request().Header.Del("Nested.Agex") c.Request().Header.Del("Name") @@ -4176,7 +4203,7 @@ func Test_Ctx_ReqHeaderParser_Schema(t *testing.T) { c.Request().Header.Del("Nested.Age") c.Request().Header.Add("Nested.Agex", "10") h2 = new(Header2) - utils.AssertEqual(t, "Nested.age is empty", c.ReqHeaderParser(h2).Error()) + utils.AssertEqual(t, "failed to decode: Nested.age is empty", c.ReqHeaderParser(h2).Error()) type Node struct { Value int `reqHeader:"Val,required"` @@ -4191,7 +4218,7 @@ func Test_Ctx_ReqHeaderParser_Schema(t *testing.T) { c.Request().Header.Del("Val") n = new(Node) - utils.AssertEqual(t, "Val is empty", c.ReqHeaderParser(n).Error()) + utils.AssertEqual(t, "failed to decode: Val is empty", c.ReqHeaderParser(n).Error()) c.Request().Header.Add("Val", "3") c.Request().Header.Del("Next.Val") @@ -4628,8 +4655,9 @@ func Test_Ctx_RepeatParserWithSameStruct(t *testing.T) { var gzipJSON bytes.Buffer w := gzip.NewWriter(&gzipJSON) - _, _ = w.Write([]byte(`{"body_param":"body_param"}`)) - _ = w.Close() + _, _ = w.Write([]byte(`{"body_param":"body_param"}`)) //nolint:errcheck // This will never fail + err := w.Close() + utils.AssertEqual(t, nil, err) c.Request().Header.SetContentType(MIMEApplicationJSON) c.Request().Header.Set(HeaderContentEncoding, "gzip") c.Request().SetBody(gzipJSON.Bytes()) diff --git a/error_test.go b/error_test.go index fe0ec36605..5cd47e33fe 100644 --- a/error_test.go +++ b/error_test.go @@ -1,11 +1,10 @@ package fiber import ( + "encoding/json" "errors" "testing" - jerrors "encoding/json" - "github.com/gofiber/fiber/v2/internal/schema" "github.com/gofiber/fiber/v2/utils" ) @@ -36,42 +35,42 @@ func TestMultiError(t *testing.T) { func TestInvalidUnmarshalError(t *testing.T) { t.Parallel() - var e *jerrors.InvalidUnmarshalError + var e *json.InvalidUnmarshalError ok := errors.As(&InvalidUnmarshalError{}, &e) utils.AssertEqual(t, true, ok) } func TestMarshalerError(t *testing.T) { t.Parallel() - var e *jerrors.MarshalerError + var e *json.MarshalerError ok := errors.As(&MarshalerError{}, &e) utils.AssertEqual(t, true, ok) } func TestSyntaxError(t *testing.T) { t.Parallel() - var e *jerrors.SyntaxError + var e *json.SyntaxError ok := errors.As(&SyntaxError{}, &e) utils.AssertEqual(t, true, ok) } func TestUnmarshalTypeError(t *testing.T) { t.Parallel() - var e *jerrors.UnmarshalTypeError + var e *json.UnmarshalTypeError ok := errors.As(&UnmarshalTypeError{}, &e) utils.AssertEqual(t, true, ok) } func TestUnsupportedTypeError(t *testing.T) { t.Parallel() - var e *jerrors.UnsupportedTypeError + var e *json.UnsupportedTypeError ok := errors.As(&UnsupportedTypeError{}, &e) utils.AssertEqual(t, true, ok) } func TestUnsupportedValeError(t *testing.T) { t.Parallel() - var e *jerrors.UnsupportedValueError + var e *json.UnsupportedValueError ok := errors.As(&UnsupportedValueError{}, &e) utils.AssertEqual(t, true, ok) } diff --git a/group.go b/group.go index ce1e1b2db5..91c2806244 100644 --- a/group.go +++ b/group.go @@ -168,7 +168,6 @@ func (grp *Group) Group(prefix string, handlers ...Handler) Router { } return newGrp - } // Route is used to define routes with a common prefix inside the common function. diff --git a/helpers.go b/helpers.go index b074c6ca8e..e0e523d8ad 100644 --- a/helpers.go +++ b/helpers.go @@ -20,13 +20,13 @@ import ( "unsafe" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) -/* #nosec */ -// getTlsConfig returns a net listener's tls config -func getTlsConfig(ln net.Listener) *tls.Config { +// getTLSConfig returns a net listener's tls config +func getTLSConfig(ln net.Listener) *tls.Config { // Get listener type pointer := reflect.ValueOf(ln) @@ -37,12 +37,16 @@ func getTlsConfig(ln net.Listener) *tls.Config { // Get private field from value if field := val.FieldByName("config"); field.Type() != nil { // Copy value from pointer field (unsafe) - newval := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())) // #nosec G103 + newval := reflect.NewAt(field.Type(), unsafe.Pointer(field.UnsafeAddr())) //nolint:gosec // Probably the only way to extract the *tls.Config from a net.Listener. TODO: Verify there really is no easier way without using unsafe. if newval.Type() != nil { // Get element from pointer if elem := newval.Elem(); elem.Type() != nil { // Cast value to *tls.Config - return elem.Interface().(*tls.Config) + c, ok := elem.Interface().(*tls.Config) + if !ok { + panic(fmt.Errorf("failed to type-assert to *tls.Config")) + } + return c } } } @@ -53,19 +57,21 @@ func getTlsConfig(ln net.Listener) *tls.Config { } // readContent opens a named file and read content from it -func readContent(rf io.ReaderFrom, name string) (n int64, err error) { +func readContent(rf io.ReaderFrom, name string) (int64, error) { // Read file f, err := os.Open(filepath.Clean(name)) if err != nil { - return 0, err + return 0, fmt.Errorf("failed to open: %w", err) } - // #nosec G307 defer func() { if err = f.Close(); err != nil { log.Printf("Error closing file: %s\n", err) } }() - return rf.ReadFrom(f) + if n, err := rf.ReadFrom(f); err != nil { + return n, fmt.Errorf("failed to read: %w", err) + } + return 0, nil } // quoteString escape special characters in a given string @@ -78,7 +84,8 @@ func (app *App) quoteString(raw string) string { } // Scan stack if other methods match the request -func (app *App) methodExist(ctx *Ctx) (exist bool) { +func (app *App) methodExist(ctx *Ctx) bool { + var exists bool methods := app.config.RequestMethods for i := 0; i < len(methods); i++ { // Skip original method @@ -108,7 +115,7 @@ func (app *App) methodExist(ctx *Ctx) (exist bool) { // No match, next route if match { // We matched - exist = true + exists = true // Add method to Allow header ctx.Append(HeaderAllow, methods[i]) // Break stack loop @@ -116,7 +123,7 @@ func (app *App) methodExist(ctx *Ctx) (exist bool) { } } } - return + return exists } // uniqueRouteStack drop all not unique routes from the slice @@ -146,7 +153,7 @@ func defaultString(value string, defaultValue []string) string { const normalizedHeaderETag = "Etag" // Generate and set ETag header to response -func setETag(c *Ctx, weak bool) { +func setETag(c *Ctx, weak bool) { //nolint: revive // Accepting a bool param is fine here // Don't generate ETags for invalid responses if c.fasthttp.Response.StatusCode() != StatusOK { return @@ -160,7 +167,8 @@ func setETag(c *Ctx, weak bool) { clientEtag := c.Get(HeaderIfNoneMatch) // Generate ETag for response - crc32q := crc32.MakeTable(0xD5828281) + const pol = 0xD5828281 + crc32q := crc32.MakeTable(pol) etag := fmt.Sprintf("\"%d-%v\"", len(body), crc32.Checksum(body, crc32q)) // Enable weak tag @@ -173,7 +181,9 @@ func setETag(c *Ctx, weak bool) { // Check if server's ETag is weak if clientEtag[2:] == etag || clientEtag[2:] == etag[2:] { // W/1 == 1 || W/1 == W/1 - _ = c.SendStatus(StatusNotModified) + if err := c.SendStatus(StatusNotModified); err != nil { + log.Printf("setETag: failed to SendStatus: %v\n", err) + } c.fasthttp.ResetBody() return } @@ -183,7 +193,9 @@ func setETag(c *Ctx, weak bool) { } if strings.Contains(clientEtag, etag) { // 1 == 1 - _ = c.SendStatus(StatusNotModified) + if err := c.SendStatus(StatusNotModified); err != nil { + log.Printf("setETag: failed to SendStatus: %v\n", err) + } c.fasthttp.ResetBody() return } @@ -239,7 +251,7 @@ func getOffer(header string, offers ...string) string { return "" } -func matchEtag(s string, etag string) bool { +func matchEtag(s, etag string) bool { if s == etag || s == "W/"+etag || "W/"+s == etag { return true } @@ -254,12 +266,12 @@ func (app *App) isEtagStale(etag string, noneMatchBytes []byte) bool { // https://github.com/jshttp/fresh/blob/10e0471669dbbfbfd8de65bc6efac2ddd0bfa057/index.js#L110 for i := range noneMatchBytes { switch noneMatchBytes[i] { - case 0x20: + case 0x20: //nolint:gomnd // This is a space (" ") if start == end { start = i + 1 end = i + 1 } - case 0x2c: + case 0x2c: //nolint:gomnd // This is a comma (",") if matchEtag(app.getString(noneMatchBytes[start:end]), etag) { return false } @@ -273,7 +285,7 @@ func (app *App) isEtagStale(etag string, noneMatchBytes []byte) bool { return !matchEtag(app.getString(noneMatchBytes[start:end]), etag) } -func parseAddr(raw string) (host, port string) { +func parseAddr(raw string) (string, string) { //nolint:revive // Returns (host, port) if i := strings.LastIndex(raw, ":"); i != -1 { return raw[:i], raw[i+1:] } @@ -313,21 +325,21 @@ type testConn struct { w bytes.Buffer } -func (c *testConn) Read(b []byte) (int, error) { return c.r.Read(b) } -func (c *testConn) Write(b []byte) (int, error) { return c.w.Write(b) } -func (c *testConn) Close() error { return nil } +func (c *testConn) Read(b []byte) (int, error) { return c.r.Read(b) } //nolint:wrapcheck // This must not be wrapped +func (c *testConn) Write(b []byte) (int, error) { return c.w.Write(b) } //nolint:wrapcheck // This must not be wrapped +func (*testConn) Close() error { return nil } -func (c *testConn) LocalAddr() net.Addr { return &net.TCPAddr{Port: 0, Zone: "", IP: net.IPv4zero} } -func (c *testConn) RemoteAddr() net.Addr { return &net.TCPAddr{Port: 0, Zone: "", IP: net.IPv4zero} } -func (c *testConn) SetDeadline(_ time.Time) error { return nil } -func (c *testConn) SetReadDeadline(_ time.Time) error { return nil } -func (c *testConn) SetWriteDeadline(_ time.Time) error { return nil } +func (*testConn) LocalAddr() net.Addr { return &net.TCPAddr{Port: 0, Zone: "", IP: net.IPv4zero} } +func (*testConn) RemoteAddr() net.Addr { return &net.TCPAddr{Port: 0, Zone: "", IP: net.IPv4zero} } +func (*testConn) SetDeadline(_ time.Time) error { return nil } +func (*testConn) SetReadDeadline(_ time.Time) error { return nil } +func (*testConn) SetWriteDeadline(_ time.Time) error { return nil } -var getStringImmutable = func(b []byte) string { +func getStringImmutable(b []byte) string { return string(b) } -var getBytesImmutable = func(s string) (b []byte) { +func getBytesImmutable(s string) []byte { return []byte(s) } @@ -335,6 +347,7 @@ var getBytesImmutable = func(s string) (b []byte) { func (app *App) methodInt(s string) int { // For better performance if len(app.configured.RequestMethods) == 0 { + //nolint:gomnd // TODO: Use iota instead switch s { case MethodGet: return 0 @@ -391,8 +404,7 @@ func IsMethodIdempotent(m string) bool { } switch m { - case MethodPut, - MethodDelete: + case MethodPut, MethodDelete: return true default: return false @@ -714,7 +726,7 @@ const ( ConstraintBool = "bool" ConstraintFloat = "float" ConstraintAlpha = "alpha" - ConstraintGuid = "guid" + ConstraintGuid = "guid" //nolint:revive,stylecheck // TODO: Rename to "ConstraintGUID" in v3 ConstraintMinLen = "minLen" ConstraintMaxLen = "maxLen" ConstraintLen = "len" diff --git a/helpers_test.go b/helpers_test.go index fefb8f2527..7bb394e520 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -11,6 +11,7 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) diff --git a/hooks.go b/hooks.go index b2766a8cab..c66e49fde8 100644 --- a/hooks.go +++ b/hooks.go @@ -1,14 +1,20 @@ package fiber +import ( + "log" +) + // OnRouteHandler 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 OnShutdownHandler = OnListenHandler -type OnForkHandler = func(int) error -type OnMountHandler = func(*App) error +type ( + OnRouteHandler = func(Route) error + OnNameHandler = OnRouteHandler + OnGroupHandler = func(Group) error + OnGroupNameHandler = OnGroupHandler + OnListenHandler = func() error + OnShutdownHandler = OnListenHandler + OnForkHandler = func(int) error + OnMountHandler = func(*App) error +) // Hooks is a struct to use it with App. type Hooks struct { @@ -180,13 +186,17 @@ func (h *Hooks) executeOnListenHooks() error { func (h *Hooks) executeOnShutdownHooks() { for _, v := range h.onShutdown { - _ = v() + if err := v(); err != nil { + log.Printf("failed to call shutdown hook: %v\n", err) + } } } func (h *Hooks) executeOnForkHooks(pid int) { for _, v := range h.onFork { - _ = v(pid) + if err := v(pid); err != nil { + log.Printf("failed to call fork hook: %v\n", err) + } } } diff --git a/hooks_test.go b/hooks_test.go index df3c2aed96..6a2fc3bfbb 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -7,10 +7,11 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" ) -var testSimpleHandler = func(c *Ctx) error { +func testSimpleHandler(c *Ctx) error { return c.SendString("simple") } diff --git a/internal/dictpool/pool.go b/internal/dictpool/pool.go index 6160a4ff65..baebe7e236 100644 --- a/internal/dictpool/pool.go +++ b/internal/dictpool/pool.go @@ -10,7 +10,7 @@ var defaultPool = sync.Pool{ // AcquireDict acquire new dict. func AcquireDict() *Dict { - return defaultPool.Get().(*Dict) // nolint:forcetypeassert + return defaultPool.Get().(*Dict) } // ReleaseDict release dict. diff --git a/internal/gopsutil/common/common.go b/internal/gopsutil/common/common.go index a02fce4b1a..ed4a3bfcb3 100644 --- a/internal/gopsutil/common/common.go +++ b/internal/gopsutil/common/common.go @@ -366,7 +366,7 @@ func HostDev(combineWith ...string) string { // getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running // sysctl commands (see DoSysctrl). func getSysctrlEnv(env []string) []string { - foundLC := false + var foundLC bool for i, line := range env { if strings.HasPrefix(line, "LC_ALL") { env[i] = "LC_ALL=C" diff --git a/internal/gopsutil/mem/mem.go b/internal/gopsutil/mem/mem.go index b039c4ce4e..e2ee01e5c6 100644 --- a/internal/gopsutil/mem/mem.go +++ b/internal/gopsutil/mem/mem.go @@ -6,8 +6,7 @@ import ( "github.com/gofiber/fiber/v2/internal/gopsutil/common" ) -//lint:ignore U1000 we need this elsewhere -var invoke common.Invoker = common.Invoke{} //nolint:all +var invoke common.Invoker = common.Invoke{} //nolint:unused // We use this only for some OS'es // Memory usage statistics. Total, Available and Used contain numbers of bytes // for human consumption. diff --git a/internal/gopsutil/mem/mem_freebsd.go b/internal/gopsutil/mem/mem_freebsd.go index 0682edb7cf..d30e7bd315 100644 --- a/internal/gopsutil/mem/mem_freebsd.go +++ b/internal/gopsutil/mem/mem_freebsd.go @@ -86,7 +86,6 @@ func SwapMemory() (*SwapMemoryStat, error) { } // Constants from vm/vm_param.h -// nolint: golint const ( XSWDEV_VERSION11 = 1 XSWDEV_VERSION = 2 diff --git a/internal/gopsutil/mem/mem_linux.go b/internal/gopsutil/mem/mem_linux.go index a0fc7fd44c..3e7e93b5a8 100644 --- a/internal/gopsutil/mem/mem_linux.go +++ b/internal/gopsutil/mem/mem_linux.go @@ -57,10 +57,12 @@ func fillFromMeminfoWithContext(ctx context.Context) (*VirtualMemoryStat, *Virtu lines, _ := common.ReadLines(filename) // flag if MemAvailable is in /proc/meminfo (kernel 3.14+) - memavail := false - activeFile := false // "Active(file)" not available: 2.6.28 / Dec 2008 - inactiveFile := false // "Inactive(file)" not available: 2.6.28 / Dec 2008 - sReclaimable := false // "SReclaimable:" not available: 2.6.19 / Nov 2006 + var ( + memavail bool + activeFile bool // "Active(file)" not available: 2.6.28 / Dec 2008 + inactiveFile bool // "Inactive(file)" not available: 2.6.28 / Dec 2008 + sReclaimable bool // "SReclaimable:" not available: 2.6.19 / Nov 2006 + ) ret := &VirtualMemoryStat{} retEx := &VirtualMemoryExStat{} diff --git a/internal/msgp/json.go b/internal/msgp/json.go index 0e11e603c0..80d39635ca 100644 --- a/internal/msgp/json.go +++ b/internal/msgp/json.go @@ -168,7 +168,7 @@ func rwArray(dst jsWriter, src *Reader) (n int, err error) { if err != nil { return } - comma := false + var comma bool for i := uint32(0); i < sz; i++ { if comma { err = dst.WriteByte(',') diff --git a/internal/schema/cache.go b/internal/schema/cache.go index bf21697cf1..cb227a6969 100644 --- a/internal/schema/cache.go +++ b/internal/schema/cache.go @@ -163,7 +163,7 @@ func (c *cache) createField(field reflect.StructField, parentAlias string) *fiel } // Check if the type is supported and don't cache it if not. // First let's get the basic type. - isSlice, isStruct := false, false + var isSlice, isStruct bool ft := field.Type m := isTextUnmarshaler(reflect.Zero(ft)) if ft.Kind() == reflect.Ptr { diff --git a/internal/storage/memory/memory_test.go b/internal/storage/memory/memory_test.go index fb2b88a0e5..7fc6b955d8 100644 --- a/internal/storage/memory/memory_test.go +++ b/internal/storage/memory/memory_test.go @@ -149,13 +149,13 @@ func Benchmark_Storage_Memory(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { for _, key := range keys { - d.Set(key, value, ttl) + _ = d.Set(key, value, ttl) } for _, key := range keys { _, _ = d.Get(key) } for _, key := range keys { - d.Delete(key) + _ = d.Delete(key) } } }) diff --git a/internal/template/html/html.go b/internal/template/html/html.go index 71f6f02cb5..446431f7b2 100644 --- a/internal/template/html/html.go +++ b/internal/template/html/html.go @@ -156,7 +156,6 @@ func (e *Engine) Load() error { name = strings.TrimSuffix(name, e.extension) // name = strings.Replace(name, e.extension, "", -1) // Read the file - // #gosec G304 buf, err := utils.ReadFile(path, e.fileSystem) if err != nil { return err diff --git a/internal/template/utils/utils.go b/internal/template/utils/utils.go index 0ae8f22fd8..ee681b4f99 100644 --- a/internal/template/utils/utils.go +++ b/internal/template/utils/utils.go @@ -21,7 +21,6 @@ func Walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error { return walk(fs, root, info, walkFn) } -// #nosec G304 // ReadFile returns the raw content of a file func ReadFile(path string, fs http.FileSystem) ([]byte, error) { if fs != nil { diff --git a/listen.go b/listen.go index ddbf8c81fc..19dbae3621 100644 --- a/listen.go +++ b/listen.go @@ -9,6 +9,7 @@ import ( "crypto/x509" "errors" "fmt" + "log" "net" "os" "path/filepath" @@ -31,7 +32,7 @@ func (app *App) Listener(ln net.Listener) error { // Print startup message if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), getTlsConfig(ln) != nil, "") + app.startupMessage(ln.Addr().String(), getTLSConfig(ln) != nil, "") } // Print routes @@ -41,7 +42,7 @@ func (app *App) Listener(ln net.Listener) error { // Prefork is not supported for custom listeners if app.config.Prefork { - fmt.Println("[Warning] Prefork isn't supported for custom listeners.") + log.Printf("[Warning] Prefork isn't supported for custom listeners.\n") } // Start listening @@ -61,7 +62,7 @@ func (app *App) Listen(addr string) error { // Setup listener ln, err := net.Listen(app.config.Network, addr) if err != nil { - return err + return fmt.Errorf("failed to listen: %w", err) } // prepare the server for the start @@ -94,7 +95,7 @@ func (app *App) ListenTLS(addr, certFile, keyFile string) error { // Set TLS config with handler cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { - return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) + return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %w", certFile, keyFile, err) } tlsHandler := &TLSHandler{} @@ -115,7 +116,7 @@ func (app *App) ListenTLS(addr, certFile, keyFile string) error { ln, err := net.Listen(app.config.Network, addr) ln = tls.NewListener(ln, config) if err != nil { - return err + return fmt.Errorf("failed to listen: %w", err) } // prepare the server for the start @@ -150,12 +151,12 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { - return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) + return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %w", certFile, keyFile, err) } clientCACert, err := os.ReadFile(filepath.Clean(clientCertFile)) if err != nil { - return err + return fmt.Errorf("failed to read file: %w", err) } clientCertPool := x509.NewCertPool() clientCertPool.AppendCertsFromPEM(clientCACert) @@ -179,7 +180,7 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) // Setup listener ln, err := tls.Listen(app.config.Network, addr, config) if err != nil { - return err + return fmt.Errorf("failed to listen: %w", err) } // prepare the server for the start @@ -203,7 +204,7 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) } // startupMessage prepares the startup message with the handler number, port, address and other information -func (app *App) startupMessage(addr string, tls bool, pids string) { +func (app *App) startupMessage(addr string, tls bool, pids string) { //nolint: revive // Accepting a bool param is fine here // ignore child processes if IsChild() { return @@ -227,7 +228,8 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { } center := func(s string, width int) string { - pad := strconv.Itoa((width - len(s)) / 2) + const padDiv = 2 + pad := strconv.Itoa((width - len(s)) / padDiv) str := fmt.Sprintf("%"+pad+"s", " ") str += s str += fmt.Sprintf("%"+pad+"s", " ") @@ -238,7 +240,8 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { } centerValue := func(s string, width int) string { - pad := strconv.Itoa((width - runewidth.StringWidth(s)) / 2) + const padDiv = 2 + pad := strconv.Itoa((width - runewidth.StringWidth(s)) / padDiv) str := fmt.Sprintf("%"+pad+"s", " ") str += fmt.Sprintf("%s%s%s", colors.Cyan, s, colors.Black) str += fmt.Sprintf("%"+pad+"s", " ") @@ -249,13 +252,13 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { return str } - pad := func(s string, width int) (str string) { + pad := func(s string, width int) string { toAdd := width - len(s) - str += s + str := s for i := 0; i < toAdd; i++ { str += " " } - return + return str } host, port := parseAddr(addr) @@ -267,9 +270,9 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { } } - scheme := "http" + scheme := schemeHTTP if tls { - scheme = "https" + scheme = schemeHTTPS } isPrefork := "Disabled" @@ -282,19 +285,18 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { procs = "1" } + const lineLen = 49 mainLogo := colors.Black + " ┌───────────────────────────────────────────────────┐\n" if app.config.AppName != "" { - mainLogo += " │ " + centerValue(app.config.AppName, 49) + " │\n" + mainLogo += " │ " + centerValue(app.config.AppName, lineLen) + " │\n" } - mainLogo += " │ " + centerValue("Fiber v"+Version, 49) + " │\n" + mainLogo += " │ " + centerValue("Fiber v"+Version, lineLen) + " │\n" if host == "0.0.0.0" { - mainLogo += - " │ " + center(fmt.Sprintf("%s://127.0.0.1:%s", scheme, port), 49) + " │\n" + - " │ " + center(fmt.Sprintf("(bound on host 0.0.0.0 and port %s)", port), 49) + " │\n" + 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 { - mainLogo += - " │ " + center(fmt.Sprintf("%s://%s:%s", scheme, host, port), 49) + " │\n" + mainLogo += " │ " + center(fmt.Sprintf("%s://%s:%s", scheme, host, port), lineLen) + " │\n" } mainLogo += fmt.Sprintf( @@ -303,8 +305,8 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { " │ Prefork .%s PID ....%s │\n"+ " └───────────────────────────────────────────────────┘"+ colors.Reset, - value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12), - value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14), + value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12), //nolint:gomnd // Using random padding lengths is fine here + value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14), //nolint:gomnd // Using random padding lengths is fine here ) var childPidsLogo string @@ -329,19 +331,21 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { thisLine := "Child PIDs ... " var itemsOnThisLine []string + const maxLineLen = 49 + addLine := func() { lines = append(lines, fmt.Sprintf( newLine, colors.Black, - thisLine+colors.Cyan+pad(strings.Join(itemsOnThisLine, ", "), 49-len(thisLine)), + thisLine+colors.Cyan+pad(strings.Join(itemsOnThisLine, ", "), maxLineLen-len(thisLine)), colors.Black, ), ) } for _, pid := range pidSlice { - if len(thisLine+strings.Join(append(itemsOnThisLine, pid), ", ")) > 49 { + if len(thisLine+strings.Join(append(itemsOnThisLine, pid), ", ")) > maxLineLen { addLine() thisLine = "" itemsOnThisLine = []string{pid} @@ -415,7 +419,7 @@ func (app *App) printRoutesMessage() { var routes []RouteMessage for _, routeStack := range app.stack { for _, route := range routeStack { - var newRoute = RouteMessage{} + var newRoute RouteMessage newRoute.name = route.Name newRoute.method = route.Method newRoute.path = route.Path @@ -443,5 +447,5 @@ func (app *App) printRoutesMessage() { _, _ = 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) } - _ = w.Flush() + _ = w.Flush() //nolint:errcheck // It is fine to ignore the error here } diff --git a/listen_test.go b/listen_test.go index 0dd2cb2937..63283d3485 100644 --- a/listen_test.go +++ b/listen_test.go @@ -7,7 +7,6 @@ package fiber import ( "bytes" "crypto/tls" - "fmt" "io" "log" "os" @@ -17,6 +16,7 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp/fasthttputil" ) @@ -125,8 +125,10 @@ func Test_App_Listener_TLS_Listener(t *testing.T) { if err != nil { utils.AssertEqual(t, nil, err) } + //nolint:gosec // We're in a test so using old ciphers is fine config := &tls.Config{Certificates: []tls.Certificate{cer}} + //nolint:gosec // We're in a test so listening on all interfaces is fine ln, err := tls.Listen(NetworkTCP4, ":0", config) utils.AssertEqual(t, nil, err) @@ -182,7 +184,6 @@ func Test_App_Master_Process_Show_Startup_Message(t *testing.T) { New(Config{Prefork: true}). startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) }) - fmt.Println(startupMessage) utils.AssertEqual(t, true, strings.Contains(startupMessage, "https://127.0.0.1:3000")) utils.AssertEqual(t, true, strings.Contains(startupMessage, "(bound on host 0.0.0.0 and port 3000)")) utils.AssertEqual(t, true, strings.Contains(startupMessage, "Child PIDs")) @@ -196,7 +197,6 @@ func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { startupMessage := captureOutput(func() { app.startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) }) - fmt.Println(startupMessage) utils.AssertEqual(t, "Test App v1.0.1", app.Config().AppName) utils.AssertEqual(t, true, strings.Contains(startupMessage, app.Config().AppName)) } @@ -208,7 +208,6 @@ func Test_App_Master_Process_Show_Startup_MessageWithAppNameNonAscii(t *testing. startupMessage := captureOutput(func() { app.startupMessage(":3000", false, "") }) - fmt.Println(startupMessage) utils.AssertEqual(t, true, strings.Contains(startupMessage, "│ Serveur de vérification des données │")) } @@ -219,8 +218,7 @@ func Test_App_print_Route(t *testing.T) { printRoutesMessage := captureOutput(func() { app.printRoutesMessage() }) - fmt.Println(printRoutesMessage) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, MethodGet)) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "routeName")) @@ -240,11 +238,11 @@ func Test_App_print_Route_with_group(t *testing.T) { app.printRoutesMessage() }) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, MethodGet)) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "POST")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, MethodPost)) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber")) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "PUT")) utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber/*")) diff --git a/middleware/basicauth/basicauth_test.go b/middleware/basicauth/basicauth_test.go index 20f103e6b6..1284163203 100644 --- a/middleware/basicauth/basicauth_test.go +++ b/middleware/basicauth/basicauth_test.go @@ -1,15 +1,15 @@ package basicauth import ( + "encoding/base64" "fmt" "io" "net/http/httptest" "testing" - b64 "encoding/base64" - "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -23,7 +23,7 @@ func Test_BasicAuth_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -39,6 +39,7 @@ func Test_Middleware_BasicAuth(t *testing.T) { }, })) + //nolint:forcetypeassert,errcheck // TODO: Do not force-type assert app.Get("/testauth", func(c *fiber.Ctx) error { username := c.Locals("username").(string) password := c.Locals("password").(string) @@ -74,9 +75,9 @@ func Test_Middleware_BasicAuth(t *testing.T) { for _, tt := range tests { // Base64 encode credentials for http auth header - creds := b64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", tt.username, tt.password))) + creds := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", tt.username, tt.password))) - req := httptest.NewRequest("GET", "/testauth", nil) + req := httptest.NewRequest(fiber.MethodGet, "/testauth", nil) req.Header.Add("Authorization", "Basic "+creds) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -108,7 +109,7 @@ func Benchmark_Middleware_BasicAuth(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") fctx.Request.Header.Set(fiber.HeaderAuthorization, "basic am9objpkb2U=") // john:doe diff --git a/middleware/basicauth/config.go b/middleware/basicauth/config.go index 3845e91538..b19d5087ea 100644 --- a/middleware/basicauth/config.go +++ b/middleware/basicauth/config.go @@ -53,6 +53,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Users: map[string]string{}, diff --git a/middleware/cache/cache.go b/middleware/cache/cache.go index f6db49e2cf..8a733b7571 100644 --- a/middleware/cache/cache.go +++ b/middleware/cache/cache.go @@ -34,6 +34,7 @@ const ( noStore = "no-store" ) +//nolint:gochecknoglobals // TODO: Do not use a global var here var ignoreHeaders = map[string]interface{}{ "Connection": nil, "Keep-Alive": nil, @@ -43,8 +44,8 @@ var ignoreHeaders = map[string]interface{}{ "Trailers": nil, "Transfer-Encoding": nil, "Upgrade": nil, - "Content-Type": nil, // already stored explicitely by the cache manager - "Content-Encoding": nil, // already stored explicitely by the cache manager + "Content-Type": nil, // already stored explicitly by the cache manager + "Content-Encoding": nil, // already stored explicitly by the cache manager } // New creates a new middleware handler @@ -69,7 +70,7 @@ func New(config ...Config) fiber.Handler { // Create indexed heap for tracking expirations ( see heap.go ) heap := &indexedHeap{} // count stored bytes (sizes of response bodies) - var storedBytes uint = 0 + var storedBytes uint // Update timestamp in the configured interval go func() { @@ -81,10 +82,10 @@ func New(config ...Config) fiber.Handler { // Delete key from both manager and storage deleteKey := func(dkey string) { - manager.delete(dkey) + manager.del(dkey) // External storage saves body data with different key if cfg.Storage != nil { - manager.delete(dkey + "_body") + manager.del(dkey + "_body") } } @@ -205,7 +206,7 @@ func New(config ...Config) fiber.Handler { if cfg.StoreResponseHeaders { e.headers = make(map[string][]byte) c.Response().Header.VisitAll( - func(key []byte, value []byte) { + func(key, value []byte) { // create real copy keyS := string(key) if _, ok := ignoreHeaders[keyS]; !ok { diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go index e261023820..404a1edf2d 100644 --- a/middleware/cache/cache_test.go +++ b/middleware/cache/cache_test.go @@ -7,7 +7,6 @@ import ( "fmt" "io" "math" - "net/http" "net/http/httptest" "os" "strconv" @@ -18,6 +17,7 @@ import ( "github.com/gofiber/fiber/v2/internal/storage/memory" "github.com/gofiber/fiber/v2/middleware/etag" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -35,10 +35,10 @@ func Test_Cache_CacheControl(t *testing.T) { return c.SendString("Hello, World!") }) - _, err := app.Test(httptest.NewRequest("GET", "/", nil)) + _, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "public, max-age=10", resp.Header.Get(fiber.HeaderCacheControl)) } @@ -53,7 +53,7 @@ func Test_Cache_Expired(t *testing.T) { return c.SendString(fmt.Sprintf("%d", time.Now().UnixNano())) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) @@ -61,7 +61,7 @@ func Test_Cache_Expired(t *testing.T) { // Sleep until the cache is expired time.Sleep(3 * time.Second) - respCached, err := app.Test(httptest.NewRequest("GET", "/", nil)) + respCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) bodyCached, err := io.ReadAll(respCached.Body) utils.AssertEqual(t, nil, err) @@ -71,7 +71,7 @@ func Test_Cache_Expired(t *testing.T) { } // Next response should be also cached - respCachedNextRound, err := app.Test(httptest.NewRequest("GET", "/", nil)) + respCachedNextRound, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) bodyCachedNextRound, err := io.ReadAll(respCachedNextRound.Body) utils.AssertEqual(t, nil, err) @@ -92,11 +92,11 @@ func Test_Cache(t *testing.T) { return c.SendString(now) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) - cachedReq := httptest.NewRequest("GET", "/", nil) + cachedReq := httptest.NewRequest(fiber.MethodGet, "/", nil) cachedResp, err := app.Test(cachedReq) utils.AssertEqual(t, nil, err) @@ -120,31 +120,31 @@ func Test_Cache_WithNoCacheRequestDirective(t *testing.T) { }) // Request id = 1 - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err := app.Test(req) - defer resp.Body.Close() - body, _ := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("1"), body) // Response cached, entry id = 1 // Request id = 2 without Cache-Control: no-cache - cachedReq := httptest.NewRequest("GET", "/?id=2", nil) + cachedReq := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) cachedResp, err := app.Test(cachedReq) - defer cachedResp.Body.Close() - cachedBody, _ := io.ReadAll(cachedResp.Body) + utils.AssertEqual(t, nil, err) + cachedBody, err := io.ReadAll(cachedResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, cachedResp.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("1"), cachedBody) // Response not cached, returns cached response, entry id = 1 // Request id = 2 with Cache-Control: no-cache - noCacheReq := httptest.NewRequest("GET", "/?id=2", nil) + noCacheReq := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) noCacheReq.Header.Set(fiber.HeaderCacheControl, noCache) noCacheResp, err := app.Test(noCacheReq) - defer noCacheResp.Body.Close() - noCacheBody, _ := io.ReadAll(noCacheResp.Body) + utils.AssertEqual(t, nil, err) + noCacheBody, err := io.ReadAll(noCacheResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, noCacheResp.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("2"), noCacheBody) @@ -152,21 +152,21 @@ func Test_Cache_WithNoCacheRequestDirective(t *testing.T) { /* Check Test_Cache_WithETagAndNoCacheRequestDirective */ // Request id = 2 with Cache-Control: no-cache again - noCacheReq1 := httptest.NewRequest("GET", "/?id=2", nil) + noCacheReq1 := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) noCacheReq1.Header.Set(fiber.HeaderCacheControl, noCache) noCacheResp1, err := app.Test(noCacheReq1) - defer noCacheResp1.Body.Close() - noCacheBody1, _ := io.ReadAll(noCacheResp1.Body) + utils.AssertEqual(t, nil, err) + noCacheBody1, err := io.ReadAll(noCacheResp1.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, noCacheResp1.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("2"), noCacheBody1) // Response cached, returns updated response, entry = 2 // Request id = 1 without Cache-Control: no-cache - cachedReq1 := httptest.NewRequest("GET", "/", nil) + cachedReq1 := httptest.NewRequest(fiber.MethodGet, "/", nil) cachedResp1, err := app.Test(cachedReq1) - defer cachedResp1.Body.Close() - cachedBody1, _ := io.ReadAll(cachedResp1.Body) + utils.AssertEqual(t, nil, err) + cachedBody1, err := io.ReadAll(cachedResp1.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, cachedResp1.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("2"), cachedBody1) @@ -188,7 +188,7 @@ func Test_Cache_WithETagAndNoCacheRequestDirective(t *testing.T) { }) // Request id = 1 - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) @@ -199,7 +199,7 @@ func Test_Cache_WithETagAndNoCacheRequestDirective(t *testing.T) { etagToken := resp.Header.Get("Etag") // Request id = 2 with ETag but without Cache-Control: no-cache - cachedReq := httptest.NewRequest("GET", "/?id=2", nil) + cachedReq := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) cachedReq.Header.Set(fiber.HeaderIfNoneMatch, etagToken) cachedResp, err := app.Test(cachedReq) utils.AssertEqual(t, nil, err) @@ -208,7 +208,7 @@ func Test_Cache_WithETagAndNoCacheRequestDirective(t *testing.T) { // Response not cached, returns cached response, entry id = 1, status not modified // Request id = 2 with ETag and Cache-Control: no-cache - noCacheReq := httptest.NewRequest("GET", "/?id=2", nil) + noCacheReq := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) noCacheReq.Header.Set(fiber.HeaderCacheControl, noCache) noCacheReq.Header.Set(fiber.HeaderIfNoneMatch, etagToken) noCacheResp, err := app.Test(noCacheReq) @@ -221,7 +221,7 @@ func Test_Cache_WithETagAndNoCacheRequestDirective(t *testing.T) { etagToken = noCacheResp.Header.Get("Etag") // Request id = 2 with ETag and Cache-Control: no-cache again - noCacheReq1 := httptest.NewRequest("GET", "/?id=2", nil) + noCacheReq1 := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) noCacheReq1.Header.Set(fiber.HeaderCacheControl, noCache) noCacheReq1.Header.Set(fiber.HeaderIfNoneMatch, etagToken) noCacheResp1, err := app.Test(noCacheReq1) @@ -231,7 +231,7 @@ func Test_Cache_WithETagAndNoCacheRequestDirective(t *testing.T) { // Response cached, returns updated response, entry id = 2, status not modified // Request id = 1 without ETag and Cache-Control: no-cache - cachedReq1 := httptest.NewRequest("GET", "/", nil) + cachedReq1 := httptest.NewRequest(fiber.MethodGet, "/", nil) cachedResp1, err := app.Test(cachedReq1) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, cachedResp1.Header.Get("X-Cache")) @@ -251,11 +251,11 @@ func Test_Cache_WithNoStoreRequestDirective(t *testing.T) { }) // Request id = 2 - noStoreReq := httptest.NewRequest("GET", "/?id=2", nil) + noStoreReq := httptest.NewRequest(fiber.MethodGet, "/?id=2", nil) noStoreReq.Header.Set(fiber.HeaderCacheControl, noStore) noStoreResp, err := app.Test(noStoreReq) - defer noStoreResp.Body.Close() - noStoreBody, _ := io.ReadAll(noStoreResp.Body) + utils.AssertEqual(t, nil, err) + noStoreBody, err := io.ReadAll(noStoreResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, []byte("2"), noStoreBody) // Response not cached, returns updated response @@ -278,11 +278,11 @@ func Test_Cache_WithSeveralRequests(t *testing.T) { for runs := 0; runs < 10; runs++ { for i := 0; i < 10; i++ { func(id int) { - rsp, err := app.Test(httptest.NewRequest(http.MethodGet, fmt.Sprintf("/%d", id), nil)) + rsp, err := app.Test(httptest.NewRequest(fiber.MethodGet, fmt.Sprintf("/%d", id), nil)) utils.AssertEqual(t, nil, err) - defer func(Body io.ReadCloser) { - err := Body.Close() + defer func(body io.ReadCloser) { + err := body.Close() utils.AssertEqual(t, nil, err) }(rsp.Body) @@ -311,11 +311,11 @@ func Test_Cache_Invalid_Expiration(t *testing.T) { return c.SendString(now) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) - cachedReq := httptest.NewRequest("GET", "/", nil) + cachedReq := httptest.NewRequest(fiber.MethodGet, "/", nil) cachedResp, err := app.Test(cachedReq) utils.AssertEqual(t, nil, err) @@ -342,25 +342,25 @@ func Test_Cache_Get(t *testing.T) { return c.SendString(c.Query("cache")) }) - resp, err := app.Test(httptest.NewRequest("POST", "/?cache=123", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodPost, "/?cache=123", nil)) utils.AssertEqual(t, nil, err) body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) - resp, err = app.Test(httptest.NewRequest("POST", "/?cache=12345", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodPost, "/?cache=12345", nil)) utils.AssertEqual(t, nil, err) body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "12345", string(body)) - resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=123", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/get?cache=123", nil)) utils.AssertEqual(t, nil, err) body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) - resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=12345", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/get?cache=12345", nil)) utils.AssertEqual(t, nil, err) body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) @@ -384,25 +384,25 @@ func Test_Cache_Post(t *testing.T) { return c.SendString(c.Query("cache")) }) - resp, err := app.Test(httptest.NewRequest("POST", "/?cache=123", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodPost, "/?cache=123", nil)) utils.AssertEqual(t, nil, err) body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) - resp, err = app.Test(httptest.NewRequest("POST", "/?cache=12345", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodPost, "/?cache=12345", nil)) utils.AssertEqual(t, nil, err) body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) - resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=123", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/get?cache=123", nil)) utils.AssertEqual(t, nil, err) body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) - resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=12345", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/get?cache=12345", nil)) utils.AssertEqual(t, nil, err) body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) @@ -420,14 +420,14 @@ func Test_Cache_NothingToCache(t *testing.T) { return c.SendString(time.Now().String()) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) time.Sleep(500 * time.Millisecond) - respCached, err := app.Test(httptest.NewRequest("GET", "/", nil)) + respCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) bodyCached, err := io.ReadAll(respCached.Body) utils.AssertEqual(t, nil, err) @@ -457,22 +457,22 @@ func Test_Cache_CustomNext(t *testing.T) { return c.Status(fiber.StatusInternalServerError).SendString(time.Now().String()) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) - respCached, err := app.Test(httptest.NewRequest("GET", "/", nil)) + respCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) bodyCached, err := io.ReadAll(respCached.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, bytes.Equal(body, bodyCached)) utils.AssertEqual(t, true, respCached.Header.Get(fiber.HeaderCacheControl) != "") - _, err = app.Test(httptest.NewRequest("GET", "/error", nil)) + _, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/error", nil)) utils.AssertEqual(t, nil, err) - errRespCached, err := app.Test(httptest.NewRequest("GET", "/error", nil)) + errRespCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/error", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, errRespCached.Header.Get(fiber.HeaderCacheControl) == "") } @@ -491,7 +491,7 @@ func Test_CustomKey(t *testing.T) { return c.SendString("hi") }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) _, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, called) @@ -505,7 +505,9 @@ func Test_CustomExpiration(t *testing.T) { var newCacheTime int app.Use(New(Config{ExpirationGenerator: func(c *fiber.Ctx, cfg *Config) time.Duration { called = true - newCacheTime, _ = strconv.Atoi(c.GetRespHeader("Cache-Time", "600")) + var err error + newCacheTime, err = strconv.Atoi(c.GetRespHeader("Cache-Time", "600")) + utils.AssertEqual(t, nil, err) return time.Second * time.Duration(newCacheTime) }})) @@ -515,7 +517,7 @@ func Test_CustomExpiration(t *testing.T) { return c.SendString(now) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, called) utils.AssertEqual(t, 1, newCacheTime) @@ -523,7 +525,7 @@ func Test_CustomExpiration(t *testing.T) { // Sleep until the cache is expired time.Sleep(1 * time.Second) - cachedResp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + cachedResp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) body, err := io.ReadAll(resp.Body) @@ -536,7 +538,7 @@ func Test_CustomExpiration(t *testing.T) { } // Next response should be cached - cachedRespNextRound, err := app.Test(httptest.NewRequest("GET", "/", nil)) + cachedRespNextRound, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) cachedBodyNextRound, err := io.ReadAll(cachedRespNextRound.Body) utils.AssertEqual(t, nil, err) @@ -559,12 +561,12 @@ func Test_AdditionalE2EResponseHeaders(t *testing.T) { return c.SendString("hi") }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "foobar", resp.Header.Get("X-Foobar")) - req = httptest.NewRequest("GET", "/", nil) + req = httptest.NewRequest(fiber.MethodGet, "/", nil) resp, err = app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "foobar", resp.Header.Get("X-Foobar")) @@ -594,19 +596,19 @@ func Test_CacheHeader(t *testing.T) { return c.Status(fiber.StatusInternalServerError).SendString(time.Now().String()) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) - resp, err = app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, resp.Header.Get("X-Cache")) - resp, err = app.Test(httptest.NewRequest("POST", "/?cache=12345", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodPost, "/?cache=12345", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheUnreachable, resp.Header.Get("X-Cache")) - errRespCached, err := app.Test(httptest.NewRequest("GET", "/error", nil)) + errRespCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/error", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheUnreachable, errRespCached.Header.Get("X-Cache")) } @@ -622,12 +624,12 @@ func Test_Cache_WithHead(t *testing.T) { return c.SendString(now) }) - req := httptest.NewRequest("HEAD", "/", nil) + req := httptest.NewRequest(fiber.MethodHead, "/", nil) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) - cachedReq := httptest.NewRequest("HEAD", "/", nil) + cachedReq := httptest.NewRequest(fiber.MethodHead, "/", nil) cachedResp, err := app.Test(cachedReq) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, cachedResp.Header.Get("X-Cache")) @@ -649,28 +651,28 @@ func Test_Cache_WithHeadThenGet(t *testing.T) { return c.SendString(c.Query("cache")) }) - headResp, err := app.Test(httptest.NewRequest("HEAD", "/?cache=123", nil)) + headResp, err := app.Test(httptest.NewRequest(fiber.MethodHead, "/?cache=123", nil)) utils.AssertEqual(t, nil, err) headBody, err := io.ReadAll(headResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "", string(headBody)) utils.AssertEqual(t, cacheMiss, headResp.Header.Get("X-Cache")) - headResp, err = app.Test(httptest.NewRequest("HEAD", "/?cache=123", nil)) + headResp, err = app.Test(httptest.NewRequest(fiber.MethodHead, "/?cache=123", nil)) utils.AssertEqual(t, nil, err) headBody, err = io.ReadAll(headResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "", string(headBody)) utils.AssertEqual(t, cacheHit, headResp.Header.Get("X-Cache")) - getResp, err := app.Test(httptest.NewRequest("GET", "/?cache=123", nil)) + getResp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/?cache=123", nil)) utils.AssertEqual(t, nil, err) getBody, err := io.ReadAll(getResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(getBody)) utils.AssertEqual(t, cacheMiss, getResp.Header.Get("X-Cache")) - getResp, err = app.Test(httptest.NewRequest("GET", "/?cache=123", nil)) + getResp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/?cache=123", nil)) utils.AssertEqual(t, nil, err) getBody, err = io.ReadAll(getResp.Body) utils.AssertEqual(t, nil, err) @@ -691,7 +693,7 @@ func Test_CustomCacheHeader(t *testing.T) { return c.SendString("Hello, World!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, resp.Header.Get("Cache-Status")) } @@ -702,7 +704,7 @@ func Test_CustomCacheHeader(t *testing.T) { func stableAscendingExpiration() func(c1 *fiber.Ctx, c2 *Config) time.Duration { i := 0 return func(c1 *fiber.Ctx, c2 *Config) time.Duration { - i += 1 + i++ return time.Hour * time.Duration(i) } } @@ -738,7 +740,7 @@ func Test_Cache_MaxBytesOrder(t *testing.T) { } for idx, tcase := range cases { - rsp, err := app.Test(httptest.NewRequest("GET", tcase[0], nil)) + rsp, err := app.Test(httptest.NewRequest(fiber.MethodGet, tcase[0], nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, tcase[1], rsp.Header.Get("X-Cache"), fmt.Sprintf("Case %v", idx)) } @@ -756,7 +758,8 @@ func Test_Cache_MaxBytesSizes(t *testing.T) { app.Get("/*", func(c *fiber.Ctx) error { path := c.Context().URI().LastPathSegment() - size, _ := strconv.Atoi(string(path)) + size, err := strconv.Atoi(string(path)) + utils.AssertEqual(t, nil, err) return c.Send(make([]byte, size)) }) @@ -772,7 +775,7 @@ func Test_Cache_MaxBytesSizes(t *testing.T) { } for idx, tcase := range cases { - rsp, err := app.Test(httptest.NewRequest("GET", tcase[0], nil)) + rsp, err := app.Test(httptest.NewRequest(fiber.MethodGet, tcase[0], nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, tcase[1], rsp.Header.Get("X-Cache"), fmt.Sprintf("Case %v", idx)) } @@ -785,14 +788,14 @@ func Benchmark_Cache(b *testing.B) { app.Use(New()) app.Get("/demo", func(c *fiber.Ctx) error { - data, _ := os.ReadFile("../../.github/README.md") + data, _ := os.ReadFile("../../.github/README.md") //nolint:errcheck // We're inside a benchmark return c.Status(fiber.StatusTeapot).Send(data) }) h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/demo") b.ReportAllocs() @@ -815,14 +818,14 @@ func Benchmark_Cache_Storage(b *testing.B) { })) app.Get("/demo", func(c *fiber.Ctx) error { - data, _ := os.ReadFile("../../.github/README.md") + data, _ := os.ReadFile("../../.github/README.md") //nolint:errcheck // We're inside a benchmark return c.Status(fiber.StatusTeapot).Send(data) }) h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/demo") b.ReportAllocs() @@ -850,7 +853,7 @@ func Benchmark_Cache_AdditionalHeaders(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/demo") b.ReportAllocs() @@ -882,7 +885,7 @@ func Benchmark_Cache_MaxSize(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) b.ReportAllocs() b.ResetTimer() diff --git a/middleware/cache/config.go b/middleware/cache/config.go index 12f81e2ae8..9c2d2e104d 100644 --- a/middleware/cache/config.go +++ b/middleware/cache/config.go @@ -1,7 +1,7 @@ package cache import ( - "fmt" + "log" "time" "github.com/gofiber/fiber/v2" @@ -49,10 +49,10 @@ type Config struct { // Default: an in memory store for this process only Storage fiber.Storage - // Deprecated, use Storage instead + // Deprecated: Use Storage instead Store fiber.Storage - // Deprecated, use KeyGenerator instead + // Deprecated: Use KeyGenerator instead Key func(*fiber.Ctx) string // allows you to store additional headers generated by next middlewares & handler @@ -75,6 +75,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Expiration: 1 * time.Minute, @@ -102,11 +104,11 @@ func configDefault(config ...Config) Config { // Set default values if cfg.Store != nil { - fmt.Println("[CACHE] Store is deprecated, please use Storage") + log.Printf("[CACHE] Store is deprecated, please use Storage\n") cfg.Storage = cfg.Store } if cfg.Key != nil { - fmt.Println("[CACHE] Key is deprecated, please use KeyGenerator") + log.Printf("[CACHE] Key is deprecated, please use KeyGenerator\n") cfg.KeyGenerator = cfg.Key } if cfg.Next == nil { diff --git a/middleware/cache/heap.go b/middleware/cache/heap.go index 70271b84b8..fcd8356749 100644 --- a/middleware/cache/heap.go +++ b/middleware/cache/heap.go @@ -41,7 +41,7 @@ func (h indexedHeap) Swap(i, j int) { } func (h *indexedHeap) Push(x interface{}) { - h.pushInternal(x.(heapEntry)) + h.pushInternal(x.(heapEntry)) //nolint:forcetypeassert // Forced type assertion required to implement the heap.Interface interface } func (h *indexedHeap) Pop() interface{} { @@ -65,7 +65,7 @@ func (h *indexedHeap) put(key string, exp uint64, bytes uint) int { idx = h.entries[:n+1][n].idx } else { idx = h.maxidx - h.maxidx += 1 + h.maxidx++ h.indices = append(h.indices, idx) } // Push manually to avoid allocation @@ -77,7 +77,7 @@ func (h *indexedHeap) put(key string, exp uint64, bytes uint) int { } func (h *indexedHeap) removeInternal(realIdx int) (string, uint) { - x := heap.Remove(h, realIdx).(heapEntry) + x := heap.Remove(h, realIdx).(heapEntry) //nolint:forcetypeassert,errcheck // Forced type assertion required to implement the heap.Interface interface return x.key, x.bytes } diff --git a/middleware/cache/manager.go b/middleware/cache/manager.go index 6b9256fd23..78660072e2 100644 --- a/middleware/cache/manager.go +++ b/middleware/cache/manager.go @@ -51,7 +51,7 @@ func newManager(storage fiber.Storage) *manager { // acquire returns an *entry from the sync.Pool func (m *manager) acquire() *item { - return m.pool.Get().(*item) + return m.pool.Get().(*item) //nolint:forcetypeassert // We store nothing else in the pool } // release and reset *entry to sync.Pool @@ -69,38 +69,47 @@ func (m *manager) release(e *item) { } // get data from storage or memory -func (m *manager) get(key string) (it *item) { +func (m *manager) get(key string) *item { + var it *item if m.storage != nil { it = m.acquire() - if raw, _ := m.storage.Get(key); raw != nil { + raw, err := m.storage.Get(key) + if err != nil { + return it + } + if raw != nil { if _, err := it.UnmarshalMsg(raw); err != nil { - return + return it } } - return + return it } - if it, _ = m.memory.Get(key).(*item); it == nil { + if it, _ = m.memory.Get(key).(*item); it == nil { //nolint:errcheck // We store nothing else in the pool it = m.acquire() + return it } - return + return it } // get raw data from storage or memory -func (m *manager) getRaw(key string) (raw []byte) { +func (m *manager) getRaw(key string) []byte { + var raw []byte if m.storage != nil { - raw, _ = m.storage.Get(key) + raw, _ = m.storage.Get(key) //nolint:errcheck // TODO: Handle error here } else { - raw, _ = m.memory.Get(key).([]byte) + raw, _ = m.memory.Get(key).([]byte) //nolint:errcheck // TODO: Handle error here } - return + return raw } // set data to storage or memory func (m *manager) set(key string, it *item, exp time.Duration) { if m.storage != nil { if raw, err := it.MarshalMsg(nil); err == nil { - _ = m.storage.Set(key, raw, exp) + _ = m.storage.Set(key, raw, exp) //nolint:errcheck // TODO: Handle error here } + // we can release data because it's serialized to database + m.release(it) } else { m.memory.Set(key, it, exp) } @@ -109,16 +118,16 @@ func (m *manager) set(key string, it *item, exp time.Duration) { // set data to storage or memory func (m *manager) setRaw(key string, raw []byte, exp time.Duration) { if m.storage != nil { - _ = m.storage.Set(key, raw, exp) + _ = m.storage.Set(key, raw, exp) //nolint:errcheck // TODO: Handle error here } else { m.memory.Set(key, raw, exp) } } // delete data from storage or memory -func (m *manager) delete(key string) { +func (m *manager) del(key string) { if m.storage != nil { - _ = m.storage.Delete(key) + _ = m.storage.Delete(key) //nolint:errcheck // TODO: Handle error here } else { m.memory.Delete(key) } diff --git a/middleware/compress/compress.go b/middleware/compress/compress.go index e65d78558b..626faf4348 100644 --- a/middleware/compress/compress.go +++ b/middleware/compress/compress.go @@ -2,6 +2,7 @@ package compress import ( "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp" ) diff --git a/middleware/compress/compress_test.go b/middleware/compress/compress_test.go index 371a755976..28dd3f2a2f 100644 --- a/middleware/compress/compress_test.go +++ b/middleware/compress/compress_test.go @@ -12,8 +12,10 @@ import ( "github.com/gofiber/fiber/v2/utils" ) +//nolint:gochecknoglobals // Using a global var is fine here var filedata []byte +//nolint:gochecknoinits // init() is used to populate a global var from a README file func init() { dat, err := os.ReadFile("../../.github/README.md") if err != nil { @@ -34,7 +36,7 @@ func Test_Compress_Gzip(t *testing.T) { return c.Send(filedata) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set("Accept-Encoding", "gzip") resp, err := app.Test(req) @@ -64,7 +66,7 @@ func Test_Compress_Different_Level(t *testing.T) { return c.Send(filedata) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set("Accept-Encoding", "gzip") resp, err := app.Test(req) @@ -90,7 +92,7 @@ func Test_Compress_Deflate(t *testing.T) { return c.Send(filedata) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set("Accept-Encoding", "deflate") resp, err := app.Test(req) @@ -114,7 +116,7 @@ func Test_Compress_Brotli(t *testing.T) { return c.Send(filedata) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set("Accept-Encoding", "br") resp, err := app.Test(req, 10000) @@ -138,7 +140,7 @@ func Test_Compress_Disabled(t *testing.T) { return c.Send(filedata) }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set("Accept-Encoding", "br") resp, err := app.Test(req) @@ -162,7 +164,7 @@ func Test_Compress_Next_Error(t *testing.T) { return errors.New("next error") }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set("Accept-Encoding", "gzip") resp, err := app.Test(req) @@ -185,7 +187,7 @@ func Test_Compress_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } diff --git a/middleware/compress/config.go b/middleware/compress/config.go index 5495ad4c42..fb176c6083 100644 --- a/middleware/compress/config.go +++ b/middleware/compress/config.go @@ -33,6 +33,8 @@ const ( ) // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Level: LevelDefault, diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index 5640cc3f2d..78dcae2dd3 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -1,7 +1,6 @@ package cors import ( - "net/http" "strconv" "strings" @@ -54,6 +53,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, AllowOrigins: "*", @@ -128,7 +129,7 @@ func New(config ...Config) fiber.Handler { } // Simple request - if c.Method() != http.MethodOptions { + if c.Method() != fiber.MethodOptions { c.Vary(fiber.HeaderOrigin) c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin) diff --git a/middleware/cors/cors_test.go b/middleware/cors/cors_test.go index 7d42c3c318..3600282331 100644 --- a/middleware/cors/cors_test.go +++ b/middleware/cors/cors_test.go @@ -6,6 +6,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -237,7 +238,7 @@ func Test_CORS_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } diff --git a/middleware/cors/utils.go b/middleware/cors/utils.go index fee658ef93..8b6114bdab 100644 --- a/middleware/cors/utils.go +++ b/middleware/cors/utils.go @@ -1,6 +1,8 @@ package cors -import "strings" +import ( + "strings" +) func matchScheme(domain, pattern string) bool { didx := strings.Index(domain, ":") @@ -20,18 +22,20 @@ func matchSubdomain(domain, pattern string) bool { } domAuth := domain[didx+3:] // to avoid long loop by invalid long domain - if len(domAuth) > 253 { + const maxDomainLen = 253 + if len(domAuth) > maxDomainLen { return false } patAuth := pattern[pidx+3:] domComp := strings.Split(domAuth, ".") patComp := strings.Split(patAuth, ".") - for i := len(domComp)/2 - 1; i >= 0; i-- { + const divHalf = 2 + for i := len(domComp)/divHalf - 1; i >= 0; i-- { opp := len(domComp) - 1 - i domComp[i], domComp[opp] = domComp[opp], domComp[i] } - for i := len(patComp)/2 - 1; i >= 0; i-- { + for i := len(patComp)/divHalf - 1; i >= 0; i-- { opp := len(patComp) - 1 - i patComp[i], patComp[opp] = patComp[opp], patComp[i] } diff --git a/middleware/csrf/config.go b/middleware/csrf/config.go index 94c5287b65..7482b60a8f 100644 --- a/middleware/csrf/config.go +++ b/middleware/csrf/config.go @@ -1,7 +1,7 @@ package csrf import ( - "fmt" + "log" "net/textproto" "strings" "time" @@ -80,13 +80,13 @@ type Config struct { // Optional. Default: utils.UUID KeyGenerator func() string - // Deprecated, please use Expiration + // Deprecated: Please use Expiration CookieExpires time.Duration - // Deprecated, please use Cookie* related fields + // Deprecated: Please use Cookie* related fields Cookie *fiber.Cookie - // Deprecated, please use KeyLookup + // Deprecated: Please use KeyLookup TokenLookup string // ErrorHandler is executed when an error is returned from fiber.Handler. @@ -105,6 +105,8 @@ type Config struct { const HeaderName = "X-Csrf-Token" // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ KeyLookup: "header:" + HeaderName, CookieName: "csrf_", @@ -116,7 +118,7 @@ var ConfigDefault = Config{ } // default ErrorHandler that process return error from fiber.Handler -var defaultErrorHandler = func(c *fiber.Ctx, err error) error { +func defaultErrorHandler(_ *fiber.Ctx, _ error) error { return fiber.ErrForbidden } @@ -132,15 +134,15 @@ func configDefault(config ...Config) Config { // Set default values if cfg.TokenLookup != "" { - fmt.Println("[CSRF] TokenLookup is deprecated, please use KeyLookup") + log.Printf("[CSRF] TokenLookup is deprecated, please use KeyLookup\n") cfg.KeyLookup = cfg.TokenLookup } if int(cfg.CookieExpires.Seconds()) > 0 { - fmt.Println("[CSRF] CookieExpires is deprecated, please use Expiration") + log.Printf("[CSRF] CookieExpires is deprecated, please use Expiration\n") cfg.Expiration = cfg.CookieExpires } if cfg.Cookie != nil { - fmt.Println("[CSRF] Cookie is deprecated, please use Cookie* related fields") + log.Printf("[CSRF] Cookie is deprecated, please use Cookie* related fields\n") if cfg.Cookie.Name != "" { cfg.CookieName = cfg.Cookie.Name } @@ -178,7 +180,8 @@ func configDefault(config ...Config) Config { // Generate the correct extractor to get the token from the correct location selectors := strings.Split(cfg.KeyLookup, ":") - if len(selectors) != 2 { + const numParts = 2 + if len(selectors) != numParts { panic("[CSRF] KeyLookup must in the form of :") } diff --git a/middleware/csrf/csrf.go b/middleware/csrf/csrf.go index e7ad4f2a72..22123441d1 100644 --- a/middleware/csrf/csrf.go +++ b/middleware/csrf/csrf.go @@ -7,9 +7,7 @@ import ( "github.com/gofiber/fiber/v2" ) -var ( - errTokenNotFound = errors.New("csrf token not found") -) +var errTokenNotFound = errors.New("csrf token not found") // New creates a new middleware handler func New(config ...Config) fiber.Handler { @@ -22,7 +20,7 @@ func New(config ...Config) fiber.Handler { dummyValue := []byte{'+'} // Return new handler - return func(c *fiber.Ctx) (err error) { + return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() @@ -39,7 +37,7 @@ func New(config ...Config) fiber.Handler { // Assume that anything not defined as 'safe' by RFC7231 needs protection // Extract token from client request i.e. header, query, param, form or cookie - token, err = cfg.Extractor(c) + token, err := cfg.Extractor(c) if err != nil { return cfg.ErrorHandler(c, err) } diff --git a/middleware/csrf/csrf_test.go b/middleware/csrf/csrf_test.go index ffa6af3e9f..446ae2f72d 100644 --- a/middleware/csrf/csrf_test.go +++ b/middleware/csrf/csrf_test.go @@ -7,6 +7,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -23,7 +24,7 @@ func Test_CSRF(t *testing.T) { h := app.Handler() ctx := &fasthttp.RequestCtx{} - methods := [4]string{"GET", "HEAD", "OPTIONS", "TRACE"} + methods := [4]string{fiber.MethodGet, fiber.MethodHead, fiber.MethodOptions, fiber.MethodTrace} for _, method := range methods { // Generate CSRF token @@ -33,14 +34,14 @@ func Test_CSRF(t *testing.T) { // Without CSRF cookie ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) // Empty/invalid CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(HeaderName, "johndoe") h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) @@ -55,7 +56,7 @@ func Test_CSRF(t *testing.T) { ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(HeaderName, token) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) @@ -72,7 +73,7 @@ func Test_CSRF_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -92,7 +93,7 @@ func Test_CSRF_Invalid_KeyLookup(t *testing.T) { h := app.Handler() ctx := &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) } @@ -110,7 +111,7 @@ func Test_CSRF_From_Form(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Invalid CSRF token - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationForm) h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) @@ -118,12 +119,12 @@ func Test_CSRF_From_Form(t *testing.T) { // Generate CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) token = strings.Split(strings.Split(token, ";")[0], "=")[1] - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationForm) ctx.Request.SetBodyString("_csrf=" + token) h(ctx) @@ -144,7 +145,7 @@ func Test_CSRF_From_Query(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Invalid CSRF token - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.SetRequestURI("/?_csrf=" + utils.UUID()) h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) @@ -152,7 +153,7 @@ func Test_CSRF_From_Query(t *testing.T) { // Generate CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) ctx.Request.SetRequestURI("/") h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) @@ -161,7 +162,7 @@ func Test_CSRF_From_Query(t *testing.T) { ctx.Request.Reset() ctx.Response.Reset() ctx.Request.SetRequestURI("/?_csrf=" + token) - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) utils.AssertEqual(t, "OK", string(ctx.Response.Body())) @@ -181,7 +182,7 @@ func Test_CSRF_From_Param(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Invalid CSRF token - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.SetRequestURI("/" + utils.UUID()) h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) @@ -189,7 +190,7 @@ func Test_CSRF_From_Param(t *testing.T) { // Generate CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) ctx.Request.SetRequestURI("/" + utils.UUID()) h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) @@ -198,7 +199,7 @@ func Test_CSRF_From_Param(t *testing.T) { ctx.Request.Reset() ctx.Response.Reset() ctx.Request.SetRequestURI("/" + token) - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) utils.AssertEqual(t, "OK", string(ctx.Response.Body())) @@ -218,7 +219,7 @@ func Test_CSRF_From_Cookie(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Invalid CSRF token - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.SetRequestURI("/") ctx.Request.Header.Set(fiber.HeaderCookie, "csrf="+utils.UUID()+";") h(ctx) @@ -227,7 +228,7 @@ func Test_CSRF_From_Cookie(t *testing.T) { // Generate CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) ctx.Request.SetRequestURI("/") h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) @@ -235,7 +236,7 @@ func Test_CSRF_From_Cookie(t *testing.T) { ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(fiber.HeaderCookie, "csrf="+token+";") ctx.Request.SetRequestURI("/") h(ctx) @@ -268,7 +269,7 @@ func Test_CSRF_From_Custom(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Invalid CSRF token - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMETextPlain) h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) @@ -276,12 +277,12 @@ func Test_CSRF_From_Custom(t *testing.T) { // Generate CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) token = strings.Split(strings.Split(token, ";")[0], "=")[1] - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMETextPlain) ctx.Request.SetBodyString("_csrf=" + token) h(ctx) @@ -307,13 +308,13 @@ func Test_CSRF_ErrorHandler_InvalidToken(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Generate CSRF token - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) // invalid CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(HeaderName, "johndoe") h(ctx) utils.AssertEqual(t, 419, ctx.Response.StatusCode()) @@ -339,69 +340,69 @@ func Test_CSRF_ErrorHandler_EmptyToken(t *testing.T) { ctx := &fasthttp.RequestCtx{} // Generate CSRF token - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) // empty CSRF token ctx.Request.Reset() ctx.Response.Reset() - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) h(ctx) utils.AssertEqual(t, 419, ctx.Response.StatusCode()) utils.AssertEqual(t, "empty CSRF token", string(ctx.Response.Body())) } // TODO: use this test case and make the unsafe header value bug from https://github.com/gofiber/fiber/issues/2045 reproducible and permanently fixed/tested by this testcase -//func Test_CSRF_UnsafeHeaderValue(t *testing.T) { +// func Test_CSRF_UnsafeHeaderValue(t *testing.T) { // t.Parallel() -// app := fiber.New() -// -// app.Use(New()) -// app.Get("/", func(c *fiber.Ctx) error { -// return c.SendStatus(fiber.StatusOK) -// }) -// app.Get("/test", func(c *fiber.Ctx) error { -// return c.SendStatus(fiber.StatusOK) -// }) -// app.Post("/", func(c *fiber.Ctx) error { -// return c.SendStatus(fiber.StatusOK) -// }) -// -// resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) -// utils.AssertEqual(t, nil, err) -// utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) -// -// var token string -// for _, c := range resp.Cookies() { -// if c.Name != ConfigDefault.CookieName { -// continue -// } -// token = c.Value -// break -// } -// -// fmt.Println("token", token) -// -// getReq := httptest.NewRequest(http.MethodGet, "/", nil) -// getReq.Header.Set(HeaderName, token) -// resp, err = app.Test(getReq) -// -// getReq = httptest.NewRequest(http.MethodGet, "/test", nil) -// getReq.Header.Set("X-Requested-With", "XMLHttpRequest") -// getReq.Header.Set(fiber.HeaderCacheControl, "no") -// getReq.Header.Set(HeaderName, token) -// -// resp, err = app.Test(getReq) -// -// getReq.Header.Set(fiber.HeaderAccept, "*/*") -// getReq.Header.Del(HeaderName) -// resp, err = app.Test(getReq) -// -// postReq := httptest.NewRequest(http.MethodPost, "/", nil) -// postReq.Header.Set("X-Requested-With", "XMLHttpRequest") -// postReq.Header.Set(HeaderName, token) -// resp, err = app.Test(postReq) -//} +// app := fiber.New() + +// app.Use(New()) +// app.Get("/", func(c *fiber.Ctx) error { +// return c.SendStatus(fiber.StatusOK) +// }) +// app.Get("/test", func(c *fiber.Ctx) error { +// return c.SendStatus(fiber.StatusOK) +// }) +// app.Post("/", func(c *fiber.Ctx) error { +// return c.SendStatus(fiber.StatusOK) +// }) + +// resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) +// utils.AssertEqual(t, nil, err) +// utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + +// var token string +// for _, c := range resp.Cookies() { +// if c.Name != ConfigDefault.CookieName { +// continue +// } +// token = c.Value +// break +// } + +// fmt.Println("token", token) + +// getReq := httptest.NewRequest(fiber.MethodGet, "/", nil) +// getReq.Header.Set(HeaderName, token) +// resp, err = app.Test(getReq) + +// getReq = httptest.NewRequest(fiber.MethodGet, "/test", nil) +// getReq.Header.Set("X-Requested-With", "XMLHttpRequest") +// getReq.Header.Set(fiber.HeaderCacheControl, "no") +// getReq.Header.Set(HeaderName, token) + +// resp, err = app.Test(getReq) + +// getReq.Header.Set(fiber.HeaderAccept, "*/*") +// getReq.Header.Del(HeaderName) +// resp, err = app.Test(getReq) + +// postReq := httptest.NewRequest(fiber.MethodPost, "/", nil) +// postReq.Header.Set("X-Requested-With", "XMLHttpRequest") +// postReq.Header.Set(HeaderName, token) +// resp, err = app.Test(postReq) +// } // go test -v -run=^$ -bench=Benchmark_Middleware_CSRF_Check -benchmem -count=4 func Benchmark_Middleware_CSRF_Check(b *testing.B) { @@ -417,12 +418,12 @@ func Benchmark_Middleware_CSRF_Check(b *testing.B) { ctx := &fasthttp.RequestCtx{} // Generate CSRF token - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) token = strings.Split(strings.Split(token, ";")[0], "=")[1] - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(HeaderName, token) b.ReportAllocs() @@ -449,7 +450,7 @@ func Benchmark_Middleware_CSRF_GenerateToken(b *testing.B) { ctx := &fasthttp.RequestCtx{} // Generate CSRF token - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) b.ReportAllocs() b.ResetTimer() diff --git a/middleware/csrf/manager.go b/middleware/csrf/manager.go index 13f0ccb657..a16f3a213a 100644 --- a/middleware/csrf/manager.go +++ b/middleware/csrf/manager.go @@ -41,74 +41,23 @@ func newManager(storage fiber.Storage) *manager { return manager } -// acquire returns an *entry from the sync.Pool -func (m *manager) acquire() *item { - return m.pool.Get().(*item) -} - -// release and reset *entry to sync.Pool -func (m *manager) release(e *item) { - // don't release item if we using memory storage - if m.storage != nil { - return - } - m.pool.Put(e) -} - -// get data from storage or memory -func (m *manager) get(key string) (it *item) { - if m.storage != nil { - it = m.acquire() - if raw, _ := m.storage.Get(key); raw != nil { - if _, err := it.UnmarshalMsg(raw); err != nil { - return - } - } - return - } - if it, _ = m.memory.Get(key).(*item); it == nil { - it = m.acquire() - } - return -} - // get raw data from storage or memory -func (m *manager) getRaw(key string) (raw []byte) { +func (m *manager) getRaw(key string) []byte { + var raw []byte if m.storage != nil { - raw, _ = m.storage.Get(key) + raw, _ = m.storage.Get(key) //nolint:errcheck // TODO: Do not ignore error } else { - raw, _ = m.memory.Get(key).([]byte) - } - return -} - -// set data to storage or memory -func (m *manager) set(key string, it *item, exp time.Duration) { - if m.storage != nil { - if raw, err := it.MarshalMsg(nil); err == nil { - _ = m.storage.Set(key, raw, exp) - } - } else { - // the key is crucial in crsf and sometimes a reference to another value which can be reused later(pool/unsafe values concept), so a copy is made here - m.memory.Set(utils.CopyString(key), it, exp) + raw, _ = m.memory.Get(key).([]byte) //nolint:errcheck // TODO: Do not ignore error } + return raw } // set data to storage or memory func (m *manager) setRaw(key string, raw []byte, exp time.Duration) { if m.storage != nil { - _ = m.storage.Set(key, raw, exp) + _ = m.storage.Set(key, raw, exp) //nolint:errcheck // TODO: Do not ignore error } else { // the key is crucial in crsf and sometimes a reference to another value which can be reused later(pool/unsafe values concept), so a copy is made here m.memory.Set(utils.CopyString(key), raw, exp) } } - -// delete data from storage or memory -func (m *manager) delete(key string) { - if m.storage != nil { - _ = m.storage.Delete(key) - } else { - m.memory.Delete(key) - } -} diff --git a/middleware/encryptcookie/config.go b/middleware/encryptcookie/config.go index 735c2f2d28..d8e4ba21da 100644 --- a/middleware/encryptcookie/config.go +++ b/middleware/encryptcookie/config.go @@ -1,6 +1,8 @@ package encryptcookie -import "github.com/gofiber/fiber/v2" +import ( + "github.com/gofiber/fiber/v2" +) // Config defines the config for middleware. type Config struct { @@ -32,6 +34,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Except: []string{"csrf_"}, diff --git a/middleware/encryptcookie/encryptcookie.go b/middleware/encryptcookie/encryptcookie.go index 1ba4779f56..9e323ce0e1 100644 --- a/middleware/encryptcookie/encryptcookie.go +++ b/middleware/encryptcookie/encryptcookie.go @@ -2,6 +2,7 @@ package encryptcookie import ( "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp" ) diff --git a/middleware/encryptcookie/encryptcookie_test.go b/middleware/encryptcookie/encryptcookie_test.go index 8241d6133c..aed45d6903 100644 --- a/middleware/encryptcookie/encryptcookie_test.go +++ b/middleware/encryptcookie/encryptcookie_test.go @@ -7,9 +7,11 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) +//nolint:gochecknoglobals // Using a global var is fine here var testKey = GenerateKey() func Test_Middleware_Encrypt_Cookie(t *testing.T) { @@ -35,14 +37,14 @@ func Test_Middleware_Encrypt_Cookie(t *testing.T) { // Test empty cookie ctx := &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) utils.AssertEqual(t, "value=", string(ctx.Response.Body())) // Test invalid cookie ctx = &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) ctx.Request.Header.SetCookie("test", "Invalid") h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) @@ -54,18 +56,19 @@ func Test_Middleware_Encrypt_Cookie(t *testing.T) { // Test valid cookie ctx = &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) encryptedCookie := fasthttp.Cookie{} encryptedCookie.SetKey("test") utils.AssertEqual(t, true, ctx.Response.Header.Cookie(&encryptedCookie), "Get cookie value") - decryptedCookieValue, _ := DecryptCookie(string(encryptedCookie.Value()), testKey) + decryptedCookieValue, err := DecryptCookie(string(encryptedCookie.Value()), testKey) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "SomeThing", decryptedCookieValue) ctx = &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) ctx.Request.Header.SetCookie("test", string(encryptedCookie.Value())) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) @@ -91,7 +94,7 @@ func Test_Encrypt_Cookie_Next(t *testing.T) { return nil }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "SomeThing", resp.Cookies()[0].Value) } @@ -123,7 +126,7 @@ func Test_Encrypt_Cookie_Except(t *testing.T) { h := app.Handler() ctx := &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) @@ -135,7 +138,8 @@ func Test_Encrypt_Cookie_Except(t *testing.T) { encryptedCookie := fasthttp.Cookie{} encryptedCookie.SetKey("test2") utils.AssertEqual(t, true, ctx.Response.Header.Cookie(&encryptedCookie), "Get cookie value") - decryptedCookieValue, _ := DecryptCookie(string(encryptedCookie.Value()), testKey) + decryptedCookieValue, err := DecryptCookie(string(encryptedCookie.Value()), testKey) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "SomeThing", decryptedCookieValue) } @@ -169,18 +173,19 @@ func Test_Encrypt_Cookie_Custom_Encryptor(t *testing.T) { h := app.Handler() ctx := &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.SetMethod(fiber.MethodPost) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) encryptedCookie := fasthttp.Cookie{} encryptedCookie.SetKey("test") utils.AssertEqual(t, true, ctx.Response.Header.Cookie(&encryptedCookie), "Get cookie value") - decodedBytes, _ := base64.StdEncoding.DecodeString(string(encryptedCookie.Value())) + decodedBytes, err := base64.StdEncoding.DecodeString(string(encryptedCookie.Value())) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "SomeThing", string(decodedBytes)) ctx = &fasthttp.RequestCtx{} - ctx.Request.Header.SetMethod("GET") + ctx.Request.Header.SetMethod(fiber.MethodGet) ctx.Request.Header.SetCookie("test", string(encryptedCookie.Value())) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) diff --git a/middleware/encryptcookie/utils.go b/middleware/encryptcookie/utils.go index 542d160c17..c35064d954 100644 --- a/middleware/encryptcookie/utils.go +++ b/middleware/encryptcookie/utils.go @@ -6,47 +6,56 @@ import ( "crypto/rand" "encoding/base64" "errors" + "fmt" "io" ) // EncryptCookie Encrypts a cookie value with specific encryption key func EncryptCookie(value, key string) (string, error) { - keyDecoded, _ := base64.StdEncoding.DecodeString(key) - plaintext := []byte(value) + keyDecoded, err := base64.StdEncoding.DecodeString(key) + if err != nil { + return "", fmt.Errorf("failed to base64-decode key: %w", err) + } block, err := aes.NewCipher(keyDecoded) if err != nil { - return "", err + return "", fmt.Errorf("failed to create AES cipher: %w", err) } gcm, err := cipher.NewGCM(block) if err != nil { - return "", err + return "", fmt.Errorf("failed to create GCM mode: %w", err) } nonce := make([]byte, gcm.NonceSize()) if _, err = io.ReadFull(rand.Reader, nonce); err != nil { - return "", err + return "", fmt.Errorf("failed to read: %w", err) } - ciphertext := gcm.Seal(nonce, nonce, plaintext, nil) + ciphertext := gcm.Seal(nonce, nonce, []byte(value), nil) return base64.StdEncoding.EncodeToString(ciphertext), nil } // DecryptCookie Decrypts a cookie value with specific encryption key func DecryptCookie(value, key string) (string, error) { - keyDecoded, _ := base64.StdEncoding.DecodeString(key) - enc, _ := base64.StdEncoding.DecodeString(value) + keyDecoded, err := base64.StdEncoding.DecodeString(key) + if err != nil { + return "", fmt.Errorf("failed to base64-decode key: %w", err) + } + enc, err := base64.StdEncoding.DecodeString(value) + if err != nil { + return "", fmt.Errorf("failed to base64-decode value: %w", err) + } block, err := aes.NewCipher(keyDecoded) if err != nil { - return "", err + return "", fmt.Errorf("failed to create AES cipher: %w", err) } gcm, err := cipher.NewGCM(block) if err != nil { - return "", err + return "", fmt.Errorf("failed to create GCM mode: %w", err) } nonceSize := gcm.NonceSize() @@ -59,7 +68,7 @@ func DecryptCookie(value, key string) (string, error) { plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) if err != nil { - return "", err + return "", fmt.Errorf("failed to decrypt ciphertext: %w", err) } return string(plaintext), nil @@ -67,7 +76,8 @@ func DecryptCookie(value, key string) (string, error) { // GenerateKey Generates an encryption key func GenerateKey() string { - ret := make([]byte, 32) + const keyLen = 32 + ret := make([]byte, keyLen) if _, err := rand.Read(ret); err != nil { panic(err) diff --git a/middleware/envvar/envvar.go b/middleware/envvar/envvar.go index c8ed80de3d..debfc1bbb5 100644 --- a/middleware/envvar/envvar.go +++ b/middleware/envvar/envvar.go @@ -23,10 +23,8 @@ func (envVar *EnvVar) set(key, val string) { envVar.Vars[key] = val } -var defaultConfig = Config{} - func New(config ...Config) fiber.Handler { - var cfg = defaultConfig + var cfg Config if len(config) > 0 { cfg = config[0] } @@ -57,8 +55,9 @@ func newEnvVar(cfg Config) *EnvVar { } } } else { + const numElems = 2 for _, envVal := range os.Environ() { - keyVal := strings.SplitN(envVal, "=", 2) + keyVal := strings.SplitN(envVal, "=", numElems) if _, exists := cfg.ExcludeVars[keyVal[0]]; !exists { vars.set(keyVal[0], keyVal[1]) } diff --git a/middleware/envvar/envvar_test.go b/middleware/envvar/envvar_test.go index 34d8a05074..83101a4761 100644 --- a/middleware/envvar/envvar_test.go +++ b/middleware/envvar/envvar_test.go @@ -1,6 +1,8 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package envvar import ( + "context" "encoding/json" "io" "net/http" @@ -12,16 +14,25 @@ import ( ) func TestEnvVarStructWithExportVarsExcludeVars(t *testing.T) { - os.Setenv("testKey", "testEnvValue") - os.Setenv("anotherEnvKey", "anotherEnvVal") - os.Setenv("excludeKey", "excludeEnvValue") - defer os.Unsetenv("testKey") - defer os.Unsetenv("anotherEnvKey") - defer os.Unsetenv("excludeKey") + err := os.Setenv("testKey", "testEnvValue") + utils.AssertEqual(t, nil, err) + err = os.Setenv("anotherEnvKey", "anotherEnvVal") + utils.AssertEqual(t, nil, err) + err = os.Setenv("excludeKey", "excludeEnvValue") + utils.AssertEqual(t, nil, err) + defer func() { + err := os.Unsetenv("testKey") + utils.AssertEqual(t, nil, err) + err = os.Unsetenv("anotherEnvKey") + utils.AssertEqual(t, nil, err) + err = os.Unsetenv("excludeKey") + utils.AssertEqual(t, nil, err) + }() vars := newEnvVar(Config{ ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, - ExcludeVars: map[string]string{"excludeKey": ""}}) + ExcludeVars: map[string]string{"excludeKey": ""}, + }) utils.AssertEqual(t, vars.Vars["testKey"], "testEnvValue") utils.AssertEqual(t, vars.Vars["testDefaultKey"], "testDefaultVal") @@ -30,21 +41,28 @@ func TestEnvVarStructWithExportVarsExcludeVars(t *testing.T) { } func TestEnvVarHandler(t *testing.T) { - os.Setenv("testKey", "testVal") - defer os.Unsetenv("testKey") + err := os.Setenv("testKey", "testVal") + utils.AssertEqual(t, nil, err) + defer func() { + err := os.Unsetenv("testKey") + utils.AssertEqual(t, nil, err) + }() - expectedEnvVarResponse, _ := json.Marshal( + expectedEnvVarResponse, err := json.Marshal( struct { Vars map[string]string `json:"vars"` }{ map[string]string{"testKey": "testVal"}, }) + utils.AssertEqual(t, nil, err) app := fiber.New() app.Use("/envvars", New(Config{ - ExportVars: map[string]string{"testKey": ""}})) + ExportVars: map[string]string{"testKey": ""}, + })) - req, _ := http.NewRequest("GET", "http://localhost/envvars", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "http://localhost/envvars", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -57,14 +75,16 @@ func TestEnvVarHandler(t *testing.T) { func TestEnvVarHandlerNotMatched(t *testing.T) { app := fiber.New() app.Use("/envvars", New(Config{ - ExportVars: map[string]string{"testKey": ""}})) + ExportVars: map[string]string{"testKey": ""}, + })) app.Get("/another-path", func(ctx *fiber.Ctx) error { utils.AssertEqual(t, nil, ctx.SendString("OK")) return nil }) - req, _ := http.NewRequest("GET", "http://localhost/another-path", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "http://localhost/another-path", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -75,13 +95,18 @@ func TestEnvVarHandlerNotMatched(t *testing.T) { } func TestEnvVarHandlerDefaultConfig(t *testing.T) { - os.Setenv("testEnvKey", "testEnvVal") - defer os.Unsetenv("testEnvKey") + err := os.Setenv("testEnvKey", "testEnvVal") + utils.AssertEqual(t, nil, err) + defer func() { + err := os.Unsetenv("testEnvKey") + utils.AssertEqual(t, nil, err) + }() app := fiber.New() app.Use("/envvars", New()) - req, _ := http.NewRequest("GET", "http://localhost/envvars", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "http://localhost/envvars", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -98,7 +123,8 @@ func TestEnvVarHandlerMethod(t *testing.T) { app := fiber.New() app.Use("/envvars", New()) - req, _ := http.NewRequest("POST", "http://localhost/envvars", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodPost, "http://localhost/envvars", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusMethodNotAllowed, resp.StatusCode) @@ -107,14 +133,19 @@ func TestEnvVarHandlerMethod(t *testing.T) { func TestEnvVarHandlerSpecialValue(t *testing.T) { testEnvKey := "testEnvKey" fakeBase64 := "testBase64:TQ==" - os.Setenv(testEnvKey, fakeBase64) - defer os.Unsetenv(testEnvKey) + err := os.Setenv(testEnvKey, fakeBase64) + utils.AssertEqual(t, nil, err) + defer func() { + err := os.Unsetenv(testEnvKey) + utils.AssertEqual(t, nil, err) + }() app := fiber.New() app.Use("/envvars", New()) app.Use("/envvars/export", New(Config{ExportVars: map[string]string{testEnvKey: ""}})) - req, _ := http.NewRequest("GET", "http://localhost/envvars", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, "http://localhost/envvars", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -126,7 +157,8 @@ func TestEnvVarHandlerSpecialValue(t *testing.T) { val := envVars.Vars[testEnvKey] utils.AssertEqual(t, fakeBase64, val) - req, _ = http.NewRequest("GET", "http://localhost/envvars/export", nil) + req, err = http.NewRequestWithContext(context.Background(), fiber.MethodGet, "http://localhost/envvars/export", nil) + utils.AssertEqual(t, nil, err) resp, err = app.Test(req) utils.AssertEqual(t, nil, err) diff --git a/middleware/etag/config.go b/middleware/etag/config.go index 57a7c787ab..efc31d86c5 100644 --- a/middleware/etag/config.go +++ b/middleware/etag/config.go @@ -23,6 +23,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Weak: false, Next: nil, diff --git a/middleware/etag/etag.go b/middleware/etag/etag.go index 72ad71ba36..98955833f8 100644 --- a/middleware/etag/etag.go +++ b/middleware/etag/etag.go @@ -5,12 +5,8 @@ import ( "hash/crc32" "github.com/gofiber/fiber/v2" - "github.com/valyala/bytebufferpool" -) -var ( - normalizedHeaderETag = []byte("Etag") - weakPrefix = []byte("W/") + "github.com/valyala/bytebufferpool" ) // New creates a new middleware handler @@ -18,32 +14,38 @@ func New(config ...Config) fiber.Handler { // Set default config cfg := configDefault(config...) - crc32q := crc32.MakeTable(0xD5828281) + var ( + normalizedHeaderETag = []byte("Etag") + weakPrefix = []byte("W/") + ) + + const crcPol = 0xD5828281 + crc32q := crc32.MakeTable(crcPol) // Return new handler - return func(c *fiber.Ctx) (err error) { + return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() } // Return err if next handler returns one - if err = c.Next(); err != nil { - return + if err := c.Next(); err != nil { + return err } // Don't generate ETags for invalid responses if c.Response().StatusCode() != fiber.StatusOK { - return + return nil } body := c.Response().Body() // Skips ETag if no response body is present if len(body) == 0 { - return + return nil } // Skip ETag if header is already present if c.Response().Header.PeekBytes(normalizedHeaderETag) != nil { - return + return nil } // Generate ETag for response @@ -52,14 +54,14 @@ func New(config ...Config) fiber.Handler { // Enable weak tag if cfg.Weak { - _, _ = bb.Write(weakPrefix) + _, _ = bb.Write(weakPrefix) //nolint:errcheck // This will never fail } - _ = bb.WriteByte('"') + _ = bb.WriteByte('"') //nolint:errcheck // This will never fail bb.B = appendUint(bb.Bytes(), uint32(len(body))) - _ = bb.WriteByte('-') + _ = bb.WriteByte('-') //nolint:errcheck // This will never fail bb.B = appendUint(bb.Bytes(), crc32.Checksum(body, crc32q)) - _ = bb.WriteByte('"') + _ = bb.WriteByte('"') //nolint:errcheck // This will never fail etag := bb.Bytes() @@ -78,7 +80,7 @@ func New(config ...Config) fiber.Handler { // W/1 != W/2 || W/1 != 2 c.Response().Header.SetCanonical(normalizedHeaderETag, etag) - return + return nil } if bytes.Contains(clientEtag, etag) { @@ -90,7 +92,7 @@ func New(config ...Config) fiber.Handler { // 1 != 2 c.Response().Header.SetCanonical(normalizedHeaderETag, etag) - return + return nil } } @@ -102,7 +104,7 @@ func appendUint(dst []byte, n uint32) []byte { var q uint32 for n >= 10 { i-- - q = n / 10 + q = n / 10 //nolint:gomnd // TODO: Explain why we divide by 10 here buf[i] = '0' + byte(n-q*10) n = q } diff --git a/middleware/etag/etag_test.go b/middleware/etag/etag_test.go index 567e1e2e94..ff9833f812 100644 --- a/middleware/etag/etag_test.go +++ b/middleware/etag/etag_test.go @@ -8,6 +8,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -21,7 +22,7 @@ func Test_ETag_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -37,7 +38,7 @@ func Test_ETag_SkipError(t *testing.T) { return fiber.ErrForbidden }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusForbidden, resp.StatusCode) } @@ -53,7 +54,7 @@ func Test_ETag_NotStatusOK(t *testing.T) { return c.SendStatus(fiber.StatusCreated) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusCreated, resp.StatusCode) } @@ -69,7 +70,7 @@ func Test_ETag_NoBody(t *testing.T) { return nil }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) } @@ -91,7 +92,7 @@ func Test_ETag_NewEtag(t *testing.T) { }) } -func testETagNewEtag(t *testing.T, headerIfNoneMatch, matched bool) { +func testETagNewEtag(t *testing.T, headerIfNoneMatch, matched bool) { //nolint:revive // We're in a test, so using bools as a flow-control is fine t.Helper() app := fiber.New() @@ -102,7 +103,7 @@ func testETagNewEtag(t *testing.T, headerIfNoneMatch, matched bool) { return c.SendString("Hello, World!") }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) if headerIfNoneMatch { etag := `"non-match"` if matched { @@ -145,7 +146,7 @@ func Test_ETag_WeakEtag(t *testing.T) { }) } -func testETagWeakEtag(t *testing.T, headerIfNoneMatch, matched bool) { +func testETagWeakEtag(t *testing.T, headerIfNoneMatch, matched bool) { //nolint:revive // We're in a test, so using bools as a flow-control is fine t.Helper() app := fiber.New() @@ -156,7 +157,7 @@ func testETagWeakEtag(t *testing.T, headerIfNoneMatch, matched bool) { return c.SendString("Hello, World!") }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) if headerIfNoneMatch { etag := `W/"non-match"` if matched { @@ -199,7 +200,7 @@ func Test_ETag_CustomEtag(t *testing.T) { }) } -func testETagCustomEtag(t *testing.T, headerIfNoneMatch, matched bool) { +func testETagCustomEtag(t *testing.T, headerIfNoneMatch, matched bool) { //nolint:revive // We're in a test, so using bools as a flow-control is fine t.Helper() app := fiber.New() @@ -214,7 +215,7 @@ func testETagCustomEtag(t *testing.T, headerIfNoneMatch, matched bool) { return c.SendString("Hello, World!") }) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) if headerIfNoneMatch { etag := `"non-match"` if matched { @@ -255,7 +256,7 @@ func Test_ETag_CustomEtagPut(t *testing.T) { return c.SendString("Hello, World!") }) - req := httptest.NewRequest("PUT", "/", nil) + req := httptest.NewRequest(fiber.MethodPut, "/", nil) req.Header.Set(fiber.HeaderIfMatch, `"non-match"`) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) @@ -275,7 +276,7 @@ func Benchmark_Etag(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") b.ReportAllocs() diff --git a/middleware/expvar/config.go b/middleware/expvar/config.go index 6691dc1a3a..c31cebcf3d 100644 --- a/middleware/expvar/config.go +++ b/middleware/expvar/config.go @@ -1,6 +1,8 @@ package expvar -import "github.com/gofiber/fiber/v2" +import ( + "github.com/gofiber/fiber/v2" +) // Config defines the config for middleware. type Config struct { @@ -10,6 +12,7 @@ type Config struct { Next func(c *fiber.Ctx) bool } +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, } diff --git a/middleware/expvar/expvar.go b/middleware/expvar/expvar.go index 7576401781..6436395914 100644 --- a/middleware/expvar/expvar.go +++ b/middleware/expvar/expvar.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp/expvarhandler" ) @@ -29,6 +30,6 @@ func New(config ...Config) fiber.Handler { return nil } - return c.Redirect("/debug/vars", 302) + return c.Redirect("/debug/vars", fiber.StatusFound) } } diff --git a/middleware/favicon/favicon.go b/middleware/favicon/favicon.go index 66b688093c..e7ef5d4dbb 100644 --- a/middleware/favicon/favicon.go +++ b/middleware/favicon/favicon.go @@ -34,6 +34,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, File: "", diff --git a/middleware/favicon/favicon_test.go b/middleware/favicon/favicon_test.go index f53311e3ae..8597b7ec47 100644 --- a/middleware/favicon/favicon_test.go +++ b/middleware/favicon/favicon_test.go @@ -1,16 +1,18 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package favicon import ( + "fmt" "net/http" "net/http/httptest" "os" "strings" "testing" - "github.com/valyala/fasthttp" - "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + + "github.com/valyala/fasthttp" ) // go test -run Test_Middleware_Favicon @@ -25,22 +27,22 @@ func Test_Middleware_Favicon(t *testing.T) { }) // Skip Favicon middleware - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") - resp, err = app.Test(httptest.NewRequest("GET", "/favicon.ico", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/favicon.ico", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusNoContent, resp.StatusCode, "Status code") - resp, err = app.Test(httptest.NewRequest("OPTIONS", "/favicon.ico", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodOptions, "/favicon.ico", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") - resp, err = app.Test(httptest.NewRequest("PUT", "/favicon.ico", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodPut, "/favicon.ico", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusMethodNotAllowed, resp.StatusCode, "Status code") - utils.AssertEqual(t, "GET, HEAD, OPTIONS", resp.Header.Get(fiber.HeaderAllow)) + utils.AssertEqual(t, strings.Join([]string{fiber.MethodGet, fiber.MethodHead, fiber.MethodOptions}, ", "), resp.Header.Get(fiber.HeaderAllow)) } // go test -run Test_Middleware_Favicon_Not_Found @@ -70,8 +72,7 @@ func Test_Middleware_Favicon_Found(t *testing.T) { return nil }) - resp, err := app.Test(httptest.NewRequest("GET", "/favicon.ico", nil)) - + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/favicon.ico", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) @@ -83,15 +84,15 @@ func Test_Middleware_Favicon_Found(t *testing.T) { // TODO use os.Dir if fiber upgrades to 1.16 type mockFS struct{} -func (m mockFS) Open(name string) (http.File, error) { +func (mockFS) Open(name string) (http.File, error) { if name == "/" { name = "." } else { name = strings.TrimPrefix(name, "/") } - file, err := os.Open(name) + file, err := os.Open(name) //nolint:gosec // We're in a test func, so this is fine if err != nil { - return nil, err + return nil, fmt.Errorf("failed to open: %w", err) } return file, nil } @@ -106,7 +107,7 @@ func Test_Middleware_Favicon_FileSystem(t *testing.T) { FileSystem: mockFS{}, })) - resp, err := app.Test(httptest.NewRequest("GET", "/favicon.ico", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/favicon.ico", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) @@ -123,7 +124,7 @@ func Test_Middleware_Favicon_CacheControl(t *testing.T) { File: "../../.github/testdata/favicon.ico", })) - resp, err := app.Test(httptest.NewRequest("GET", "/favicon.ico", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/favicon.ico", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) @@ -159,7 +160,7 @@ func Test_Favicon_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } diff --git a/middleware/filesystem/filesystem.go b/middleware/filesystem/filesystem.go index 890fdf9824..1913ef59e5 100644 --- a/middleware/filesystem/filesystem.go +++ b/middleware/filesystem/filesystem.go @@ -1,6 +1,7 @@ package filesystem import ( + "fmt" "net/http" "os" "strconv" @@ -55,6 +56,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Root: nil, @@ -102,7 +105,7 @@ func New(config ...Config) fiber.Handler { cacheControlStr := "public, max-age=" + strconv.Itoa(cfg.MaxAge) // Return new handler - return func(c *fiber.Ctx) (err error) { + return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() @@ -131,28 +134,23 @@ func New(config ...Config) fiber.Handler { path = cfg.PathPrefix + path } - var ( - file http.File - stat os.FileInfo - ) - if len(path) > 1 { path = utils.TrimRight(path, '/') } - file, err = cfg.Root.Open(path) + file, err := cfg.Root.Open(path) if err != nil && os.IsNotExist(err) && cfg.NotFoundFile != "" { file, err = cfg.Root.Open(cfg.NotFoundFile) } - if err != nil { if os.IsNotExist(err) { return c.Status(fiber.StatusNotFound).Next() } - return + return fmt.Errorf("failed to open: %w", err) } - if stat, err = file.Stat(); err != nil { - return + stat, err := file.Stat() + if err != nil { + return fmt.Errorf("failed to stat: %w", err) } // Serve index if path is directory @@ -200,7 +198,7 @@ func New(config ...Config) fiber.Handler { c.Response().SkipBody = true c.Response().Header.SetContentLength(contentLength) if err := file.Close(); err != nil { - return err + return fmt.Errorf("failed to close: %w", err) } return nil } @@ -210,22 +208,18 @@ func New(config ...Config) fiber.Handler { } // SendFile ... -func SendFile(c *fiber.Ctx, fs http.FileSystem, path string) (err error) { - var ( - file http.File - stat os.FileInfo - ) - - file, err = fs.Open(path) +func SendFile(c *fiber.Ctx, fs http.FileSystem, path string) error { + file, err := fs.Open(path) if err != nil { if os.IsNotExist(err) { return fiber.ErrNotFound } - return err + return fmt.Errorf("failed to open: %w", err) } - if stat, err = file.Stat(); err != nil { - return err + stat, err := file.Stat() + if err != nil { + return fmt.Errorf("failed to stat: %w", err) } // Serve index if path is directory @@ -268,7 +262,7 @@ func SendFile(c *fiber.Ctx, fs http.FileSystem, path string) (err error) { c.Response().SkipBody = true c.Response().Header.SetContentLength(contentLength) if err := file.Close(); err != nil { - return err + return fmt.Errorf("failed to close: %w", err) } return nil } diff --git a/middleware/filesystem/filesystem_test.go b/middleware/filesystem/filesystem_test.go index dcbfcace75..56c113e0c5 100644 --- a/middleware/filesystem/filesystem_test.go +++ b/middleware/filesystem/filesystem_test.go @@ -1,6 +1,8 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package filesystem import ( + "context" "net/http" "net/http/httptest" "testing" @@ -119,7 +121,7 @@ func Test_FileSystem(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - resp, err := app.Test(httptest.NewRequest("GET", tt.url, nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, tt.url, nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, tt.statusCode, resp.StatusCode) @@ -142,7 +144,7 @@ func Test_FileSystem_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -168,7 +170,8 @@ func Test_FileSystem_Head(t *testing.T) { Root: http.Dir("../../.github/testdata/fs"), })) - req, _ := http.NewRequest(fiber.MethodHead, "/test", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodHead, "/test", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) @@ -182,7 +185,8 @@ func Test_FileSystem_NoRoot(t *testing.T) { app := fiber.New() app.Use(New()) - _, _ = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + _, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) } func Test_FileSystem_UsingParam(t *testing.T) { @@ -193,7 +197,8 @@ func Test_FileSystem_UsingParam(t *testing.T) { return SendFile(c, http.Dir("../../.github/testdata/fs"), c.Params("path")+".html") }) - req, _ := http.NewRequest(fiber.MethodHead, "/index", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodHead, "/index", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) @@ -207,7 +212,8 @@ func Test_FileSystem_UsingParam_NonFile(t *testing.T) { return SendFile(c, http.Dir("../../.github/testdata/fs"), c.Params("path")+".html") }) - req, _ := http.NewRequest(fiber.MethodHead, "/template", nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodHead, "/template", nil) + utils.AssertEqual(t, nil, err) resp, err := app.Test(req) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 404, resp.StatusCode) diff --git a/middleware/filesystem/utils.go b/middleware/filesystem/utils.go index 386c13e389..4e96db62ac 100644 --- a/middleware/filesystem/utils.go +++ b/middleware/filesystem/utils.go @@ -13,18 +13,18 @@ import ( "github.com/gofiber/fiber/v2/utils" ) -func getFileExtension(path string) string { - n := strings.LastIndexByte(path, '.') +func getFileExtension(p string) string { + n := strings.LastIndexByte(p, '.') if n < 0 { return "" } - return path[n:] + return p[n:] } func dirList(c *fiber.Ctx, f http.File) error { fileinfos, err := f.Readdir(-1) if err != nil { - return err + return fmt.Errorf("failed to read dir: %w", err) } fm := make(map[string]os.FileInfo, len(fileinfos)) @@ -36,13 +36,13 @@ func dirList(c *fiber.Ctx, f http.File) error { } basePathEscaped := html.EscapeString(c.Path()) - fmt.Fprintf(c, "%s", basePathEscaped) - fmt.Fprintf(c, "

%s

", basePathEscaped) - fmt.Fprint(c, "
    ") + _, _ = fmt.Fprintf(c, "%s", basePathEscaped) + _, _ = fmt.Fprintf(c, "

    %s

    ", basePathEscaped) + _, _ = fmt.Fprint(c, "
      ") if len(basePathEscaped) > 1 { parentPathEscaped := html.EscapeString(utils.TrimRight(c.Path(), '/') + "/..") - fmt.Fprintf(c, `
    • ..
    • `, parentPathEscaped) + _, _ = fmt.Fprintf(c, `
    • ..
    • `, parentPathEscaped) } sort.Strings(filenames) @@ -55,10 +55,10 @@ func dirList(c *fiber.Ctx, f http.File) error { auxStr = fmt.Sprintf("file, %d bytes", fi.Size()) className = "file" } - fmt.Fprintf(c, `
    • %s, %s, last modified %s
    • `, + _, _ = fmt.Fprintf(c, `
    • %s, %s, last modified %s
    • `, pathEscaped, className, html.EscapeString(name), auxStr, fi.ModTime()) } - fmt.Fprint(c, "
    ") + _, _ = fmt.Fprint(c, "
") c.Type("html") diff --git a/middleware/idempotency/config.go b/middleware/idempotency/config.go index c8f249a074..21aeb6909c 100644 --- a/middleware/idempotency/config.go +++ b/middleware/idempotency/config.go @@ -9,9 +9,7 @@ import ( "github.com/gofiber/fiber/v2/internal/storage/memory" ) -var ( - ErrInvalidIdempotencyKey = errors.New("invalid idempotency key") -) +var ErrInvalidIdempotencyKey = errors.New("invalid idempotency key") // Config defines the config for middleware. type Config struct { @@ -51,13 +49,15 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here 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, + Lifetime: 30 * time.Minute, //nolint:gomnd // No magic number, just the default config KeyHeader: "X-Idempotency-Key", KeyHeaderValidate: func(k string) error { @@ -112,7 +112,7 @@ func configDefault(config ...Config) Config { if cfg.Storage == nil { cfg.Storage = memory.New(memory.Config{ - GCInterval: cfg.Lifetime / 2, + GCInterval: cfg.Lifetime / 2, //nolint:gomnd // Half the lifetime interval }) } diff --git a/middleware/idempotency/idempotency_test.go b/middleware/idempotency/idempotency_test.go index e6fdbcd8a3..6612cc6417 100644 --- a/middleware/idempotency/idempotency_test.go +++ b/middleware/idempotency/idempotency_test.go @@ -1,3 +1,4 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package idempotency_test import ( @@ -14,6 +15,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/idempotency" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -172,5 +174,4 @@ func Benchmark_Idempotency(b *testing.B) { h(c) } }) - } diff --git a/middleware/limiter/config.go b/middleware/limiter/config.go index 6da6672b88..a123ea227b 100644 --- a/middleware/limiter/config.go +++ b/middleware/limiter/config.go @@ -1,7 +1,7 @@ package limiter import ( - "fmt" + "log" "time" "github.com/gofiber/fiber/v2" @@ -58,19 +58,21 @@ type Config struct { // Default: a new Fixed Window Rate Limiter LimiterMiddleware LimiterHandler - // DEPRECATED: Use Expiration instead + // Deprecated: Use Expiration instead Duration time.Duration - // DEPRECATED, use Storage instead + // Deprecated: Use Storage instead Store fiber.Storage - // DEPRECATED, use KeyGenerator instead + // Deprecated: Use KeyGenerator instead Key func(*fiber.Ctx) string } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ - Max: 5, + Max: 5, //nolint:gomnd // No magic number, just the default config Expiration: 1 * time.Minute, KeyGenerator: func(c *fiber.Ctx) string { return c.IP() @@ -95,15 +97,15 @@ func configDefault(config ...Config) Config { // Set default values if int(cfg.Duration.Seconds()) > 0 { - fmt.Println("[LIMITER] Duration is deprecated, please use Expiration") + log.Printf("[LIMITER] Duration is deprecated, please use Expiration\n") cfg.Expiration = cfg.Duration } if cfg.Key != nil { - fmt.Println("[LIMITER] Key is deprecated, please us KeyGenerator") + log.Printf("[LIMITER] Key is deprecated, please us KeyGenerator\n") cfg.KeyGenerator = cfg.Key } if cfg.Store != nil { - fmt.Println("[LIMITER] Store is deprecated, please use Storage") + log.Printf("[LIMITER] Store is deprecated, please use Storage\n") cfg.Storage = cfg.Store } if cfg.Next == nil { diff --git a/middleware/limiter/limiter_test.go b/middleware/limiter/limiter_test.go index 847aba354d..a57ca477f4 100644 --- a/middleware/limiter/limiter_test.go +++ b/middleware/limiter/limiter_test.go @@ -2,7 +2,6 @@ package limiter import ( "io" - "net/http" "net/http/httptest" "sync" "testing" @@ -11,6 +10,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/storage/memory" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -34,7 +34,7 @@ func Test_Limiter_Concurrency_Store(t *testing.T) { var wg sync.WaitGroup singleRequest := func(wg *sync.WaitGroup) { defer wg.Done() - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) @@ -50,13 +50,13 @@ func Test_Limiter_Concurrency_Store(t *testing.T) { wg.Wait() - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 429, resp.StatusCode) time.Sleep(3 * time.Second) - resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) } @@ -80,7 +80,7 @@ func Test_Limiter_Concurrency(t *testing.T) { var wg sync.WaitGroup singleRequest := func(wg *sync.WaitGroup) { defer wg.Done() - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) @@ -96,13 +96,13 @@ func Test_Limiter_Concurrency(t *testing.T) { wg.Wait() - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 429, resp.StatusCode) time.Sleep(3 * time.Second) - resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) } @@ -120,21 +120,21 @@ func Test_Limiter_No_Skip_Choices(t *testing.T) { })) app.Get("/:status", func(c *fiber.Ctx) error { - if c.Params("status") == "fail" { + if c.Params("status") == "fail" { //nolint:goconst // False positive return c.SendStatus(400) } return c.SendStatus(200) }) - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil)) + 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(http.MethodGet, "/success", nil)) + 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(http.MethodGet, "/success", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 429, resp.StatusCode) } @@ -157,21 +157,21 @@ func Test_Limiter_Skip_Failed_Requests(t *testing.T) { return c.SendStatus(200) }) - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil)) + 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(http.MethodGet, "/success", nil)) + 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(http.MethodGet, "/success", nil)) + 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(http.MethodGet, "/success", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) } @@ -196,21 +196,21 @@ func Test_Limiter_Skip_Successful_Requests(t *testing.T) { return c.SendStatus(200) }) - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/success", nil)) + 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(http.MethodGet, "/fail", nil)) + 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(http.MethodGet, "/fail", nil)) + 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(http.MethodGet, "/fail", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 400, resp.StatusCode) } @@ -232,7 +232,7 @@ func Benchmark_Limiter_Custom_Store(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") b.ResetTimer() @@ -252,7 +252,7 @@ func Test_Limiter_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -271,7 +271,7 @@ func Test_Limiter_Headers(t *testing.T) { }) fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") app.Handler()(fctx) @@ -301,7 +301,7 @@ func Benchmark_Limiter(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") b.ResetTimer() @@ -327,7 +327,7 @@ func Test_Sliding_Window(t *testing.T) { }) singleRequest := func(shouldFail bool) { - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) if shouldFail { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 429, resp.StatusCode) diff --git a/middleware/limiter/manager.go b/middleware/limiter/manager.go index 68a785a7c2..6b257f4593 100644 --- a/middleware/limiter/manager.go +++ b/middleware/limiter/manager.go @@ -46,7 +46,7 @@ func newManager(storage fiber.Storage) *manager { // acquire returns an *entry from the sync.Pool func (m *manager) acquire() *item { - return m.pool.Get().(*item) + return m.pool.Get().(*item) //nolint:forcetypeassert // We store nothing else in the pool } // release and reset *entry to sync.Pool @@ -58,37 +58,33 @@ func (m *manager) release(e *item) { } // get data from storage or memory -func (m *manager) get(key string) (it *item) { +func (m *manager) get(key string) *item { + var it *item if m.storage != nil { it = m.acquire() - if raw, _ := m.storage.Get(key); raw != nil { + raw, err := m.storage.Get(key) + if err != nil { + return it + } + if raw != nil { if _, err := it.UnmarshalMsg(raw); err != nil { - return + return it } } - return + return it } - if it, _ = m.memory.Get(key).(*item); it == nil { + if it, _ = m.memory.Get(key).(*item); it == nil { //nolint:errcheck // We store nothing else in the pool it = m.acquire() + return it } - return -} - -// get raw data from storage or memory -func (m *manager) getRaw(key string) (raw []byte) { - if m.storage != nil { - raw, _ = m.storage.Get(key) - } else { - raw, _ = m.memory.Get(key).([]byte) - } - return + return it } // set data to storage or memory func (m *manager) set(key string, it *item, exp time.Duration) { if m.storage != nil { if raw, err := it.MarshalMsg(nil); err == nil { - _ = m.storage.Set(key, raw, exp) + _ = m.storage.Set(key, raw, exp) //nolint:errcheck // TODO: Handle error here } // we can release data because it's serialized to database m.release(it) @@ -96,21 +92,3 @@ func (m *manager) set(key string, it *item, exp time.Duration) { m.memory.Set(key, it, exp) } } - -// set data to storage or memory -func (m *manager) setRaw(key string, raw []byte, exp time.Duration) { - if m.storage != nil { - _ = m.storage.Set(key, raw, exp) - } else { - m.memory.Set(key, raw, exp) - } -} - -// delete data from storage or memory -func (m *manager) delete(key string) { - if m.storage != nil { - _ = m.storage.Delete(key) - } else { - m.memory.Delete(key) - } -} diff --git a/middleware/logger/README.md b/middleware/logger/README.md index 60bb2eca68..652f375fe5 100644 --- a/middleware/logger/README.md +++ b/middleware/logger/README.md @@ -95,7 +95,7 @@ app.Use(logger.New(logger.Config{ TimeZone: "Asia/Shanghai", Done: func(c *fiber.Ctx, logString []byte) { if c.Response().StatusCode() != fiber.StatusOK { - reporter.SendToSlack(logString) + reporter.SendToSlack(logString) } }, })) @@ -189,7 +189,7 @@ const ( TagBytesReceived = "bytesReceived" TagRoute = "route" TagError = "error" - // DEPRECATED: Use TagReqHeader instead + // Deprecated: Use TagReqHeader instead TagHeader = "header:" // request header TagReqHeader = "reqHeader:" // request header TagRespHeader = "respHeader:" // response header diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 21f34aad7c..0d45468230 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -79,13 +79,15 @@ type Buffer interface { type LogFunc func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Done: nil, Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", TimeFormat: "15:04:05", TimeZone: "Local", - TimeInterval: 500 * time.Millisecond, + TimeInterval: 500 * time.Millisecond, //nolint:gomnd // No magic number, just the default config Output: os.Stdout, enableColors: true, } diff --git a/middleware/logger/data.go b/middleware/logger/data.go index 611a0aeee2..912d016a4b 100644 --- a/middleware/logger/data.go +++ b/middleware/logger/data.go @@ -1,13 +1,10 @@ package logger import ( - "sync" "sync/atomic" "time" ) -var DataPool = sync.Pool{New: func() interface{} { return new(Data) }} - // Data is a struct to define some variables to use in custom logger function. type Data struct { Pid string diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 1fa07439b4..daa97063f9 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -11,6 +11,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/valyala/bytebufferpool" @@ -55,6 +56,8 @@ func New(config ...Config) fiber.Handler { once sync.Once mu sync.Mutex errHandler fiber.ErrorHandler + + dataPool = sync.Pool{New: func() interface{} { return new(Data) }} ) // If colors are enabled, check terminal compatibility @@ -75,7 +78,7 @@ func New(config ...Config) fiber.Handler { } // Return new handler - return func(c *fiber.Ctx) (err error) { + return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() @@ -101,13 +104,13 @@ func New(config ...Config) fiber.Handler { }) // Logger data - data := DataPool.Get().(*Data) + data := dataPool.Get().(*Data) //nolint:forcetypeassert,errcheck // We store nothing else in the pool // no need for a reset, as long as we always override everything data.Pid = pid data.ErrPaddingStr = errPaddingStr data.Timestamp = timestamp // put data back in the pool - defer DataPool.Put(data) + defer dataPool.Put(data) // Set latency start time if cfg.enableLatency { @@ -121,7 +124,7 @@ func New(config ...Config) fiber.Handler { // Manually call error handler if chainErr != nil { if err := errHandler(c, chainErr); err != nil { - _ = c.SendStatus(fiber.StatusInternalServerError) + _ = c.SendStatus(fiber.StatusInternalServerError) //nolint:errcheck // TODO: Explain why we ignore the error here } } @@ -142,18 +145,20 @@ func New(config ...Config) fiber.Handler { } // Format log to buffer - _, _ = buf.WriteString(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, - )) + _, _ = 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()) + _, _ = cfg.Output.Write(buf.Bytes()) //nolint:errcheck // This will never fail if cfg.Done != nil { cfg.Done(c, buf.Bytes()) @@ -169,7 +174,7 @@ func New(config ...Config) fiber.Handler { // Loop over template parts execute dynamic parts and add fixed parts to the buffer for i, logFunc := range logFunChain { if logFunc == nil { - _, _ = buf.Write(templateChain[i]) + _, _ = buf.Write(templateChain[i]) //nolint:errcheck // This will never fail } else if templateChain[i] == nil { _, err = logFunc(buf, c, data, "") } else { @@ -182,7 +187,7 @@ func New(config ...Config) fiber.Handler { // Also write errors to the buffer if err != nil { - _, _ = buf.WriteString(err.Error()) + _, _ = buf.WriteString(err.Error()) //nolint:errcheck // This will never fail } mu.Lock() // Write buffer to output @@ -190,7 +195,7 @@ func New(config ...Config) fiber.Handler { // Write error to output if _, err := cfg.Output.Write([]byte(err.Error())); err != nil { // There is something wrong with the given io.Writer - fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) + _, _ = fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) } } mu.Unlock() diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 2295e8a4f5..fb81875223 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -1,3 +1,4 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package logger import ( @@ -16,6 +17,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/requestid" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) @@ -37,7 +39,7 @@ func Test_Logger(t *testing.T) { return errors.New("some random error") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) utils.AssertEqual(t, "some random error", buf.String()) @@ -70,21 +72,21 @@ func Test_Logger_locals(t *testing.T) { return c.SendStatus(fiber.StatusOK) }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "johndoe", buf.String()) buf.Reset() - resp, err = app.Test(httptest.NewRequest("GET", "/int", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/int", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "55", buf.String()) buf.Reset() - resp, err = app.Test(httptest.NewRequest("GET", "/empty", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/empty", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "", buf.String()) @@ -100,7 +102,7 @@ func Test_Logger_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -113,15 +115,15 @@ func Test_Logger_Done(t *testing.T) { app.Use(New(Config{ Done: func(c *fiber.Ctx, logString []byte) { if c.Response().StatusCode() == fiber.StatusOK { - buf.Write(logString) + _, err := buf.Write(logString) + utils.AssertEqual(t, nil, err) } }, })).Get("/logging", func(ctx *fiber.Ctx) error { return ctx.SendStatus(fiber.StatusOK) }) - resp, err := app.Test(httptest.NewRequest("GET", "/logging", nil)) - + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/logging", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, true, buf.Len() > 0) @@ -135,7 +137,7 @@ func Test_Logger_ErrorTimeZone(t *testing.T) { TimeZone: "invalid", })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -156,7 +158,7 @@ func Test_Logger_ErrorOutput(t *testing.T) { Output: o, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) @@ -178,7 +180,7 @@ func Test_Logger_All(t *testing.T) { // Alias colors colors := app.Config().ColorScheme - resp, err := app.Test(httptest.NewRequest("GET", "/?foo=bar", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/?foo=bar", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) @@ -198,7 +200,7 @@ func Test_Query_Params(t *testing.T) { Output: buf, })) - resp, err := app.Test(httptest.NewRequest("GET", "/?foo=bar&baz=moz", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/?foo=bar&baz=moz", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) @@ -226,7 +228,7 @@ func Test_Response_Body(t *testing.T) { return c.Send([]byte("Post in test")) }) - _, err := app.Test(httptest.NewRequest("GET", "/", nil)) + _, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) expectedGetResponse := "Sample response body" @@ -234,7 +236,7 @@ func Test_Response_Body(t *testing.T) { buf.Reset() // Reset buffer to test POST - _, err = app.Test(httptest.NewRequest("POST", "/test", nil)) + _, err = app.Test(httptest.NewRequest(fiber.MethodPost, "/test", nil)) utils.AssertEqual(t, nil, err) expectedPostResponse := "Post in test" @@ -258,7 +260,7 @@ func Test_Logger_AppendUint(t *testing.T) { return c.SendString("hello") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "0 5 200", buf.String()) @@ -285,12 +287,11 @@ func Test_Logger_Data_Race(t *testing.T) { wg := &sync.WaitGroup{} wg.Add(1) go func() { - resp1, err1 = app.Test(httptest.NewRequest("GET", "/", nil)) + resp1, err1 = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) wg.Done() }() - resp2, err2 = app.Test(httptest.NewRequest("GET", "/", nil)) + resp2, err2 = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) wg.Wait() - utils.AssertEqual(t, nil, err1) utils.AssertEqual(t, fiber.StatusOK, resp1.StatusCode) utils.AssertEqual(t, nil, err2) @@ -299,21 +300,23 @@ func Test_Logger_Data_Race(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Logger -benchmem -count=4 func Benchmark_Logger(b *testing.B) { - benchSetup := func(bb *testing.B, app *fiber.App) { + benchSetup := func(b *testing.B, app *fiber.App) { + b.Helper() + h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") - bb.ReportAllocs() - bb.ResetTimer() + b.ReportAllocs() + b.ResetTimer() - for n := 0; n < bb.N; n++ { + for n := 0; n < b.N; n++ { h(fctx) } - utils.AssertEqual(bb, 200, fctx.Response.Header.StatusCode()) + utils.AssertEqual(b, 200, fctx.Response.Header.StatusCode()) } b.Run("Base", func(bb *testing.B) { @@ -375,8 +378,7 @@ func Test_Response_Header(t *testing.T) { return c.SendString("Hello fiber!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "Hello fiber!", buf.String()) @@ -396,10 +398,10 @@ func Test_Req_Header(t *testing.T) { app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello fiber!") }) - headerReq := httptest.NewRequest("GET", "/", nil) + headerReq := httptest.NewRequest(fiber.MethodGet, "/", nil) headerReq.Header.Add("test", "Hello fiber!") - resp, err := app.Test(headerReq) + resp, err := app.Test(headerReq) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "Hello fiber!", buf.String()) @@ -419,10 +421,10 @@ func Test_ReqHeader_Header(t *testing.T) { app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello fiber!") }) - reqHeaderReq := httptest.NewRequest("GET", "/", nil) + reqHeaderReq := httptest.NewRequest(fiber.MethodGet, "/", nil) reqHeaderReq.Header.Add("test", "Hello fiber!") - resp, err := app.Test(reqHeaderReq) + resp, err := app.Test(reqHeaderReq) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "Hello fiber!", buf.String()) @@ -449,10 +451,10 @@ func Test_CustomTags(t *testing.T) { app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello fiber!") }) - reqHeaderReq := httptest.NewRequest("GET", "/", nil) + reqHeaderReq := httptest.NewRequest(fiber.MethodGet, "/", nil) reqHeaderReq.Header.Add("test", "Hello fiber!") - resp, err := app.Test(reqHeaderReq) + resp, err := app.Test(reqHeaderReq) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, customTag, buf.String()) @@ -492,7 +494,7 @@ func Test_Logger_ByteSent_Streaming(t *testing.T) { return nil }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "0 0 200", buf.String()) diff --git a/middleware/logger/tags.go b/middleware/logger/tags.go index 7eb67582bc..2f746ddd4e 100644 --- a/middleware/logger/tags.go +++ b/middleware/logger/tags.go @@ -31,7 +31,7 @@ const ( TagBytesReceived = "bytesReceived" TagRoute = "route" TagError = "error" - // DEPRECATED: Use TagReqHeader instead + // Deprecated: Use TagReqHeader instead TagHeader = "header:" TagReqHeader = "reqHeader:" TagRespHeader = "respHeader:" @@ -195,7 +195,7 @@ func createTagMap(cfg *Config) map[string]LogFunc { return output.WriteString(fmt.Sprintf("%7v", latency)) }, TagTime: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return output.WriteString(data.Timestamp.Load().(string)) + return output.WriteString(data.Timestamp.Load().(string)) //nolint:forcetypeassert // We always store a string in here }, } // merge with custom tags from user diff --git a/middleware/logger/template_chain.go b/middleware/logger/template_chain.go index ceb31a3356..1a5dd448f6 100644 --- a/middleware/logger/template_chain.go +++ b/middleware/logger/template_chain.go @@ -14,13 +14,16 @@ import ( // funcChain contains for the parts which exist the functions for the dynamic parts // funcChain and fixParts always have the same length and contain nil for the parts where no data is required in the chain, // if a function exists for the part, a parameter for it can also exist in the fixParts slice -func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) (fixParts [][]byte, funcChain []LogFunc, err error) { +func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) ([][]byte, []LogFunc, error) { // process flow is copied from the fasttemplate flow https://github.com/valyala/fasttemplate/blob/2a2d1afadadf9715bfa19683cdaeac8347e5d9f9/template.go#L23-L62 templateB := utils.UnsafeBytes(cfg.Format) startTagB := utils.UnsafeBytes(startTag) endTagB := utils.UnsafeBytes(endTag) paramSeparatorB := utils.UnsafeBytes(paramSeparator) + var fixParts [][]byte + var funcChain []LogFunc + for { currentPos := bytes.Index(templateB, startTagB) if currentPos < 0 { @@ -42,13 +45,13 @@ func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) (fixParts [ // ## function block ## // first check for tags with parameters if index := bytes.Index(templateB[:currentPos], paramSeparatorB); index != -1 { - if logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:index+1])]; ok { - funcChain = append(funcChain, logFunc) - // add param to the fixParts - fixParts = append(fixParts, templateB[index+1:currentPos]) - } else { + logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:index+1])] + if !ok { return nil, nil, errors.New("No parameter found in \"" + utils.UnsafeString(templateB[:currentPos]) + "\"") } + funcChain = append(funcChain, logFunc) + // add param to the fixParts + fixParts = append(fixParts, templateB[index+1:currentPos]) } else if logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:currentPos])]; ok { // add functions without parameter funcChain = append(funcChain, logFunc) @@ -63,5 +66,5 @@ func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) (fixParts [ funcChain = append(funcChain, nil) fixParts = append(fixParts, templateB) - return + return fixParts, funcChain, nil } diff --git a/middleware/monitor/config.go b/middleware/monitor/config.go index 559dd405ef..10889707f9 100644 --- a/middleware/monitor/config.go +++ b/middleware/monitor/config.go @@ -41,36 +41,48 @@ type Config struct { // 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 + ChartJSURL string index string } +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Title: defaultTitle, Refresh: defaultRefresh, FontURL: defaultFontURL, - ChartJsURL: defaultChartJsURL, + ChartJSURL: defaultChartJSURL, CustomHead: defaultCustomHead, APIOnly: false, Next: nil, - index: newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, - defaultCustomHead}), + index: newIndex(viewBag{ + defaultTitle, + defaultRefresh, + defaultFontURL, + defaultChartJSURL, + defaultCustomHead, + }), } func configDefault(config ...Config) Config { // Users can change ConfigDefault.Title/Refresh which then // become incompatible with ConfigDefault.index - if ConfigDefault.Title != defaultTitle || ConfigDefault.Refresh != defaultRefresh || - ConfigDefault.FontURL != defaultFontURL || ConfigDefault.ChartJsURL != defaultChartJsURL || + if ConfigDefault.Title != defaultTitle || + ConfigDefault.Refresh != defaultRefresh || + ConfigDefault.FontURL != defaultFontURL || + ConfigDefault.ChartJSURL != defaultChartJSURL || ConfigDefault.CustomHead != defaultCustomHead { - if ConfigDefault.Refresh < minRefresh { ConfigDefault.Refresh = minRefresh } // update default index with new default title/refresh - ConfigDefault.index = newIndex(viewBag{ConfigDefault.Title, - ConfigDefault.Refresh, ConfigDefault.FontURL, ConfigDefault.ChartJsURL, ConfigDefault.CustomHead}) + ConfigDefault.index = newIndex(viewBag{ + ConfigDefault.Title, + ConfigDefault.Refresh, + ConfigDefault.FontURL, + ConfigDefault.ChartJSURL, + ConfigDefault.CustomHead, + }) } // Return default config if nothing provided @@ -93,8 +105,8 @@ func configDefault(config ...Config) Config { cfg.FontURL = defaultFontURL } - if cfg.ChartJsURL == "" { - cfg.ChartJsURL = defaultChartJsURL + if cfg.ChartJSURL == "" { + cfg.ChartJSURL = defaultChartJSURL } if cfg.Refresh < minRefresh { cfg.Refresh = minRefresh @@ -112,8 +124,8 @@ func configDefault(config ...Config) Config { cfg.index = newIndex(viewBag{ title: cfg.Title, refresh: cfg.Refresh, - fontUrl: cfg.FontURL, - chartJsUrl: cfg.ChartJsURL, + fontURL: cfg.FontURL, + chartJSURL: cfg.ChartJSURL, customHead: cfg.CustomHead, }) diff --git a/middleware/monitor/config_test.go b/middleware/monitor/config_test.go index 062d1e4070..60bf09ca70 100644 --- a/middleware/monitor/config_test.go +++ b/middleware/monitor/config_test.go @@ -18,11 +18,11 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) t.Run("set title", func(t *testing.T) { @@ -35,11 +35,11 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, title, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{title, defaultRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{title, defaultRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) t.Run("set refresh less than default", func(t *testing.T) { @@ -51,11 +51,11 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, minRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, minRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, minRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) t.Run("set refresh", func(t *testing.T) { @@ -68,45 +68,45 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, refresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, refresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, refresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) t.Run("set font url", func(t *testing.T) { t.Parallel() - fontUrl := "https://example.com" + fontURL := "https://example.com" cfg := configDefault(Config{ - FontURL: fontUrl, + FontURL: fontURL, }) utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) - utils.AssertEqual(t, fontUrl, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, fontURL, cfg.FontURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, fontUrl, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, fontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) t.Run("set chart js url", func(t *testing.T) { t.Parallel() - chartUrl := "http://example.com" + chartURL := "http://example.com" cfg := configDefault(Config{ - ChartJsURL: chartUrl, + ChartJSURL: chartURL, }) utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, chartUrl, cfg.ChartJsURL) + utils.AssertEqual(t, chartURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, chartUrl, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, chartURL, defaultCustomHead}), cfg.index) }) t.Run("set custom head", func(t *testing.T) { @@ -119,11 +119,11 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, head, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, head}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJSURL, head}), cfg.index) }) t.Run("set api only", func(t *testing.T) { @@ -135,11 +135,11 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, true, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) t.Run("set next", func(t *testing.T) { @@ -154,10 +154,10 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, f(nil), cfg.Next(nil)) - utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) }) } diff --git a/middleware/monitor/index.go b/middleware/monitor/index.go index b17c95f9e4..c873290c46 100644 --- a/middleware/monitor/index.go +++ b/middleware/monitor/index.go @@ -9,23 +9,22 @@ import ( type viewBag struct { title string refresh time.Duration - fontUrl string - chartJsUrl string + fontURL string + chartJSURL string customHead string } // returns index with new title/refresh func newIndex(dat viewBag) string { - timeout := dat.refresh.Milliseconds() - timeoutDiff if timeout < timeoutDiff { timeout = timeoutDiff } ts := strconv.FormatInt(timeout, 10) replacer := strings.NewReplacer("$TITLE", dat.title, "$TIMEOUT", ts, - "$FONT_URL", dat.fontUrl, "$CHART_JS_URL", dat.chartJsUrl, "$CUSTOM_HEAD", dat.customHead, + "$FONT_URL", dat.fontURL, "$CHART_JS_URL", dat.chartJSURL, "$CUSTOM_HEAD", dat.customHead, ) - return replacer.Replace(indexHtml) + return replacer.Replace(indexHTML) } const ( @@ -35,11 +34,11 @@ const ( timeoutDiff = 200 // timeout will be Refresh (in milliseconds) - timeoutDiff minRefresh = timeoutDiff * time.Millisecond defaultFontURL = `https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap` - defaultChartJsURL = `https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js` + defaultChartJSURL = `https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js` defaultCustomHead = `` // parametrized by $TITLE and $TIMEOUT - indexHtml = ` + indexHTML = ` diff --git a/middleware/monitor/monitor.go b/middleware/monitor/monitor.go index 729bc00988..e3e39f06fe 100644 --- a/middleware/monitor/monitor.go +++ b/middleware/monitor/monitor.go @@ -33,18 +33,20 @@ type statsOS struct { Conns int `json:"conns"` } +//nolint:gochecknoglobals // TODO: Do not use a global var here var ( - monitPidCpu atomic.Value - monitPidRam atomic.Value - monitPidConns atomic.Value - - monitOsCpu atomic.Value - monitOsRam atomic.Value - monitOsTotalRam atomic.Value - monitOsLoadAvg atomic.Value - monitOsConns atomic.Value + monitPIDCPU atomic.Value + monitPIDRAM atomic.Value + monitPIDConns atomic.Value + + monitOSCPU atomic.Value + monitOSRAM atomic.Value + monitOSTotalRAM atomic.Value + monitOSLoadAvg atomic.Value + monitOSConns atomic.Value ) +//nolint:gochecknoglobals // TODO: Do not use a global var here var ( mutex sync.RWMutex once sync.Once @@ -58,7 +60,7 @@ func New(config ...Config) fiber.Handler { // Start routine to update statistics once.Do(func() { - p, _ := process.NewProcess(int32(os.Getpid())) + p, _ := process.NewProcess(int32(os.Getpid())) //nolint:errcheck // TODO: Handle error updateStatistics(p) @@ -72,6 +74,7 @@ func New(config ...Config) fiber.Handler { }) // Return new handler + //nolint:errcheck // Ignore the type-assertion errors return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { @@ -83,15 +86,15 @@ func New(config ...Config) fiber.Handler { } if c.Get(fiber.HeaderAccept) == fiber.MIMEApplicationJSON || cfg.APIOnly { mutex.Lock() - data.PID.CPU = monitPidCpu.Load().(float64) - data.PID.RAM = monitPidRam.Load().(uint64) - data.PID.Conns = monitPidConns.Load().(int) - - data.OS.CPU = monitOsCpu.Load().(float64) - data.OS.RAM = monitOsRam.Load().(uint64) - data.OS.TotalRAM = monitOsTotalRam.Load().(uint64) - data.OS.LoadAvg = monitOsLoadAvg.Load().(float64) - data.OS.Conns = monitOsConns.Load().(int) + data.PID.CPU, _ = monitPIDCPU.Load().(float64) + data.PID.RAM, _ = monitPIDRAM.Load().(uint64) + data.PID.Conns, _ = monitPIDConns.Load().(int) + + data.OS.CPU, _ = monitOSCPU.Load().(float64) + data.OS.RAM, _ = monitOSRAM.Load().(uint64) + data.OS.TotalRAM, _ = monitOSTotalRAM.Load().(uint64) + data.OS.LoadAvg, _ = monitOSLoadAvg.Load().(float64) + data.OS.Conns, _ = monitOSConns.Load().(int) mutex.Unlock() return c.Status(fiber.StatusOK).JSON(data) } @@ -101,29 +104,35 @@ func New(config ...Config) fiber.Handler { } func updateStatistics(p *process.Process) { - pidCpu, _ := p.CPUPercent() - monitPidCpu.Store(pidCpu / 10) + pidCPU, err := p.CPUPercent() + if err != nil { + monitPIDCPU.Store(pidCPU / 10) //nolint:gomnd // TODO: Explain why we divide by 10 here + } - if osCpu, _ := cpu.Percent(0, false); len(osCpu) > 0 { - monitOsCpu.Store(osCpu[0]) + if osCPU, err := cpu.Percent(0, false); err != nil && len(osCPU) > 0 { + monitOSCPU.Store(osCPU[0]) } - if pidMem, _ := p.MemoryInfo(); pidMem != nil { - monitPidRam.Store(pidMem.RSS) + if pidRAM, err := p.MemoryInfo(); err != nil && pidRAM != nil { + monitPIDRAM.Store(pidRAM.RSS) } - if osMem, _ := mem.VirtualMemory(); osMem != nil { - monitOsRam.Store(osMem.Used) - monitOsTotalRam.Store(osMem.Total) + if osRAM, err := mem.VirtualMemory(); err != nil && osRAM != nil { + monitOSRAM.Store(osRAM.Used) + monitOSTotalRAM.Store(osRAM.Total) } - if loadAvg, _ := load.Avg(); loadAvg != nil { - monitOsLoadAvg.Store(loadAvg.Load1) + if loadAvg, err := load.Avg(); err != nil && loadAvg != nil { + monitOSLoadAvg.Store(loadAvg.Load1) } - pidConns, _ := net.ConnectionsPid("tcp", p.Pid) - monitPidConns.Store(len(pidConns)) + pidConns, err := net.ConnectionsPid("tcp", p.Pid) + if err != nil { + monitPIDConns.Store(len(pidConns)) + } - osConns, _ := net.Connections("tcp") - monitOsConns.Store(len(osConns)) + osConns, err := net.Connections("tcp") + if err != nil { + monitOSConns.Store(len(osConns)) + } } diff --git a/middleware/monitor/monitor_test.go b/middleware/monitor/monitor_test.go index d069bba08d..f6e63fd9d4 100644 --- a/middleware/monitor/monitor_test.go +++ b/middleware/monitor/monitor_test.go @@ -10,6 +10,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -61,6 +62,7 @@ func Test_Monitor_Html(t *testing.T) { conf.Refresh.Milliseconds()-timeoutDiff) utils.AssertEqual(t, true, bytes.Contains(buf, []byte(timeoutLine))) } + func Test_Monitor_Html_CustomCodes(t *testing.T) { t.Parallel() @@ -82,8 +84,10 @@ func Test_Monitor_Html_CustomCodes(t *testing.T) { utils.AssertEqual(t, true, bytes.Contains(buf, []byte(timeoutLine))) // custom config - conf := Config{Title: "New " + defaultTitle, Refresh: defaultRefresh + time.Second, - ChartJsURL: "https://cdnjs.com/libraries/Chart.js", + conf := Config{ + Title: "New " + defaultTitle, + Refresh: defaultRefresh + time.Second, + ChartJSURL: "https://cdnjs.com/libraries/Chart.js", FontURL: "/public/my-font.css", CustomHead: ``, } @@ -136,7 +140,7 @@ func Benchmark_Monitor(b *testing.B) { h := app.Handler() fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") + fctx.Request.Header.SetMethod(fiber.MethodGet) fctx.Request.SetRequestURI("/") fctx.Request.Header.Set(fiber.HeaderAccept, fiber.MIMEApplicationJSON) diff --git a/middleware/pprof/config.go b/middleware/pprof/config.go index e69ffd7094..fb3e0978ae 100644 --- a/middleware/pprof/config.go +++ b/middleware/pprof/config.go @@ -1,6 +1,8 @@ package pprof -import "github.com/gofiber/fiber/v2" +import ( + "github.com/gofiber/fiber/v2" +) // Config defines the config for middleware. type Config struct { @@ -17,6 +19,7 @@ type Config struct { Prefix string } +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, } diff --git a/middleware/pprof/pprof.go b/middleware/pprof/pprof.go index 8ad85c9a23..6978d325e3 100644 --- a/middleware/pprof/pprof.go +++ b/middleware/pprof/pprof.go @@ -5,22 +5,8 @@ import ( "strings" "github.com/gofiber/fiber/v2" - "github.com/valyala/fasthttp/fasthttpadaptor" -) -// Set pprof adaptors -var ( - pprofIndex = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Index) - pprofCmdline = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Cmdline) - pprofProfile = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Profile) - pprofSymbol = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Symbol) - pprofTrace = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Trace) - pprofAllocs = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("allocs").ServeHTTP) - pprofBlock = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("block").ServeHTTP) - pprofGoroutine = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("goroutine").ServeHTTP) - pprofHeap = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("heap").ServeHTTP) - pprofMutex = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("mutex").ServeHTTP) - pprofThreadcreate = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("threadcreate").ServeHTTP) + "github.com/valyala/fasthttp/fasthttpadaptor" ) // New creates a new middleware handler @@ -28,6 +14,21 @@ func New(config ...Config) fiber.Handler { // Set default config cfg := configDefault(config...) + // Set pprof adaptors + var ( + pprofIndex = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Index) + pprofCmdline = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Cmdline) + pprofProfile = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Profile) + pprofSymbol = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Symbol) + pprofTrace = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Trace) + pprofAllocs = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("allocs").ServeHTTP) + pprofBlock = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("block").ServeHTTP) + pprofGoroutine = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("goroutine").ServeHTTP) + pprofHeap = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("heap").ServeHTTP) + pprofMutex = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("mutex").ServeHTTP) + pprofThreadcreate = fasthttpadaptor.NewFastHTTPHandlerFunc(pprof.Handler("threadcreate").ServeHTTP) + ) + // Return new handler return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true diff --git a/middleware/proxy/config.go b/middleware/proxy/config.go index be5e043e51..094b2c32a0 100644 --- a/middleware/proxy/config.go +++ b/middleware/proxy/config.go @@ -5,6 +5,7 @@ import ( "time" "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp" ) @@ -48,7 +49,7 @@ type Config struct { WriteBufferSize int // tls config for the http client. - TlsConfig *tls.Config + TlsConfig *tls.Config //nolint:stylecheck,revive // TODO: Rename to "TLSConfig" in v3 // Client is custom client when client config is complex. // Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig @@ -57,6 +58,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, ModifyRequest: nil, diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index a468f41b8b..356ebc10cb 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -3,19 +3,20 @@ package proxy import ( "bytes" "crypto/tls" - "fmt" + "log" "net/url" "strings" "sync" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) // New is deprecated func New(config Config) fiber.Handler { - fmt.Println("proxy.New is deprecated, please use proxy.Balancer instead") + log.Printf("proxy.New is deprecated, please use proxy.Balancer instead\n") return Balancer(config) } @@ -25,7 +26,7 @@ func Balancer(config Config) fiber.Handler { cfg := configDefault(config) // Load balanced client - var lbc = &fasthttp.LBClient{} + lbc := &fasthttp.LBClient{} // Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig // will not be used if the client are set. if config.Client == nil { @@ -61,7 +62,7 @@ func Balancer(config Config) fiber.Handler { } // Return new handler - return func(c *fiber.Ctx) (err error) { + return func(c *fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() @@ -76,7 +77,7 @@ func Balancer(config Config) fiber.Handler { // Modify request if cfg.ModifyRequest != nil { - if err = cfg.ModifyRequest(c); err != nil { + if err := cfg.ModifyRequest(c); err != nil { return err } } @@ -84,7 +85,7 @@ func Balancer(config Config) fiber.Handler { req.SetRequestURI(utils.UnsafeString(req.RequestURI())) // Forward request - if err = lbc.Do(req, res); err != nil { + if err := lbc.Do(req, res); err != nil { return err } @@ -93,7 +94,7 @@ func Balancer(config Config) fiber.Handler { // Modify response if cfg.ModifyResponse != nil { - if err = cfg.ModifyResponse(c); err != nil { + if err := cfg.ModifyResponse(c); err != nil { return err } } @@ -103,16 +104,20 @@ func Balancer(config Config) fiber.Handler { } } +//nolint:gochecknoglobals // TODO: Do not use a global var here var client = &fasthttp.Client{ NoDefaultUserAgentHeader: true, DisablePathNormalizing: true, } +//nolint:gochecknoglobals // TODO: Do not use a global var here var lock sync.RWMutex // WithTlsConfig update http client with a user specified tls.config // This function should be called before Do and Forward. // Deprecated: use WithClient instead. +// +//nolint:stylecheck,revive // TODO: Rename to "WithTLSConfig" in v3 func WithTlsConfig(tlsConfig *tls.Config) { client.TLSConfig = tlsConfig } diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index d7626da34e..d87145514a 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -4,7 +4,6 @@ import ( "crypto/tls" "io" "net" - "net/http" "net/http/httptest" "strings" "testing" @@ -13,10 +12,11 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/tlstest" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) -func createProxyTestServer(handler fiber.Handler, t *testing.T) (*fiber.App, string) { +func createProxyTestServer(t *testing.T, handler fiber.Handler) (*fiber.App, string) { t.Helper() target := fiber.New(fiber.Config{DisableStartupMessage: true}) @@ -60,7 +60,7 @@ func Test_Proxy_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -69,11 +69,11 @@ func Test_Proxy_Next(t *testing.T) { func Test_Proxy(t *testing.T) { t.Parallel() - target, addr := createProxyTestServer( - func(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusTeapot) }, t, - ) + target, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusTeapot) + }) - resp, err := target.Test(httptest.NewRequest("GET", "/", nil), 2000) + resp, err := target.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 2000) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) @@ -81,7 +81,7 @@ func Test_Proxy(t *testing.T) { app.Use(Balancer(Config{Servers: []string{addr}})) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Host = addr resp, err = app.Test(req) utils.AssertEqual(t, nil, err) @@ -107,7 +107,7 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) { }) addr := ln.Addr().String() - clientTLSConf := &tls.Config{InsecureSkipVerify: true} + clientTLSConf := &tls.Config{InsecureSkipVerify: true} //nolint:gosec // We're in a test func, so this is fine // disable certificate verification in Balancer app.Use(Balancer(Config{ @@ -128,9 +128,9 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) { func Test_Proxy_Forward_WithTlsConfig_To_Http(t *testing.T) { t.Parallel() - _, targetAddr := createProxyTestServer(func(c *fiber.Ctx) error { + _, targetAddr := createProxyTestServer(t, func(c *fiber.Ctx) error { return c.SendString("hello from target") - }, t) + }) proxyServerTLSConf, _, err := tlstest.GetTLSConfigs() utils.AssertEqual(t, nil, err) @@ -164,13 +164,13 @@ func Test_Proxy_Forward(t *testing.T) { app := fiber.New() - _, addr := createProxyTestServer( - func(c *fiber.Ctx) error { return c.SendString("forwarded") }, t, - ) + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { + return c.SendString("forwarded") + }) app.Use(Forward("http://" + addr)) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) @@ -198,7 +198,7 @@ func Test_Proxy_Forward_WithTlsConfig(t *testing.T) { }) addr := ln.Addr().String() - clientTLSConf := &tls.Config{InsecureSkipVerify: true} + clientTLSConf := &tls.Config{InsecureSkipVerify: true} //nolint:gosec // We're in a test func, so this is fine // disable certificate verification WithTlsConfig(clientTLSConf) @@ -217,9 +217,9 @@ func Test_Proxy_Forward_WithTlsConfig(t *testing.T) { func Test_Proxy_Modify_Response(t *testing.T) { t.Parallel() - _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { return c.Status(500).SendString("not modified") - }, t) + }) app := fiber.New() app.Use(Balancer(Config{ @@ -230,7 +230,7 @@ func Test_Proxy_Modify_Response(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) @@ -243,10 +243,10 @@ func Test_Proxy_Modify_Response(t *testing.T) { func Test_Proxy_Modify_Request(t *testing.T) { t.Parallel() - _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { b := c.Request().Body() return c.SendString(string(b)) - }, t) + }) app := fiber.New() app.Use(Balancer(Config{ @@ -257,7 +257,7 @@ func Test_Proxy_Modify_Request(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) @@ -270,10 +270,10 @@ func Test_Proxy_Modify_Request(t *testing.T) { func Test_Proxy_Timeout_Slow_Server(t *testing.T) { t.Parallel() - _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { time.Sleep(2 * time.Second) return c.SendString("fiber is awesome") - }, t) + }) app := fiber.New() app.Use(Balancer(Config{ @@ -281,7 +281,7 @@ func Test_Proxy_Timeout_Slow_Server(t *testing.T) { Timeout: 3 * time.Second, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil), 5000) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 5000) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) @@ -294,10 +294,10 @@ func Test_Proxy_Timeout_Slow_Server(t *testing.T) { func Test_Proxy_With_Timeout(t *testing.T) { t.Parallel() - _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { time.Sleep(1 * time.Second) return c.SendString("fiber is awesome") - }, t) + }) app := fiber.New() app.Use(Balancer(Config{ @@ -305,7 +305,7 @@ func Test_Proxy_With_Timeout(t *testing.T) { Timeout: 100 * time.Millisecond, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil), 2000) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 2000) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) @@ -318,16 +318,16 @@ func Test_Proxy_With_Timeout(t *testing.T) { func Test_Proxy_Buffer_Size_Response(t *testing.T) { t.Parallel() - _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { long := strings.Join(make([]string, 5000), "-") c.Set("Very-Long-Header", long) return c.SendString("ok") - }, t) + }) app := fiber.New() app.Use(Balancer(Config{Servers: []string{addr}})) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) @@ -337,7 +337,7 @@ func Test_Proxy_Buffer_Size_Response(t *testing.T) { ReadBufferSize: 1024 * 8, })) - resp, err = app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) } @@ -357,9 +357,9 @@ func Test_Proxy_Do_RestoreOriginalURL(t *testing.T) { utils.AssertEqual(t, originalURL, c.OriginalURL()) return c.SendString("ok") }) - _, err1 := app.Test(httptest.NewRequest("GET", "/test", nil)) + _, 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("GET", "/test", nil)) + _, err2 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) utils.AssertEqual(t, nil, err1) utils.AssertEqual(t, nil, err2) @@ -369,9 +369,9 @@ func Test_Proxy_Do_RestoreOriginalURL(t *testing.T) { func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) { t.Parallel() - _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { return c.SendString("hello world") - }, t) + }) app := fiber.New(fiber.Config{DisableStartupMessage: true}) app.Get("/*", func(c *fiber.Ctx) error { @@ -386,7 +386,7 @@ func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) { return nil }) - resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/http://"+addr, nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/http://"+addr, nil)) utils.AssertEqual(t, nil, err) s, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) @@ -431,9 +431,8 @@ func Test_Proxy_Forward_Local_Client(t *testing.T) { app.Use(Forward("http://"+addr+"/test_local_client", &fasthttp.Client{ NoDefaultUserAgentHeader: true, DisablePathNormalizing: true, - Dial: func(addr string) (net.Conn, error) { - return fasthttp.Dial(addr) - }, + + Dial: fasthttp.Dial, })) go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() @@ -447,11 +446,11 @@ func Test_Proxy_Forward_Local_Client(t *testing.T) { func Test_ProxyBalancer_Custom_Client(t *testing.T) { t.Parallel() - target, addr := createProxyTestServer( - func(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusTeapot) }, t, - ) + target, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusTeapot) + }) - resp, err := target.Test(httptest.NewRequest("GET", "/", nil), 2000) + resp, err := target.Test(httptest.NewRequest(fiber.MethodGet, "/", nil), 2000) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) @@ -468,7 +467,7 @@ func Test_ProxyBalancer_Custom_Client(t *testing.T) { Timeout: time.Second, }})) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Host = addr resp, err = app.Test(req) utils.AssertEqual(t, nil, err) diff --git a/middleware/recover/config.go b/middleware/recover/config.go index 8e372a5aec..56b805d2e0 100644 --- a/middleware/recover/config.go +++ b/middleware/recover/config.go @@ -1,4 +1,4 @@ -package recover +package recover //nolint:predeclared // TODO: Rename to some non-builtin import ( "github.com/gofiber/fiber/v2" @@ -23,6 +23,8 @@ type Config struct { } // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, EnableStackTrace: false, diff --git a/middleware/recover/recover.go b/middleware/recover/recover.go index 5d77d6d082..252d8be757 100644 --- a/middleware/recover/recover.go +++ b/middleware/recover/recover.go @@ -1,4 +1,4 @@ -package recover +package recover //nolint:predeclared // TODO: Rename to some non-builtin import ( "fmt" @@ -9,7 +9,7 @@ import ( ) func defaultStackTraceHandler(_ *fiber.Ctx, e interface{}) { - _, _ = os.Stderr.WriteString(fmt.Sprintf("panic: %v\n%s\n", e, debug.Stack())) + _, _ = os.Stderr.WriteString(fmt.Sprintf("panic: %v\n%s\n", e, debug.Stack())) //nolint:errcheck // This will never fail } // New creates a new middleware handler @@ -18,7 +18,7 @@ func New(config ...Config) fiber.Handler { cfg := configDefault(config...) // Return new handler - return func(c *fiber.Ctx) (err error) { + return func(c *fiber.Ctx) (err error) { //nolint:nonamedreturns // Uses recover() to overwrite the error // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() diff --git a/middleware/recover/recover_test.go b/middleware/recover/recover_test.go index 16bf164c12..82e34b63ef 100644 --- a/middleware/recover/recover_test.go +++ b/middleware/recover/recover_test.go @@ -1,4 +1,4 @@ -package recover +package recover //nolint:predeclared // TODO: Rename to some non-builtin import ( "net/http/httptest" @@ -24,7 +24,7 @@ func Test_Recover(t *testing.T) { panic("Hi, I'm an error!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/panic", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/panic", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) } @@ -39,7 +39,7 @@ func Test_Recover_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } @@ -55,7 +55,7 @@ func Test_Recover_EnableStackTrace(t *testing.T) { panic("Hi, I'm an error!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/panic", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/panic", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) } diff --git a/middleware/requestid/config.go b/middleware/requestid/config.go index b3b605e590..ca27b47f75 100644 --- a/middleware/requestid/config.go +++ b/middleware/requestid/config.go @@ -33,6 +33,8 @@ type Config struct { // It 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. +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Header: fiber.HeaderXRequestID, diff --git a/middleware/requestid/requestid_test.go b/middleware/requestid/requestid_test.go index eddafff01b..451e96b4b2 100644 --- a/middleware/requestid/requestid_test.go +++ b/middleware/requestid/requestid_test.go @@ -19,14 +19,14 @@ func Test_RequestID(t *testing.T) { return c.SendString("Hello, World 👋!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) reqid := resp.Header.Get(fiber.HeaderXRequestID) utils.AssertEqual(t, 36, len(reqid)) - req := httptest.NewRequest("GET", "/", nil) + req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Add(fiber.HeaderXRequestID, reqid) resp, err = app.Test(req) @@ -45,7 +45,7 @@ func Test_RequestID_Next(t *testing.T) { }, })) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, resp.Header.Get(fiber.HeaderXRequestID), "") utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) @@ -54,13 +54,13 @@ func Test_RequestID_Next(t *testing.T) { // go test -run Test_RequestID_Locals func Test_RequestID_Locals(t *testing.T) { t.Parallel() - reqId := "ThisIsARequestId" + reqID := "ThisIsARequestId" ctxKey := "ThisIsAContextKey" app := fiber.New() app.Use(New(Config{ Generator: func() string { - return reqId + return reqID }, ContextKey: ctxKey, })) @@ -68,11 +68,11 @@ func Test_RequestID_Locals(t *testing.T) { var ctxVal string app.Use(func(c *fiber.Ctx) error { - ctxVal = c.Locals(ctxKey).(string) + ctxVal = c.Locals(ctxKey).(string) //nolint:forcetypeassert,errcheck // We always store a string in here return c.Next() }) - _, err := app.Test(httptest.NewRequest("GET", "/", nil)) + _, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, reqId, ctxVal) + utils.AssertEqual(t, reqID, ctxVal) } diff --git a/middleware/session/README.md b/middleware/session/README.md index 32d8c1eda3..5e46cb1a6f 100644 --- a/middleware/session/README.md +++ b/middleware/session/README.md @@ -32,7 +32,7 @@ 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) +func (s *Session) SetExpiry(time.Duration) ``` **⚠ _Storing `interface{}` values are limited to built-ins Go types_** @@ -148,7 +148,7 @@ type Config struct { // Optional. Default value utils.UUID KeyGenerator func() string - // Deprecated, please use KeyLookup + // Deprecated: Please use KeyLookup CookieName string // Source defines where to obtain the session id diff --git a/middleware/session/config.go b/middleware/session/config.go index ad08c8935e..c7c8662704 100644 --- a/middleware/session/config.go +++ b/middleware/session/config.go @@ -2,6 +2,7 @@ package session import ( "fmt" + "log" "strings" "time" @@ -49,7 +50,7 @@ type Config struct { // Optional. Default value utils.UUIDv4 KeyGenerator func() string - // Deprecated, please use KeyLookup + // Deprecated: Please use KeyLookup CookieName string // Source defines where to obtain the session id @@ -68,8 +69,10 @@ const ( ) // ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ - Expiration: 24 * time.Hour, + Expiration: 24 * time.Hour, //nolint:gomnd // No magic number, just the default config KeyLookup: "cookie:session_id", KeyGenerator: utils.UUIDv4, source: "cookie", @@ -91,7 +94,7 @@ func configDefault(config ...Config) Config { cfg.Expiration = ConfigDefault.Expiration } if cfg.CookieName != "" { - fmt.Println("[session] CookieName is deprecated, please use KeyLookup") + log.Printf("[session] CookieName is deprecated, please use KeyLookup\n") cfg.KeyLookup = fmt.Sprintf("cookie:%s", cfg.CookieName) } if cfg.KeyLookup == "" { @@ -102,7 +105,8 @@ func configDefault(config ...Config) Config { } selectors := strings.Split(cfg.KeyLookup, ":") - if len(selectors) != 2 { + const numSelectors = 2 + if len(selectors) != numSelectors { panic("[session] KeyLookup must in the form of :") } switch Source(selectors[0]) { diff --git a/middleware/session/data.go b/middleware/session/data.go index 7c8f793103..f213b252f5 100644 --- a/middleware/session/data.go +++ b/middleware/session/data.go @@ -13,6 +13,7 @@ type data struct { Data map[string]interface{} } +//nolint:gochecknoglobals // TODO: Do not use a global var here var dataPool = sync.Pool{ New: func() interface{} { d := new(data) @@ -22,7 +23,7 @@ var dataPool = sync.Pool{ } func acquireData() *data { - return dataPool.Get().(*data) + return dataPool.Get().(*data) //nolint:forcetypeassert // We store nothing else in the pool } func (d *data) Reset() { diff --git a/middleware/session/session.go b/middleware/session/session.go index 33be2a1a7d..f18abbf8b2 100644 --- a/middleware/session/session.go +++ b/middleware/session/session.go @@ -3,11 +3,13 @@ package session import ( "bytes" "encoding/gob" + "fmt" "sync" "time" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -21,6 +23,7 @@ type Session struct { exp time.Duration // expiration of this session } +//nolint:gochecknoglobals // TODO: Do not use a global var here var sessionPool = sync.Pool{ New: func() interface{} { return new(Session) @@ -28,7 +31,7 @@ var sessionPool = sync.Pool{ } func acquireSession() *Session { - s := sessionPool.Get().(*Session) + s := sessionPool.Get().(*Session) //nolint:forcetypeassert,errcheck // We store nothing else in the pool if s.data == nil { s.data = acquireData() } @@ -153,7 +156,7 @@ func (s *Session) Save() error { encCache := gob.NewEncoder(s.byteBuffer) err := encCache.Encode(&s.data.Data) if err != nil { - return err + return fmt.Errorf("failed to encode data: %w", err) } // copy the data in buffer diff --git a/middleware/session/session_test.go b/middleware/session/session_test.go index 489ea46edf..20d683f458 100644 --- a/middleware/session/session_test.go +++ b/middleware/session/session_test.go @@ -7,6 +7,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/storage/memory" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -94,6 +95,8 @@ func Test_Session(t *testing.T) { } // go test -run Test_Session_Types +// +//nolint:forcetypeassert // TODO: Do not force-type assert func Test_Session_Types(t *testing.T) { t.Parallel() @@ -127,25 +130,27 @@ func Test_Session_Types(t *testing.T) { Name: "John", } // set value - var vbool = true - var vstring = "str" - var vint = 13 - var vint8 int8 = 13 - var vint16 int16 = 13 - var vint32 int32 = 13 - var vint64 int64 = 13 - var vuint uint = 13 - var vuint8 uint8 = 13 - var vuint16 uint16 = 13 - var vuint32 uint32 = 13 - var vuint64 uint64 = 13 - var vuintptr uintptr = 13 - var vbyte byte = 'k' - var vrune rune = 'k' - var vfloat32 float32 = 13 - var vfloat64 float64 = 13 - var vcomplex64 complex64 = 13 - var vcomplex128 complex128 = 13 + var ( + vbool = true + vstring = "str" + vint = 13 + vint8 int8 = 13 + vint16 int16 = 13 + vint32 int32 = 13 + vint64 int64 = 13 + vuint uint = 13 + vuint8 uint8 = 13 + vuint16 uint16 = 13 + vuint32 uint32 = 13 + vuint64 uint64 = 13 + vuintptr uintptr = 13 + vbyte byte = 'k' + vrune = 'k' + vfloat32 float32 = 13 + vfloat64 float64 = 13 + vcomplex64 complex64 = 13 + vcomplex128 complex128 = 13 + ) sess.Set("vuser", vuser) sess.Set("vbool", vbool) sess.Set("vstring", vstring) @@ -212,7 +217,8 @@ func Test_Session_Store_Reset(t *testing.T) { defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) // make sure its new utils.AssertEqual(t, true, sess.Fresh()) // set value & save @@ -224,7 +230,8 @@ func Test_Session_Store_Reset(t *testing.T) { utils.AssertEqual(t, nil, store.Reset()) // make sure the session is recreated - sess, _ = store.Get(ctx) + sess, err = store.Get(ctx) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, sess.Fresh()) utils.AssertEqual(t, nil, sess.Get("hello")) } @@ -242,12 +249,13 @@ func Test_Session_Save(t *testing.T) { ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) // set value sess.Set("name", "john") // save session - err := sess.Save() + err = sess.Save() utils.AssertEqual(t, nil, err) }) @@ -262,12 +270,13 @@ func Test_Session_Save(t *testing.T) { ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) // set value sess.Set("name", "john") // save session - err := sess.Save() + err = sess.Save() utils.AssertEqual(t, nil, err) utils.AssertEqual(t, store.getSessionID(ctx), string(ctx.Response().Header.Peek(store.sessionName))) utils.AssertEqual(t, store.getSessionID(ctx), string(ctx.Request().Header.Peek(store.sessionName))) @@ -287,7 +296,8 @@ func Test_Session_Save_Expiration(t *testing.T) { ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) // set value sess.Set("name", "john") @@ -295,18 +305,20 @@ func Test_Session_Save_Expiration(t *testing.T) { sess.SetExpiry(time.Second * 5) // save session - err := sess.Save() + err = sess.Save() utils.AssertEqual(t, nil, err) // here you need to get the old session yet - sess, _ = store.Get(ctx) + sess, err = store.Get(ctx) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "john", sess.Get("name")) // just to make sure the session has been expired time.Sleep(time.Second * 5) // here you should get a new session - sess, _ = store.Get(ctx) + sess, err = store.Get(ctx) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, sess.Get("name")) }) } @@ -325,7 +337,8 @@ func Test_Session_Reset(t *testing.T) { ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) sess.Set("name", "fenny") utils.AssertEqual(t, nil, sess.Destroy()) @@ -345,14 +358,16 @@ func Test_Session_Reset(t *testing.T) { ctx := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) // set value & save sess.Set("name", "fenny") utils.AssertEqual(t, nil, sess.Save()) - sess, _ = store.Get(ctx) + sess, err = store.Get(ctx) + utils.AssertEqual(t, nil, err) - err := sess.Destroy() + err = sess.Destroy() utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "", string(ctx.Response().Header.Peek(store.sessionName))) utils.AssertEqual(t, "", string(ctx.Request().Header.Peek(store.sessionName))) @@ -383,7 +398,8 @@ func Test_Session_Cookie(t *testing.T) { defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, nil, sess.Save()) // cookie should be set on Save ( even if empty data ) @@ -401,12 +417,14 @@ func Test_Session_Cookie_In_Response(t *testing.T) { defer app.ReleaseCtx(ctx) // get session - sess, _ := store.Get(ctx) + sess, err := store.Get(ctx) + utils.AssertEqual(t, nil, err) sess.Set("id", "1") utils.AssertEqual(t, true, sess.Fresh()) utils.AssertEqual(t, nil, sess.Save()) - sess, _ = store.Get(ctx) + sess, err = store.Get(ctx) + utils.AssertEqual(t, nil, err) sess.Set("name", "john") utils.AssertEqual(t, true, sess.Fresh()) @@ -497,7 +515,7 @@ func Benchmark_Session(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - sess, _ := store.Get(c) + sess, _ := store.Get(c) //nolint:errcheck // We're inside a benchmark sess.Set("john", "doe") err = sess.Save() } @@ -512,7 +530,7 @@ func Benchmark_Session(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - sess, _ := store.Get(c) + sess, _ := store.Get(c) //nolint:errcheck // We're inside a benchmark sess.Set("john", "doe") err = sess.Save() } diff --git a/middleware/session/store.go b/middleware/session/store.go index cc8a80a0cf..fd3b08d029 100644 --- a/middleware/session/store.go +++ b/middleware/session/store.go @@ -2,11 +2,13 @@ package session import ( "encoding/gob" + "fmt" "sync" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/storage/memory" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -14,6 +16,7 @@ type Store struct { Config } +//nolint:gochecknoglobals // TODO: Do not use a global var here var mux sync.Mutex func New(config ...Config) *Store { @@ -31,7 +34,7 @@ func New(config ...Config) *Store { // RegisterType will allow you to encode/decode custom types // into any Storage provider -func (s *Store) RegisterType(i interface{}) { +func (*Store) RegisterType(i interface{}) { gob.Register(i) } @@ -70,11 +73,11 @@ func (s *Store) Get(c *fiber.Ctx) (*Session, error) { if raw != nil && err == nil { mux.Lock() defer mux.Unlock() - _, _ = sess.byteBuffer.Write(raw) + _, _ = sess.byteBuffer.Write(raw) //nolint:errcheck // This will never fail encCache := gob.NewDecoder(sess.byteBuffer) err := encCache.Decode(&sess.data.Data) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to decode session data: %w", err) } } else if err != nil { return nil, err diff --git a/middleware/session/store_test.go b/middleware/session/store_test.go index 4c755c3290..c7e435f6ae 100644 --- a/middleware/session/store_test.go +++ b/middleware/session/store_test.go @@ -6,6 +6,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) diff --git a/middleware/skip/skip.go b/middleware/skip/skip.go index 616baf2dc3..de64601fda 100644 --- a/middleware/skip/skip.go +++ b/middleware/skip/skip.go @@ -1,6 +1,8 @@ package skip -import "github.com/gofiber/fiber/v2" +import ( + "github.com/gofiber/fiber/v2" +) // New creates a middleware handler which skips the wrapped handler // if the exclude predicate returns true. diff --git a/middleware/skip/skip_test.go b/middleware/skip/skip_test.go index cfbbec9d8d..6d0925591a 100644 --- a/middleware/skip/skip_test.go +++ b/middleware/skip/skip_test.go @@ -17,7 +17,7 @@ func Test_Skip(t *testing.T) { app.Use(skip.New(errTeapotHandler, func(*fiber.Ctx) bool { return true })) app.Get("/", helloWorldHandler) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) } @@ -30,7 +30,7 @@ func Test_SkipFalse(t *testing.T) { app.Use(skip.New(errTeapotHandler, func(*fiber.Ctx) bool { return false })) app.Get("/", helloWorldHandler) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) } @@ -43,7 +43,7 @@ func Test_SkipNilFunc(t *testing.T) { app.Use(skip.New(errTeapotHandler, nil)) app.Get("/", helloWorldHandler) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) } diff --git a/middleware/timeout/timeout_test.go b/middleware/timeout/timeout_test.go index aa60a3504c..8498cb816d 100644 --- a/middleware/timeout/timeout_test.go +++ b/middleware/timeout/timeout_test.go @@ -18,7 +18,8 @@ func Test_Timeout(t *testing.T) { // fiber instance app := fiber.New() h := New(func(c *fiber.Ctx) error { - sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") + sleepTime, err := time.ParseDuration(c.Params("sleepTime") + "ms") + utils.AssertEqual(t, nil, err) if err := sleepWithContext(c.UserContext(), sleepTime, context.DeadlineExceeded); err != nil { return fmt.Errorf("%w: l2 wrap", fmt.Errorf("%w: l1 wrap ", err)) } @@ -26,12 +27,12 @@ func Test_Timeout(t *testing.T) { }, 100*time.Millisecond) app.Get("/test/:sleepTime", h) testTimeout := func(timeoutStr string) { - resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test/"+timeoutStr, nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusRequestTimeout, resp.StatusCode, "Status code") } testSucces := func(timeoutStr string) { - resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test/"+timeoutStr, nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") } @@ -49,7 +50,8 @@ func Test_TimeoutWithCustomError(t *testing.T) { // fiber instance app := fiber.New() h := New(func(c *fiber.Ctx) error { - sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") + sleepTime, err := time.ParseDuration(c.Params("sleepTime") + "ms") + utils.AssertEqual(t, nil, err) if err := sleepWithContext(c.UserContext(), sleepTime, ErrFooTimeOut); err != nil { return fmt.Errorf("%w: execution error", err) } @@ -57,12 +59,12 @@ func Test_TimeoutWithCustomError(t *testing.T) { }, 100*time.Millisecond, ErrFooTimeOut) app.Get("/test/:sleepTime", h) testTimeout := func(timeoutStr string) { - resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test/"+timeoutStr, nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusRequestTimeout, resp.StatusCode, "Status code") } testSucces := func(timeoutStr string) { - resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test/"+timeoutStr, nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") } diff --git a/mount.go b/mount.go index abb4a73930..2becfef6bd 100644 --- a/mount.go +++ b/mount.go @@ -120,7 +120,6 @@ func (app *App) appendSubAppLists(appList map[string]*App, parent ...string) { if len(subApp.mountFields.appList) > 1 { app.appendSubAppLists(subApp.mountFields.appList, prefix) } - } } diff --git a/mount_test.go b/mount_test.go index 580366e69d..b70be3085c 100644 --- a/mount_test.go +++ b/mount_test.go @@ -2,6 +2,7 @@ // 🤖 Github Repository: https://github.com/gofiber/fiber // 📌 API Documentation: https://docs.gofiber.io +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package fiber import ( @@ -369,7 +370,6 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

I'm Bruh

", string(body)) - } func Test_Ctx_Render_MountGroup(t *testing.T) { diff --git a/path.go b/path.go index 2bc041d3d1..ba357328e8 100644 --- a/path.go +++ b/path.go @@ -88,12 +88,14 @@ const ( ) // list of possible parameter and segment delimiter +// +//nolint:gochecknoglobals // TODO: Do not use a global var here var ( // slash has a special role, unlike the other parameters it must not be interpreted as a parameter routeDelimiter = []byte{slashDelimiter, '-', '.'} // list of greedy parameters greedyParameters = []byte{wildcardParam, plusParam} - // list of chars for the parameter recognising + // list of chars for the parameter recognizing parameterStartChars = []byte{wildcardParam, plusParam, paramStarterChar} // list of chars of delimiters and the starting parameter name char parameterDelimiterChars = append([]byte{paramStarterChar, escapeChar}, routeDelimiter...) @@ -268,7 +270,7 @@ func findNextParamPosition(pattern string) int { } // analyseConstantPart find the end of the constant part and create the route segment -func (routeParser *routeParser) analyseConstantPart(pattern string, nextParamPosition int) (string, *routeSegment) { +func (*routeParser) analyseConstantPart(pattern string, nextParamPosition int) (string, *routeSegment) { // handle the constant part processedPart := pattern if nextParamPosition != -1 { @@ -297,11 +299,12 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r parameterConstraintStart := -1 parameterConstraintEnd := -1 // handle wildcard end - if isWildCard || isPlusParam { + switch { + case isWildCard, isPlusParam: parameterEndPosition = 0 - } else if parameterEndPosition == -1 { + case parameterEndPosition == -1: parameterEndPosition = len(pattern) - 1 - } else if !isInCharset(pattern[parameterEndPosition+1], parameterDelimiterChars) { + case !isInCharset(pattern[parameterEndPosition+1], parameterDelimiterChars): parameterEndPosition++ } @@ -338,7 +341,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r constraint.Data = splitNonEscaped(c[start+1:end], string(parameterConstraintDataSeparatorChars)) if len(constraint.Data) == 1 { constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) - } else if len(constraint.Data) == 2 { + } else if len(constraint.Data) == 2 { //nolint:gomnd // This is fine, we simply expect two parts constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) constraint.Data[1] = RemoveEscapeChar(constraint.Data[1]) } @@ -474,7 +477,7 @@ func splitNonEscaped(s, sep string) []string { } // getMatch parses the passed url and tries to match it against the route segments and determine the parameter positions -func (routeParser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { +func (routeParser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { //nolint: revive // Accepting a bool param is fine here var i, paramsIterator, partLen int for _, segment := range routeParser.segs { partLen = len(detectionPath) @@ -638,9 +641,9 @@ func getParamConstraintType(constraintPart string) TypeConstraint { default: return noConstraint } - } +//nolint:errcheck // TODO: Properly check _all_ errors in here, log them & immediately return func (c *Constraint) CheckConstraint(param string) bool { var err error var num int @@ -663,6 +666,8 @@ func (c *Constraint) CheckConstraint(param string) bool { // check constraints switch c.ID { + case noConstraint: + // Nothing to check case intConstraint: _, err = strconv.Atoi(param) case boolConstraint: diff --git a/path_test.go b/path_test.go index 656b82e12a..62a421ed3a 100644 --- a/path_test.go +++ b/path_test.go @@ -1119,7 +1119,6 @@ func Benchmark_Path_matchParams(t *testing.B) { 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)) } }) - } } benchCase("/api/:param/fixedEnd", []testparams{ @@ -1348,7 +1347,6 @@ func Benchmark_RoutePatternMatch(t *testing.B) { } utils.AssertEqual(t, c.match, matchRes, fmt.Sprintf("route: '%s', url: '%s'", pattern, c.url)) }) - } } benchCase("/api/:param/fixedEnd", []testparams{ diff --git a/prefork.go b/prefork.go index b3049abc60..7eebd50636 100644 --- a/prefork.go +++ b/prefork.go @@ -2,13 +2,15 @@ package fiber import ( "crypto/tls" + "errors" "fmt" - "net" + "log" "os" "os/exec" "runtime" "strconv" "strings" + "sync/atomic" "time" "github.com/valyala/fasthttp/reuseport" @@ -19,8 +21,11 @@ const ( envPreforkChildVal = "1" ) -var testPreforkMaster = false -var testOnPrefork = false +//nolint:gochecknoglobals // TODO: Do not use global vars here +var ( + testPreforkMaster = false + testOnPrefork = false +) // IsChild determines if the current process is a child of Prefork func IsChild() bool { @@ -28,19 +33,20 @@ func IsChild() bool { } // prefork manages child processes to make use of the OS REUSEPORT or REUSEADDR feature -func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) { +func (app *App) prefork(network, addr string, tlsConfig *tls.Config) error { // 👶 child process 👶 if IsChild() { // use 1 cpu core per child process runtime.GOMAXPROCS(1) - var ln net.Listener // Linux will use SO_REUSEPORT and Windows falls back to SO_REUSEADDR // Only tcp4 or tcp6 is supported when preforking, both are not supported - if ln, err = reuseport.Listen(network, addr); err != nil { + ln, err := reuseport.Listen(network, addr) + if err != nil { if !app.config.DisableStartupMessage { - time.Sleep(100 * time.Millisecond) // avoid colliding with startup message + const sleepDuration = 100 * time.Millisecond + time.Sleep(sleepDuration) // avoid colliding with startup message } - return fmt.Errorf("prefork: %v", err) + return fmt.Errorf("prefork: %w", err) } // wrap a tls config around the listener if provided if tlsConfig != nil { @@ -70,7 +76,11 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) // kill child procs when master exits defer func() { for _, proc := range childs { - _ = proc.Process.Kill() + if err := proc.Process.Kill(); err != nil { + if !errors.Is(err, os.ErrProcessDone) { + log.Printf("prefork: failed to kill child: %v\n", err) + } + } } }() @@ -79,8 +89,7 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) // launch child procs for i := 0; i < max; i++ { - /* #nosec G204 */ - cmd := exec.Command(os.Args[0], os.Args[1:]...) // #nosec G204 + cmd := exec.Command(os.Args[0], os.Args[1:]...) //nolint:gosec // It's fine to launch the same process again if testPreforkMaster { // When test prefork master, // just start the child process with a dummy cmd, @@ -94,8 +103,8 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) cmd.Env = append(os.Environ(), fmt.Sprintf("%s=%s", envPreforkChildKey, envPreforkChildVal), ) - if err = cmd.Start(); err != nil { - return fmt.Errorf("failed to start a child prefork process, error: %v", err) + if err := cmd.Start(); err != nil { + return fmt.Errorf("failed to start a child prefork process, error: %w", err) } // store child process @@ -134,27 +143,34 @@ func watchMaster() { // and waits for it to exit p, err := os.FindProcess(os.Getppid()) if err == nil { - _, _ = p.Wait() + _, _ = p.Wait() //nolint:errcheck // It is fine to ignore the error here } - os.Exit(1) + os.Exit(1) //nolint:revive // Calling os.Exit is fine here in the prefork } // if it is equal to 1 (init process ID), // it indicates that the master process has exited - for range time.NewTicker(time.Millisecond * 500).C { + const watchInterval = 500 * time.Millisecond + for range time.NewTicker(watchInterval).C { if os.Getppid() == 1 { - os.Exit(1) + os.Exit(1) //nolint:revive // Calling os.Exit is fine here in the prefork } } } -var dummyChildCmd = "go" +//nolint:gochecknoglobals // TODO: Do not use global vars here +var ( + dummyPid = 1 + dummyChildCmd atomic.Value +) // dummyCmd is for internal prefork testing func dummyCmd() *exec.Cmd { + command := "go" + if storeCommand := dummyChildCmd.Load(); storeCommand != nil && storeCommand != "" { + command = storeCommand.(string) //nolint:forcetypeassert,errcheck // We always store a string in here + } if runtime.GOOS == "windows" { - return exec.Command("cmd", "/C", dummyChildCmd, "version") + return exec.Command("cmd", "/C", command, "version") } - return exec.Command(dummyChildCmd, "version") + return exec.Command(command, "version") } - -var dummyPid = 1 diff --git a/prefork_test.go b/prefork_test.go index 4842774f26..506244a263 100644 --- a/prefork_test.go +++ b/prefork_test.go @@ -38,6 +38,7 @@ func Test_App_Prefork_Child_Process(t *testing.T) { if err != nil { utils.AssertEqual(t, nil, err) } + //nolint:gosec // We're in a test so using old ciphers is fine config := &tls.Config{Certificates: []tls.Certificate{cer}} go func() { @@ -61,10 +62,12 @@ func Test_App_Prefork_Master_Process(t *testing.T) { utils.AssertEqual(t, nil, app.prefork(NetworkTCP4, ":3000", nil)) - dummyChildCmd = "invalid" + dummyChildCmd.Store("invalid") err := app.prefork(NetworkTCP4, "127.0.0.1:", nil) utils.AssertEqual(t, false, err == nil) + + dummyChildCmd.Store("") } func Test_App_Prefork_Child_Process_Never_Show_Startup_Message(t *testing.T) { diff --git a/router.go b/router.go index 84ff606443..ce27583955 100644 --- a/router.go +++ b/router.go @@ -13,6 +13,7 @@ import ( "time" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) @@ -55,14 +56,15 @@ type Route struct { group *Group // Group instance. used for routes in groups // Public fields - Method string `json:"method"` // HTTP method - Name string `json:"name"` // Route's name + Method string `json:"method"` // HTTP method + Name string `json:"name"` // Route's name + //nolint:revive // Having both a Path (uppercase) and a path (lowercase) is fine Path string `json:"path"` // Original registered route path Params []string `json:"params"` // Case sensitive param keys Handlers []Handler `json:"-"` // Ctx handlers } -func (r *Route) match(detectionPath, path string, params *[maxParams]string) (match bool) { +func (r *Route) match(detectionPath, path string, params *[maxParams]string) bool { // root detectionPath check if r.root && detectionPath == "/" { return true @@ -97,7 +99,7 @@ func (r *Route) match(detectionPath, path string, params *[maxParams]string) (ma return false } -func (app *App) next(c *Ctx) (match bool, err error) { +func (app *App) next(c *Ctx) (bool, error) { // Get stack length tree, ok := app.treeStack[c.methodINT][c.treePath] if !ok { @@ -114,10 +116,9 @@ func (app *App) next(c *Ctx) (match bool, err error) { route := tree[c.indexRoute] // Check if it matches the request path - match = route.match(c.detectionPath, c.path, &c.values) - - // No match, next route + match := route.match(c.detectionPath, c.path, &c.values) if !match { + // No match, next route continue } // Pass route reference and param values @@ -130,29 +131,28 @@ func (app *App) next(c *Ctx) (match bool, err error) { // Execute first handler of route c.indexHandler = 0 - err = route.Handlers[0](c) + err := route.Handlers[0](c) return match, err // Stop scanning the stack } // If c.Next() does not match, return 404 - err = NewError(StatusNotFound, "Cannot "+c.method+" "+c.pathOriginal) - - // If no match, scan stack again if other methods match the request - // Moved from app.handler because middleware may break the route chain + err := NewError(StatusNotFound, "Cannot "+c.method+" "+c.pathOriginal) if !c.matched && app.methodExist(c) { + // If no match, scan stack again if other methods match the request + // Moved from app.handler because middleware may break the route chain err = ErrMethodNotAllowed } - return + return false, err } -func (app *App) handler(rctx *fasthttp.RequestCtx) { +func (app *App) handler(rctx *fasthttp.RequestCtx) { //revive:disable-line:confusing-naming // Having both a Handler() (uppercase) and a handler() (lowercase) is fine. TODO: Use nolint:revive directive instead. See https://github.com/golangci/golangci-lint/issues/3476 // Acquire Ctx with fasthttp request from pool c := app.AcquireCtx(rctx) + defer app.ReleaseCtx(c) // handle invalid http method directly if c.methodINT == -1 { - _ = c.Status(StatusBadRequest).SendString("Invalid http method") - app.ReleaseCtx(c) + _ = c.Status(StatusBadRequest).SendString("Invalid http method") //nolint:errcheck // It is fine to ignore the error here return } @@ -160,16 +160,14 @@ func (app *App) handler(rctx *fasthttp.RequestCtx) { match, err := app.next(c) if err != nil { if catch := c.app.ErrorHandler(c, err); catch != nil { - _ = c.SendStatus(StatusInternalServerError) + _ = c.SendStatus(StatusInternalServerError) //nolint:errcheck // It is fine to ignore the error here } + // TODO: Do we need to return here? } // Generate ETag if enabled if match && app.config.ETag { setETag(c, false) } - - // Release Ctx - app.ReleaseCtx(c) } func (app *App) addPrefixToRoute(prefix string, route *Route) *Route { @@ -193,7 +191,7 @@ func (app *App) addPrefixToRoute(prefix string, route *Route) *Route { return route } -func (app *App) copyRoute(route *Route) *Route { +func (*App) copyRoute(route *Route) *Route { return &Route{ // Router booleans use: route.use, @@ -327,6 +325,7 @@ func (app *App) registerStatic(prefix, root string, config ...Static) Router { prefixLen-- prefix = prefix[:prefixLen] } + const cacheDuration = 10 * time.Second // Fileserver settings fs := &fasthttp.FS{ Root: root, @@ -335,7 +334,7 @@ func (app *App) registerStatic(prefix, root string, config ...Static) Router { AcceptByteRange: false, Compress: false, CompressedFileSuffix: app.config.CompressedFileSuffix, - CacheDuration: 10 * time.Second, + CacheDuration: cacheDuration, IndexNames: []string{"index.html"}, PathRewrite: func(fctx *fasthttp.RequestCtx) []byte { path := fctx.Path() diff --git a/router_test.go b/router_test.go index 26407ce10e..0fbb7c8f46 100644 --- a/router_test.go +++ b/router_test.go @@ -2,6 +2,7 @@ // 📃 Github Repository: https://github.com/gofiber/fiber // 📌 API Documentation: https://docs.gofiber.io +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package fiber import ( @@ -15,11 +16,14 @@ import ( "testing" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) -var routesFixture = routeJSON{} +//nolint:gochecknoglobals // TODO: Do not use a global var here +var routesFixture routeJSON +//nolint:gochecknoinits // init() is used to populate a global struct from a JSON file func init() { dat, err := os.ReadFile("./.github/testdata/testRoutes.json") if err != nil { @@ -579,7 +583,7 @@ func Benchmark_Router_Chain(b *testing.B) { c := &fasthttp.RequestCtx{} - c.Request.Header.SetMethod("GET") + c.Request.Header.SetMethod(MethodGet) c.URI().SetPath("/") b.ResetTimer() for n := 0; n < b.N; n++ { @@ -603,7 +607,7 @@ func Benchmark_Router_WithCompression(b *testing.B) { appHandler := app.Handler() c := &fasthttp.RequestCtx{} - c.Request.Header.SetMethod("GET") + c.Request.Header.SetMethod(MethodGet) c.URI().SetPath("/") b.ResetTimer() for n := 0; n < b.N; n++ { @@ -829,6 +833,6 @@ type testRoute struct { } type routeJSON struct { - TestRoutes []testRoute `json:"testRoutes"` - GithubAPI []testRoute `json:"githubAPI"` + TestRoutes []testRoute `json:"test_routes"` + GithubAPI []testRoute `json:"github_api"` } diff --git a/utils/assertions.go b/utils/assertions.go index ec9a119c0f..2cc4cac21a 100644 --- a/utils/assertions.go +++ b/utils/assertions.go @@ -16,7 +16,7 @@ import ( ) // AssertEqual checks if values are equal -func AssertEqual(tb testing.TB, expected, actual interface{}, description ...string) { +func AssertEqual(tb testing.TB, expected, actual interface{}, description ...string) { //nolint:thelper // TODO: Verify if tb can be nil if tb != nil { tb.Helper() } @@ -43,14 +43,15 @@ func AssertEqual(tb testing.TB, expected, actual interface{}, description ...str _, file, line, _ := runtime.Caller(1) var buf bytes.Buffer - w := tabwriter.NewWriter(&buf, 0, 0, 5, ' ', 0) - fmt.Fprintf(w, "\nTest:\t%s", testName) - fmt.Fprintf(w, "\nTrace:\t%s:%d", filepath.Base(file), line) + const pad = 5 + w := tabwriter.NewWriter(&buf, 0, 0, pad, ' ', 0) + _, _ = fmt.Fprintf(w, "\nTest:\t%s", testName) + _, _ = fmt.Fprintf(w, "\nTrace:\t%s:%d", filepath.Base(file), line) if len(description) > 0 { - fmt.Fprintf(w, "\nDescription:\t%s", description[0]) + _, _ = fmt.Fprintf(w, "\nDescription:\t%s", description[0]) } - fmt.Fprintf(w, "\nExpect:\t%v\t(%s)", expected, aType) - fmt.Fprintf(w, "\nResult:\t%v\t(%s)", actual, bType) + _, _ = fmt.Fprintf(w, "\nExpect:\t%v\t(%s)", expected, aType) + _, _ = fmt.Fprintf(w, "\nResult:\t%v\t(%s)", actual, bType) result := "" if err := w.Flush(); err != nil { @@ -62,6 +63,6 @@ func AssertEqual(tb testing.TB, expected, actual interface{}, description ...str if tb != nil { tb.Fatal(result) } else { - log.Fatal(result) + log.Fatal(result) //nolint:revive // tb might be nil, so we need a fallback } } diff --git a/utils/assertions_test.go b/utils/assertions_test.go index d2d32dc0cf..d1e7f08732 100644 --- a/utils/assertions_test.go +++ b/utils/assertions_test.go @@ -4,7 +4,9 @@ package utils -import "testing" +import ( + "testing" +) func Test_AssertEqual(t *testing.T) { t.Parallel() diff --git a/utils/common.go b/utils/common.go index b0f4d76625..1789a239b0 100644 --- a/utils/common.go +++ b/utils/common.go @@ -31,6 +31,11 @@ const ( // github.com/rogpeppe/fastuuid // All rights reserved. +const ( + emptyUUID = "00000000-0000-0000-0000-000000000000" +) + +//nolint:gochecknoglobals // TODO: Do not use a global var here var ( uuidSeed [24]byte uuidCounter uint64 @@ -39,6 +44,8 @@ var ( ) // UUID generates an universally unique identifier (UUID) +// +//nolint:gomnd // Those are not random numbers, it's the fastuuid algorithm func UUID() string { // Setup seed & counter once uuidSetup.Do(func() { @@ -48,7 +55,7 @@ func UUID() string { uuidCounter = binary.LittleEndian.Uint64(uuidSeed[:8]) }) if atomic.LoadUint64(&uuidCounter) <= 0 { - return "00000000-0000-0000-0000-000000000000" + return emptyUUID } // first 8 bytes differ, taking a slice of the first 16 bytes x := atomic.AddUint64(&uuidCounter, 1) @@ -147,7 +154,8 @@ func ConvertToBytes(humanReadableString string) int { // convert multiplier char to lowercase and check if exists in units slice index := bytes.IndexByte(unitsSlice, toLowerTable[humanReadableString[unitPrefixPos]]) if index != -1 { - size *= math.Pow(1000, float64(index+1)) + const bytesPerKB = 1000 + size *= math.Pow(bytesPerKB, float64(index+1)) } } diff --git a/utils/common_test.go b/utils/common_test.go index b4b2c61cf9..1b0884fc86 100644 --- a/utils/common_test.go +++ b/utils/common_test.go @@ -24,7 +24,7 @@ func Test_UUID(t *testing.T) { t.Parallel() res := UUID() AssertEqual(t, 36, len(res)) - AssertEqual(t, true, res != "00000000-0000-0000-0000-000000000000") + AssertEqual(t, true, res != emptyUUID) } func Test_UUID_Concurrency(t *testing.T) { @@ -49,7 +49,7 @@ func Test_UUIDv4(t *testing.T) { t.Parallel() res := UUIDv4() AssertEqual(t, 36, len(res)) - AssertEqual(t, true, res != "00000000-0000-0000-0000-000000000000") + AssertEqual(t, true, res != emptyUUID) } func Test_UUIDv4_Concurrency(t *testing.T) { @@ -82,7 +82,8 @@ func Benchmark_UUID(b *testing.B) { }) b.Run("default", func(b *testing.B) { rnd := make([]byte, 16) - _, _ = rand.Read(rnd) + _, err := rand.Read(rnd) + AssertEqual(b, nil, err) for n := 0; n < b.N; n++ { res = fmt.Sprintf("%x-%x-%x-%x-%x", rnd[0:4], rnd[4:6], rnd[6:8], rnd[8:10], rnd[10:]) } diff --git a/utils/convert.go b/utils/convert.go index d1d3f52034..4a91ead995 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -15,15 +15,17 @@ import ( const MaxStringLen = 0x7fff0000 // Maximum string length for UnsafeBytes. (decimal: 2147418112) -// #nosec G103 // 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)) } -// #nosec G103 // 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 @@ -47,7 +49,7 @@ func CopyBytes(b []byte) []byte { } const ( - uByte = 1 << (10 * iota) + uByte = 1 << (10 * iota) //nolint:gomnd // 1 << 10 == 1024 uKilobyte uMegabyte uGigabyte @@ -92,7 +94,7 @@ func ByteSize(bytes uint64) string { // ToString Change arg to string func ToString(arg interface{}, timeFormat ...string) string { - var tmp = reflect.Indirect(reflect.ValueOf(arg)).Interface() + tmp := reflect.Indirect(reflect.ValueOf(arg)).Interface() switch v := tmp.(type) { case int: return strconv.Itoa(v) diff --git a/utils/convert_test.go b/utils/convert_test.go index 59ce625e53..4bfd6bf079 100644 --- a/utils/convert_test.go +++ b/utils/convert_test.go @@ -4,7 +4,9 @@ package utils -import "testing" +import ( + "testing" +) func Test_UnsafeString(t *testing.T) { t.Parallel() diff --git a/utils/deprecated.go b/utils/deprecated.go index ae6fd34e7b..a436e67a5b 100644 --- a/utils/deprecated.go +++ b/utils/deprecated.go @@ -1,18 +1,16 @@ package utils -// #nosec G103 -// DEPRECATED, Please use UnsafeString instead +// Deprecated: Please use UnsafeString instead func GetString(b []byte) string { return UnsafeString(b) } -// #nosec G103 -// DEPRECATED, Please use UnsafeBytes instead +// Deprecated: Please use UnsafeBytes instead func GetBytes(s string) []byte { return UnsafeBytes(s) } -// DEPRECATED, Please use CopyString instead +// Deprecated: Please use CopyString instead func ImmutableString(s string) string { return CopyString(s) } diff --git a/utils/http.go b/utils/http.go index bdcd834c98..69f25ef193 100644 --- a/utils/http.go +++ b/utils/http.go @@ -4,15 +4,18 @@ package utils -import "strings" +import ( + "strings" +) const MIMEOctetStream = "application/octet-stream" // GetMIME returns the content-type of a file extension -func GetMIME(extension string) (mime string) { +func GetMIME(extension string) string { if len(extension) == 0 { - return mime + return "" } + var mime string if extension[0] == '.' { mime = mimeExtensions[extension[1:]] } else { @@ -35,6 +38,7 @@ func ParseVendorSpecificContentType(cType string) string { } var parsableType string + //nolint:revive // Actually not simpler if semiColonIndex := strings.Index(cType, ";"); semiColonIndex == -1 { parsableType = cType[plusIndex+1:] } else if plusIndex < semiColonIndex { @@ -67,6 +71,8 @@ func StatusMessage(status int) string { } // NOTE: Keep this in sync with the status code list +// +//nolint:gochecknoglobals // Using a global var is fine here var statusMessage = []string{ 100: "Continue", // StatusContinue 101: "Switching Protocols", // StatusSwitchingProtocols @@ -140,6 +146,8 @@ var statusMessage = []string{ // MIME types were copied from https://github.com/nginx/nginx/blob/67d2a9541826ecd5db97d604f23460210fd3e517/conf/mime.types with the following updates: // - Use "application/xml" instead of "text/xml" as recommended per https://datatracker.ietf.org/doc/html/rfc7303#section-4.1 // - Use "text/javascript" instead of "application/javascript" as recommended per https://www.rfc-editor.org/rfc/rfc9239#name-text-javascript +// +//nolint:gochecknoglobals // Using a global var is fine here var mimeExtensions = map[string]string{ "html": "text/html", "htm": "text/html", diff --git a/utils/ips.go b/utils/ips.go index 4886c117f7..adac9d4255 100644 --- a/utils/ips.go +++ b/utils/ips.go @@ -6,6 +6,8 @@ import ( // IsIPv4 works the same way as net.ParseIP, // but without check for IPv6 case and without returning net.IP slice, whereby IsIPv4 makes no allocations. +// +//nolint:gomnd // Magic numbers used for a faster algorithm than net.ParseIP func IsIPv4(s string) bool { for i := 0; i < net.IPv4len; i++ { if len(s) == 0 { @@ -40,6 +42,8 @@ func IsIPv4(s string) bool { // IsIPv6 works the same way as net.ParseIP, // but without check for IPv4 case and without returning net.IP slice, whereby IsIPv6 makes no allocations. +// +//nolint:gomnd // Magic numbers used for a faster algorithm than net.ParseIP func IsIPv6(s string) bool { ellipsis := -1 // position of ellipsis in ip diff --git a/utils/time.go b/utils/time.go index 8ea13c2262..93fe88087e 100644 --- a/utils/time.go +++ b/utils/time.go @@ -6,6 +6,7 @@ import ( "time" ) +//nolint:gochecknoglobals // TODO: Do not use global vars here var ( timestampTimer sync.Once // Timestamp please start the timer function before you use this value diff --git a/utils/time_test.go b/utils/time_test.go index 75c13b1dd8..0f467f9f85 100644 --- a/utils/time_test.go +++ b/utils/time_test.go @@ -6,9 +6,12 @@ import ( "time" ) -func checkTimeStamp(t testing.TB, expectedCurrent, actualCurrent uint32) { +func checkTimeStamp(tb testing.TB, expectedCurrent, actualCurrent uint32) { //nolint:thelper // TODO: Verify if tb can be nil + if tb != nil { + tb.Helper() + } // test with some buffer in front and back of the expectedCurrent time -> because of the timing on the work machine - AssertEqual(t, true, actualCurrent >= expectedCurrent-1 || actualCurrent <= expectedCurrent+1) + AssertEqual(tb, true, actualCurrent >= expectedCurrent-1 || actualCurrent <= expectedCurrent+1) } func Test_TimeStampUpdater(t *testing.T) { diff --git a/utils/xml_test.go b/utils/xml_test.go index bbb11708d6..d0e53817f1 100644 --- a/utils/xml_test.go +++ b/utils/xml_test.go @@ -16,7 +16,7 @@ type serverXMLStructure struct { Name string `xml:"name"` } -var xmlString = `fiber onefiber two` +const xmlString = `fiber onefiber two` func Test_GolangXMLEncoder(t *testing.T) { t.Parallel() From 7170b2a40d58dbc0f3f2ec460d59633b3afddd42 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Fri, 27 Jan 2023 11:21:18 +0100 Subject: [PATCH 036/212] =?UTF-8?q?=F0=9F=9A=80=20Feature:=20Add=20earlyda?= =?UTF-8?q?ta=20middleware=20(#2270)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * middleware: add earlydata middleware * middleware/earlydata: address comments * Update README.md * Update README.md Co-authored-by: RW --- middleware/earlydata/README.md | 101 +++++++++++++ middleware/earlydata/config.go | 73 ++++++++++ middleware/earlydata/earlydata.go | 47 ++++++ middleware/earlydata/earlydata_test.go | 189 +++++++++++++++++++++++++ 4 files changed, 410 insertions(+) create mode 100644 middleware/earlydata/README.md create mode 100644 middleware/earlydata/config.go create mode 100644 middleware/earlydata/earlydata.go create mode 100644 middleware/earlydata/earlydata_test.go diff --git a/middleware/earlydata/README.md b/middleware/earlydata/README.md new file mode 100644 index 0000000000..d12d694653 --- /dev/null +++ b/middleware/earlydata/README.md @@ -0,0 +1,101 @@ +# 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/v3" + "github.com/gofiber/fiber/v3/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/earlydata/config.go b/middleware/earlydata/config.go new file mode 100644 index 0000000000..ced705dd57 --- /dev/null +++ b/middleware/earlydata/config.go @@ -0,0 +1,73 @@ +package earlydata + +import ( + "github.com/gofiber/fiber/v3" +) + +const ( + DefaultHeaderName = "Early-Data" + DefaultHeaderTrueValue = "1" +) + +// 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 +} + +// ConfigDefault is the default config +var ConfigDefault = Config{ + IsEarlyData: func(c fiber.Ctx) bool { + return c.Get(DefaultHeaderName) == DefaultHeaderTrueValue + }, + + AllowEarlyData: func(c fiber.Ctx) bool { + return fiber.IsMethodSafe(c.Method()) + }, + + Error: fiber.ErrTooEarly, +} + +// 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.IsEarlyData == nil { + cfg.IsEarlyData = ConfigDefault.IsEarlyData + } + + if cfg.AllowEarlyData == nil { + cfg.AllowEarlyData = ConfigDefault.AllowEarlyData + } + + if cfg.Error == nil { + cfg.Error = ConfigDefault.Error + } + + return cfg +} diff --git a/middleware/earlydata/earlydata.go b/middleware/earlydata/earlydata.go new file mode 100644 index 0000000000..2b53341f9c --- /dev/null +++ b/middleware/earlydata/earlydata.go @@ -0,0 +1,47 @@ +package earlydata + +import ( + "github.com/gofiber/fiber/v3" +) + +const ( + localsKeyAllowed = "earlydata_allowed" +) + +func IsEarly(c fiber.Ctx) bool { + return c.Locals(localsKeyAllowed) != nil +} + +// New creates a new middleware handler +// https://datatracker.ietf.org/doc/html/rfc8470#section-5.1 +func New(config ...Config) fiber.Handler { + // Set default config + cfg := configDefault(config...) + + // Return new handler + return func(c fiber.Ctx) error { + // Don't execute middleware if Next returns true + if cfg.Next != nil && cfg.Next(c) { + return c.Next() + } + + // Abort if we can't trust the early-data header + if !c.IsProxyTrusted() { + return cfg.Error + } + + // Continue stack if request is not an early-data request + if !cfg.IsEarlyData(c) { + return c.Next() + } + + // Continue stack if we allow early-data for this request + if cfg.AllowEarlyData(c) { + _ = c.Locals(localsKeyAllowed, true) + return c.Next() + } + + // Else return our error + return cfg.Error + } +} diff --git a/middleware/earlydata/earlydata_test.go b/middleware/earlydata/earlydata_test.go new file mode 100644 index 0000000000..aee22c61f2 --- /dev/null +++ b/middleware/earlydata/earlydata_test.go @@ -0,0 +1,189 @@ +package earlydata_test + +import ( + "errors" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/earlydata" + "github.com/stretchr/testify/require" +) + +const ( + headerName = "Early-Data" + headerValOn = "1" + headerValOff = "0" +) + +func appWithConfig(t *testing.T, c *fiber.Config) *fiber.App { + t.Helper() + t.Parallel() + + var app *fiber.App + if c == nil { + app = fiber.New() + } else { + app = fiber.New(*c) + } + + app.Use(earlydata.New()) + + // Middleware to test IsEarly func + const localsKeyTestValid = "earlydata_testvalid" + app.Use(func(c fiber.Ctx) error { + isEarly := earlydata.IsEarly(c) + + switch h := c.Get(headerName); h { + case "", + headerValOff: + if isEarly { + return errors.New("is early-data even though it's not") + } + + case headerValOn: + switch { + case fiber.IsMethodSafe(c.Method()): + if !isEarly { + return errors.New("should be early-data on safe HTTP methods") + } + default: + if isEarly { + return errors.New("early-data unsuported on unsafe HTTP methods") + } + } + + default: + return fmt.Errorf("header has unsupported value: %s", h) + } + + _ = c.Locals(localsKeyTestValid, true) + + return c.Next() + }) + + app.Add([]string{ + fiber.MethodGet, + fiber.MethodPost, + }, "/", func(c fiber.Ctx) error { + if !c.Locals(localsKeyTestValid).(bool) { + return errors.New("handler called even though validation failed") + } + + return nil + }) + + return app +} + +// go test -run Test_EarlyData +func Test_EarlyData(t *testing.T) { + t.Parallel() + + trustedRun := func(t *testing.T, app *fiber.App) { + t.Helper() + + { + req := httptest.NewRequest(fiber.MethodGet, "/", http.NoBody) + + resp, err := app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + + req.Header.Set(headerName, headerValOff) + resp, err = app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + + req.Header.Set(headerName, headerValOn) + resp, err = app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + } + + { + req := httptest.NewRequest(fiber.MethodPost, "/", http.NoBody) + + resp, err := app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + + req.Header.Set(headerName, headerValOff) + resp, err = app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + + req.Header.Set(headerName, headerValOn) + resp, err = app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusTooEarly, resp.StatusCode) + } + } + + untrustedRun := func(t *testing.T, app *fiber.App) { + t.Helper() + + { + req := httptest.NewRequest(fiber.MethodGet, "/", http.NoBody) + + resp, err := app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusTooEarly, resp.StatusCode) + + req.Header.Set(headerName, headerValOff) + resp, err = app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusTooEarly, resp.StatusCode) + + req.Header.Set(headerName, headerValOn) + resp, err = app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusTooEarly, resp.StatusCode) + } + + { + req := httptest.NewRequest(fiber.MethodPost, "/", http.NoBody) + + resp, err := app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusTooEarly, resp.StatusCode) + + req.Header.Set(headerName, headerValOff) + resp, err = app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusTooEarly, resp.StatusCode) + + req.Header.Set(headerName, headerValOn) + resp, err = app.Test(req) + require.NoError(t, err) + require.Equal(t, fiber.StatusTooEarly, resp.StatusCode) + } + } + + t.Run("empty config", func(t *testing.T) { + app := appWithConfig(t, nil) + trustedRun(t, app) + }) + t.Run("default config", func(t *testing.T) { + app := appWithConfig(t, &fiber.Config{}) + trustedRun(t, app) + }) + + t.Run("config with EnableTrustedProxyCheck", func(t *testing.T) { + app := appWithConfig(t, &fiber.Config{ + EnableTrustedProxyCheck: true, + }) + untrustedRun(t, app) + }) + t.Run("config with EnableTrustedProxyCheck and trusted TrustedProxies", func(t *testing.T) { + app := appWithConfig(t, &fiber.Config{ + EnableTrustedProxyCheck: true, + TrustedProxies: []string{ + "0.0.0.0", + }, + }) + trustedRun(t, app) + }) +} From 6dc7a123fb542eba462f64cb3ebe84912026533c Mon Sep 17 00:00:00 2001 From: Xiaoyue Lin <36526527+100gle@users.noreply.github.com> Date: Sat, 28 Jan 2023 15:48:43 +0800 Subject: [PATCH 037/212] =?UTF-8?q?=F0=9F=93=9D=20docs(filesystem):=20clea?= =?UTF-8?q?n=20duplicated=20namespace=20for=20example=20(#2313)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit chore: clean duplicated namespace --- middleware/filesystem/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/filesystem/README.md b/middleware/filesystem/README.md index 95e4aa7e91..80a76c6cc7 100644 --- a/middleware/filesystem/README.md +++ b/middleware/filesystem/README.md @@ -218,7 +218,7 @@ import ( // Use blank to invoke init function and register data to statik _ "/statik" - fs "github.com/rakyll/statik/fs" + "github.com/rakyll/statik/fs" ) func main() { From de7e2b57e57c7b5ccb0787a391f10b77cc400d5b Mon Sep 17 00:00:00 2001 From: meehow Date: Mon, 30 Jan 2023 08:47:47 +0100 Subject: [PATCH 038/212] openssl rand -base64 32 (#2316) * openssl rand -base64 32 * Apply suggestions from code review --------- Co-authored-by: RW --- middleware/encryptcookie/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/middleware/encryptcookie/README.md b/middleware/encryptcookie/README.md index 7f0f5edf3f..94cb7fc836 100644 --- a/middleware/encryptcookie/README.md +++ b/middleware/encryptcookie/README.md @@ -69,8 +69,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. @@ -89,7 +89,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", @@ -110,4 +110,4 @@ app.Use(csrf.New(csrf.Config{ CookieName: "csrf_1", CookieHTTPOnly: true, })) -``` \ No newline at end of file +``` From 44d09209e730a8bf73ed7e03d431fdded87bfc9f Mon Sep 17 00:00:00 2001 From: leonklingele Date: Mon, 30 Jan 2023 12:08:01 +0100 Subject: [PATCH 039/212] =?UTF-8?q?=F0=9F=9A=80=20Feature:=20Add=20earlyda?= =?UTF-8?q?ta=20middleware=20(v2=20backport)=20(#2314)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🚀 Feature: Add earlydata middleware (#2270) * middleware: add earlydata middleware * middleware/earlydata: address comments * Update README.md * Update README.md Co-authored-by: RW * middleware/earlydata: backport to v2 Backport of https://github.com/gofiber/fiber/pull/2270 to v2. --------- Co-authored-by: RW --- middleware/earlydata/README.md | 101 +++++++++++++ middleware/earlydata/config.go | 75 ++++++++++ middleware/earlydata/earlydata.go | 47 ++++++ middleware/earlydata/earlydata_test.go | 193 +++++++++++++++++++++++++ 4 files changed, 416 insertions(+) create mode 100644 middleware/earlydata/README.md create mode 100644 middleware/earlydata/config.go create mode 100644 middleware/earlydata/earlydata.go create mode 100644 middleware/earlydata/earlydata_test.go diff --git a/middleware/earlydata/README.md b/middleware/earlydata/README.md new file mode 100644 index 0000000000..862e78b496 --- /dev/null +++ b/middleware/earlydata/README.md @@ -0,0 +1,101 @@ +# 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/earlydata/config.go b/middleware/earlydata/config.go new file mode 100644 index 0000000000..9ec223a8b7 --- /dev/null +++ b/middleware/earlydata/config.go @@ -0,0 +1,75 @@ +package earlydata + +import ( + "github.com/gofiber/fiber/v2" +) + +const ( + DefaultHeaderName = "Early-Data" + DefaultHeaderTrueValue = "1" +) + +// 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 +} + +// ConfigDefault is the default config +// +//nolint:gochecknoglobals // Using a global var is fine here +var ConfigDefault = Config{ + IsEarlyData: func(c *fiber.Ctx) bool { + return c.Get(DefaultHeaderName) == DefaultHeaderTrueValue + }, + + AllowEarlyData: func(c *fiber.Ctx) bool { + return fiber.IsMethodSafe(c.Method()) + }, + + Error: fiber.ErrTooEarly, +} + +// 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.IsEarlyData == nil { + cfg.IsEarlyData = ConfigDefault.IsEarlyData + } + + if cfg.AllowEarlyData == nil { + cfg.AllowEarlyData = ConfigDefault.AllowEarlyData + } + + if cfg.Error == nil { + cfg.Error = ConfigDefault.Error + } + + return cfg +} diff --git a/middleware/earlydata/earlydata.go b/middleware/earlydata/earlydata.go new file mode 100644 index 0000000000..638db3c6fb --- /dev/null +++ b/middleware/earlydata/earlydata.go @@ -0,0 +1,47 @@ +package earlydata + +import ( + "github.com/gofiber/fiber/v2" +) + +const ( + localsKeyAllowed = "earlydata_allowed" +) + +func IsEarly(c *fiber.Ctx) bool { + return c.Locals(localsKeyAllowed) != nil +} + +// New creates a new middleware handler +// https://datatracker.ietf.org/doc/html/rfc8470#section-5.1 +func New(config ...Config) fiber.Handler { + // Set default config + cfg := configDefault(config...) + + // Return new handler + return func(c *fiber.Ctx) error { + // Don't execute middleware if Next returns true + if cfg.Next != nil && cfg.Next(c) { + return c.Next() + } + + // Abort if we can't trust the early-data header + if !c.IsProxyTrusted() { + return cfg.Error + } + + // Continue stack if request is not an early-data request + if !cfg.IsEarlyData(c) { + return c.Next() + } + + // Continue stack if we allow early-data for this request + if cfg.AllowEarlyData(c) { + _ = c.Locals(localsKeyAllowed, true) + return c.Next() + } + + // Else return our error + return cfg.Error + } +} diff --git a/middleware/earlydata/earlydata_test.go b/middleware/earlydata/earlydata_test.go new file mode 100644 index 0000000000..c4d960664d --- /dev/null +++ b/middleware/earlydata/earlydata_test.go @@ -0,0 +1,193 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests +package earlydata_test + +import ( + "errors" + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/earlydata" + "github.com/gofiber/fiber/v2/utils" +) + +const ( + headerName = "Early-Data" + headerValOn = "1" + headerValOff = "0" +) + +func appWithConfig(t *testing.T, c *fiber.Config) *fiber.App { + t.Helper() + t.Parallel() + + var app *fiber.App + if c == nil { + app = fiber.New() + } else { + app = fiber.New(*c) + } + + app.Use(earlydata.New()) + + // Middleware to test IsEarly func + const localsKeyTestValid = "earlydata_testvalid" + app.Use(func(c *fiber.Ctx) error { + isEarly := earlydata.IsEarly(c) + + switch h := c.Get(headerName); h { + case "", headerValOff: + if isEarly { + return errors.New("is early-data even though it's not") + } + + case headerValOn: + switch { + case fiber.IsMethodSafe(c.Method()): + if !isEarly { + return errors.New("should be early-data on safe HTTP methods") + } + default: + if isEarly { + return errors.New("early-data unsuported on unsafe HTTP methods") + } + } + + default: + return fmt.Errorf("header has unsupported value: %s", h) + } + + _ = c.Locals(localsKeyTestValid, true) + + return c.Next() + }) + + { + { + handler := func(c *fiber.Ctx) error { + if !c.Locals(localsKeyTestValid).(bool) { //nolint:forcetypeassert // We store nothing else in the pool + return errors.New("handler called even though validation failed") + } + + return nil + } + + app.Get("/", handler) + app.Post("/", handler) + } + } + + return app +} + +// go test -run Test_EarlyData +func Test_EarlyData(t *testing.T) { + t.Parallel() + + trustedRun := func(t *testing.T, app *fiber.App) { + t.Helper() + + { + req := httptest.NewRequest(fiber.MethodGet, "/", http.NoBody) + + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + + req.Header.Set(headerName, headerValOff) + resp, err = app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + + req.Header.Set(headerName, headerValOn) + resp, err = app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + } + + { + req := httptest.NewRequest(fiber.MethodPost, "/", http.NoBody) + + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + + req.Header.Set(headerName, headerValOff) + resp, err = app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + + req.Header.Set(headerName, headerValOn) + resp, err = app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusTooEarly, resp.StatusCode) + } + } + + untrustedRun := func(t *testing.T, app *fiber.App) { + t.Helper() + + { + req := httptest.NewRequest(fiber.MethodGet, "/", http.NoBody) + + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusTooEarly, resp.StatusCode) + + req.Header.Set(headerName, headerValOff) + resp, err = app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusTooEarly, resp.StatusCode) + + req.Header.Set(headerName, headerValOn) + resp, err = app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusTooEarly, resp.StatusCode) + } + + { + req := httptest.NewRequest(fiber.MethodPost, "/", http.NoBody) + + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusTooEarly, resp.StatusCode) + + req.Header.Set(headerName, headerValOff) + resp, err = app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusTooEarly, resp.StatusCode) + + req.Header.Set(headerName, headerValOn) + resp, err = app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusTooEarly, resp.StatusCode) + } + } + + t.Run("empty config", func(t *testing.T) { + app := appWithConfig(t, nil) + trustedRun(t, app) + }) + t.Run("default config", func(t *testing.T) { + app := appWithConfig(t, &fiber.Config{}) + trustedRun(t, app) + }) + + t.Run("config with EnableTrustedProxyCheck", func(t *testing.T) { + app := appWithConfig(t, &fiber.Config{ + EnableTrustedProxyCheck: true, + }) + untrustedRun(t, app) + }) + t.Run("config with EnableTrustedProxyCheck and trusted TrustedProxies", func(t *testing.T) { + app := appWithConfig(t, &fiber.Config{ + EnableTrustedProxyCheck: true, + TrustedProxies: []string{ + "0.0.0.0", + }, + }) + trustedRun(t, app) + }) +} From ac4ce21d9cf35ce56fefd347a198b10232a595ae Mon Sep 17 00:00:00 2001 From: leonklingele Date: Thu, 2 Feb 2023 15:57:40 +0100 Subject: [PATCH 040/212] =?UTF-8?q?=F0=9F=90=9B=20Bug:=20Fix=20issues=20in?= =?UTF-8?q?troduced=20in=20linting=20PR=20(#2319)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * internal: revert linting changes Changes to the internal package should not have been made in 167a8b5e9421e0ab51fbf44c5621632f4a1a90c5. * middleware/monitor: revert changes to exported field "ChartJSURL" This is a breaking change introduced in 167a8b5e9421e0ab51fbf44c5621632f4a1a90c5. * middleware/monitor: fix error checking Fix the errorenous error checking introduced in 167a8b5e9421e0ab51fbf44c5621632f4a1a90c5. * 🐛 Bug: Fix issues introduced in linting PR #2319 * 🐛 Bug: Fix issues introduced in linting PR #2319 * Bug: Fix issues introduced in linting PR #2319 --------- Co-authored-by: René Werner --- .golangci.yml | 67 +- app.go | 2 - client.go | 2 - color.go | 2 - ctx.go | 9 +- go.mod | 5 + go.sum | 30 + helpers.go | 6 +- internal/dictpool/LICENSE | 201 --- internal/dictpool/dict.go | 164 -- internal/dictpool/pool.go | 20 - internal/dictpool/types.go | 25 - internal/dictpool/types_gen.go | 509 ------ internal/fwd/LICENSE.md | 7 - internal/fwd/reader.go | 395 ----- internal/fwd/writer.go | 236 --- internal/fwd/writer_appengine.go | 6 - internal/fwd/writer_unsafe.go | 19 - internal/gopsutil/common/common.go | 2 +- internal/gopsutil/mem/mem.go | 3 +- internal/gopsutil/mem/mem_freebsd.go | 1 + internal/gopsutil/mem/mem_linux.go | 10 +- internal/msgp/LICENSE | 8 - internal/msgp/advise_linux.go | 25 - internal/msgp/advise_other.go | 18 - internal/msgp/circular.go | 39 - internal/msgp/defs.go | 145 -- internal/msgp/edit.go | 242 --- internal/msgp/elsize.go | 98 -- internal/msgp/errors.go | 317 ---- internal/msgp/extension.go | 549 ------- internal/msgp/file.go | 92 -- internal/msgp/file_port.go | 48 - internal/msgp/integers.go | 174 --- internal/msgp/json.go | 568 ------- internal/msgp/json_bytes.go | 363 ----- internal/msgp/number.go | 267 ---- internal/msgp/purego.go | 16 - internal/msgp/read.go | 1363 ----------------- internal/msgp/read_bytes.go | 1197 --------------- internal/msgp/size.go | 38 - internal/msgp/unsafe.go | 42 - internal/msgp/write.go | 863 ----------- internal/msgp/write_bytes.go | 411 ----- internal/schema/cache.go | 2 +- internal/storage/memory/memory_test.go | 4 +- internal/template/html/html.go | 1 + internal/template/utils/utils.go | 1 + internal/uuid/CONTRIBUTORS | 9 - internal/uuid/LICENSE | 27 - internal/uuid/dce.go | 81 - internal/uuid/doc.go | 12 - internal/uuid/hash.go | 53 - internal/uuid/marshal.go | 41 - internal/uuid/node.go | 90 -- internal/uuid/node_js.go | 13 - internal/uuid/node_net.go | 34 - internal/uuid/sql.go | 61 - internal/uuid/time.go | 123 -- internal/uuid/util.go | 43 - internal/uuid/uuid.go | 246 --- internal/uuid/version1.go | 44 - internal/uuid/version4.go | 43 - listen.go | 4 +- middleware/basicauth/config.go | 2 - middleware/cache/cache.go | 1 - middleware/cache/config.go | 2 - middleware/cache/manager.go | 2 - middleware/cache/manager_msgp.go | 2 +- middleware/compress/compress_test.go | 2 - middleware/compress/config.go | 2 - middleware/cors/cors.go | 2 - middleware/csrf/config.go | 2 - middleware/csrf/manager.go | 2 - middleware/csrf/manager_msgp.go | 2 +- middleware/earlydata/config.go | 2 - middleware/encryptcookie/config.go | 2 - .../encryptcookie/encryptcookie_test.go | 1 - middleware/etag/config.go | 2 - middleware/etag/etag.go | 2 +- middleware/expvar/config.go | 1 - middleware/favicon/favicon.go | 2 - middleware/filesystem/filesystem.go | 2 - middleware/idempotency/config.go | 6 +- middleware/idempotency/response_msgp.go | 2 +- middleware/idempotency/response_msgp_test.go | 2 +- middleware/limiter/config.go | 4 +- middleware/limiter/manager.go | 2 - middleware/limiter/manager_msgp.go | 2 +- middleware/logger/config.go | 4 +- middleware/monitor/config.go | 15 +- middleware/monitor/config_test.go | 20 +- middleware/monitor/monitor.go | 18 +- middleware/monitor/monitor_test.go | 2 +- middleware/pprof/config.go | 1 - middleware/proxy/config.go | 2 - middleware/proxy/proxy.go | 2 - middleware/recover/config.go | 2 - middleware/requestid/config.go | 2 - middleware/session/config.go | 4 +- middleware/session/data.go | 3 - middleware/session/session.go | 1 - middleware/session/store.go | 1 - path.go | 6 +- prefork.go | 2 - router_test.go | 2 - utils/common.go | 5 +- utils/convert.go | 2 +- utils/http.go | 4 - utils/ips.go | 4 - utils/time.go | 1 - 111 files changed, 98 insertions(+), 9594 deletions(-) delete mode 100644 internal/dictpool/LICENSE delete mode 100644 internal/dictpool/dict.go delete mode 100644 internal/dictpool/pool.go delete mode 100644 internal/dictpool/types.go delete mode 100644 internal/dictpool/types_gen.go delete mode 100644 internal/fwd/LICENSE.md delete mode 100644 internal/fwd/reader.go delete mode 100644 internal/fwd/writer.go delete mode 100644 internal/fwd/writer_appengine.go delete mode 100644 internal/fwd/writer_unsafe.go delete mode 100644 internal/msgp/LICENSE delete mode 100644 internal/msgp/advise_linux.go delete mode 100644 internal/msgp/advise_other.go delete mode 100644 internal/msgp/circular.go delete mode 100644 internal/msgp/defs.go delete mode 100644 internal/msgp/edit.go delete mode 100644 internal/msgp/elsize.go delete mode 100644 internal/msgp/errors.go delete mode 100644 internal/msgp/extension.go delete mode 100644 internal/msgp/file.go delete mode 100644 internal/msgp/file_port.go delete mode 100644 internal/msgp/integers.go delete mode 100644 internal/msgp/json.go delete mode 100644 internal/msgp/json_bytes.go delete mode 100644 internal/msgp/number.go delete mode 100644 internal/msgp/purego.go delete mode 100644 internal/msgp/read.go delete mode 100644 internal/msgp/read_bytes.go delete mode 100644 internal/msgp/size.go delete mode 100644 internal/msgp/unsafe.go delete mode 100644 internal/msgp/write.go delete mode 100644 internal/msgp/write_bytes.go delete mode 100644 internal/uuid/CONTRIBUTORS delete mode 100644 internal/uuid/LICENSE delete mode 100644 internal/uuid/dce.go delete mode 100644 internal/uuid/doc.go delete mode 100644 internal/uuid/hash.go delete mode 100644 internal/uuid/marshal.go delete mode 100644 internal/uuid/node.go delete mode 100644 internal/uuid/node_js.go delete mode 100644 internal/uuid/node_net.go delete mode 100644 internal/uuid/sql.go delete mode 100644 internal/uuid/time.go delete mode 100644 internal/uuid/util.go delete mode 100644 internal/uuid/uuid.go delete mode 100644 internal/uuid/version1.go delete mode 100644 internal/uuid/version4.go diff --git a/.golangci.yml b/.golangci.yml index 87e975e036..f41b559f47 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -5,26 +5,12 @@ run: modules-download-mode: readonly skip-dirs-use-default: false skip-dirs: - - internal # TODO: Also apply proper linting for internal dir + - internal output: sort-results: true linters-settings: - # TODO: Eventually enable these checks - # depguard: - # include-go-root: true - # packages: - # - flag - # - io/ioutil - # - reflect - # - unsafe - # packages-with-error-message: - # - flag: '`flag` package is only allowed in main.go' - # - io/ioutil: '`io/ioutil` package is deprecated, use the `io` and `os` package instead' - # - reflect: '`reflect` package is dangerous to use' - # - unsafe: '`unsafe` package is dangerous to use' - errcheck: check-type-assertions: true check-blank: true @@ -44,15 +30,6 @@ linters-settings: # - 'time\.Sleep' # - 'panic' - gci: - sections: - - standard - - prefix(github.com/gofiber/fiber) - - default - - blank - - dot - custom-order: true - gocritic: disabled-checks: - ifElseChain @@ -162,13 +139,8 @@ linters: - bodyclose - containedctx - contextcheck - # - cyclop - # - deadcode - # - decorder - depguard - dogsled - # - dupl - # - dupword - durationcheck - errcheck - errchkjson @@ -176,72 +148,38 @@ linters: - errorlint - execinquery - exhaustive - # - exhaustivestruct - # - exhaustruct - exportloopref - forbidigo - forcetypeassert - # - funlen - - gci - - gochecknoglobals - - gochecknoinits - # - gocognit - goconst - gocritic - # - gocyclo - # - godot - # - godox - # - goerr113 - gofmt - gofumpt - # - goheader - goimports - # - golint - - gomnd - gomoddirectives - # - gomodguard - goprintffuncname - gosec - gosimple - govet - grouper - # - ifshort - # - importas - - ineffassign - # - interfacebloat - # - interfacer - # - ireturn - # - lll - loggercheck - # - maintidx - # - makezero - # - maligned - misspell - nakedret - # - nestif - nilerr - nilnil - # - nlreturn - noctx - nolintlint - nonamedreturns - # - nosnakecase - nosprintfhostport - # - paralleltest # TODO: Enable once https://github.com/gofiber/fiber/issues/2254 is implemented - # - prealloc - predeclared - promlinter - reassign - revive - rowserrcheck - # - scopelint - sqlclosecheck - staticcheck - # - structcheck - stylecheck - tagliatelle - # - tenv # TODO: Enable once we drop support for Go 1.16 - # - testableexamples # - testpackage # TODO: Enable once https://github.com/gofiber/fiber/issues/2252 is implemented - thelper # - tparallel # TODO: Enable once https://github.com/gofiber/fiber/issues/2254 is implemented @@ -250,9 +188,6 @@ linters: - unparam - unused - usestdlibvars - # - varcheck - # - varnamelen - wastedassign - whitespace - wrapcheck - # - wsl diff --git a/app.go b/app.go index 002da62c4b..4cc2334a2b 100644 --- a/app.go +++ b/app.go @@ -456,8 +456,6 @@ const ( ) // HTTP methods enabled by default -// -//nolint:gochecknoglobals // Using a global var is fine here var DefaultMethods = []string{ MethodGet, MethodHead, diff --git a/client.go b/client.go index 334b99a890..ab45a58703 100644 --- a/client.go +++ b/client.go @@ -51,7 +51,6 @@ type Args = fasthttp.Args // Copy from fasthttp type RetryIfFunc = fasthttp.RetryIfFunc -//nolint:gochecknoglobals // TODO: Do not use a global var here var defaultClient Client // Client implements http client. @@ -859,7 +858,6 @@ func (a *Agent) reset() { a.formFiles = a.formFiles[:0] } -//nolint:gochecknoglobals // TODO: Do not use global vars here var ( clientPool sync.Pool agentPool = sync.Pool{ diff --git a/color.go b/color.go index 944a9b3695..cbccd2ebee 100644 --- a/color.go +++ b/color.go @@ -53,8 +53,6 @@ type Colors struct { } // DefaultColors Default color codes -// -//nolint:gochecknoglobals // Using a global var is fine here var DefaultColors = Colors{ Black: "\u001b[90m", Red: "\u001b[91m", diff --git a/ctx.go b/ctx.go index 2b648fe8d7..046307e1ff 100644 --- a/ctx.go +++ b/ctx.go @@ -24,10 +24,10 @@ import ( "text/template" "time" - "github.com/gofiber/fiber/v2/internal/dictpool" "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" ) @@ -51,7 +51,6 @@ const ( // userContextKey define the key name for storing context.Context in *fasthttp.RequestCtx const userContextKey = "__local_user_context__" -//nolint:gochecknoglobals // TODO: Do not use global vars here var ( // decoderPoolMap helps to improve BodyParser's, QueryParser's and ReqHeaderParser's performance decoderPoolMap = map[string]*sync.Pool{} @@ -59,7 +58,6 @@ var ( tags = []string{queryTag, bodyTag, reqHeaderTag, paramsTag} ) -//nolint:gochecknoinits // init() is used to initialize a global map variable func init() { for _, tag := range tags { decoderPoolMap[tag] = &sync.Pool{New: func() interface{} { @@ -730,7 +728,7 @@ iploop: var v4, v6 bool // Manually splitting string without allocating slice, working with parts directly - i, j = j+1, j+2 //nolint:gomnd // Using these values is fine + i, j = j+1, j+2 if j > len(headerValue) { break @@ -780,7 +778,7 @@ func (c *Ctx) extractIPFromHeader(header string) string { var v4, v6 bool // Manually splitting string without allocating slice, working with parts directly - i, j = j+1, j+2 //nolint:gomnd // Using these values is fine + i, j = j+1, j+2 if j > len(headerValue) { break @@ -1559,7 +1557,6 @@ func (c *Ctx) Send(body []byte) error { return nil } -//nolint:gochecknoglobals // TODO: Do not use global vars here var ( sendFileOnce sync.Once sendFileFS *fasthttp.FS diff --git a/go.mod b/go.mod index 35d8a95af4..869be78451 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,12 @@ module github.com/gofiber/fiber/v2 go 1.19 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-runewidth v0.0.14 + github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 + github.com/tinylib/msgp v1.1.6 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.44.0 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab @@ -14,6 +17,8 @@ require ( 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/rivo/uniseg v0.2.0 // indirect + github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect github.com/valyala/tcplisten v1.0.0 // indirect ) diff --git a/go.sum b/go.sum index fcae1f2638..279ea15b4f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/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/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -9,17 +11,38 @@ 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/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/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= 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/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= +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/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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/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/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= @@ -28,6 +51,13 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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/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/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/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/helpers.go b/helpers.go index e0e523d8ad..ec7bd2395b 100644 --- a/helpers.go +++ b/helpers.go @@ -266,12 +266,12 @@ func (app *App) isEtagStale(etag string, noneMatchBytes []byte) bool { // https://github.com/jshttp/fresh/blob/10e0471669dbbfbfd8de65bc6efac2ddd0bfa057/index.js#L110 for i := range noneMatchBytes { switch noneMatchBytes[i] { - case 0x20: //nolint:gomnd // This is a space (" ") + case 0x20: if start == end { start = i + 1 end = i + 1 } - case 0x2c: //nolint:gomnd // This is a comma (",") + case 0x2c: if matchEtag(app.getString(noneMatchBytes[start:end]), etag) { return false } @@ -347,7 +347,7 @@ func getBytesImmutable(s string) []byte { func (app *App) methodInt(s string) int { // For better performance if len(app.configured.RequestMethods) == 0 { - //nolint:gomnd // TODO: Use iota instead + // TODO: Use iota instead switch s { case MethodGet: return 0 diff --git a/internal/dictpool/LICENSE b/internal/dictpool/LICENSE deleted file mode 100644 index 2b440abc7b..0000000000 --- a/internal/dictpool/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2020-present Sergio Andres Virviescas Santana - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/internal/dictpool/dict.go b/internal/dictpool/dict.go deleted file mode 100644 index 39b740d2f3..0000000000 --- a/internal/dictpool/dict.go +++ /dev/null @@ -1,164 +0,0 @@ -package dictpool - -import ( - "sort" - - "github.com/gofiber/fiber/v2/utils" -) - -func (d *Dict) allocKV() *KV { - n := len(d.D) - - if cap(d.D) > n { - d.D = d.D[:n+1] - } else { - d.D = append(d.D, KV{}) - } - - return &d.D[n] -} - -func (d *Dict) append(key string, value interface{}) { - kv := d.allocKV() - kv.Key = key - kv.Value = value -} - -func (d *Dict) indexOf(key string) int { - n := len(d.D) - - if d.BinarySearch { - idx := sort.Search(n, func(i int) bool { - return key <= d.D[i].Key - }) - - if idx < n && d.D[idx].Key == key { - return idx - } - } else { - for i := 0; i < n; i++ { - if d.D[i].Key == key { - return i - } - } - } - - return -1 -} - -// Len is the number of elements in the Dict. -func (d *Dict) Len() int { - return len(d.D) -} - -// Swap swaps the elements with indexes i and j. -func (d *Dict) Swap(i, j int) { - iKey, iValue := d.D[i].Key, d.D[i].Value - jKey, jValue := d.D[j].Key, d.D[j].Value - - d.D[i].Key, d.D[i].Value = jKey, jValue - d.D[j].Key, d.D[j].Value = iKey, iValue -} - -// Less reports whether the element with -// index i should sort before the element with index j. -func (d *Dict) Less(i, j int) bool { - return d.D[i].Key < d.D[j].Key -} - -// Get get data from key. -func (d *Dict) Get(key string) interface{} { - idx := d.indexOf(key) - if idx > -1 { - return d.D[idx].Value - } - - return nil -} - -// GetBytes get data from key. -func (d *Dict) GetBytes(key []byte) interface{} { - return d.Get(utils.UnsafeString(key)) -} - -// Set set new key. -func (d *Dict) Set(key string, value interface{}) { - idx := d.indexOf(key) - if idx > -1 { - kv := &d.D[idx] - kv.Value = value - } else { - d.append(key, value) - - if d.BinarySearch { - sort.Sort(d) - } - } -} - -// SetBytes set new key. -func (d *Dict) SetBytes(key []byte, value interface{}) { - d.Set(utils.UnsafeString(key), value) -} - -// Del delete key. -func (d *Dict) Del(key string) { - idx := d.indexOf(key) - if idx > -1 { - n := len(d.D) - 1 - d.Swap(idx, n) - d.D = d.D[:n] // Remove last position - } -} - -// DelBytes delete key. -func (d *Dict) DelBytes(key []byte) { - d.Del(utils.UnsafeString(key)) -} - -// Has check if key exists. -func (d *Dict) Has(key string) bool { - return d.indexOf(key) > -1 -} - -// HasBytes check if key exists. -func (d *Dict) HasBytes(key []byte) bool { - return d.Has(utils.UnsafeString(key)) -} - -// Reset reset dict. -func (d *Dict) Reset() { - d.D = d.D[:0] -} - -// Map convert to map. -func (d *Dict) Map(dst DictMap) { - for i := range d.D { - kv := &d.D[i] - - sd, ok := kv.Value.(*Dict) - if ok { - subDst := make(DictMap) - sd.Map(subDst) - dst[kv.Key] = subDst - } else { - dst[kv.Key] = kv.Value - } - } -} - -// Parse convert map to Dict. -func (d *Dict) Parse(src DictMap) { - d.Reset() - - for k, v := range src { - sv, ok := v.(map[string]interface{}) - if ok { - subDict := new(Dict) - subDict.Parse(sv) - d.append(k, subDict) - } else { - d.append(k, v) - } - } -} diff --git a/internal/dictpool/pool.go b/internal/dictpool/pool.go deleted file mode 100644 index baebe7e236..0000000000 --- a/internal/dictpool/pool.go +++ /dev/null @@ -1,20 +0,0 @@ -package dictpool - -import "sync" - -var defaultPool = sync.Pool{ - New: func() interface{} { - return new(Dict) - }, -} - -// AcquireDict acquire new dict. -func AcquireDict() *Dict { - return defaultPool.Get().(*Dict) -} - -// ReleaseDict release dict. -func ReleaseDict(d *Dict) { - d.Reset() - defaultPool.Put(d) -} diff --git a/internal/dictpool/types.go b/internal/dictpool/types.go deleted file mode 100644 index bfcd97796c..0000000000 --- a/internal/dictpool/types.go +++ /dev/null @@ -1,25 +0,0 @@ -package dictpool - -//go:generate msgp - -// KV struct so it storages key/value data. -type KV struct { - Key string - Value interface{} -} - -// Dict dictionary as slice with better performance. -type Dict struct { - // D slice of KV for storage the data - D []KV - - // Use binary search to the get an item. - // It's only useful on big heaps. - // - // WARNING: Increase searching performance on big heaps, - // but whe set new items could be slowier due to the sorting. - BinarySearch bool -} - -// DictMap dictionary as map. -type DictMap map[string]interface{} diff --git a/internal/dictpool/types_gen.go b/internal/dictpool/types_gen.go deleted file mode 100644 index e1213d3e10..0000000000 --- a/internal/dictpool/types_gen.go +++ /dev/null @@ -1,509 +0,0 @@ -package dictpool - -// Code generated by github.com/tinylib/msgp DO NOT EDIT. - -import ( - "github.com/gofiber/fiber/v2/internal/msgp" -) - -// DecodeMsg implements msgp.Decodable -func (z *Dict) DecodeMsg(dc *msgp.Reader) (err error) { - var field []byte - _ = field - var zb0001 uint32 - zb0001, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0001 > 0 { - zb0001-- - field, err = dc.ReadMapKeyPtr() - if err != nil { - err = msgp.WrapError(err) - return - } - switch msgp.UnsafeString(field) { - case "D": - var zb0002 uint32 - zb0002, err = dc.ReadArrayHeader() - if err != nil { - err = msgp.WrapError(err, "D") - return - } - if cap(z.D) >= int(zb0002) { - z.D = (z.D)[:zb0002] - } else { - z.D = make([]KV, zb0002) - } - for za0001 := range z.D { - var zb0003 uint32 - zb0003, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err, "D", za0001) - return - } - for zb0003 > 0 { - zb0003-- - field, err = dc.ReadMapKeyPtr() - if err != nil { - err = msgp.WrapError(err, "D", za0001) - return - } - switch msgp.UnsafeString(field) { - case "Key": - z.D[za0001].Key, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "D", za0001, "Key") - return - } - case "Value": - z.D[za0001].Value, err = dc.ReadIntf() - if err != nil { - err = msgp.WrapError(err, "D", za0001, "Value") - return - } - default: - err = dc.Skip() - if err != nil { - err = msgp.WrapError(err, "D", za0001) - return - } - } - } - } - case "BinarySearch": - z.BinarySearch, err = dc.ReadBool() - if err != nil { - err = msgp.WrapError(err, "BinarySearch") - return - } - default: - err = dc.Skip() - if err != nil { - err = msgp.WrapError(err) - return - } - } - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z *Dict) EncodeMsg(en *msgp.Writer) (err error) { - // map header, size 2 - // write "D" - err = en.Append(0x82, 0xa1, 0x44) - if err != nil { - return - } - err = en.WriteArrayHeader(uint32(len(z.D))) - if err != nil { - err = msgp.WrapError(err, "D") - return - } - for za0001 := range z.D { - // map header, size 2 - // write "Key" - err = en.Append(0x82, 0xa3, 0x4b, 0x65, 0x79) - if err != nil { - return - } - err = en.WriteString(z.D[za0001].Key) - if err != nil { - err = msgp.WrapError(err, "D", za0001, "Key") - return - } - // write "Value" - err = en.Append(0xa5, 0x56, 0x61, 0x6c, 0x75, 0x65) - if err != nil { - return - } - err = en.WriteIntf(z.D[za0001].Value) - if err != nil { - err = msgp.WrapError(err, "D", za0001, "Value") - return - } - } - // write "BinarySearch" - err = en.Append(0xac, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68) - if err != nil { - return - } - err = en.WriteBool(z.BinarySearch) - if err != nil { - err = msgp.WrapError(err, "BinarySearch") - return - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z *Dict) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - // map header, size 2 - // string "D" - o = append(o, 0x82, 0xa1, 0x44) - o = msgp.AppendArrayHeader(o, uint32(len(z.D))) - for za0001 := range z.D { - // map header, size 2 - // string "Key" - o = append(o, 0x82, 0xa3, 0x4b, 0x65, 0x79) - o = msgp.AppendString(o, z.D[za0001].Key) - // string "Value" - o = append(o, 0xa5, 0x56, 0x61, 0x6c, 0x75, 0x65) - o, err = msgp.AppendIntf(o, z.D[za0001].Value) - if err != nil { - err = msgp.WrapError(err, "D", za0001, "Value") - return - } - } - // string "BinarySearch" - o = append(o, 0xac, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x53, 0x65, 0x61, 0x72, 0x63, 0x68) - o = msgp.AppendBool(o, z.BinarySearch) - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *Dict) UnmarshalMsg(bts []byte) (o []byte, err error) { - var field []byte - _ = field - var zb0001 uint32 - zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0001 > 0 { - zb0001-- - field, bts, err = msgp.ReadMapKeyZC(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - switch msgp.UnsafeString(field) { - case "D": - var zb0002 uint32 - zb0002, bts, err = msgp.ReadArrayHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "D") - return - } - if cap(z.D) >= int(zb0002) { - z.D = (z.D)[:zb0002] - } else { - z.D = make([]KV, zb0002) - } - for za0001 := range z.D { - var zb0003 uint32 - zb0003, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err, "D", za0001) - return - } - for zb0003 > 0 { - zb0003-- - field, bts, err = msgp.ReadMapKeyZC(bts) - if err != nil { - err = msgp.WrapError(err, "D", za0001) - return - } - switch msgp.UnsafeString(field) { - case "Key": - z.D[za0001].Key, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "D", za0001, "Key") - return - } - case "Value": - z.D[za0001].Value, bts, err = msgp.ReadIntfBytes(bts) - if err != nil { - err = msgp.WrapError(err, "D", za0001, "Value") - return - } - default: - bts, err = msgp.Skip(bts) - if err != nil { - err = msgp.WrapError(err, "D", za0001) - return - } - } - } - } - case "BinarySearch": - z.BinarySearch, bts, err = msgp.ReadBoolBytes(bts) - if err != nil { - err = msgp.WrapError(err, "BinarySearch") - return - } - default: - bts, err = msgp.Skip(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - } - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z *Dict) Msgsize() (s int) { - s = 1 + 2 + msgp.ArrayHeaderSize - for za0001 := range z.D { - s += 1 + 4 + msgp.StringPrefixSize + len(z.D[za0001].Key) + 6 + msgp.GuessSize(z.D[za0001].Value) - } - s += 13 + msgp.BoolSize - return -} - -// DecodeMsg implements msgp.Decodable -func (z *DictMap) DecodeMsg(dc *msgp.Reader) (err error) { - var zb0003 uint32 - zb0003, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err) - return - } - if (*z) == nil { - (*z) = make(DictMap, zb0003) - } else if len((*z)) > 0 { - for key := range *z { - delete((*z), key) - } - } - for zb0003 > 0 { - zb0003-- - var zb0001 string - var zb0002 interface{} - zb0001, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err) - return - } - zb0002, err = dc.ReadIntf() - if err != nil { - err = msgp.WrapError(err, zb0001) - return - } - (*z)[zb0001] = zb0002 - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z DictMap) EncodeMsg(en *msgp.Writer) (err error) { - err = en.WriteMapHeader(uint32(len(z))) - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0004, zb0005 := range z { - err = en.WriteString(zb0004) - if err != nil { - err = msgp.WrapError(err) - return - } - err = en.WriteIntf(zb0005) - if err != nil { - err = msgp.WrapError(err, zb0004) - return - } - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z DictMap) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - o = msgp.AppendMapHeader(o, uint32(len(z))) - for zb0004, zb0005 := range z { - o = msgp.AppendString(o, zb0004) - o, err = msgp.AppendIntf(o, zb0005) - if err != nil { - err = msgp.WrapError(err, zb0004) - return - } - } - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *DictMap) UnmarshalMsg(bts []byte) (o []byte, err error) { - var zb0003 uint32 - zb0003, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - if (*z) == nil { - (*z) = make(DictMap, zb0003) - } else if len((*z)) > 0 { - for key := range *z { - delete((*z), key) - } - } - for zb0003 > 0 { - var zb0001 string - var zb0002 interface{} - zb0003-- - zb0001, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - zb0002, bts, err = msgp.ReadIntfBytes(bts) - if err != nil { - err = msgp.WrapError(err, zb0001) - return - } - (*z)[zb0001] = zb0002 - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z DictMap) Msgsize() (s int) { - s = msgp.MapHeaderSize - if z != nil { - for zb0004, zb0005 := range z { - _ = zb0005 - s += msgp.StringPrefixSize + len(zb0004) + msgp.GuessSize(zb0005) - } - } - return -} - -// DecodeMsg implements msgp.Decodable -func (z *KV) DecodeMsg(dc *msgp.Reader) (err error) { - var field []byte - _ = field - var zb0001 uint32 - zb0001, err = dc.ReadMapHeader() - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0001 > 0 { - zb0001-- - field, err = dc.ReadMapKeyPtr() - if err != nil { - err = msgp.WrapError(err) - return - } - switch msgp.UnsafeString(field) { - case "Key": - z.Key, err = dc.ReadString() - if err != nil { - err = msgp.WrapError(err, "Key") - return - } - case "Value": - z.Value, err = dc.ReadIntf() - if err != nil { - err = msgp.WrapError(err, "Value") - return - } - default: - err = dc.Skip() - if err != nil { - err = msgp.WrapError(err) - return - } - } - } - return -} - -// EncodeMsg implements msgp.Encodable -func (z KV) EncodeMsg(en *msgp.Writer) (err error) { - // map header, size 2 - // write "Key" - err = en.Append(0x82, 0xa3, 0x4b, 0x65, 0x79) - if err != nil { - return - } - err = en.WriteString(z.Key) - if err != nil { - err = msgp.WrapError(err, "Key") - return - } - // write "Value" - err = en.Append(0xa5, 0x56, 0x61, 0x6c, 0x75, 0x65) - if err != nil { - return - } - err = en.WriteIntf(z.Value) - if err != nil { - err = msgp.WrapError(err, "Value") - return - } - return -} - -// MarshalMsg implements msgp.Marshaler -func (z KV) MarshalMsg(b []byte) (o []byte, err error) { - o = msgp.Require(b, z.Msgsize()) - // map header, size 2 - // string "Key" - o = append(o, 0x82, 0xa3, 0x4b, 0x65, 0x79) - o = msgp.AppendString(o, z.Key) - // string "Value" - o = append(o, 0xa5, 0x56, 0x61, 0x6c, 0x75, 0x65) - o, err = msgp.AppendIntf(o, z.Value) - if err != nil { - err = msgp.WrapError(err, "Value") - return - } - return -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (z *KV) UnmarshalMsg(bts []byte) (o []byte, err error) { - var field []byte - _ = field - var zb0001 uint32 - zb0001, bts, err = msgp.ReadMapHeaderBytes(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - for zb0001 > 0 { - zb0001-- - field, bts, err = msgp.ReadMapKeyZC(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - switch msgp.UnsafeString(field) { - case "Key": - z.Key, bts, err = msgp.ReadStringBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Key") - return - } - case "Value": - z.Value, bts, err = msgp.ReadIntfBytes(bts) - if err != nil { - err = msgp.WrapError(err, "Value") - return - } - default: - bts, err = msgp.Skip(bts) - if err != nil { - err = msgp.WrapError(err) - return - } - } - } - o = bts - return -} - -// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message -func (z KV) Msgsize() (s int) { - s = 1 + 4 + msgp.StringPrefixSize + len(z.Key) + 6 + msgp.GuessSize(z.Value) - return -} diff --git a/internal/fwd/LICENSE.md b/internal/fwd/LICENSE.md deleted file mode 100644 index 1ac6a81f6a..0000000000 --- a/internal/fwd/LICENSE.md +++ /dev/null @@ -1,7 +0,0 @@ -Copyright (c) 2014-2015, Philip Hofer - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/internal/fwd/reader.go b/internal/fwd/reader.go deleted file mode 100644 index c80a617bb2..0000000000 --- a/internal/fwd/reader.go +++ /dev/null @@ -1,395 +0,0 @@ -// The `fwd` package provides a buffered reader -// and writer. Each has methods that help improve -// the encoding/decoding performance of some binary -// protocols. -// -// The `fwd.Writer` and `fwd.Reader` type provide similar -// functionality to their counterparts in `bufio`, plus -// a few extra utility methods that simplify read-ahead -// and write-ahead. I wrote this package to improve serialization -// performance for http://github.com/tinylib/msgp, -// where it provided about a 2x speedup over `bufio` for certain -// workloads. However, care must be taken to understand the semantics of the -// extra methods provided by this package, as they allow -// the user to access and manipulate the buffer memory -// directly. -// -// The extra methods for `fwd.Reader` are `Peek`, `Skip` -// and `Next`. `(*fwd.Reader).Peek`, unlike `(*bufio.Reader).Peek`, -// will re-allocate the read buffer in order to accommodate arbitrarily -// large read-ahead. `(*fwd.Reader).Skip` skips the next `n` bytes -// in the stream, and uses the `io.Seeker` interface if the underlying -// stream implements it. `(*fwd.Reader).Next` returns a slice pointing -// to the next `n` bytes in the read buffer (like `Peek`), but also -// increments the read position. This allows users to process streams -// in arbitrary block sizes without having to manage appropriately-sized -// slices. Additionally, obviating the need to copy the data from the -// buffer to another location in memory can improve performance dramatically -// in CPU-bound applications. -// -// `fwd.Writer` only has one extra method, which is `(*fwd.Writer).Next`, which -// returns a slice pointing to the next `n` bytes of the writer, and increments -// the write position by the length of the returned slice. This allows users -// to write directly to the end of the buffer. -package fwd - -import "io" - -const ( - // DefaultReaderSize is the default size of the read buffer - DefaultReaderSize = 2048 - - // minimum read buffer; straight from bufio - minReaderSize = 16 -) - -// NewReader returns a new *Reader that reads from 'r' -func NewReader(r io.Reader) *Reader { - return NewReaderSize(r, DefaultReaderSize) -} - -// NewReaderSize returns a new *Reader that -// reads from 'r' and has a buffer size 'n'. -func NewReaderSize(r io.Reader, n int) *Reader { - buf := make([]byte, 0, max(n, minReaderSize)) - return NewReaderBuf(r, buf) -} - -// NewReaderBuf returns a new *Reader that -// reads from 'r' and uses 'buf' as a buffer. -// 'buf' is not used when has smaller capacity than 16, -// custom buffer is allocated instead. -func NewReaderBuf(r io.Reader, buf []byte) *Reader { - if cap(buf) < minReaderSize { - buf = make([]byte, 0, minReaderSize) - } - buf = buf[:0] - rd := &Reader{ - r: r, - data: buf, - } - if s, ok := r.(io.Seeker); ok { - rd.rs = s - } - return rd -} - -// Reader is a buffered look-ahead reader -type Reader struct { - r io.Reader // underlying reader - - // data[n:len(data)] is buffered data; data[len(data):cap(data)] is free buffer space - data []byte // data - n int // read offset - state error // last read error - - // if the reader past to NewReader was - // also an io.Seeker, this is non-nil - rs io.Seeker -} - -// Reset resets the underlying reader -// and the read buffer. -func (r *Reader) Reset(rd io.Reader) { - r.r = rd - r.data = r.data[0:0] - r.n = 0 - r.state = nil - if s, ok := rd.(io.Seeker); ok { - r.rs = s - } else { - r.rs = nil - } -} - -// more() does one read on the underlying reader -func (r *Reader) more() { - // move data backwards so that - // the read offset is 0; this way - // we can supply the maximum number of - // bytes to the reader - if r.n != 0 { - if r.n < len(r.data) { - r.data = r.data[:copy(r.data[0:], r.data[r.n:])] - } else { - r.data = r.data[:0] - } - r.n = 0 - } - var a int - a, r.state = r.r.Read(r.data[len(r.data):cap(r.data)]) - if a == 0 && r.state == nil { - r.state = io.ErrNoProgress - return - } else if a > 0 && r.state == io.EOF { - // discard the io.EOF if we read more than 0 bytes. - // the next call to Read should return io.EOF again. - r.state = nil - } - r.data = r.data[:len(r.data)+a] -} - -// pop error -func (r *Reader) err() (e error) { - e, r.state = r.state, nil - return -} - -// pop error; EOF -> io.ErrUnexpectedEOF -func (r *Reader) noEOF() (e error) { - e, r.state = r.state, nil - if e == io.EOF { - e = io.ErrUnexpectedEOF - } - return -} - -// buffered bytes -func (r *Reader) buffered() int { return len(r.data) - r.n } - -// Buffered returns the number of bytes currently in the buffer -func (r *Reader) Buffered() int { return len(r.data) - r.n } - -// BufferSize returns the total size of the buffer -func (r *Reader) BufferSize() int { return cap(r.data) } - -// Peek returns the next 'n' buffered bytes, -// reading from the underlying reader if necessary. -// It will only return a slice shorter than 'n' bytes -// if it also returns an error. Peek does not advance -// the reader. EOF errors are *not* returned as -// io.ErrUnexpectedEOF. -func (r *Reader) Peek(n int) ([]byte, error) { - // in the degenerate case, - // we may need to realloc - // (the caller asked for more - // bytes than the size of the buffer) - if cap(r.data) < n { - old := r.data[r.n:] - r.data = make([]byte, n+r.buffered()) - r.data = r.data[:copy(r.data, old)] - r.n = 0 - } - - // keep filling until - // we hit an error or - // read enough bytes - for r.buffered() < n && r.state == nil { - r.more() - } - - // we must have hit an error - if r.buffered() < n { - return r.data[r.n:], r.err() - } - - return r.data[r.n : r.n+n], nil -} - -// Skip moves the reader forward 'n' bytes. -// Returns the number of bytes skipped and any -// errors encountered. It is analogous to Seek(n, 1). -// If the underlying reader implements io.Seeker, then -// that method will be used to skip forward. -// -// If the reader encounters -// an EOF before skipping 'n' bytes, it -// returns io.ErrUnexpectedEOF. If the -// underlying reader implements io.Seeker, then -// those rules apply instead. (Many implementations -// will not return `io.EOF` until the next call -// to Read.) -func (r *Reader) Skip(n int) (int, error) { - - // fast path - if r.buffered() >= n { - r.n += n - return n, nil - } - - // use seeker implementation - // if we can - if r.rs != nil { - return r.skipSeek(n) - } - - // loop on filling - // and then erasing - o := n - for r.buffered() < n && r.state == nil { - r.more() - // we can skip forward - // up to r.buffered() bytes - step := min(r.buffered(), n) - r.n += step - n -= step - } - // at this point, n should be - // 0 if everything went smoothly - return o - n, r.noEOF() -} - -// Next returns the next 'n' bytes in the stream. -// Unlike Peek, Next advances the reader position. -// The returned bytes point to the same -// data as the buffer, so the slice is -// only valid until the next reader method call. -// An EOF is considered an unexpected error. -// If an the returned slice is less than the -// length asked for, an error will be returned, -// and the reader position will not be incremented. -func (r *Reader) Next(n int) ([]byte, error) { - - // in case the buffer is too small - if cap(r.data) < n { - old := r.data[r.n:] - r.data = make([]byte, n+r.buffered()) - r.data = r.data[:copy(r.data, old)] - r.n = 0 - } - - // fill at least 'n' bytes - for r.buffered() < n && r.state == nil { - r.more() - } - - if r.buffered() < n { - return r.data[r.n:], r.noEOF() - } - out := r.data[r.n : r.n+n] - r.n += n - return out, nil -} - -// skipSeek uses the io.Seeker to seek forward. -// only call this function when n > r.buffered() -func (r *Reader) skipSeek(n int) (int, error) { - o := r.buffered() - // first, clear buffer - n -= o - r.n = 0 - r.data = r.data[:0] - - // then seek forward remaning bytes - i, err := r.rs.Seek(int64(n), 1) - return int(i) + o, err -} - -// Read implements `io.Reader` -func (r *Reader) Read(b []byte) (int, error) { - // if we have data in the buffer, just - // return that. - if r.buffered() != 0 { - x := copy(b, r.data[r.n:]) - r.n += x - return x, nil - } - var n int - // we have no buffered data; determine - // whether or not to buffer or call - // the underlying reader directly - if len(b) >= cap(r.data) { - n, r.state = r.r.Read(b) - } else { - r.more() - n = copy(b, r.data) - r.n = n - } - if n == 0 { - return 0, r.err() - } - return n, nil -} - -// ReadFull attempts to read len(b) bytes into -// 'b'. It returns the number of bytes read into -// 'b', and an error if it does not return len(b). -// EOF is considered an unexpected error. -func (r *Reader) ReadFull(b []byte) (int, error) { - var n int // read into b - var nn int // scratch - l := len(b) - // either read buffered data, - // or read directly for the underlying - // buffer, or fetch more buffered data. - for n < l && r.state == nil { - if r.buffered() != 0 { - nn = copy(b[n:], r.data[r.n:]) - n += nn - r.n += nn - } else if l-n > cap(r.data) { - nn, r.state = r.r.Read(b[n:]) - n += nn - } else { - r.more() - } - } - if n < l { - return n, r.noEOF() - } - return n, nil -} - -// ReadByte implements `io.ByteReader` -func (r *Reader) ReadByte() (byte, error) { - for r.buffered() < 1 && r.state == nil { - r.more() - } - if r.buffered() < 1 { - return 0, r.err() - } - b := r.data[r.n] - r.n++ - return b, nil -} - -// WriteTo implements `io.WriterTo` -func (r *Reader) WriteTo(w io.Writer) (int64, error) { - var ( - i int64 - ii int - err error - ) - // first, clear buffer - if r.buffered() > 0 { - ii, err = w.Write(r.data[r.n:]) - i += int64(ii) - if err != nil { - return i, err - } - r.data = r.data[0:0] - r.n = 0 - } - for r.state == nil { - // here we just do - // 1:1 reads and writes - r.more() - if r.buffered() > 0 { - ii, err = w.Write(r.data) - i += int64(ii) - if err != nil { - return i, err - } - r.data = r.data[0:0] - r.n = 0 - } - } - if r.state != io.EOF { - return i, r.err() - } - return i, nil -} - -func min(a int, b int) int { - if a < b { - return a - } - return b -} - -func max(a int, b int) int { - if a < b { - return b - } - return a -} diff --git a/internal/fwd/writer.go b/internal/fwd/writer.go deleted file mode 100644 index 4d6ea15b33..0000000000 --- a/internal/fwd/writer.go +++ /dev/null @@ -1,236 +0,0 @@ -package fwd - -import "io" - -const ( - // DefaultWriterSize is the - // default write buffer size. - DefaultWriterSize = 2048 - - minWriterSize = minReaderSize -) - -// Writer is a buffered writer -type Writer struct { - w io.Writer // writer - buf []byte // 0:len(buf) is bufered data -} - -// NewWriter returns a new writer -// that writes to 'w' and has a buffer -// that is `DefaultWriterSize` bytes. -func NewWriter(w io.Writer) *Writer { - if wr, ok := w.(*Writer); ok { - return wr - } - return &Writer{ - w: w, - buf: make([]byte, 0, DefaultWriterSize), - } -} - -// NewWriterSize returns a new writer that -// writes to 'w' and has a buffer size 'n'. -func NewWriterSize(w io.Writer, n int) *Writer { - if wr, ok := w.(*Writer); ok && cap(wr.buf) >= n { - return wr - } - buf := make([]byte, 0, max(n, minWriterSize)) - return NewWriterBuf(w, buf) -} - -// NewWriterBuf returns a new writer -// that writes to 'w' and has 'buf' as a buffer. -// 'buf' is not used when has smaller capacity than 18, -// custom buffer is allocated instead. -func NewWriterBuf(w io.Writer, buf []byte) *Writer { - if cap(buf) < minWriterSize { - buf = make([]byte, 0, minWriterSize) - } - buf = buf[:0] - return &Writer{ - w: w, - buf: buf, - } -} - -// Buffered returns the number of buffered bytes -// in the reader. -func (w *Writer) Buffered() int { return len(w.buf) } - -// BufferSize returns the maximum size of the buffer. -func (w *Writer) BufferSize() int { return cap(w.buf) } - -// Flush flushes any buffered bytes -// to the underlying writer. -func (w *Writer) Flush() error { - l := len(w.buf) - if l > 0 { - n, err := w.w.Write(w.buf) - - // if we didn't write the whole - // thing, copy the unwritten - // bytes to the beginnning of the - // buffer. - if n < l && n > 0 { - w.pushback(n) - if err == nil { - err = io.ErrShortWrite - } - } - if err != nil { - return err - } - w.buf = w.buf[:0] - return nil - } - return nil -} - -// Write implements `io.Writer` -func (w *Writer) Write(p []byte) (int, error) { - c, l, ln := cap(w.buf), len(w.buf), len(p) - avail := c - l - - // requires flush - if avail < ln { - if err := w.Flush(); err != nil { - return 0, err - } - l = len(w.buf) - } - // too big to fit in buffer; - // write directly to w.w - if c < ln { - return w.w.Write(p) - } - - // grow buf slice; copy; return - w.buf = w.buf[:l+ln] - return copy(w.buf[l:], p), nil -} - -// WriteString is analogous to Write, but it takes a string. -func (w *Writer) WriteString(s string) (int, error) { - c, l, ln := cap(w.buf), len(w.buf), len(s) - avail := c - l - - // requires flush - if avail < ln { - if err := w.Flush(); err != nil { - return 0, err - } - l = len(w.buf) - } - // too big to fit in buffer; - // write directly to w.w - // - // yes, this is unsafe. *but* - // io.Writer is not allowed - // to mutate its input or - // maintain a reference to it, - // per the spec in package io. - // - // plus, if the string is really - // too big to fit in the buffer, then - // creating a copy to write it is - // expensive (and, strictly speaking, - // unnecessary) - if c < ln { - return w.w.Write(unsafestr(s)) - } - - // grow buf slice; copy; return - w.buf = w.buf[:l+ln] - return copy(w.buf[l:], s), nil -} - -// WriteByte implements `io.ByteWriter` -func (w *Writer) WriteByte(b byte) error { - if len(w.buf) == cap(w.buf) { - if err := w.Flush(); err != nil { - return err - } - } - w.buf = append(w.buf, b) - return nil -} - -// Next returns the next 'n' free bytes -// in the write buffer, flushing the writer -// as necessary. Next will return `io.ErrShortBuffer` -// if 'n' is greater than the size of the write buffer. -// Calls to 'next' increment the write position by -// the size of the returned buffer. -func (w *Writer) Next(n int) ([]byte, error) { - c, l := cap(w.buf), len(w.buf) - if n > c { - return nil, io.ErrShortBuffer - } - avail := c - l - if avail < n { - if err := w.Flush(); err != nil { - return nil, err - } - l = len(w.buf) - } - w.buf = w.buf[:l+n] - return w.buf[l:], nil -} - -// take the bytes from w.buf[n:len(w.buf)] -// and put them at the beginning of w.buf, -// and resize to the length of the copied segment. -func (w *Writer) pushback(n int) { - w.buf = w.buf[:copy(w.buf, w.buf[n:])] -} - -// ReadFrom implements `io.ReaderFrom` -func (w *Writer) ReadFrom(r io.Reader) (int64, error) { - // anticipatory flush - if err := w.Flush(); err != nil { - return 0, err - } - - w.buf = w.buf[0:cap(w.buf)] // expand buffer - - var nn int64 // written - var err error // error - var x int // read - - // 1:1 reads and writes - for err == nil { - x, err = r.Read(w.buf) - if x > 0 { - n, werr := w.w.Write(w.buf[:x]) - nn += int64(n) - - if err != nil { - if n < x && n > 0 { - w.pushback(n - x) - } - return nn, werr - } - if n < x { - w.pushback(n - x) - return nn, io.ErrShortWrite - } - } else if err == nil { - err = io.ErrNoProgress - break - } - } - if err != io.EOF { - return nn, err - } - - // we only clear here - // because we are sure - // the writes have - // succeeded. otherwise, - // we retain the data in case - // future writes succeed. - w.buf = w.buf[0:0] - - return nn, nil -} diff --git a/internal/fwd/writer_appengine.go b/internal/fwd/writer_appengine.go deleted file mode 100644 index a978e3b6a0..0000000000 --- a/internal/fwd/writer_appengine.go +++ /dev/null @@ -1,6 +0,0 @@ -//go:build appengine -// +build appengine - -package fwd - -func unsafestr(s string) []byte { return []byte(s) } diff --git a/internal/fwd/writer_unsafe.go b/internal/fwd/writer_unsafe.go deleted file mode 100644 index b40fcda100..0000000000 --- a/internal/fwd/writer_unsafe.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build !appengine -// +build !appengine - -package fwd - -import ( - "reflect" - "unsafe" -) - -// unsafe cast string as []byte -func unsafestr(b string) []byte { - l := len(b) - return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ - Len: l, - Cap: l, - Data: (*reflect.StringHeader)(unsafe.Pointer(&b)).Data, - })) -} diff --git a/internal/gopsutil/common/common.go b/internal/gopsutil/common/common.go index ed4a3bfcb3..a02fce4b1a 100644 --- a/internal/gopsutil/common/common.go +++ b/internal/gopsutil/common/common.go @@ -366,7 +366,7 @@ func HostDev(combineWith ...string) string { // getSysctrlEnv sets LC_ALL=C in a list of env vars for use when running // sysctl commands (see DoSysctrl). func getSysctrlEnv(env []string) []string { - var foundLC bool + foundLC := false for i, line := range env { if strings.HasPrefix(line, "LC_ALL") { env[i] = "LC_ALL=C" diff --git a/internal/gopsutil/mem/mem.go b/internal/gopsutil/mem/mem.go index e2ee01e5c6..b039c4ce4e 100644 --- a/internal/gopsutil/mem/mem.go +++ b/internal/gopsutil/mem/mem.go @@ -6,7 +6,8 @@ import ( "github.com/gofiber/fiber/v2/internal/gopsutil/common" ) -var invoke common.Invoker = common.Invoke{} //nolint:unused // We use this only for some OS'es +//lint:ignore U1000 we need this elsewhere +var invoke common.Invoker = common.Invoke{} //nolint:all // Memory usage statistics. Total, Available and Used contain numbers of bytes // for human consumption. diff --git a/internal/gopsutil/mem/mem_freebsd.go b/internal/gopsutil/mem/mem_freebsd.go index d30e7bd315..0682edb7cf 100644 --- a/internal/gopsutil/mem/mem_freebsd.go +++ b/internal/gopsutil/mem/mem_freebsd.go @@ -86,6 +86,7 @@ func SwapMemory() (*SwapMemoryStat, error) { } // Constants from vm/vm_param.h +// nolint: golint const ( XSWDEV_VERSION11 = 1 XSWDEV_VERSION = 2 diff --git a/internal/gopsutil/mem/mem_linux.go b/internal/gopsutil/mem/mem_linux.go index 3e7e93b5a8..a0fc7fd44c 100644 --- a/internal/gopsutil/mem/mem_linux.go +++ b/internal/gopsutil/mem/mem_linux.go @@ -57,12 +57,10 @@ func fillFromMeminfoWithContext(ctx context.Context) (*VirtualMemoryStat, *Virtu lines, _ := common.ReadLines(filename) // flag if MemAvailable is in /proc/meminfo (kernel 3.14+) - var ( - memavail bool - activeFile bool // "Active(file)" not available: 2.6.28 / Dec 2008 - inactiveFile bool // "Inactive(file)" not available: 2.6.28 / Dec 2008 - sReclaimable bool // "SReclaimable:" not available: 2.6.19 / Nov 2006 - ) + memavail := false + activeFile := false // "Active(file)" not available: 2.6.28 / Dec 2008 + inactiveFile := false // "Inactive(file)" not available: 2.6.28 / Dec 2008 + sReclaimable := false // "SReclaimable:" not available: 2.6.19 / Nov 2006 ret := &VirtualMemoryStat{} retEx := &VirtualMemoryExStat{} diff --git a/internal/msgp/LICENSE b/internal/msgp/LICENSE deleted file mode 100644 index 14d60424e8..0000000000 --- a/internal/msgp/LICENSE +++ /dev/null @@ -1,8 +0,0 @@ -Copyright (c) 2014 Philip Hofer -Portions Copyright (c) 2009 The Go Authors (license at http://golang.org) where indicated - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/internal/msgp/advise_linux.go b/internal/msgp/advise_linux.go deleted file mode 100644 index 76ea337e49..0000000000 --- a/internal/msgp/advise_linux.go +++ /dev/null @@ -1,25 +0,0 @@ -//go:build linux && !appengine -// +build linux,!appengine - -package msgp - -import ( - "os" - "syscall" -) - -func adviseRead(mem []byte) { - syscall.Madvise(mem, syscall.MADV_SEQUENTIAL|syscall.MADV_WILLNEED) -} - -func adviseWrite(mem []byte) { - syscall.Madvise(mem, syscall.MADV_SEQUENTIAL) -} - -func fallocate(f *os.File, sz int64) error { - err := syscall.Fallocate(int(f.Fd()), 0, 0, sz) - if err == syscall.ENOTSUP { - return f.Truncate(sz) - } - return err -} diff --git a/internal/msgp/advise_other.go b/internal/msgp/advise_other.go deleted file mode 100644 index 32f56e0404..0000000000 --- a/internal/msgp/advise_other.go +++ /dev/null @@ -1,18 +0,0 @@ -//go:build !linux || appengine -// +build !linux appengine - -package msgp - -import ( - "os" -) - -// TODO: darwin, BSD support - -func adviseRead(mem []byte) {} - -func adviseWrite(mem []byte) {} - -func fallocate(f *os.File, sz int64) error { - return f.Truncate(sz) -} diff --git a/internal/msgp/circular.go b/internal/msgp/circular.go deleted file mode 100644 index a0434c7ea1..0000000000 --- a/internal/msgp/circular.go +++ /dev/null @@ -1,39 +0,0 @@ -package msgp - -type timer interface { - StartTimer() - StopTimer() -} - -// EndlessReader is an io.Reader -// that loops over the same data -// endlessly. It is used for benchmarking. -type EndlessReader struct { - tb timer - data []byte - offset int -} - -// NewEndlessReader returns a new endless reader -func NewEndlessReader(b []byte, tb timer) *EndlessReader { - return &EndlessReader{tb: tb, data: b, offset: 0} -} - -// Read implements io.Reader. In practice, it -// always returns (len(p), nil), although it -// fills the supplied slice while the benchmark -// timer is stopped. -func (c *EndlessReader) Read(p []byte) (int, error) { - c.tb.StopTimer() - var n int - l := len(p) - m := len(c.data) - for n < l { - nn := copy(p[n:], c.data[c.offset:]) - n += nn - c.offset += nn - c.offset %= m - } - c.tb.StartTimer() - return n, nil -} diff --git a/internal/msgp/defs.go b/internal/msgp/defs.go deleted file mode 100644 index 4597ab1a28..0000000000 --- a/internal/msgp/defs.go +++ /dev/null @@ -1,145 +0,0 @@ -// This package is the support library for the msgp code generator (http://github.com/tinylib/msgp). -// -// This package defines the utilites used by the msgp code generator for encoding and decoding MessagePack -// from []byte and io.Reader/io.Writer types. Much of this package is devoted to helping the msgp code -// generator implement the Marshaler/Unmarshaler and Encodable/Decodable interfaces. -// -// This package defines four "families" of functions: -// - AppendXxxx() appends an object to a []byte in MessagePack encoding. -// - ReadXxxxBytes() reads an object from a []byte and returns the remaining bytes. -// - (*Writer).WriteXxxx() writes an object to the buffered *Writer type. -// - (*Reader).ReadXxxx() reads an object from a buffered *Reader type. -// -// Once a type has satisfied the `Encodable` and `Decodable` interfaces, -// it can be written and read from arbitrary `io.Writer`s and `io.Reader`s using -// -// msgp.Encode(io.Writer, msgp.Encodable) -// -// and -// -// msgp.Decode(io.Reader, msgp.Decodable) -// -// There are also methods for converting MessagePack to JSON without -// an explicit de-serialization step. -// -// For additional tips, tricks, and gotchas, please visit -// the wiki at http://github.com/tinylib/msgp -package msgp - -const last4 = 0x0f -const first4 = 0xf0 -const last5 = 0x1f -const first3 = 0xe0 -const last7 = 0x7f - -func isfixint(b byte) bool { - return b>>7 == 0 -} - -func isnfixint(b byte) bool { - return b&first3 == mnfixint -} - -func isfixmap(b byte) bool { - return b&first4 == mfixmap -} - -func isfixarray(b byte) bool { - return b&first4 == mfixarray -} - -func isfixstr(b byte) bool { - return b&first3 == mfixstr -} - -func wfixint(u uint8) byte { - return u & last7 -} - -func rfixint(b byte) uint8 { - return b -} - -func wnfixint(i int8) byte { - return byte(i) | mnfixint -} - -func rnfixint(b byte) int8 { - return int8(b) -} - -func rfixmap(b byte) uint8 { - return b & last4 -} - -func wfixmap(u uint8) byte { - return mfixmap | (u & last4) -} - -func rfixstr(b byte) uint8 { - return b & last5 -} - -func wfixstr(u uint8) byte { - return (u & last5) | mfixstr -} - -func rfixarray(b byte) uint8 { - return (b & last4) -} - -func wfixarray(u uint8) byte { - return (u & last4) | mfixarray -} - -// These are all the byte -// prefixes defined by the -// msgpack standard -const ( - // 0XXXXXXX - mfixint uint8 = 0x00 - - // 111XXXXX - mnfixint uint8 = 0xe0 - - // 1000XXXX - mfixmap uint8 = 0x80 - - // 1001XXXX - mfixarray uint8 = 0x90 - - // 101XXXXX - mfixstr uint8 = 0xa0 - - mnil uint8 = 0xc0 - mfalse uint8 = 0xc2 - mtrue uint8 = 0xc3 - mbin8 uint8 = 0xc4 - mbin16 uint8 = 0xc5 - mbin32 uint8 = 0xc6 - mext8 uint8 = 0xc7 - mext16 uint8 = 0xc8 - mext32 uint8 = 0xc9 - mfloat32 uint8 = 0xca - mfloat64 uint8 = 0xcb - muint8 uint8 = 0xcc - muint16 uint8 = 0xcd - muint32 uint8 = 0xce - muint64 uint8 = 0xcf - mint8 uint8 = 0xd0 - mint16 uint8 = 0xd1 - mint32 uint8 = 0xd2 - mint64 uint8 = 0xd3 - mfixext1 uint8 = 0xd4 - mfixext2 uint8 = 0xd5 - mfixext4 uint8 = 0xd6 - mfixext8 uint8 = 0xd7 - mfixext16 uint8 = 0xd8 - mstr8 uint8 = 0xd9 - mstr16 uint8 = 0xda - mstr32 uint8 = 0xdb - marray16 uint8 = 0xdc - marray32 uint8 = 0xdd - mmap16 uint8 = 0xde - mmap32 uint8 = 0xdf -) diff --git a/internal/msgp/edit.go b/internal/msgp/edit.go deleted file mode 100644 index b473a6f668..0000000000 --- a/internal/msgp/edit.go +++ /dev/null @@ -1,242 +0,0 @@ -package msgp - -import ( - "math" -) - -// Locate returns a []byte pointing to the field -// in a messagepack map with the provided key. (The returned []byte -// points to a sub-slice of 'raw'; Locate does no allocations.) If the -// key doesn't exist in the map, a zero-length []byte will be returned. -func Locate(key string, raw []byte) []byte { - s, n := locate(raw, key) - return raw[s:n] -} - -// Replace takes a key ("key") in a messagepack map ("raw") -// and replaces its value with the one provided and returns -// the new []byte. The returned []byte may point to the same -// memory as "raw". Replace makes no effort to evaluate the validity -// of the contents of 'val'. It may use up to the full capacity of 'raw.' -// Replace returns 'nil' if the field doesn't exist or if the object in 'raw' -// is not a map. -func Replace(key string, raw []byte, val []byte) []byte { - start, end := locate(raw, key) - if start == end { - return nil - } - return replace(raw, start, end, val, true) -} - -// CopyReplace works similarly to Replace except that the returned -// byte slice does not point to the same memory as 'raw'. CopyReplace -// returns 'nil' if the field doesn't exist or 'raw' isn't a map. -func CopyReplace(key string, raw []byte, val []byte) []byte { - start, end := locate(raw, key) - if start == end { - return nil - } - return replace(raw, start, end, val, false) -} - -// Remove removes a key-value pair from 'raw'. It returns -// 'raw' unchanged if the key didn't exist. -func Remove(key string, raw []byte) []byte { - start, end := locateKV(raw, key) - if start == end { - return raw - } - raw = raw[:start+copy(raw[start:], raw[end:])] - return resizeMap(raw, -1) -} - -// HasKey returns whether the map in 'raw' has -// a field with key 'key' -func HasKey(key string, raw []byte) bool { - sz, bts, err := ReadMapHeaderBytes(raw) - if err != nil { - return false - } - var field []byte - for i := uint32(0); i < sz; i++ { - field, bts, err = ReadStringZC(bts) - if err != nil { - return false - } - if UnsafeString(field) == key { - return true - } - } - return false -} - -func replace(raw []byte, start int, end int, val []byte, inplace bool) []byte { - ll := end - start // length of segment to replace - lv := len(val) - - if inplace { - extra := lv - ll - - // fastest case: we're doing - // a 1:1 replacement - if extra == 0 { - copy(raw[start:], val) - return raw - - } else if extra < 0 { - // 'val' smaller than replaced value - // copy in place and shift back - - x := copy(raw[start:], val) - y := copy(raw[start+x:], raw[end:]) - return raw[:start+x+y] - - } else if extra < cap(raw)-len(raw) { - // 'val' less than (cap-len) extra bytes - // copy in place and shift forward - raw = raw[0 : len(raw)+extra] - // shift end forward - copy(raw[end+extra:], raw[end:]) - copy(raw[start:], val) - return raw - } - } - - // we have to allocate new space - out := make([]byte, len(raw)+len(val)-ll) - x := copy(out, raw[:start]) - y := copy(out[x:], val) - copy(out[x+y:], raw[end:]) - return out -} - -// locate does a naive O(n) search for the map key; returns start, end -// (returns 0,0 on error) -func locate(raw []byte, key string) (start int, end int) { - var ( - sz uint32 - bts []byte - field []byte - err error - ) - sz, bts, err = ReadMapHeaderBytes(raw) - if err != nil { - return - } - - // loop and locate field - for i := uint32(0); i < sz; i++ { - field, bts, err = ReadStringZC(bts) - if err != nil { - return 0, 0 - } - if UnsafeString(field) == key { - // start location - l := len(raw) - start = l - len(bts) - bts, err = Skip(bts) - if err != nil { - return 0, 0 - } - end = l - len(bts) - return - } - bts, err = Skip(bts) - if err != nil { - return 0, 0 - } - } - return 0, 0 -} - -// locate key AND value -func locateKV(raw []byte, key string) (start int, end int) { - var ( - sz uint32 - bts []byte - field []byte - err error - ) - sz, bts, err = ReadMapHeaderBytes(raw) - if err != nil { - return 0, 0 - } - - for i := uint32(0); i < sz; i++ { - tmp := len(bts) - field, bts, err = ReadStringZC(bts) - if err != nil { - return 0, 0 - } - if UnsafeString(field) == key { - start = len(raw) - tmp - bts, err = Skip(bts) - if err != nil { - return 0, 0 - } - end = len(raw) - len(bts) - return - } - bts, err = Skip(bts) - if err != nil { - return 0, 0 - } - } - return 0, 0 -} - -// delta is delta on map size -func resizeMap(raw []byte, delta int64) []byte { - var sz int64 - switch raw[0] { - case mmap16: - sz = int64(big.Uint16(raw[1:])) - if sz+delta <= math.MaxUint16 { - big.PutUint16(raw[1:], uint16(sz+delta)) - return raw - } - if cap(raw)-len(raw) >= 2 { - raw = raw[0 : len(raw)+2] - copy(raw[5:], raw[3:]) - raw[0] = mmap32 - big.PutUint32(raw[1:], uint32(sz+delta)) - return raw - } - n := make([]byte, 0, len(raw)+5) - n = AppendMapHeader(n, uint32(sz+delta)) - return append(n, raw[3:]...) - - case mmap32: - sz = int64(big.Uint32(raw[1:])) - big.PutUint32(raw[1:], uint32(sz+delta)) - return raw - - default: - sz = int64(rfixmap(raw[0])) - if sz+delta < 16 { - raw[0] = wfixmap(uint8(sz + delta)) - return raw - } else if sz+delta <= math.MaxUint16 { - if cap(raw)-len(raw) >= 2 { - raw = raw[0 : len(raw)+2] - copy(raw[3:], raw[1:]) - raw[0] = mmap16 - big.PutUint16(raw[1:], uint16(sz+delta)) - return raw - } - n := make([]byte, 0, len(raw)+5) - n = AppendMapHeader(n, uint32(sz+delta)) - return append(n, raw[1:]...) - } - if cap(raw)-len(raw) >= 4 { - raw = raw[0 : len(raw)+4] - copy(raw[5:], raw[1:]) - raw[0] = mmap32 - big.PutUint32(raw[1:], uint32(sz+delta)) - return raw - } - n := make([]byte, 0, len(raw)+5) - n = AppendMapHeader(n, uint32(sz+delta)) - return append(n, raw[1:]...) - } -} diff --git a/internal/msgp/elsize.go b/internal/msgp/elsize.go deleted file mode 100644 index 601d388636..0000000000 --- a/internal/msgp/elsize.go +++ /dev/null @@ -1,98 +0,0 @@ -package msgp - -// size of every object on the wire, -// plus type information. gives us -// constant-time type information -// for traversing composite objects. -var sizes = [256]bytespec{ - mnil: {size: 1, extra: constsize, typ: NilType}, - mfalse: {size: 1, extra: constsize, typ: BoolType}, - mtrue: {size: 1, extra: constsize, typ: BoolType}, - mbin8: {size: 2, extra: extra8, typ: BinType}, - mbin16: {size: 3, extra: extra16, typ: BinType}, - mbin32: {size: 5, extra: extra32, typ: BinType}, - mext8: {size: 3, extra: extra8, typ: ExtensionType}, - mext16: {size: 4, extra: extra16, typ: ExtensionType}, - mext32: {size: 6, extra: extra32, typ: ExtensionType}, - mfloat32: {size: 5, extra: constsize, typ: Float32Type}, - mfloat64: {size: 9, extra: constsize, typ: Float64Type}, - muint8: {size: 2, extra: constsize, typ: UintType}, - muint16: {size: 3, extra: constsize, typ: UintType}, - muint32: {size: 5, extra: constsize, typ: UintType}, - muint64: {size: 9, extra: constsize, typ: UintType}, - mint8: {size: 2, extra: constsize, typ: IntType}, - mint16: {size: 3, extra: constsize, typ: IntType}, - mint32: {size: 5, extra: constsize, typ: IntType}, - mint64: {size: 9, extra: constsize, typ: IntType}, - mfixext1: {size: 3, extra: constsize, typ: ExtensionType}, - mfixext2: {size: 4, extra: constsize, typ: ExtensionType}, - mfixext4: {size: 6, extra: constsize, typ: ExtensionType}, - mfixext8: {size: 10, extra: constsize, typ: ExtensionType}, - mfixext16: {size: 18, extra: constsize, typ: ExtensionType}, - mstr8: {size: 2, extra: extra8, typ: StrType}, - mstr16: {size: 3, extra: extra16, typ: StrType}, - mstr32: {size: 5, extra: extra32, typ: StrType}, - marray16: {size: 3, extra: array16v, typ: ArrayType}, - marray32: {size: 5, extra: array32v, typ: ArrayType}, - mmap16: {size: 3, extra: map16v, typ: MapType}, - mmap32: {size: 5, extra: map32v, typ: MapType}, -} - -func init() { - // set up fixed fields - - // fixint - for i := mfixint; i < 0x80; i++ { - sizes[i] = bytespec{size: 1, extra: constsize, typ: IntType} - } - - // nfixint - for i := uint16(mnfixint); i < 0x100; i++ { - sizes[uint8(i)] = bytespec{size: 1, extra: constsize, typ: IntType} - } - - // fixstr gets constsize, - // since the prefix yields the size - for i := mfixstr; i < 0xc0; i++ { - sizes[i] = bytespec{size: 1 + rfixstr(i), extra: constsize, typ: StrType} - } - - // fixmap - for i := mfixmap; i < 0x90; i++ { - sizes[i] = bytespec{size: 1, extra: varmode(2 * rfixmap(i)), typ: MapType} - } - - // fixarray - for i := mfixarray; i < 0xa0; i++ { - sizes[i] = bytespec{size: 1, extra: varmode(rfixarray(i)), typ: ArrayType} - } -} - -// a valid bytespsec has -// non-zero 'size' and -// non-zero 'typ' -type bytespec struct { - size uint8 // prefix size information - extra varmode // extra size information - typ Type // type - _ byte // makes bytespec 4 bytes (yes, this matters) -} - -// size mode -// if positive, # elements for composites -type varmode int8 - -const ( - constsize varmode = -iota // constant size (size bytes + uint8(varmode) objects) - extra8 // has uint8(p[1]) extra bytes - extra16 // has be16(p[1:]) extra bytes - extra32 // has be32(p[1:]) extra bytes - map16v // use map16 - map32v // use map32 - array16v // use array16 - array32v // use array32 -) - -func getType(v byte) Type { - return sizes[v].typ -} diff --git a/internal/msgp/errors.go b/internal/msgp/errors.go deleted file mode 100644 index d0757a4bcb..0000000000 --- a/internal/msgp/errors.go +++ /dev/null @@ -1,317 +0,0 @@ -package msgp - -import ( - "fmt" - "reflect" - "strings" -) - -const resumableDefault = false - -var ( - // ErrShortBytes is returned when the - // slice being decoded is too short to - // contain the contents of the message - ErrShortBytes error = errShort{} - - // this error is only returned - // if we reach code that should - // be unreachable - fatal error = errFatal{} -) - -// Error is the interface satisfied -// by all of the errors that originate -// from this package. -type Error interface { - error - - // Resumable returns whether - // or not the error means that - // the stream of data is malformed - // and the information is unrecoverable. - Resumable() bool -} - -// contextError allows msgp Error instances to be enhanced with additional -// context about their origin. -type contextError interface { - Error - - // withContext must not modify the error instance - it must clone and - // return a new error with the context added. - withContext(ctx string) error -} - -// Cause returns the underlying cause of an error that has been wrapped -// with additional context. -func Cause(e error) error { - out := e - if e, ok := e.(errWrapped); ok && e.cause != nil { - out = e.cause - } - return out -} - -// Resumable returns whether or not the error means that the stream of data is -// malformed and the information is unrecoverable. -func Resumable(e error) bool { - if e, ok := e.(Error); ok { - return e.Resumable() - } - return resumableDefault -} - -// WrapError wraps an error with additional context that allows the part of the -// serialized type that caused the problem to be identified. Underlying errors -// can be retrieved using Cause() -// -// The input error is not modified - a new error should be returned. -// -// ErrShortBytes is not wrapped with any context due to backward compatibility -// issues with the public API. -func WrapError(err error, ctx ...interface{}) error { - switch e := err.(type) { - case errShort: - return e - case contextError: - return e.withContext(ctxString(ctx)) - default: - return errWrapped{cause: err, ctx: ctxString(ctx)} - } -} - -// ctxString converts the incoming interface{} slice into a single string. -func ctxString(ctx []interface{}) string { - out := "" - for idx, cv := range ctx { - if idx > 0 { - out += "/" - } - out += fmt.Sprintf("%v", cv) - } - return out -} - -func addCtx(ctx, add string) string { - if ctx != "" { - return add + "/" + ctx - } else { - return add - } -} - -// errWrapped allows arbitrary errors passed to WrapError to be enhanced with -// context and unwrapped with Cause() -type errWrapped struct { - cause error - ctx string -} - -func (e errWrapped) Error() string { - if e.ctx != "" { - return strings.Join([]string{e.cause.Error(), "at", e.ctx}, " ") - } else { - return e.cause.Error() - } -} - -func (e errWrapped) Resumable() bool { - if e, ok := e.cause.(Error); ok { - return e.Resumable() - } - return resumableDefault -} - -// Unwrap returns the cause. -func (e errWrapped) Unwrap() error { return e.cause } - -type errShort struct{} - -func (e errShort) Error() string { return "msgp: too few bytes left to read object" } -func (e errShort) Resumable() bool { return false } - -type errFatal struct { - ctx string -} - -func (f errFatal) Error() string { - out := "msgp: fatal decoding error (unreachable code)" - if f.ctx != "" { - out += " at " + f.ctx - } - return out -} - -func (f errFatal) Resumable() bool { return false } - -func (f errFatal) withContext(ctx string) error { f.ctx = addCtx(f.ctx, ctx); return f } - -// ArrayError is an error returned -// when decoding a fix-sized array -// of the wrong size -type ArrayError struct { - Wanted uint32 - Got uint32 - ctx string -} - -// Error implements the error interface -func (a ArrayError) Error() string { - out := fmt.Sprintf("msgp: wanted array of size %d; got %d", a.Wanted, a.Got) - if a.ctx != "" { - out += " at " + a.ctx - } - return out -} - -// Resumable is always 'true' for ArrayErrors -func (a ArrayError) Resumable() bool { return true } - -func (a ArrayError) withContext(ctx string) error { a.ctx = addCtx(a.ctx, ctx); return a } - -// IntOverflow is returned when a call -// would downcast an integer to a type -// with too few bits to hold its value. -type IntOverflow struct { - Value int64 // the value of the integer - FailedBitsize int // the bit size that the int64 could not fit into - ctx string -} - -// Error implements the error interface -func (i IntOverflow) Error() string { - str := fmt.Sprintf("msgp: %d overflows int%d", i.Value, i.FailedBitsize) - if i.ctx != "" { - str += " at " + i.ctx - } - return str -} - -// Resumable is always 'true' for overflows -func (i IntOverflow) Resumable() bool { return true } - -func (i IntOverflow) withContext(ctx string) error { i.ctx = addCtx(i.ctx, ctx); return i } - -// UintOverflow is returned when a call -// would downcast an unsigned integer to a type -// with too few bits to hold its value -type UintOverflow struct { - Value uint64 // value of the uint - FailedBitsize int // the bit size that couldn't fit the value - ctx string -} - -// Error implements the error interface -func (u UintOverflow) Error() string { - str := fmt.Sprintf("msgp: %d overflows uint%d", u.Value, u.FailedBitsize) - if u.ctx != "" { - str += " at " + u.ctx - } - return str -} - -// Resumable is always 'true' for overflows -func (u UintOverflow) Resumable() bool { return true } - -func (u UintOverflow) withContext(ctx string) error { u.ctx = addCtx(u.ctx, ctx); return u } - -// UintBelowZero is returned when a call -// would cast a signed integer below zero -// to an unsigned integer. -type UintBelowZero struct { - Value int64 // value of the incoming int - ctx string -} - -// Error implements the error interface -func (u UintBelowZero) Error() string { - str := fmt.Sprintf("msgp: attempted to cast int %d to unsigned", u.Value) - if u.ctx != "" { - str += " at " + u.ctx - } - return str -} - -// Resumable is always 'true' for overflows -func (u UintBelowZero) Resumable() bool { return true } - -func (u UintBelowZero) withContext(ctx string) error { - u.ctx = ctx - return u -} - -// A TypeError is returned when a particular -// decoding method is unsuitable for decoding -// a particular MessagePack value. -type TypeError struct { - Method Type // Type expected by method - Encoded Type // Type actually encoded - - ctx string -} - -// Error implements the error interface -func (t TypeError) Error() string { - out := fmt.Sprintf("msgp: attempted to decode type %q with method for %q", t.Encoded, t.Method) - if t.ctx != "" { - out += " at " + t.ctx - } - return out -} - -// Resumable returns 'true' for TypeErrors -func (t TypeError) Resumable() bool { return true } - -func (t TypeError) withContext(ctx string) error { t.ctx = addCtx(t.ctx, ctx); return t } - -// returns either InvalidPrefixError or -// TypeError depending on whether or not -// the prefix is recognized -func badPrefix(want Type, lead byte) error { - t := sizes[lead].typ - if t == InvalidType { - return InvalidPrefixError(lead) - } - return TypeError{Method: want, Encoded: t} -} - -// InvalidPrefixError is returned when a bad encoding -// uses a prefix that is not recognized in the MessagePack standard. -// This kind of error is unrecoverable. -type InvalidPrefixError byte - -// Error implements the error interface -func (i InvalidPrefixError) Error() string { - return fmt.Sprintf("msgp: unrecognized type prefix 0x%x", byte(i)) -} - -// Resumable returns 'false' for InvalidPrefixErrors -func (i InvalidPrefixError) Resumable() bool { return false } - -// ErrUnsupportedType is returned -// when a bad argument is supplied -// to a function that takes `interface{}`. -type ErrUnsupportedType struct { - T reflect.Type - - ctx string -} - -// Error implements error -func (e *ErrUnsupportedType) Error() string { - out := fmt.Sprintf("msgp: type %q not supported", e.T) - if e.ctx != "" { - out += " at " + e.ctx - } - return out -} - -// Resumable returns 'true' for ErrUnsupportedType -func (e *ErrUnsupportedType) Resumable() bool { return true } - -func (e *ErrUnsupportedType) withContext(ctx string) error { - o := *e - o.ctx = addCtx(o.ctx, ctx) - return &o -} diff --git a/internal/msgp/extension.go b/internal/msgp/extension.go deleted file mode 100644 index 310a4749be..0000000000 --- a/internal/msgp/extension.go +++ /dev/null @@ -1,549 +0,0 @@ -package msgp - -import ( - "fmt" - "math" -) - -const ( - // Complex64Extension is the extension number used for complex64 - Complex64Extension = 3 - - // Complex128Extension is the extension number used for complex128 - Complex128Extension = 4 - - // TimeExtension is the extension number used for time.Time - TimeExtension = 5 -) - -// our extensions live here -var extensionReg = make(map[int8]func() Extension) - -// RegisterExtension registers extensions so that they -// can be initialized and returned by methods that -// decode `interface{}` values. This should only -// be called during initialization. f() should return -// a newly-initialized zero value of the extension. Keep in -// mind that extensions 3, 4, and 5 are reserved for -// complex64, complex128, and time.Time, respectively, -// and that MessagePack reserves extension types from -127 to -1. -// -// For example, if you wanted to register a user-defined struct: -// -// msgp.RegisterExtension(10, func() msgp.Extension { &MyExtension{} }) -// -// RegisterExtension will panic if you call it multiple times -// with the same 'typ' argument, or if you use a reserved -// type (3, 4, or 5). -func RegisterExtension(typ int8, f func() Extension) { - switch typ { - case Complex64Extension, Complex128Extension, TimeExtension: - panic(fmt.Sprint("msgp: forbidden extension type:", typ)) - } - if _, ok := extensionReg[typ]; ok { - panic(fmt.Sprint("msgp: RegisterExtension() called with typ", typ, "more than once")) - } - extensionReg[typ] = f -} - -// ExtensionTypeError is an error type returned -// when there is a mis-match between an extension type -// and the type encoded on the wire -type ExtensionTypeError struct { - Got int8 - Want int8 -} - -// Error implements the error interface -func (e ExtensionTypeError) Error() string { - return fmt.Sprintf("msgp: error decoding extension: wanted type %d; got type %d", e.Want, e.Got) -} - -// Resumable returns 'true' for ExtensionTypeErrors -func (e ExtensionTypeError) Resumable() bool { return true } - -func errExt(got int8, wanted int8) error { - return ExtensionTypeError{Got: got, Want: wanted} -} - -// Extension is the interface fulfilled -// by types that want to define their -// own binary encoding. -type Extension interface { - // ExtensionType should return - // a int8 that identifies the concrete - // type of the extension. (Types <0 are - // officially reserved by the MessagePack - // specifications.) - ExtensionType() int8 - - // Len should return the length - // of the data to be encoded - Len() int - - // MarshalBinaryTo should copy - // the data into the supplied slice, - // assuming that the slice has length Len() - MarshalBinaryTo([]byte) error - - UnmarshalBinary([]byte) error -} - -// RawExtension implements the Extension interface -type RawExtension struct { - Data []byte - Type int8 -} - -// ExtensionType implements Extension.ExtensionType, and returns r.Type -func (r *RawExtension) ExtensionType() int8 { return r.Type } - -// Len implements Extension.Len, and returns len(r.Data) -func (r *RawExtension) Len() int { return len(r.Data) } - -// MarshalBinaryTo implements Extension.MarshalBinaryTo, -// and returns a copy of r.Data -func (r *RawExtension) MarshalBinaryTo(d []byte) error { - copy(d, r.Data) - return nil -} - -// UnmarshalBinary implements Extension.UnmarshalBinary, -// and sets r.Data to the contents of the provided slice -func (r *RawExtension) UnmarshalBinary(b []byte) error { - if cap(r.Data) >= len(b) { - r.Data = r.Data[0:len(b)] - } else { - r.Data = make([]byte, len(b)) - } - copy(r.Data, b) - return nil -} - -// WriteExtension writes an extension type to the writer -func (mw *Writer) WriteExtension(e Extension) error { - l := e.Len() - var err error - switch l { - case 0: - o, err := mw.require(3) - if err != nil { - return err - } - mw.buf[o] = mext8 - mw.buf[o+1] = 0 - mw.buf[o+2] = byte(e.ExtensionType()) - case 1: - o, err := mw.require(2) - if err != nil { - return err - } - mw.buf[o] = mfixext1 - mw.buf[o+1] = byte(e.ExtensionType()) - case 2: - o, err := mw.require(2) - if err != nil { - return err - } - mw.buf[o] = mfixext2 - mw.buf[o+1] = byte(e.ExtensionType()) - case 4: - o, err := mw.require(2) - if err != nil { - return err - } - mw.buf[o] = mfixext4 - mw.buf[o+1] = byte(e.ExtensionType()) - case 8: - o, err := mw.require(2) - if err != nil { - return err - } - mw.buf[o] = mfixext8 - mw.buf[o+1] = byte(e.ExtensionType()) - case 16: - o, err := mw.require(2) - if err != nil { - return err - } - mw.buf[o] = mfixext16 - mw.buf[o+1] = byte(e.ExtensionType()) - default: - switch { - case l < math.MaxUint8: - o, err := mw.require(3) - if err != nil { - return err - } - mw.buf[o] = mext8 - mw.buf[o+1] = byte(uint8(l)) - mw.buf[o+2] = byte(e.ExtensionType()) - case l < math.MaxUint16: - o, err := mw.require(4) - if err != nil { - return err - } - mw.buf[o] = mext16 - big.PutUint16(mw.buf[o+1:], uint16(l)) - mw.buf[o+3] = byte(e.ExtensionType()) - default: - o, err := mw.require(6) - if err != nil { - return err - } - mw.buf[o] = mext32 - big.PutUint32(mw.buf[o+1:], uint32(l)) - mw.buf[o+5] = byte(e.ExtensionType()) - } - } - // we can only write directly to the - // buffer if we're sure that it - // fits the object - if l <= mw.bufsize() { - o, err := mw.require(l) - if err != nil { - return err - } - return e.MarshalBinaryTo(mw.buf[o:]) - } - // here we create a new buffer - // just large enough for the body - // and save it as the write buffer - err = mw.flush() - if err != nil { - return err - } - buf := make([]byte, l) - err = e.MarshalBinaryTo(buf) - if err != nil { - return err - } - mw.buf = buf - mw.wloc = l - return nil -} - -// peek at the extension type, assuming the next -// kind to be read is Extension -func (m *Reader) peekExtensionType() (int8, error) { - p, err := m.R.Peek(2) - if err != nil { - return 0, err - } - spec := sizes[p[0]] - if spec.typ != ExtensionType { - return 0, badPrefix(ExtensionType, p[0]) - } - if spec.extra == constsize { - return int8(p[1]), nil - } - size := spec.size - p, err = m.R.Peek(int(size)) - if err != nil { - return 0, err - } - return int8(p[size-1]), nil -} - -// peekExtension peeks at the extension encoding type -// (must guarantee at least 1 byte in 'b') -func peekExtension(b []byte) (int8, error) { - spec := sizes[b[0]] - size := spec.size - if spec.typ != ExtensionType { - return 0, badPrefix(ExtensionType, b[0]) - } - if len(b) < int(size) { - return 0, ErrShortBytes - } - // for fixed extensions, - // the type information is in - // the second byte - if spec.extra == constsize { - return int8(b[1]), nil - } - // otherwise, it's in the last - // part of the prefix - return int8(b[size-1]), nil -} - -// ReadExtension reads the next object from the reader -// as an extension. ReadExtension will fail if the next -// object in the stream is not an extension, or if -// e.Type() is not the same as the wire type. -func (m *Reader) ReadExtension(e Extension) (err error) { - var p []byte - p, err = m.R.Peek(2) - if err != nil { - return - } - lead := p[0] - var read int - var off int - switch lead { - case mfixext1: - if int8(p[1]) != e.ExtensionType() { - err = errExt(int8(p[1]), e.ExtensionType()) - return - } - p, err = m.R.Peek(3) - if err != nil { - return - } - err = e.UnmarshalBinary(p[2:]) - if err == nil { - _, err = m.R.Skip(3) - } - return - - case mfixext2: - if int8(p[1]) != e.ExtensionType() { - err = errExt(int8(p[1]), e.ExtensionType()) - return - } - p, err = m.R.Peek(4) - if err != nil { - return - } - err = e.UnmarshalBinary(p[2:]) - if err == nil { - _, err = m.R.Skip(4) - } - return - - case mfixext4: - if int8(p[1]) != e.ExtensionType() { - err = errExt(int8(p[1]), e.ExtensionType()) - return - } - p, err = m.R.Peek(6) - if err != nil { - return - } - err = e.UnmarshalBinary(p[2:]) - if err == nil { - _, err = m.R.Skip(6) - } - return - - case mfixext8: - if int8(p[1]) != e.ExtensionType() { - err = errExt(int8(p[1]), e.ExtensionType()) - return - } - p, err = m.R.Peek(10) - if err != nil { - return - } - err = e.UnmarshalBinary(p[2:]) - if err == nil { - _, err = m.R.Skip(10) - } - return - - case mfixext16: - if int8(p[1]) != e.ExtensionType() { - err = errExt(int8(p[1]), e.ExtensionType()) - return - } - p, err = m.R.Peek(18) - if err != nil { - return - } - err = e.UnmarshalBinary(p[2:]) - if err == nil { - _, err = m.R.Skip(18) - } - return - - case mext8: - p, err = m.R.Peek(3) - if err != nil { - return - } - if int8(p[2]) != e.ExtensionType() { - err = errExt(int8(p[2]), e.ExtensionType()) - return - } - read = int(uint8(p[1])) - off = 3 - - case mext16: - p, err = m.R.Peek(4) - if err != nil { - return - } - if int8(p[3]) != e.ExtensionType() { - err = errExt(int8(p[3]), e.ExtensionType()) - return - } - read = int(big.Uint16(p[1:])) - off = 4 - - case mext32: - p, err = m.R.Peek(6) - if err != nil { - return - } - if int8(p[5]) != e.ExtensionType() { - err = errExt(int8(p[5]), e.ExtensionType()) - return - } - read = int(big.Uint32(p[1:])) - off = 6 - - default: - err = badPrefix(ExtensionType, lead) - return - } - - p, err = m.R.Peek(read + off) - if err != nil { - return - } - err = e.UnmarshalBinary(p[off:]) - if err == nil { - _, err = m.R.Skip(read + off) - } - return -} - -// AppendExtension appends a MessagePack extension to the provided slice -func AppendExtension(b []byte, e Extension) ([]byte, error) { - l := e.Len() - var o []byte - var n int - switch l { - case 0: - o, n = ensure(b, 3) - o[n] = mext8 - o[n+1] = 0 - o[n+2] = byte(e.ExtensionType()) - return o[:n+3], nil - case 1: - o, n = ensure(b, 3) - o[n] = mfixext1 - o[n+1] = byte(e.ExtensionType()) - n += 2 - case 2: - o, n = ensure(b, 4) - o[n] = mfixext2 - o[n+1] = byte(e.ExtensionType()) - n += 2 - case 4: - o, n = ensure(b, 6) - o[n] = mfixext4 - o[n+1] = byte(e.ExtensionType()) - n += 2 - case 8: - o, n = ensure(b, 10) - o[n] = mfixext8 - o[n+1] = byte(e.ExtensionType()) - n += 2 - case 16: - o, n = ensure(b, 18) - o[n] = mfixext16 - o[n+1] = byte(e.ExtensionType()) - n += 2 - default: - switch { - case l < math.MaxUint8: - o, n = ensure(b, l+3) - o[n] = mext8 - o[n+1] = byte(uint8(l)) - o[n+2] = byte(e.ExtensionType()) - n += 3 - case l < math.MaxUint16: - o, n = ensure(b, l+4) - o[n] = mext16 - big.PutUint16(o[n+1:], uint16(l)) - o[n+3] = byte(e.ExtensionType()) - n += 4 - default: - o, n = ensure(b, l+6) - o[n] = mext32 - big.PutUint32(o[n+1:], uint32(l)) - o[n+5] = byte(e.ExtensionType()) - n += 6 - } - } - return o, e.MarshalBinaryTo(o[n:]) -} - -// ReadExtensionBytes reads an extension from 'b' into 'e' -// and returns any remaining bytes. -// Possible errors: -// - ErrShortBytes ('b' not long enough) -// - ExtensionTypeError{} (wire type not the same as e.Type()) -// - TypeError{} (next object not an extension) -// - InvalidPrefixError -// - An umarshal error returned from e.UnmarshalBinary -func ReadExtensionBytes(b []byte, e Extension) ([]byte, error) { - l := len(b) - if l < 3 { - return b, ErrShortBytes - } - lead := b[0] - var ( - sz int // size of 'data' - off int // offset of 'data' - typ int8 - ) - switch lead { - case mfixext1: - typ = int8(b[1]) - sz = 1 - off = 2 - case mfixext2: - typ = int8(b[1]) - sz = 2 - off = 2 - case mfixext4: - typ = int8(b[1]) - sz = 4 - off = 2 - case mfixext8: - typ = int8(b[1]) - sz = 8 - off = 2 - case mfixext16: - typ = int8(b[1]) - sz = 16 - off = 2 - case mext8: - sz = int(uint8(b[1])) - typ = int8(b[2]) - off = 3 - if sz == 0 { - return b[3:], e.UnmarshalBinary(b[3:3]) - } - case mext16: - if l < 4 { - return b, ErrShortBytes - } - sz = int(big.Uint16(b[1:])) - typ = int8(b[3]) - off = 4 - case mext32: - if l < 6 { - return b, ErrShortBytes - } - sz = int(big.Uint32(b[1:])) - typ = int8(b[5]) - off = 6 - default: - return b, badPrefix(ExtensionType, lead) - } - - if typ != e.ExtensionType() { - return b, errExt(typ, e.ExtensionType()) - } - - // the data of the extension starts - // at 'off' and is 'sz' bytes long - if len(b[off:]) < sz { - return b, ErrShortBytes - } - tot := off + sz - return b[tot:], e.UnmarshalBinary(b[off:tot]) -} diff --git a/internal/msgp/file.go b/internal/msgp/file.go deleted file mode 100644 index b0e86a978b..0000000000 --- a/internal/msgp/file.go +++ /dev/null @@ -1,92 +0,0 @@ -//go:build (linux || darwin || dragonfly || freebsd || netbsd || openbsd) && !appengine -// +build linux darwin dragonfly freebsd netbsd openbsd -// +build !appengine - -package msgp - -import ( - "os" - "syscall" -) - -// ReadFile reads a file into 'dst' using -// a read-only memory mapping. Consequently, -// the file must be mmap-able, and the -// Unmarshaler should never write to -// the source memory. (Methods generated -// by the msgp tool obey that constraint, but -// user-defined implementations may not.) -// -// Reading and writing through file mappings -// is only efficient for large files; small -// files are best read and written using -// the ordinary streaming interfaces. -func ReadFile(dst Unmarshaler, file *os.File) error { - stat, err := file.Stat() - if err != nil { - return err - } - data, err := syscall.Mmap(int(file.Fd()), 0, int(stat.Size()), syscall.PROT_READ, syscall.MAP_SHARED) - if err != nil { - return err - } - adviseRead(data) - _, err = dst.UnmarshalMsg(data) - uerr := syscall.Munmap(data) - if err == nil { - err = uerr - } - return err -} - -// MarshalSizer is the combination -// of the Marshaler and Sizer -// interfaces. -type MarshalSizer interface { - Marshaler - Sizer -} - -// WriteFile writes a file from 'src' using -// memory mapping. It overwrites the entire -// contents of the previous file. -// The mapping size is calculated -// using the `Msgsize()` method -// of 'src', so it must produce a result -// equal to or greater than the actual encoded -// size of the object. Otherwise, -// a fault (SIGBUS) will occur. -// -// Reading and writing through file mappings -// is only efficient for large files; small -// files are best read and written using -// the ordinary streaming interfaces. -// -// NOTE: The performance of this call -// is highly OS- and filesystem-dependent. -// Users should take care to test that this -// performs as expected in a production environment. -// (Linux users should run a kernel and filesystem -// that support fallocate(2) for the best results.) -func WriteFile(src MarshalSizer, file *os.File) error { - sz := src.Msgsize() - err := fallocate(file, int64(sz)) - if err != nil { - return err - } - data, err := syscall.Mmap(int(file.Fd()), 0, sz, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED) - if err != nil { - return err - } - adviseWrite(data) - chunk := data[:0] - chunk, err = src.MarshalMsg(chunk) - if err != nil { - return err - } - uerr := syscall.Munmap(data) - if uerr != nil { - return uerr - } - return file.Truncate(int64(len(chunk))) -} diff --git a/internal/msgp/file_port.go b/internal/msgp/file_port.go deleted file mode 100644 index 5f44b2f581..0000000000 --- a/internal/msgp/file_port.go +++ /dev/null @@ -1,48 +0,0 @@ -//go:build windows || appengine -// +build windows appengine - -package msgp - -import ( - "io" - "os" -) - -// MarshalSizer is the combination -// of the Marshaler and Sizer -// interfaces. -type MarshalSizer interface { - Marshaler - Sizer -} - -func ReadFile(dst Unmarshaler, file *os.File) error { - if u, ok := dst.(Decodable); ok { - return u.DecodeMsg(NewReader(file)) - } - - data, err := io.ReadAll(file) - if err != nil { - return err - } - _, err = dst.UnmarshalMsg(data) - return err -} - -func WriteFile(src MarshalSizer, file *os.File) error { - if e, ok := src.(Encodable); ok { - w := NewWriter(file) - err := e.EncodeMsg(w) - if err == nil { - err = w.Flush() - } - return err - } - - raw, err := src.MarshalMsg(nil) - if err != nil { - return err - } - _, err = file.Write(raw) - return err -} diff --git a/internal/msgp/integers.go b/internal/msgp/integers.go deleted file mode 100644 index f817d77598..0000000000 --- a/internal/msgp/integers.go +++ /dev/null @@ -1,174 +0,0 @@ -package msgp - -/* ---------------------------------- - integer encoding utilities - (inline-able) - - TODO(tinylib): there are faster, - albeit non-portable solutions - to the code below. implement - byteswap? - ---------------------------------- */ - -func putMint64(b []byte, i int64) { - b[0] = mint64 - b[1] = byte(i >> 56) - b[2] = byte(i >> 48) - b[3] = byte(i >> 40) - b[4] = byte(i >> 32) - b[5] = byte(i >> 24) - b[6] = byte(i >> 16) - b[7] = byte(i >> 8) - b[8] = byte(i) -} - -func getMint64(b []byte) int64 { - return (int64(b[1]) << 56) | (int64(b[2]) << 48) | - (int64(b[3]) << 40) | (int64(b[4]) << 32) | - (int64(b[5]) << 24) | (int64(b[6]) << 16) | - (int64(b[7]) << 8) | (int64(b[8])) -} - -func putMint32(b []byte, i int32) { - b[0] = mint32 - b[1] = byte(i >> 24) - b[2] = byte(i >> 16) - b[3] = byte(i >> 8) - b[4] = byte(i) -} - -func getMint32(b []byte) int32 { - return (int32(b[1]) << 24) | (int32(b[2]) << 16) | (int32(b[3]) << 8) | (int32(b[4])) -} - -func putMint16(b []byte, i int16) { - b[0] = mint16 - b[1] = byte(i >> 8) - b[2] = byte(i) -} - -func getMint16(b []byte) (i int16) { - return (int16(b[1]) << 8) | int16(b[2]) -} - -func putMint8(b []byte, i int8) { - b[0] = mint8 - b[1] = byte(i) -} - -func getMint8(b []byte) (i int8) { - return int8(b[1]) -} - -func putMuint64(b []byte, u uint64) { - b[0] = muint64 - b[1] = byte(u >> 56) - b[2] = byte(u >> 48) - b[3] = byte(u >> 40) - b[4] = byte(u >> 32) - b[5] = byte(u >> 24) - b[6] = byte(u >> 16) - b[7] = byte(u >> 8) - b[8] = byte(u) -} - -func getMuint64(b []byte) uint64 { - return (uint64(b[1]) << 56) | (uint64(b[2]) << 48) | - (uint64(b[3]) << 40) | (uint64(b[4]) << 32) | - (uint64(b[5]) << 24) | (uint64(b[6]) << 16) | - (uint64(b[7]) << 8) | (uint64(b[8])) -} - -func putMuint32(b []byte, u uint32) { - b[0] = muint32 - b[1] = byte(u >> 24) - b[2] = byte(u >> 16) - b[3] = byte(u >> 8) - b[4] = byte(u) -} - -func getMuint32(b []byte) uint32 { - return (uint32(b[1]) << 24) | (uint32(b[2]) << 16) | (uint32(b[3]) << 8) | (uint32(b[4])) -} - -func putMuint16(b []byte, u uint16) { - b[0] = muint16 - b[1] = byte(u >> 8) - b[2] = byte(u) -} - -func getMuint16(b []byte) uint16 { - return (uint16(b[1]) << 8) | uint16(b[2]) -} - -func putMuint8(b []byte, u uint8) { - b[0] = muint8 - b[1] = byte(u) -} - -func getMuint8(b []byte) uint8 { - return uint8(b[1]) -} - -func getUnix(b []byte) (sec int64, nsec int32) { - sec = (int64(b[0]) << 56) | (int64(b[1]) << 48) | - (int64(b[2]) << 40) | (int64(b[3]) << 32) | - (int64(b[4]) << 24) | (int64(b[5]) << 16) | - (int64(b[6]) << 8) | (int64(b[7])) - - nsec = (int32(b[8]) << 24) | (int32(b[9]) << 16) | (int32(b[10]) << 8) | (int32(b[11])) - return -} - -func putUnix(b []byte, sec int64, nsec int32) { - b[0] = byte(sec >> 56) - b[1] = byte(sec >> 48) - b[2] = byte(sec >> 40) - b[3] = byte(sec >> 32) - b[4] = byte(sec >> 24) - b[5] = byte(sec >> 16) - b[6] = byte(sec >> 8) - b[7] = byte(sec) - b[8] = byte(nsec >> 24) - b[9] = byte(nsec >> 16) - b[10] = byte(nsec >> 8) - b[11] = byte(nsec) -} - -/* ----------------------------- - prefix utilities - ----------------------------- */ - -// write prefix and uint8 -func prefixu8(b []byte, pre byte, sz uint8) { - b[0] = pre - b[1] = byte(sz) -} - -// write prefix and big-endian uint16 -func prefixu16(b []byte, pre byte, sz uint16) { - b[0] = pre - b[1] = byte(sz >> 8) - b[2] = byte(sz) -} - -// write prefix and big-endian uint32 -func prefixu32(b []byte, pre byte, sz uint32) { - b[0] = pre - b[1] = byte(sz >> 24) - b[2] = byte(sz >> 16) - b[3] = byte(sz >> 8) - b[4] = byte(sz) -} - -func prefixu64(b []byte, pre byte, sz uint64) { - b[0] = pre - b[1] = byte(sz >> 56) - b[2] = byte(sz >> 48) - b[3] = byte(sz >> 40) - b[4] = byte(sz >> 32) - b[5] = byte(sz >> 24) - b[6] = byte(sz >> 16) - b[7] = byte(sz >> 8) - b[8] = byte(sz) -} diff --git a/internal/msgp/json.go b/internal/msgp/json.go deleted file mode 100644 index 80d39635ca..0000000000 --- a/internal/msgp/json.go +++ /dev/null @@ -1,568 +0,0 @@ -package msgp - -import ( - "bufio" - "encoding/base64" - "encoding/json" - "io" - "strconv" - "unicode/utf8" -) - -var ( - null = []byte("null") - hex = []byte("0123456789abcdef") -) - -var defuns [_maxtype]func(jsWriter, *Reader) (int, error) - -// note: there is an initialization loop if -// this isn't set up during init() -func init() { - // since none of these functions are inline-able, - // there is not much of a penalty to the indirect - // call. however, this is best expressed as a jump-table... - defuns = [_maxtype]func(jsWriter, *Reader) (int, error){ - StrType: rwString, - BinType: rwBytes, - MapType: rwMap, - ArrayType: rwArray, - Float64Type: rwFloat64, - Float32Type: rwFloat32, - BoolType: rwBool, - IntType: rwInt, - UintType: rwUint, - NilType: rwNil, - ExtensionType: rwExtension, - Complex64Type: rwExtension, - Complex128Type: rwExtension, - TimeType: rwTime, - } -} - -// this is the interface -// used to write json -type jsWriter interface { - io.Writer - io.ByteWriter - WriteString(string) (int, error) -} - -// CopyToJSON reads MessagePack from 'src' and copies it -// as JSON to 'dst' until EOF. -func CopyToJSON(dst io.Writer, src io.Reader) (n int64, err error) { - r := NewReader(src) - n, err = r.WriteToJSON(dst) - freeR(r) - return -} - -// WriteToJSON translates MessagePack from 'r' and writes it as -// JSON to 'w' until the underlying reader returns io.EOF. It returns -// the number of bytes written, and an error if it stopped before EOF. -func (r *Reader) WriteToJSON(w io.Writer) (n int64, err error) { - var j jsWriter - var bf *bufio.Writer - if jsw, ok := w.(jsWriter); ok { - j = jsw - } else { - bf = bufio.NewWriter(w) - j = bf - } - var nn int - for err == nil { - nn, err = rwNext(j, r) - n += int64(nn) - } - if err != io.EOF { - if bf != nil { - bf.Flush() - } - return - } - err = nil - if bf != nil { - err = bf.Flush() - } - return -} - -func rwNext(w jsWriter, src *Reader) (int, error) { - t, err := src.NextType() - if err != nil { - return 0, err - } - return defuns[t](w, src) -} - -func rwMap(dst jsWriter, src *Reader) (n int, err error) { - var comma bool - var sz uint32 - var field []byte - - sz, err = src.ReadMapHeader() - if err != nil { - return - } - - if sz == 0 { - return dst.WriteString("{}") - } - - err = dst.WriteByte('{') - if err != nil { - return - } - n++ - var nn int - for i := uint32(0); i < sz; i++ { - if comma { - err = dst.WriteByte(',') - if err != nil { - return - } - n++ - } - - field, err = src.ReadMapKeyPtr() - if err != nil { - return - } - nn, err = rwquoted(dst, field) - n += nn - if err != nil { - return - } - - err = dst.WriteByte(':') - if err != nil { - return - } - n++ - nn, err = rwNext(dst, src) - n += nn - if err != nil { - return - } - if !comma { - comma = true - } - } - - err = dst.WriteByte('}') - if err != nil { - return - } - n++ - return -} - -func rwArray(dst jsWriter, src *Reader) (n int, err error) { - err = dst.WriteByte('[') - if err != nil { - return - } - var sz uint32 - var nn int - sz, err = src.ReadArrayHeader() - if err != nil { - return - } - var comma bool - for i := uint32(0); i < sz; i++ { - if comma { - err = dst.WriteByte(',') - if err != nil { - return - } - n++ - } - nn, err = rwNext(dst, src) - n += nn - if err != nil { - return - } - comma = true - } - - err = dst.WriteByte(']') - if err != nil { - return - } - n++ - return -} - -func rwNil(dst jsWriter, src *Reader) (int, error) { - err := src.ReadNil() - if err != nil { - return 0, err - } - return dst.Write(null) -} - -func rwFloat32(dst jsWriter, src *Reader) (int, error) { - f, err := src.ReadFloat32() - if err != nil { - return 0, err - } - src.scratch = strconv.AppendFloat(src.scratch[:0], float64(f), 'f', -1, 32) - return dst.Write(src.scratch) -} - -func rwFloat64(dst jsWriter, src *Reader) (int, error) { - f, err := src.ReadFloat64() - if err != nil { - return 0, err - } - src.scratch = strconv.AppendFloat(src.scratch[:0], f, 'f', -1, 64) - return dst.Write(src.scratch) -} - -func rwInt(dst jsWriter, src *Reader) (int, error) { - i, err := src.ReadInt64() - if err != nil { - return 0, err - } - src.scratch = strconv.AppendInt(src.scratch[:0], i, 10) - return dst.Write(src.scratch) -} - -func rwUint(dst jsWriter, src *Reader) (int, error) { - u, err := src.ReadUint64() - if err != nil { - return 0, err - } - src.scratch = strconv.AppendUint(src.scratch[:0], u, 10) - return dst.Write(src.scratch) -} - -func rwBool(dst jsWriter, src *Reader) (int, error) { - b, err := src.ReadBool() - if err != nil { - return 0, err - } - if b { - return dst.WriteString("true") - } - return dst.WriteString("false") -} - -func rwTime(dst jsWriter, src *Reader) (int, error) { - t, err := src.ReadTime() - if err != nil { - return 0, err - } - bts, err := t.MarshalJSON() - if err != nil { - return 0, err - } - return dst.Write(bts) -} - -func rwExtension(dst jsWriter, src *Reader) (n int, err error) { - et, err := src.peekExtensionType() - if err != nil { - return 0, err - } - - // registered extensions can override - // the JSON encoding - if j, ok := extensionReg[et]; ok { - var bts []byte - e := j() - err = src.ReadExtension(e) - if err != nil { - return - } - bts, err = json.Marshal(e) - if err != nil { - return - } - return dst.Write(bts) - } - - e := RawExtension{} - e.Type = et - err = src.ReadExtension(&e) - if err != nil { - return - } - - var nn int - err = dst.WriteByte('{') - if err != nil { - return - } - n++ - - nn, err = dst.WriteString(`"type:"`) - n += nn - if err != nil { - return - } - - src.scratch = strconv.AppendInt(src.scratch[0:0], int64(e.Type), 10) - nn, err = dst.Write(src.scratch) - n += nn - if err != nil { - return - } - - nn, err = dst.WriteString(`,"data":"`) - n += nn - if err != nil { - return - } - - enc := base64.NewEncoder(base64.StdEncoding, dst) - - nn, err = enc.Write(e.Data) - n += nn - if err != nil { - return - } - err = enc.Close() - if err != nil { - return - } - nn, err = dst.WriteString(`"}`) - n += nn - return -} - -func rwString(dst jsWriter, src *Reader) (n int, err error) { - var p []byte - p, err = src.R.Peek(1) - if err != nil { - return - } - lead := p[0] - var read int - - if isfixstr(lead) { - read = int(rfixstr(lead)) - src.R.Skip(1) - goto write - } - - switch lead { - case mstr8: - p, err = src.R.Next(2) - if err != nil { - return - } - read = int(uint8(p[1])) - case mstr16: - p, err = src.R.Next(3) - if err != nil { - return - } - read = int(big.Uint16(p[1:])) - case mstr32: - p, err = src.R.Next(5) - if err != nil { - return - } - read = int(big.Uint32(p[1:])) - default: - err = badPrefix(StrType, lead) - return - } -write: - p, err = src.R.Next(read) - if err != nil { - return - } - n, err = rwquoted(dst, p) - return -} - -func rwBytes(dst jsWriter, src *Reader) (n int, err error) { - var nn int - err = dst.WriteByte('"') - if err != nil { - return - } - n++ - src.scratch, err = src.ReadBytes(src.scratch[:0]) - if err != nil { - return - } - enc := base64.NewEncoder(base64.StdEncoding, dst) - nn, err = enc.Write(src.scratch) - n += nn - if err != nil { - return - } - err = enc.Close() - if err != nil { - return - } - err = dst.WriteByte('"') - if err != nil { - return - } - n++ - return -} - -// Below (c) The Go Authors, 2009-2014 -// Subject to the BSD-style license found at http://golang.org -// -// see: encoding/json/encode.go:(*encodeState).stringbytes() -func rwquoted(dst jsWriter, s []byte) (n int, err error) { - var nn int - err = dst.WriteByte('"') - if err != nil { - return - } - n++ - start := 0 - for i := 0; i < len(s); { - if b := s[i]; b < utf8.RuneSelf { - if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' { - i++ - continue - } - if start < i { - nn, err = dst.Write(s[start:i]) - n += nn - if err != nil { - return - } - } - switch b { - case '\\', '"': - err = dst.WriteByte('\\') - if err != nil { - return - } - n++ - err = dst.WriteByte(b) - if err != nil { - return - } - n++ - case '\n': - err = dst.WriteByte('\\') - if err != nil { - return - } - n++ - err = dst.WriteByte('n') - if err != nil { - return - } - n++ - case '\r': - err = dst.WriteByte('\\') - if err != nil { - return - } - n++ - err = dst.WriteByte('r') - if err != nil { - return - } - n++ - case '\t': - err = dst.WriteByte('\\') - if err != nil { - return - } - n++ - err = dst.WriteByte('t') - if err != nil { - return - } - n++ - default: - // This encodes bytes < 0x20 except for \t, \n and \r. - // It also escapes <, >, and & - // because they can lead to security holes when - // user-controlled strings are rendered into JSON - // and served to some browsers. - nn, err = dst.WriteString(`\u00`) - n += nn - if err != nil { - return - } - err = dst.WriteByte(hex[b>>4]) - if err != nil { - return - } - n++ - err = dst.WriteByte(hex[b&0xF]) - if err != nil { - return - } - n++ - } - i++ - start = i - continue - } - c, size := utf8.DecodeRune(s[i:]) - if c == utf8.RuneError && size == 1 { - if start < i { - nn, err = dst.Write(s[start:i]) - n += nn - if err != nil { - return - } - } - nn, err = dst.WriteString(`\ufffd`) - n += nn - if err != nil { - return - } - i += size - start = i - continue - } - // U+2028 is LINE SEPARATOR. - // U+2029 is PARAGRAPH SEPARATOR. - // They are both technically valid characters in JSON strings, - // but don't work in JSONP, which has to be evaluated as JavaScript, - // and can lead to security holes there. It is valid JSON to - // escape them, so we do so unconditionally. - // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. - if c == '\u2028' || c == '\u2029' { - if start < i { - nn, err = dst.Write(s[start:i]) - n += nn - if err != nil { - return - } - } - nn, err = dst.WriteString(`\u202`) - n += nn - if err != nil { - return - } - err = dst.WriteByte(hex[c&0xF]) - if err != nil { - return - } - n++ - i += size - start = i - continue - } - i += size - } - if start < len(s) { - nn, err = dst.Write(s[start:]) - n += nn - if err != nil { - return - } - } - err = dst.WriteByte('"') - if err != nil { - return - } - n++ - return -} diff --git a/internal/msgp/json_bytes.go b/internal/msgp/json_bytes.go deleted file mode 100644 index 438caf5392..0000000000 --- a/internal/msgp/json_bytes.go +++ /dev/null @@ -1,363 +0,0 @@ -package msgp - -import ( - "bufio" - "encoding/base64" - "encoding/json" - "io" - "strconv" - "time" -) - -var unfuns [_maxtype]func(jsWriter, []byte, []byte) ([]byte, []byte, error) - -func init() { - - // NOTE(pmh): this is best expressed as a jump table, - // but gc doesn't do that yet. revisit post-go1.5. - unfuns = [_maxtype]func(jsWriter, []byte, []byte) ([]byte, []byte, error){ - StrType: rwStringBytes, - BinType: rwBytesBytes, - MapType: rwMapBytes, - ArrayType: rwArrayBytes, - Float64Type: rwFloat64Bytes, - Float32Type: rwFloat32Bytes, - BoolType: rwBoolBytes, - IntType: rwIntBytes, - UintType: rwUintBytes, - NilType: rwNullBytes, - ExtensionType: rwExtensionBytes, - Complex64Type: rwExtensionBytes, - Complex128Type: rwExtensionBytes, - TimeType: rwTimeBytes, - } -} - -// UnmarshalAsJSON takes raw messagepack and writes -// it as JSON to 'w'. If an error is returned, the -// bytes not translated will also be returned. If -// no errors are encountered, the length of the returned -// slice will be zero. -func UnmarshalAsJSON(w io.Writer, msg []byte) ([]byte, error) { - var ( - scratch []byte - cast bool - dst jsWriter - err error - ) - if jsw, ok := w.(jsWriter); ok { - dst = jsw - cast = true - } else { - dst = bufio.NewWriterSize(w, 512) - } - for len(msg) > 0 && err == nil { - msg, scratch, err = writeNext(dst, msg, scratch) - } - if !cast && err == nil { - err = dst.(*bufio.Writer).Flush() - } - return msg, err -} - -func writeNext(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - if len(msg) < 1 { - return msg, scratch, ErrShortBytes - } - t := getType(msg[0]) - if t == InvalidType { - return msg, scratch, InvalidPrefixError(msg[0]) - } - if t == ExtensionType { - et, err := peekExtension(msg) - if err != nil { - return nil, scratch, err - } - if et == TimeExtension { - t = TimeType - } - } - return unfuns[t](w, msg, scratch) -} - -func rwArrayBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - sz, msg, err := ReadArrayHeaderBytes(msg) - if err != nil { - return msg, scratch, err - } - err = w.WriteByte('[') - if err != nil { - return msg, scratch, err - } - for i := uint32(0); i < sz; i++ { - if i != 0 { - err = w.WriteByte(',') - if err != nil { - return msg, scratch, err - } - } - msg, scratch, err = writeNext(w, msg, scratch) - if err != nil { - return msg, scratch, err - } - } - err = w.WriteByte(']') - return msg, scratch, err -} - -func rwMapBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - sz, msg, err := ReadMapHeaderBytes(msg) - if err != nil { - return msg, scratch, err - } - err = w.WriteByte('{') - if err != nil { - return msg, scratch, err - } - for i := uint32(0); i < sz; i++ { - if i != 0 { - err = w.WriteByte(',') - if err != nil { - return msg, scratch, err - } - } - msg, scratch, err = rwMapKeyBytes(w, msg, scratch) - if err != nil { - return msg, scratch, err - } - err = w.WriteByte(':') - if err != nil { - return msg, scratch, err - } - msg, scratch, err = writeNext(w, msg, scratch) - if err != nil { - return msg, scratch, err - } - } - err = w.WriteByte('}') - return msg, scratch, err -} - -func rwMapKeyBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - msg, scratch, err := rwStringBytes(w, msg, scratch) - if err != nil { - if tperr, ok := err.(TypeError); ok && tperr.Encoded == BinType { - return rwBytesBytes(w, msg, scratch) - } - } - return msg, scratch, err -} - -func rwStringBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - str, msg, err := ReadStringZC(msg) - if err != nil { - return msg, scratch, err - } - _, err = rwquoted(w, str) - return msg, scratch, err -} - -func rwBytesBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - bts, msg, err := ReadBytesZC(msg) - if err != nil { - return msg, scratch, err - } - l := base64.StdEncoding.EncodedLen(len(bts)) - if cap(scratch) >= l { - scratch = scratch[0:l] - } else { - scratch = make([]byte, l) - } - base64.StdEncoding.Encode(scratch, bts) - err = w.WriteByte('"') - if err != nil { - return msg, scratch, err - } - _, err = w.Write(scratch) - if err != nil { - return msg, scratch, err - } - err = w.WriteByte('"') - return msg, scratch, err -} - -func rwNullBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - msg, err := ReadNilBytes(msg) - if err != nil { - return msg, scratch, err - } - _, err = w.Write(null) - return msg, scratch, err -} - -func rwBoolBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - b, msg, err := ReadBoolBytes(msg) - if err != nil { - return msg, scratch, err - } - if b { - _, err = w.WriteString("true") - return msg, scratch, err - } - _, err = w.WriteString("false") - return msg, scratch, err -} - -func rwIntBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - i, msg, err := ReadInt64Bytes(msg) - if err != nil { - return msg, scratch, err - } - scratch = strconv.AppendInt(scratch[0:0], i, 10) - _, err = w.Write(scratch) - return msg, scratch, err -} - -func rwUintBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - u, msg, err := ReadUint64Bytes(msg) - if err != nil { - return msg, scratch, err - } - scratch = strconv.AppendUint(scratch[0:0], u, 10) - _, err = w.Write(scratch) - return msg, scratch, err -} - -func rwFloatBytes(w jsWriter, msg []byte, f64 bool, scratch []byte) ([]byte, []byte, error) { - var f float64 - var err error - var sz int - if f64 { - sz = 64 - f, msg, err = ReadFloat64Bytes(msg) - } else { - sz = 32 - var v float32 - v, msg, err = ReadFloat32Bytes(msg) - f = float64(v) - } - if err != nil { - return msg, scratch, err - } - scratch = strconv.AppendFloat(scratch, f, 'f', -1, sz) - _, err = w.Write(scratch) - return msg, scratch, err -} - -func rwFloat32Bytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - var f float32 - var err error - f, msg, err = ReadFloat32Bytes(msg) - if err != nil { - return msg, scratch, err - } - scratch = strconv.AppendFloat(scratch[:0], float64(f), 'f', -1, 32) - _, err = w.Write(scratch) - return msg, scratch, err -} - -func rwFloat64Bytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - var f float64 - var err error - f, msg, err = ReadFloat64Bytes(msg) - if err != nil { - return msg, scratch, err - } - scratch = strconv.AppendFloat(scratch[:0], f, 'f', -1, 64) - _, err = w.Write(scratch) - return msg, scratch, err -} - -func rwTimeBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - var t time.Time - var err error - t, msg, err = ReadTimeBytes(msg) - if err != nil { - return msg, scratch, err - } - bts, err := t.MarshalJSON() - if err != nil { - return msg, scratch, err - } - _, err = w.Write(bts) - return msg, scratch, err -} - -func rwExtensionBytes(w jsWriter, msg []byte, scratch []byte) ([]byte, []byte, error) { - var err error - var et int8 - et, err = peekExtension(msg) - if err != nil { - return msg, scratch, err - } - - // if it's time.Time - if et == TimeExtension { - var tm time.Time - tm, msg, err = ReadTimeBytes(msg) - if err != nil { - return msg, scratch, err - } - bts, err := tm.MarshalJSON() - if err != nil { - return msg, scratch, err - } - _, err = w.Write(bts) - return msg, scratch, err - } - - // if the extension is registered, - // use its canonical JSON form - if f, ok := extensionReg[et]; ok { - e := f() - msg, err = ReadExtensionBytes(msg, e) - if err != nil { - return msg, scratch, err - } - bts, err := json.Marshal(e) - if err != nil { - return msg, scratch, err - } - _, err = w.Write(bts) - return msg, scratch, err - } - - // otherwise, write `{"type": , "data": ""}` - r := RawExtension{} - r.Type = et - msg, err = ReadExtensionBytes(msg, &r) - if err != nil { - return msg, scratch, err - } - scratch, err = writeExt(w, r, scratch) - return msg, scratch, err -} - -func writeExt(w jsWriter, r RawExtension, scratch []byte) ([]byte, error) { - _, err := w.WriteString(`{"type":`) - if err != nil { - return scratch, err - } - scratch = strconv.AppendInt(scratch[0:0], int64(r.Type), 10) - _, err = w.Write(scratch) - if err != nil { - return scratch, err - } - _, err = w.WriteString(`,"data":"`) - if err != nil { - return scratch, err - } - l := base64.StdEncoding.EncodedLen(len(r.Data)) - if cap(scratch) >= l { - scratch = scratch[0:l] - } else { - scratch = make([]byte, l) - } - base64.StdEncoding.Encode(scratch, r.Data) - _, err = w.Write(scratch) - if err != nil { - return scratch, err - } - _, err = w.WriteString(`"}`) - return scratch, err -} diff --git a/internal/msgp/number.go b/internal/msgp/number.go deleted file mode 100644 index ad07ef9958..0000000000 --- a/internal/msgp/number.go +++ /dev/null @@ -1,267 +0,0 @@ -package msgp - -import ( - "math" - "strconv" -) - -// The portable parts of the Number implementation - -// Number can be -// an int64, uint64, float32, -// or float64 internally. -// It can decode itself -// from any of the native -// messagepack number types. -// The zero-value of Number -// is Int(0). Using the equality -// operator with Number compares -// both the type and the value -// of the number. -type Number struct { - // internally, this - // is just a tagged union. - // the raw bits of the number - // are stored the same way regardless. - bits uint64 - typ Type -} - -// AsInt sets the number to an int64. -func (n *Number) AsInt(i int64) { - - // we always store int(0) - // as {0, InvalidType} in - // order to preserve - // the behavior of the == operator - if i == 0 { - n.typ = InvalidType - n.bits = 0 - return - } - - n.typ = IntType - n.bits = uint64(i) -} - -// AsUint sets the number to a uint64. -func (n *Number) AsUint(u uint64) { - n.typ = UintType - n.bits = u -} - -// AsFloat32 sets the value of the number -// to a float32. -func (n *Number) AsFloat32(f float32) { - n.typ = Float32Type - n.bits = uint64(math.Float32bits(f)) -} - -// AsFloat64 sets the value of the -// number to a float64. -func (n *Number) AsFloat64(f float64) { - n.typ = Float64Type - n.bits = math.Float64bits(f) -} - -// Int casts the number as an int64, and -// returns whether or not that was the -// underlying type. -func (n *Number) Int() (int64, bool) { - return int64(n.bits), n.typ == IntType || n.typ == InvalidType -} - -// Uint casts the number as a uint64, and returns -// whether or not that was the underlying type. -func (n *Number) Uint() (uint64, bool) { - return n.bits, n.typ == UintType -} - -// Float casts the number to a float64, and -// returns whether or not that was the underlying -// type (either a float64 or a float32). -func (n *Number) Float() (float64, bool) { - switch n.typ { - case Float32Type: - return float64(math.Float32frombits(uint32(n.bits))), true - case Float64Type: - return math.Float64frombits(n.bits), true - default: - return 0.0, false - } -} - -// Type will return one of: -// Float64Type, Float32Type, UintType, or IntType. -func (n *Number) Type() Type { - if n.typ == InvalidType { - return IntType - } - return n.typ -} - -// DecodeMsg implements msgp.Decodable -func (n *Number) DecodeMsg(r *Reader) error { - typ, err := r.NextType() - if err != nil { - return err - } - switch typ { - case Float32Type: - f, err := r.ReadFloat32() - if err != nil { - return err - } - n.AsFloat32(f) - return nil - case Float64Type: - f, err := r.ReadFloat64() - if err != nil { - return err - } - n.AsFloat64(f) - return nil - case IntType: - i, err := r.ReadInt64() - if err != nil { - return err - } - n.AsInt(i) - return nil - case UintType: - u, err := r.ReadUint64() - if err != nil { - return err - } - n.AsUint(u) - return nil - default: - return TypeError{Encoded: typ, Method: IntType} - } -} - -// UnmarshalMsg implements msgp.Unmarshaler -func (n *Number) UnmarshalMsg(b []byte) ([]byte, error) { - typ := NextType(b) - switch typ { - case IntType: - i, o, err := ReadInt64Bytes(b) - if err != nil { - return b, err - } - n.AsInt(i) - return o, nil - case UintType: - u, o, err := ReadUint64Bytes(b) - if err != nil { - return b, err - } - n.AsUint(u) - return o, nil - case Float64Type: - f, o, err := ReadFloat64Bytes(b) - if err != nil { - return b, err - } - n.AsFloat64(f) - return o, nil - case Float32Type: - f, o, err := ReadFloat32Bytes(b) - if err != nil { - return b, err - } - n.AsFloat32(f) - return o, nil - default: - return b, TypeError{Method: IntType, Encoded: typ} - } -} - -// MarshalMsg implements msgp.Marshaler -func (n *Number) MarshalMsg(b []byte) ([]byte, error) { - switch n.typ { - case IntType: - return AppendInt64(b, int64(n.bits)), nil - case UintType: - return AppendUint64(b, uint64(n.bits)), nil - case Float64Type: - return AppendFloat64(b, math.Float64frombits(n.bits)), nil - case Float32Type: - return AppendFloat32(b, math.Float32frombits(uint32(n.bits))), nil - default: - return AppendInt64(b, 0), nil - } -} - -// EncodeMsg implements msgp.Encodable -func (n *Number) EncodeMsg(w *Writer) error { - switch n.typ { - case IntType: - return w.WriteInt64(int64(n.bits)) - case UintType: - return w.WriteUint64(n.bits) - case Float64Type: - return w.WriteFloat64(math.Float64frombits(n.bits)) - case Float32Type: - return w.WriteFloat32(math.Float32frombits(uint32(n.bits))) - default: - return w.WriteInt64(0) - } -} - -// Msgsize implements msgp.Sizer -func (n *Number) Msgsize() int { - switch n.typ { - case Float32Type: - return Float32Size - case Float64Type: - return Float64Size - case IntType: - return Int64Size - case UintType: - return Uint64Size - default: - return 1 // fixint(0) - } -} - -// MarshalJSON implements json.Marshaler -func (n *Number) MarshalJSON() ([]byte, error) { - t := n.Type() - if t == InvalidType { - return []byte{'0'}, nil - } - out := make([]byte, 0, 32) - switch t { - case Float32Type, Float64Type: - f, _ := n.Float() - return strconv.AppendFloat(out, f, 'f', -1, 64), nil - case IntType: - i, _ := n.Int() - return strconv.AppendInt(out, i, 10), nil - case UintType: - u, _ := n.Uint() - return strconv.AppendUint(out, u, 10), nil - default: - panic("(*Number).typ is invalid") - } -} - -// String implements fmt.Stringer -func (n *Number) String() string { - switch n.typ { - case InvalidType: - return "0" - case Float32Type, Float64Type: - f, _ := n.Float() - return strconv.FormatFloat(f, 'f', -1, 64) - case IntType: - i, _ := n.Int() - return strconv.FormatInt(i, 10) - case UintType: - u, _ := n.Uint() - return strconv.FormatUint(u, 10) - default: - panic("(*Number).typ is invalid") - } -} diff --git a/internal/msgp/purego.go b/internal/msgp/purego.go deleted file mode 100644 index 2cd35c3e1d..0000000000 --- a/internal/msgp/purego.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build purego || appengine -// +build purego appengine - -package msgp - -// let's just assume appengine -// uses 64-bit hardware... -const smallint = false - -func UnsafeString(b []byte) string { - return string(b) -} - -func UnsafeBytes(s string) []byte { - return []byte(s) -} diff --git a/internal/msgp/read.go b/internal/msgp/read.go deleted file mode 100644 index 5f0de04895..0000000000 --- a/internal/msgp/read.go +++ /dev/null @@ -1,1363 +0,0 @@ -package msgp - -import ( - "io" - "math" - "sync" - "time" - - "github.com/gofiber/fiber/v2/internal/fwd" -) - -// where we keep old *Readers -var readerPool = sync.Pool{New: func() interface{} { return &Reader{} }} - -// Type is a MessagePack wire type, -// including this package's built-in -// extension types. -type Type byte - -// MessagePack Types -// -// The zero value of Type -// is InvalidType. -const ( - InvalidType Type = iota - - // MessagePack built-in types - - StrType - BinType - MapType - ArrayType - Float64Type - Float32Type - BoolType - IntType - UintType - NilType - ExtensionType - - // pseudo-types provided - // by extensions - - Complex64Type - Complex128Type - TimeType - - _maxtype -) - -// String implements fmt.Stringer -func (t Type) String() string { - switch t { - case StrType: - return "str" - case BinType: - return "bin" - case MapType: - return "map" - case ArrayType: - return "array" - case Float64Type: - return "float64" - case Float32Type: - return "float32" - case BoolType: - return "bool" - case UintType: - return "uint" - case IntType: - return "int" - case ExtensionType: - return "ext" - case NilType: - return "nil" - default: - return "" - } -} - -func freeR(m *Reader) { - readerPool.Put(m) -} - -// Unmarshaler is the interface fulfilled -// by objects that know how to unmarshal -// themselves from MessagePack. -// UnmarshalMsg unmarshals the object -// from binary, returing any leftover -// bytes and any errors encountered. -type Unmarshaler interface { - UnmarshalMsg([]byte) ([]byte, error) -} - -// Decodable is the interface fulfilled -// by objects that know how to read -// themselves from a *Reader. -type Decodable interface { - DecodeMsg(*Reader) error -} - -// Decode decodes 'd' from 'r'. -func Decode(r io.Reader, d Decodable) error { - rd := NewReader(r) - err := d.DecodeMsg(rd) - freeR(rd) - return err -} - -// NewReader returns a *Reader that -// reads from the provided reader. The -// reader will be buffered. -func NewReader(r io.Reader) *Reader { - p := readerPool.Get().(*Reader) - if p.R == nil { - p.R = fwd.NewReader(r) - } else { - p.R.Reset(r) - } - return p -} - -// NewReaderSize returns a *Reader with a buffer of the given size. -// (This is vastly preferable to passing the decoder a reader that is already buffered.) -func NewReaderSize(r io.Reader, sz int) *Reader { - return &Reader{R: fwd.NewReaderSize(r, sz)} -} - -// NewReaderBuf returns a *Reader with a provided buffer. -func NewReaderBuf(r io.Reader, buf []byte) *Reader { - return &Reader{R: fwd.NewReaderBuf(r, buf)} -} - -// Reader wraps an io.Reader and provides -// methods to read MessagePack-encoded values -// from it. Readers are buffered. -type Reader struct { - // R is the buffered reader - // that the Reader uses - // to decode MessagePack. - // The Reader itself - // is stateless; all the - // buffering is done - // within R. - R *fwd.Reader - scratch []byte -} - -// Read implements `io.Reader` -func (m *Reader) Read(p []byte) (int, error) { - return m.R.Read(p) -} - -// CopyNext reads the next object from m without decoding it and writes it to w. -// It avoids unnecessary copies internally. -func (m *Reader) CopyNext(w io.Writer) (int64, error) { - sz, o, err := getNextSize(m.R) - if err != nil { - return 0, err - } - - var n int64 - // Opportunistic optimization: if we can fit the whole thing in the m.R - // buffer, then just get a pointer to that, and pass it to w.Write, - // avoiding an allocation. - if int(sz) <= m.R.BufferSize() { - var nn int - var buf []byte - buf, err = m.R.Next(int(sz)) - if err != nil { - if err == io.ErrUnexpectedEOF { - err = ErrShortBytes - } - return 0, err - } - nn, err = w.Write(buf) - n += int64(nn) - } else { - // Fall back to io.CopyN. - // May avoid allocating if w is a ReaderFrom (e.g. bytes.Buffer) - n, err = io.CopyN(w, m.R, int64(sz)) - if err == io.ErrUnexpectedEOF { - err = ErrShortBytes - } - } - if err != nil { - return n, err - } else if n < int64(sz) { - return n, io.ErrShortWrite - } - - // for maps and slices, read elements - for x := uintptr(0); x < o; x++ { - var n2 int64 - n2, err = m.CopyNext(w) - if err != nil { - return n, err - } - n += n2 - } - return n, nil -} - -// ReadFull implements `io.ReadFull` -func (m *Reader) ReadFull(p []byte) (int, error) { - return m.R.ReadFull(p) -} - -// Reset resets the underlying reader. -func (m *Reader) Reset(r io.Reader) { m.R.Reset(r) } - -// Buffered returns the number of bytes currently in the read buffer. -func (m *Reader) Buffered() int { return m.R.Buffered() } - -// BufferSize returns the capacity of the read buffer. -func (m *Reader) BufferSize() int { return m.R.BufferSize() } - -// NextType returns the next object type to be decoded. -func (m *Reader) NextType() (Type, error) { - p, err := m.R.Peek(1) - if err != nil { - return InvalidType, err - } - t := getType(p[0]) - if t == InvalidType { - return t, InvalidPrefixError(p[0]) - } - if t == ExtensionType { - v, err := m.peekExtensionType() - if err != nil { - return InvalidType, err - } - switch v { - case Complex64Extension: - return Complex64Type, nil - case Complex128Extension: - return Complex128Type, nil - case TimeExtension: - return TimeType, nil - } - } - return t, nil -} - -// IsNil returns whether or not -// the next byte is a null messagepack byte -func (m *Reader) IsNil() bool { - p, err := m.R.Peek(1) - return err == nil && p[0] == mnil -} - -// getNextSize returns the size of the next object on the wire. -// returns (obj size, obj elements, error) -// only maps and arrays have non-zero obj elements -// for maps and arrays, obj size does not include elements -// -// use uintptr b/c it's guaranteed to be large enough -// to hold whatever we can fit in memory. -func getNextSize(r *fwd.Reader) (uintptr, uintptr, error) { - b, err := r.Peek(1) - if err != nil { - return 0, 0, err - } - lead := b[0] - spec := &sizes[lead] - size, mode := spec.size, spec.extra - if size == 0 { - return 0, 0, InvalidPrefixError(lead) - } - if mode >= 0 { - return uintptr(size), uintptr(mode), nil - } - b, err = r.Peek(int(size)) - if err != nil { - return 0, 0, err - } - switch mode { - case extra8: - return uintptr(size) + uintptr(b[1]), 0, nil - case extra16: - return uintptr(size) + uintptr(big.Uint16(b[1:])), 0, nil - case extra32: - return uintptr(size) + uintptr(big.Uint32(b[1:])), 0, nil - case map16v: - return uintptr(size), 2 * uintptr(big.Uint16(b[1:])), nil - case map32v: - return uintptr(size), 2 * uintptr(big.Uint32(b[1:])), nil - case array16v: - return uintptr(size), uintptr(big.Uint16(b[1:])), nil - case array32v: - return uintptr(size), uintptr(big.Uint32(b[1:])), nil - default: - return 0, 0, fatal - } -} - -// Skip skips over the next object, regardless of -// its type. If it is an array or map, the whole array -// or map will be skipped. -func (m *Reader) Skip() error { - var ( - v uintptr // bytes - o uintptr // objects - err error - p []byte - ) - - // we can use the faster - // method if we have enough - // buffered data - if m.R.Buffered() >= 5 { - p, err = m.R.Peek(5) - if err != nil { - return err - } - v, o, err = getSize(p) - if err != nil { - return err - } - } else { - v, o, err = getNextSize(m.R) - if err != nil { - return err - } - } - - // 'v' is always non-zero - // if err == nil - _, err = m.R.Skip(int(v)) - if err != nil { - return err - } - - // for maps and slices, skip elements - for x := uintptr(0); x < o; x++ { - err = m.Skip() - if err != nil { - return err - } - } - return nil -} - -// ReadMapHeader reads the next object -// as a map header and returns the size -// of the map and the number of bytes written. -// It will return a TypeError{} if the next -// object is not a map. -func (m *Reader) ReadMapHeader() (sz uint32, err error) { - var p []byte - var lead byte - p, err = m.R.Peek(1) - if err != nil { - return - } - lead = p[0] - if isfixmap(lead) { - sz = uint32(rfixmap(lead)) - _, err = m.R.Skip(1) - return - } - switch lead { - case mmap16: - p, err = m.R.Next(3) - if err != nil { - return - } - sz = uint32(big.Uint16(p[1:])) - return - case mmap32: - p, err = m.R.Next(5) - if err != nil { - return - } - sz = big.Uint32(p[1:]) - return - default: - err = badPrefix(MapType, lead) - return - } -} - -// ReadMapKey reads either a 'str' or 'bin' field from -// the reader and returns the value as a []byte. It uses -// scratch for storage if it is large enough. -func (m *Reader) ReadMapKey(scratch []byte) ([]byte, error) { - out, err := m.ReadStringAsBytes(scratch) - if err != nil { - if tperr, ok := err.(TypeError); ok && tperr.Encoded == BinType { - return m.ReadBytes(scratch) - } - return nil, err - } - return out, nil -} - -// MapKeyPtr returns a []byte pointing to the contents -// of a valid map key. The key cannot be empty, and it -// must be shorter than the total buffer size of the -// *Reader. Additionally, the returned slice is only -// valid until the next *Reader method call. Users -// should exercise extreme care when using this -// method; writing into the returned slice may -// corrupt future reads. -func (m *Reader) ReadMapKeyPtr() ([]byte, error) { - p, err := m.R.Peek(1) - if err != nil { - return nil, err - } - lead := p[0] - var read int - if isfixstr(lead) { - read = int(rfixstr(lead)) - m.R.Skip(1) - goto fill - } - switch lead { - case mstr8, mbin8: - p, err = m.R.Next(2) - if err != nil { - return nil, err - } - read = int(p[1]) - case mstr16, mbin16: - p, err = m.R.Next(3) - if err != nil { - return nil, err - } - read = int(big.Uint16(p[1:])) - case mstr32, mbin32: - p, err = m.R.Next(5) - if err != nil { - return nil, err - } - read = int(big.Uint32(p[1:])) - default: - return nil, badPrefix(StrType, lead) - } -fill: - if read == 0 { - return nil, ErrShortBytes - } - return m.R.Next(read) -} - -// ReadArrayHeader reads the next object as an -// array header and returns the size of the array -// and the number of bytes read. -func (m *Reader) ReadArrayHeader() (sz uint32, err error) { - var lead byte - var p []byte - p, err = m.R.Peek(1) - if err != nil { - return - } - lead = p[0] - if isfixarray(lead) { - sz = uint32(rfixarray(lead)) - _, err = m.R.Skip(1) - return - } - switch lead { - case marray16: - p, err = m.R.Next(3) - if err != nil { - return - } - sz = uint32(big.Uint16(p[1:])) - return - - case marray32: - p, err = m.R.Next(5) - if err != nil { - return - } - sz = big.Uint32(p[1:]) - return - - default: - err = badPrefix(ArrayType, lead) - return - } -} - -// ReadNil reads a 'nil' MessagePack byte from the reader -func (m *Reader) ReadNil() error { - p, err := m.R.Peek(1) - if err != nil { - return err - } - if p[0] != mnil { - return badPrefix(NilType, p[0]) - } - _, err = m.R.Skip(1) - return err -} - -// ReadFloat64 reads a float64 from the reader. -// (If the value on the wire is encoded as a float32, -// it will be up-cast to a float64.) -func (m *Reader) ReadFloat64() (f float64, err error) { - var p []byte - p, err = m.R.Peek(9) - if err != nil { - // we'll allow a coversion from float32 to float64, - // since we don't lose any precision - if err == io.EOF && len(p) > 0 && p[0] == mfloat32 { - ef, err := m.ReadFloat32() - return float64(ef), err - } - return - } - if p[0] != mfloat64 { - // see above - if p[0] == mfloat32 { - ef, err := m.ReadFloat32() - return float64(ef), err - } - err = badPrefix(Float64Type, p[0]) - return - } - f = math.Float64frombits(getMuint64(p)) - _, err = m.R.Skip(9) - return -} - -// ReadFloat32 reads a float32 from the reader -func (m *Reader) ReadFloat32() (f float32, err error) { - var p []byte - p, err = m.R.Peek(5) - if err != nil { - return - } - if p[0] != mfloat32 { - err = badPrefix(Float32Type, p[0]) - return - } - f = math.Float32frombits(getMuint32(p)) - _, err = m.R.Skip(5) - return -} - -// ReadBool reads a bool from the reader -func (m *Reader) ReadBool() (b bool, err error) { - var p []byte - p, err = m.R.Peek(1) - if err != nil { - return - } - switch p[0] { - case mtrue: - b = true - case mfalse: - default: - err = badPrefix(BoolType, p[0]) - return - } - _, err = m.R.Skip(1) - return -} - -// ReadInt64 reads an int64 from the reader -func (m *Reader) ReadInt64() (i int64, err error) { - var p []byte - var lead byte - p, err = m.R.Peek(1) - if err != nil { - return - } - lead = p[0] - - if isfixint(lead) { - i = int64(rfixint(lead)) - _, err = m.R.Skip(1) - return - } else if isnfixint(lead) { - i = int64(rnfixint(lead)) - _, err = m.R.Skip(1) - return - } - - switch lead { - case mint8: - p, err = m.R.Next(2) - if err != nil { - return - } - i = int64(getMint8(p)) - return - - case muint8: - p, err = m.R.Next(2) - if err != nil { - return - } - i = int64(getMuint8(p)) - return - - case mint16: - p, err = m.R.Next(3) - if err != nil { - return - } - i = int64(getMint16(p)) - return - - case muint16: - p, err = m.R.Next(3) - if err != nil { - return - } - i = int64(getMuint16(p)) - return - - case mint32: - p, err = m.R.Next(5) - if err != nil { - return - } - i = int64(getMint32(p)) - return - - case muint32: - p, err = m.R.Next(5) - if err != nil { - return - } - i = int64(getMuint32(p)) - return - - case mint64: - p, err = m.R.Next(9) - if err != nil { - return - } - i = getMint64(p) - return - - case muint64: - p, err = m.R.Next(9) - if err != nil { - return - } - u := getMuint64(p) - if u > math.MaxInt64 { - err = UintOverflow{Value: u, FailedBitsize: 64} - return - } - i = int64(u) - return - - default: - err = badPrefix(IntType, lead) - return - } -} - -// ReadInt32 reads an int32 from the reader -func (m *Reader) ReadInt32() (i int32, err error) { - var in int64 - in, err = m.ReadInt64() - if in > math.MaxInt32 || in < math.MinInt32 { - err = IntOverflow{Value: in, FailedBitsize: 32} - return - } - i = int32(in) - return -} - -// ReadInt16 reads an int16 from the reader -func (m *Reader) ReadInt16() (i int16, err error) { - var in int64 - in, err = m.ReadInt64() - if in > math.MaxInt16 || in < math.MinInt16 { - err = IntOverflow{Value: in, FailedBitsize: 16} - return - } - i = int16(in) - return -} - -// ReadInt8 reads an int8 from the reader -func (m *Reader) ReadInt8() (i int8, err error) { - var in int64 - in, err = m.ReadInt64() - if in > math.MaxInt8 || in < math.MinInt8 { - err = IntOverflow{Value: in, FailedBitsize: 8} - return - } - i = int8(in) - return -} - -// ReadInt reads an int from the reader -func (m *Reader) ReadInt() (i int, err error) { - if smallint { - var in int32 - in, err = m.ReadInt32() - i = int(in) - return - } - var in int64 - in, err = m.ReadInt64() - i = int(in) - return -} - -// ReadUint64 reads a uint64 from the reader -func (m *Reader) ReadUint64() (u uint64, err error) { - var p []byte - var lead byte - p, err = m.R.Peek(1) - if err != nil { - return - } - lead = p[0] - if isfixint(lead) { - u = uint64(rfixint(lead)) - _, err = m.R.Skip(1) - return - } - switch lead { - case mint8: - p, err = m.R.Next(2) - if err != nil { - return - } - v := int64(getMint8(p)) - if v < 0 { - err = UintBelowZero{Value: v} - return - } - u = uint64(v) - return - - case muint8: - p, err = m.R.Next(2) - if err != nil { - return - } - u = uint64(getMuint8(p)) - return - - case mint16: - p, err = m.R.Next(3) - if err != nil { - return - } - v := int64(getMint16(p)) - if v < 0 { - err = UintBelowZero{Value: v} - return - } - u = uint64(v) - return - - case muint16: - p, err = m.R.Next(3) - if err != nil { - return - } - u = uint64(getMuint16(p)) - return - - case mint32: - p, err = m.R.Next(5) - if err != nil { - return - } - v := int64(getMint32(p)) - if v < 0 { - err = UintBelowZero{Value: v} - return - } - u = uint64(v) - return - - case muint32: - p, err = m.R.Next(5) - if err != nil { - return - } - u = uint64(getMuint32(p)) - return - - case mint64: - p, err = m.R.Next(9) - if err != nil { - return - } - v := int64(getMint64(p)) - if v < 0 { - err = UintBelowZero{Value: v} - return - } - u = uint64(v) - return - - case muint64: - p, err = m.R.Next(9) - if err != nil { - return - } - u = getMuint64(p) - return - - default: - if isnfixint(lead) { - err = UintBelowZero{Value: int64(rnfixint(lead))} - } else { - err = badPrefix(UintType, lead) - } - return - - } -} - -// ReadUint32 reads a uint32 from the reader -func (m *Reader) ReadUint32() (u uint32, err error) { - var in uint64 - in, err = m.ReadUint64() - if in > math.MaxUint32 { - err = UintOverflow{Value: in, FailedBitsize: 32} - return - } - u = uint32(in) - return -} - -// ReadUint16 reads a uint16 from the reader -func (m *Reader) ReadUint16() (u uint16, err error) { - var in uint64 - in, err = m.ReadUint64() - if in > math.MaxUint16 { - err = UintOverflow{Value: in, FailedBitsize: 16} - return - } - u = uint16(in) - return -} - -// ReadUint8 reads a uint8 from the reader -func (m *Reader) ReadUint8() (u uint8, err error) { - var in uint64 - in, err = m.ReadUint64() - if in > math.MaxUint8 { - err = UintOverflow{Value: in, FailedBitsize: 8} - return - } - u = uint8(in) - return -} - -// ReadUint reads a uint from the reader -func (m *Reader) ReadUint() (u uint, err error) { - if smallint { - var un uint32 - un, err = m.ReadUint32() - u = uint(un) - return - } - var un uint64 - un, err = m.ReadUint64() - u = uint(un) - return -} - -// ReadByte is analogous to ReadUint8. -// -// NOTE: this is *not* an implementation -// of io.ByteReader. -func (m *Reader) ReadByte() (b byte, err error) { - var in uint64 - in, err = m.ReadUint64() - if in > math.MaxUint8 { - err = UintOverflow{Value: in, FailedBitsize: 8} - return - } - b = byte(in) - return -} - -// ReadBytes reads a MessagePack 'bin' object -// from the reader and returns its value. It may -// use 'scratch' for storage if it is non-nil. -func (m *Reader) ReadBytes(scratch []byte) (b []byte, err error) { - var p []byte - var lead byte - p, err = m.R.Peek(2) - if err != nil { - return - } - lead = p[0] - var read int64 - switch lead { - case mbin8: - read = int64(p[1]) - m.R.Skip(2) - case mbin16: - p, err = m.R.Next(3) - if err != nil { - return - } - read = int64(big.Uint16(p[1:])) - case mbin32: - p, err = m.R.Next(5) - if err != nil { - return - } - read = int64(big.Uint32(p[1:])) - default: - err = badPrefix(BinType, lead) - return - } - if int64(cap(scratch)) < read { - b = make([]byte, read) - } else { - b = scratch[0:read] - } - _, err = m.R.ReadFull(b) - return -} - -// ReadBytesHeader reads the size header -// of a MessagePack 'bin' object. The user -// is responsible for dealing with the next -// 'sz' bytes from the reader in an application-specific -// way. -func (m *Reader) ReadBytesHeader() (sz uint32, err error) { - var p []byte - p, err = m.R.Peek(1) - if err != nil { - return - } - switch p[0] { - case mbin8: - p, err = m.R.Next(2) - if err != nil { - return - } - sz = uint32(p[1]) - return - case mbin16: - p, err = m.R.Next(3) - if err != nil { - return - } - sz = uint32(big.Uint16(p[1:])) - return - case mbin32: - p, err = m.R.Next(5) - if err != nil { - return - } - sz = uint32(big.Uint32(p[1:])) - return - default: - err = badPrefix(BinType, p[0]) - return - } -} - -// ReadExactBytes reads a MessagePack 'bin'-encoded -// object off of the wire into the provided slice. An -// ArrayError will be returned if the object is not -// exactly the length of the input slice. -func (m *Reader) ReadExactBytes(into []byte) error { - p, err := m.R.Peek(2) - if err != nil { - return err - } - lead := p[0] - var read int64 // bytes to read - var skip int // prefix size to skip - switch lead { - case mbin8: - read = int64(p[1]) - skip = 2 - case mbin16: - p, err = m.R.Peek(3) - if err != nil { - return err - } - read = int64(big.Uint16(p[1:])) - skip = 3 - case mbin32: - p, err = m.R.Peek(5) - if err != nil { - return err - } - read = int64(big.Uint32(p[1:])) - skip = 5 - default: - return badPrefix(BinType, lead) - } - if read != int64(len(into)) { - return ArrayError{Wanted: uint32(len(into)), Got: uint32(read)} - } - m.R.Skip(skip) - _, err = m.R.ReadFull(into) - return err -} - -// ReadStringAsBytes reads a MessagePack 'str' (utf-8) string -// and returns its value as bytes. It may use 'scratch' for storage -// if it is non-nil. -func (m *Reader) ReadStringAsBytes(scratch []byte) (b []byte, err error) { - var p []byte - var lead byte - p, err = m.R.Peek(1) - if err != nil { - return - } - lead = p[0] - var read int64 - - if isfixstr(lead) { - read = int64(rfixstr(lead)) - m.R.Skip(1) - goto fill - } - - switch lead { - case mstr8: - p, err = m.R.Next(2) - if err != nil { - return - } - read = int64(uint8(p[1])) - case mstr16: - p, err = m.R.Next(3) - if err != nil { - return - } - read = int64(big.Uint16(p[1:])) - case mstr32: - p, err = m.R.Next(5) - if err != nil { - return - } - read = int64(big.Uint32(p[1:])) - default: - err = badPrefix(StrType, lead) - return - } -fill: - if int64(cap(scratch)) < read { - b = make([]byte, read) - } else { - b = scratch[0:read] - } - _, err = m.R.ReadFull(b) - return -} - -// ReadStringHeader reads a string header -// off of the wire. The user is then responsible -// for dealing with the next 'sz' bytes from -// the reader in an application-specific manner. -func (m *Reader) ReadStringHeader() (sz uint32, err error) { - var p []byte - p, err = m.R.Peek(1) - if err != nil { - return - } - lead := p[0] - if isfixstr(lead) { - sz = uint32(rfixstr(lead)) - m.R.Skip(1) - return - } - switch lead { - case mstr8: - p, err = m.R.Next(2) - if err != nil { - return - } - sz = uint32(p[1]) - return - case mstr16: - p, err = m.R.Next(3) - if err != nil { - return - } - sz = uint32(big.Uint16(p[1:])) - return - case mstr32: - p, err = m.R.Next(5) - if err != nil { - return - } - sz = big.Uint32(p[1:]) - return - default: - err = badPrefix(StrType, lead) - return - } -} - -// ReadString reads a utf-8 string from the reader -func (m *Reader) ReadString() (s string, err error) { - var p []byte - var lead byte - var read int64 - p, err = m.R.Peek(1) - if err != nil { - return - } - lead = p[0] - - if isfixstr(lead) { - read = int64(rfixstr(lead)) - m.R.Skip(1) - goto fill - } - - switch lead { - case mstr8: - p, err = m.R.Next(2) - if err != nil { - return - } - read = int64(uint8(p[1])) - case mstr16: - p, err = m.R.Next(3) - if err != nil { - return - } - read = int64(big.Uint16(p[1:])) - case mstr32: - p, err = m.R.Next(5) - if err != nil { - return - } - read = int64(big.Uint32(p[1:])) - default: - err = badPrefix(StrType, lead) - return - } -fill: - if read == 0 { - s, err = "", nil - return - } - // reading into the memory - // that will become the string - // itself has vastly superior - // worst-case performance, because - // the reader buffer doesn't have - // to be large enough to hold the string. - // the idea here is to make it more - // difficult for someone malicious - // to cause the system to run out of - // memory by sending very large strings. - // - // NOTE: this works because the argument - // passed to (*fwd.Reader).ReadFull escapes - // to the heap; its argument may, in turn, - // be passed to the underlying reader, and - // thus escape analysis *must* conclude that - // 'out' escapes. - out := make([]byte, read) - _, err = m.R.ReadFull(out) - if err != nil { - return - } - s = UnsafeString(out) - return -} - -// ReadComplex64 reads a complex64 from the reader -func (m *Reader) ReadComplex64() (f complex64, err error) { - var p []byte - p, err = m.R.Peek(10) - if err != nil { - return - } - if p[0] != mfixext8 { - err = badPrefix(Complex64Type, p[0]) - return - } - if int8(p[1]) != Complex64Extension { - err = errExt(int8(p[1]), Complex64Extension) - return - } - f = complex(math.Float32frombits(big.Uint32(p[2:])), - math.Float32frombits(big.Uint32(p[6:]))) - _, err = m.R.Skip(10) - return -} - -// ReadComplex128 reads a complex128 from the reader -func (m *Reader) ReadComplex128() (f complex128, err error) { - var p []byte - p, err = m.R.Peek(18) - if err != nil { - return - } - if p[0] != mfixext16 { - err = badPrefix(Complex128Type, p[0]) - return - } - if int8(p[1]) != Complex128Extension { - err = errExt(int8(p[1]), Complex128Extension) - return - } - f = complex(math.Float64frombits(big.Uint64(p[2:])), - math.Float64frombits(big.Uint64(p[10:]))) - _, err = m.R.Skip(18) - return -} - -// ReadMapStrIntf reads a MessagePack map into a map[string]interface{}. -// (You must pass a non-nil map into the function.) -func (m *Reader) ReadMapStrIntf(mp map[string]interface{}) (err error) { - var sz uint32 - sz, err = m.ReadMapHeader() - if err != nil { - return - } - for key := range mp { - delete(mp, key) - } - for i := uint32(0); i < sz; i++ { - var key string - var val interface{} - key, err = m.ReadString() - if err != nil { - return - } - val, err = m.ReadIntf() - if err != nil { - return - } - mp[key] = val - } - return -} - -// ReadTime reads a time.Time object from the reader. -// The returned time's location will be set to time.Local. -func (m *Reader) ReadTime() (t time.Time, err error) { - var p []byte - p, err = m.R.Peek(15) - if err != nil { - return - } - if p[0] != mext8 || p[1] != 12 { - err = badPrefix(TimeType, p[0]) - return - } - if int8(p[2]) != TimeExtension { - err = errExt(int8(p[2]), TimeExtension) - return - } - sec, nsec := getUnix(p[3:]) - t = time.Unix(sec, int64(nsec)).Local() - _, err = m.R.Skip(15) - return -} - -// ReadIntf reads out the next object as a raw interface{}. -// Arrays are decoded as []interface{}, and maps are decoded -// as map[string]interface{}. Integers are decoded as int64 -// and unsigned integers are decoded as uint64. -func (m *Reader) ReadIntf() (i interface{}, err error) { - var t Type - t, err = m.NextType() - if err != nil { - return - } - switch t { - case BoolType: - i, err = m.ReadBool() - return - - case IntType: - i, err = m.ReadInt64() - return - - case UintType: - i, err = m.ReadUint64() - return - - case BinType: - i, err = m.ReadBytes(nil) - return - - case StrType: - i, err = m.ReadString() - return - - case Complex64Type: - i, err = m.ReadComplex64() - return - - case Complex128Type: - i, err = m.ReadComplex128() - return - - case TimeType: - i, err = m.ReadTime() - return - - case ExtensionType: - var t int8 - t, err = m.peekExtensionType() - if err != nil { - return - } - f, ok := extensionReg[t] - if ok { - e := f() - err = m.ReadExtension(e) - i = e - return - } - var e RawExtension - e.Type = t - err = m.ReadExtension(&e) - i = &e - return - - case MapType: - mp := make(map[string]interface{}) - err = m.ReadMapStrIntf(mp) - i = mp - return - - case NilType: - err = m.ReadNil() - i = nil - return - - case Float32Type: - i, err = m.ReadFloat32() - return - - case Float64Type: - i, err = m.ReadFloat64() - return - - case ArrayType: - var sz uint32 - sz, err = m.ReadArrayHeader() - - if err != nil { - return - } - out := make([]interface{}, int(sz)) - for j := range out { - out[j], err = m.ReadIntf() - if err != nil { - return - } - } - i = out - return - - default: - return nil, fatal // unreachable - } -} diff --git a/internal/msgp/read_bytes.go b/internal/msgp/read_bytes.go deleted file mode 100644 index 61f7b7124f..0000000000 --- a/internal/msgp/read_bytes.go +++ /dev/null @@ -1,1197 +0,0 @@ -package msgp - -import ( - "bytes" - "encoding/binary" - "math" - "time" -) - -var big = binary.BigEndian - -// NextType returns the type of the next -// object in the slice. If the length -// of the input is zero, it returns -// InvalidType. -func NextType(b []byte) Type { - if len(b) == 0 { - return InvalidType - } - spec := sizes[b[0]] - t := spec.typ - if t == ExtensionType && len(b) > int(spec.size) { - var tp int8 - if spec.extra == constsize { - tp = int8(b[1]) - } else { - tp = int8(b[spec.size-1]) - } - switch tp { - case TimeExtension: - return TimeType - case Complex128Extension: - return Complex128Type - case Complex64Extension: - return Complex64Type - default: - return ExtensionType - } - } - return t -} - -// IsNil returns true if len(b)>0 and -// the leading byte is a 'nil' MessagePack -// byte; false otherwise -func IsNil(b []byte) bool { - if len(b) != 0 && b[0] == mnil { - return true - } - return false -} - -// Raw is raw MessagePack. -// Raw allows you to read and write -// data without interpreting its contents. -type Raw []byte - -// MarshalMsg implements msgp.Marshaler. -// It appends the raw contents of 'raw' -// to the provided byte slice. If 'raw' -// is 0 bytes, 'nil' will be appended instead. -func (r Raw) MarshalMsg(b []byte) ([]byte, error) { - i := len(r) - if i == 0 { - return AppendNil(b), nil - } - o, l := ensure(b, i) - copy(o[l:], []byte(r)) - return o, nil -} - -// UnmarshalMsg implements msgp.Unmarshaler. -// It sets the contents of *Raw to be the next -// object in the provided byte slice. -func (r *Raw) UnmarshalMsg(b []byte) ([]byte, error) { - l := len(b) - out, err := Skip(b) - if err != nil { - return b, err - } - rlen := l - len(out) - if IsNil(b[:rlen]) { - rlen = 0 - } - if cap(*r) < rlen { - *r = make(Raw, rlen) - } else { - *r = (*r)[0:rlen] - } - copy(*r, b[:rlen]) - return out, nil -} - -// EncodeMsg implements msgp.Encodable. -// It writes the raw bytes to the writer. -// If r is empty, it writes 'nil' instead. -func (r Raw) EncodeMsg(w *Writer) error { - if len(r) == 0 { - return w.WriteNil() - } - _, err := w.Write([]byte(r)) - return err -} - -// DecodeMsg implements msgp.Decodable. -// It sets the value of *Raw to be the -// next object on the wire. -func (r *Raw) DecodeMsg(f *Reader) error { - *r = (*r)[:0] - err := appendNext(f, (*[]byte)(r)) - if IsNil(*r) { - *r = (*r)[:0] - } - return err -} - -// Msgsize implements msgp.Sizer -func (r Raw) Msgsize() int { - l := len(r) - if l == 0 { - return 1 // for 'nil' - } - return l -} - -func appendNext(f *Reader, d *[]byte) error { - amt, o, err := getNextSize(f.R) - if err != nil { - return err - } - var i int - *d, i = ensure(*d, int(amt)) - _, err = f.R.ReadFull((*d)[i:]) - if err != nil { - return err - } - for o > 0 { - err = appendNext(f, d) - if err != nil { - return err - } - o-- - } - return nil -} - -// MarshalJSON implements json.Marshaler -func (r *Raw) MarshalJSON() ([]byte, error) { - var buf bytes.Buffer - _, err := UnmarshalAsJSON(&buf, []byte(*r)) - return buf.Bytes(), err -} - -// ReadMapHeaderBytes reads a map header size -// from 'b' and returns the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a map) -func ReadMapHeaderBytes(b []byte) (sz uint32, o []byte, err error) { - l := len(b) - if l < 1 { - err = ErrShortBytes - return - } - - lead := b[0] - if isfixmap(lead) { - sz = uint32(rfixmap(lead)) - o = b[1:] - return - } - - switch lead { - case mmap16: - if l < 3 { - err = ErrShortBytes - return - } - sz = uint32(big.Uint16(b[1:])) - o = b[3:] - return - - case mmap32: - if l < 5 { - err = ErrShortBytes - return - } - sz = big.Uint32(b[1:]) - o = b[5:] - return - - default: - err = badPrefix(MapType, lead) - return - } -} - -// ReadMapKeyZC attempts to read a map key -// from 'b' and returns the key bytes and the remaining bytes -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a str or bin) -func ReadMapKeyZC(b []byte) ([]byte, []byte, error) { - o, x, err := ReadStringZC(b) - if err != nil { - if tperr, ok := err.(TypeError); ok && tperr.Encoded == BinType { - return ReadBytesZC(b) - } - return nil, b, err - } - return o, x, nil -} - -// ReadArrayHeaderBytes attempts to read -// the array header size off of 'b' and return -// the size and remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not an array) -func ReadArrayHeaderBytes(b []byte) (sz uint32, o []byte, err error) { - if len(b) < 1 { - return 0, nil, ErrShortBytes - } - lead := b[0] - if isfixarray(lead) { - sz = uint32(rfixarray(lead)) - o = b[1:] - return - } - - switch lead { - case marray16: - if len(b) < 3 { - err = ErrShortBytes - return - } - sz = uint32(big.Uint16(b[1:])) - o = b[3:] - return - - case marray32: - if len(b) < 5 { - err = ErrShortBytes - return - } - sz = big.Uint32(b[1:]) - o = b[5:] - return - - default: - err = badPrefix(ArrayType, lead) - return - } -} - -// ReadNilBytes tries to read a "nil" byte -// off of 'b' and return the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a 'nil') -// - InvalidPrefixError -func ReadNilBytes(b []byte) ([]byte, error) { - if len(b) < 1 { - return nil, ErrShortBytes - } - if b[0] != mnil { - return b, badPrefix(NilType, b[0]) - } - return b[1:], nil -} - -// ReadFloat64Bytes tries to read a float64 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a float64) -func ReadFloat64Bytes(b []byte) (f float64, o []byte, err error) { - if len(b) < 9 { - if len(b) >= 5 && b[0] == mfloat32 { - var tf float32 - tf, o, err = ReadFloat32Bytes(b) - f = float64(tf) - return - } - err = ErrShortBytes - return - } - - if b[0] != mfloat64 { - if b[0] == mfloat32 { - var tf float32 - tf, o, err = ReadFloat32Bytes(b) - f = float64(tf) - return - } - err = badPrefix(Float64Type, b[0]) - return - } - - f = math.Float64frombits(getMuint64(b)) - o = b[9:] - return -} - -// ReadFloat32Bytes tries to read a float64 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a float32) -func ReadFloat32Bytes(b []byte) (f float32, o []byte, err error) { - if len(b) < 5 { - err = ErrShortBytes - return - } - - if b[0] != mfloat32 { - err = TypeError{Method: Float32Type, Encoded: getType(b[0])} - return - } - - f = math.Float32frombits(getMuint32(b)) - o = b[5:] - return -} - -// ReadBoolBytes tries to read a float64 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a bool) -func ReadBoolBytes(b []byte) (bool, []byte, error) { - if len(b) < 1 { - return false, b, ErrShortBytes - } - switch b[0] { - case mtrue: - return true, b[1:], nil - case mfalse: - return false, b[1:], nil - default: - return false, b, badPrefix(BoolType, b[0]) - } -} - -// ReadInt64Bytes tries to read an int64 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError (not a int) -func ReadInt64Bytes(b []byte) (i int64, o []byte, err error) { - l := len(b) - if l < 1 { - return 0, nil, ErrShortBytes - } - - lead := b[0] - if isfixint(lead) { - i = int64(rfixint(lead)) - o = b[1:] - return - } - if isnfixint(lead) { - i = int64(rnfixint(lead)) - o = b[1:] - return - } - - switch lead { - case mint8: - if l < 2 { - err = ErrShortBytes - return - } - i = int64(getMint8(b)) - o = b[2:] - return - - case muint8: - if l < 2 { - err = ErrShortBytes - return - } - i = int64(getMuint8(b)) - o = b[2:] - return - - case mint16: - if l < 3 { - err = ErrShortBytes - return - } - i = int64(getMint16(b)) - o = b[3:] - return - - case muint16: - if l < 3 { - err = ErrShortBytes - return - } - i = int64(getMuint16(b)) - o = b[3:] - return - - case mint32: - if l < 5 { - err = ErrShortBytes - return - } - i = int64(getMint32(b)) - o = b[5:] - return - - case muint32: - if l < 5 { - err = ErrShortBytes - return - } - i = int64(getMuint32(b)) - o = b[5:] - return - - case mint64: - if l < 9 { - err = ErrShortBytes - return - } - i = int64(getMint64(b)) - o = b[9:] - return - - case muint64: - if l < 9 { - err = ErrShortBytes - return - } - u := getMuint64(b) - if u > math.MaxInt64 { - err = UintOverflow{Value: u, FailedBitsize: 64} - return - } - i = int64(u) - o = b[9:] - return - - default: - err = badPrefix(IntType, lead) - return - } -} - -// ReadInt32Bytes tries to read an int32 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a int) -// - IntOverflow{} (value doesn't fit in int32) -func ReadInt32Bytes(b []byte) (int32, []byte, error) { - i, o, err := ReadInt64Bytes(b) - if i > math.MaxInt32 || i < math.MinInt32 { - return 0, o, IntOverflow{Value: i, FailedBitsize: 32} - } - return int32(i), o, err -} - -// ReadInt16Bytes tries to read an int16 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a int) -// - IntOverflow{} (value doesn't fit in int16) -func ReadInt16Bytes(b []byte) (int16, []byte, error) { - i, o, err := ReadInt64Bytes(b) - if i > math.MaxInt16 || i < math.MinInt16 { - return 0, o, IntOverflow{Value: i, FailedBitsize: 16} - } - return int16(i), o, err -} - -// ReadInt8Bytes tries to read an int16 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a int) -// - IntOverflow{} (value doesn't fit in int8) -func ReadInt8Bytes(b []byte) (int8, []byte, error) { - i, o, err := ReadInt64Bytes(b) - if i > math.MaxInt8 || i < math.MinInt8 { - return 0, o, IntOverflow{Value: i, FailedBitsize: 8} - } - return int8(i), o, err -} - -// ReadIntBytes tries to read an int -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a int) -// - IntOverflow{} (value doesn't fit in int; 32-bit platforms only) -func ReadIntBytes(b []byte) (int, []byte, error) { - if smallint { - i, b, err := ReadInt32Bytes(b) - return int(i), b, err - } - i, b, err := ReadInt64Bytes(b) - return int(i), b, err -} - -// ReadUint64Bytes tries to read a uint64 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -func ReadUint64Bytes(b []byte) (u uint64, o []byte, err error) { - l := len(b) - if l < 1 { - return 0, nil, ErrShortBytes - } - - lead := b[0] - if isfixint(lead) { - u = uint64(rfixint(lead)) - o = b[1:] - return - } - - switch lead { - case mint8: - if l < 2 { - err = ErrShortBytes - return - } - v := int64(getMint8(b)) - if v < 0 { - err = UintBelowZero{Value: v} - return - } - u = uint64(v) - o = b[2:] - return - - case muint8: - if l < 2 { - err = ErrShortBytes - return - } - u = uint64(getMuint8(b)) - o = b[2:] - return - - case mint16: - if l < 3 { - err = ErrShortBytes - return - } - v := int64(getMint16(b)) - if v < 0 { - err = UintBelowZero{Value: v} - return - } - u = uint64(v) - o = b[3:] - return - - case muint16: - if l < 3 { - err = ErrShortBytes - return - } - u = uint64(getMuint16(b)) - o = b[3:] - return - - case mint32: - if l < 5 { - err = ErrShortBytes - return - } - v := int64(getMint32(b)) - if v < 0 { - err = UintBelowZero{Value: v} - return - } - u = uint64(v) - o = b[5:] - return - - case muint32: - if l < 5 { - err = ErrShortBytes - return - } - u = uint64(getMuint32(b)) - o = b[5:] - return - - case mint64: - if l < 9 { - err = ErrShortBytes - return - } - v := int64(getMint64(b)) - if v < 0 { - err = UintBelowZero{Value: v} - return - } - u = uint64(v) - o = b[9:] - return - - case muint64: - if l < 9 { - err = ErrShortBytes - return - } - u = getMuint64(b) - o = b[9:] - return - - default: - if isnfixint(lead) { - err = UintBelowZero{Value: int64(rnfixint(lead))} - } else { - err = badPrefix(UintType, lead) - } - return - } -} - -// ReadUint32Bytes tries to read a uint32 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -// - UintOverflow{} (value too large for uint32) -func ReadUint32Bytes(b []byte) (uint32, []byte, error) { - v, o, err := ReadUint64Bytes(b) - if v > math.MaxUint32 { - return 0, nil, UintOverflow{Value: v, FailedBitsize: 32} - } - return uint32(v), o, err -} - -// ReadUint16Bytes tries to read a uint16 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -// - UintOverflow{} (value too large for uint16) -func ReadUint16Bytes(b []byte) (uint16, []byte, error) { - v, o, err := ReadUint64Bytes(b) - if v > math.MaxUint16 { - return 0, nil, UintOverflow{Value: v, FailedBitsize: 16} - } - return uint16(v), o, err -} - -// ReadUint8Bytes tries to read a uint8 -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -// - UintOverflow{} (value too large for uint8) -func ReadUint8Bytes(b []byte) (uint8, []byte, error) { - v, o, err := ReadUint64Bytes(b) - if v > math.MaxUint8 { - return 0, nil, UintOverflow{Value: v, FailedBitsize: 8} - } - return uint8(v), o, err -} - -// ReadUintBytes tries to read a uint -// from 'b' and return the value and the remaining bytes. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a uint) -// - UintOverflow{} (value too large for uint; 32-bit platforms only) -func ReadUintBytes(b []byte) (uint, []byte, error) { - if smallint { - u, b, err := ReadUint32Bytes(b) - return uint(u), b, err - } - u, b, err := ReadUint64Bytes(b) - return uint(u), b, err -} - -// ReadByteBytes is analogous to ReadUint8Bytes -func ReadByteBytes(b []byte) (byte, []byte, error) { - return ReadUint8Bytes(b) -} - -// ReadBytesBytes reads a 'bin' object -// from 'b' and returns its vaue and -// the remaining bytes in 'b'. -// Possible errors: -// - ErrShortBytes (too few bytes) -// - TypeError{} (not a 'bin' object) -func ReadBytesBytes(b []byte, scratch []byte) (v []byte, o []byte, err error) { - return readBytesBytes(b, scratch, false) -} - -func readBytesBytes(b, scratch []byte, zc bool) (v, o []byte, err error) { - l := len(b) - if l < 1 { - return nil, nil, ErrShortBytes - } - - lead := b[0] - var read int - switch lead { - case mbin8: - if l < 2 { - err = ErrShortBytes - return - } - - read = int(b[1]) - b = b[2:] - - case mbin16: - if l < 3 { - err = ErrShortBytes - return - } - read = int(big.Uint16(b[1:])) - b = b[3:] - - case mbin32: - if l < 5 { - err = ErrShortBytes - return - } - read = int(big.Uint32(b[1:])) - b = b[5:] - - default: - err = badPrefix(BinType, lead) - return - } - - if len(b) < read { - err = ErrShortBytes - return - } - - // zero-copy - if zc { - v = b[0:read] - o = b[read:] - return - } - - if cap(scratch) >= read { - v = scratch[0:read] - } else { - v = make([]byte, read) - } - - o = b[copy(v, b):] - return -} - -// ReadBytesZC extracts the messagepack-encoded -// binary field without copying. The returned []byte -// points to the same memory as the input slice. -// Possible errors: -// - ErrShortBytes (b not long enough) -// - TypeError{} (object not 'bin') -func ReadBytesZC(b []byte) (v, o []byte, err error) { - return readBytesBytes(b, nil, true) -} - -func ReadExactBytes(b []byte, into []byte) (o []byte, err error) { - l := len(b) - if l < 1 { - err = ErrShortBytes - return - } - - lead := b[0] - var read uint32 - var skip int - switch lead { - case mbin8: - if l < 2 { - err = ErrShortBytes - return - } - - read = uint32(b[1]) - skip = 2 - - case mbin16: - if l < 3 { - err = ErrShortBytes - return - } - read = uint32(big.Uint16(b[1:])) - skip = 3 - - case mbin32: - if l < 5 { - err = ErrShortBytes - return - } - read = uint32(big.Uint32(b[1:])) - skip = 5 - - default: - err = badPrefix(BinType, lead) - return - } - - if read != uint32(len(into)) { - err = ArrayError{Wanted: uint32(len(into)), Got: read} - return - } - - o = b[skip+copy(into, b[skip:]):] - return -} - -// ReadStringZC reads a messagepack string field -// without copying. The returned []byte points -// to the same memory as the input slice. -// Possible errors: -// - ErrShortBytes (b not long enough) -// - TypeError{} (object not 'str') -func ReadStringZC(b []byte) (v []byte, o []byte, err error) { - l := len(b) - if l < 1 { - return nil, nil, ErrShortBytes - } - - lead := b[0] - var read int - - if isfixstr(lead) { - read = int(rfixstr(lead)) - b = b[1:] - } else { - switch lead { - case mstr8: - if l < 2 { - err = ErrShortBytes - return - } - read = int(b[1]) - b = b[2:] - - case mstr16: - if l < 3 { - err = ErrShortBytes - return - } - read = int(big.Uint16(b[1:])) - b = b[3:] - - case mstr32: - if l < 5 { - err = ErrShortBytes - return - } - read = int(big.Uint32(b[1:])) - b = b[5:] - - default: - err = TypeError{Method: StrType, Encoded: getType(lead)} - return - } - } - - if len(b) < read { - err = ErrShortBytes - return - } - - v = b[0:read] - o = b[read:] - return -} - -// ReadStringBytes reads a 'str' object -// from 'b' and returns its value and the -// remaining bytes in 'b'. -// Possible errors: -// - ErrShortBytes (b not long enough) -// - TypeError{} (not 'str' type) -// - InvalidPrefixError -func ReadStringBytes(b []byte) (string, []byte, error) { - v, o, err := ReadStringZC(b) - return string(v), o, err -} - -// ReadStringAsBytes reads a 'str' object -// into a slice of bytes. 'v' is the value of -// the 'str' object, which may reside in memory -// pointed to by 'scratch.' 'o' is the remaining bytes -// in 'b.” -// Possible errors: -// - ErrShortBytes (b not long enough) -// - TypeError{} (not 'str' type) -// - InvalidPrefixError (unknown type marker) -func ReadStringAsBytes(b []byte, scratch []byte) (v []byte, o []byte, err error) { - var tmp []byte - tmp, o, err = ReadStringZC(b) - v = append(scratch[:0], tmp...) - return -} - -// ReadComplex128Bytes reads a complex128 -// extension object from 'b' and returns the -// remaining bytes. -// Possible errors: -// - ErrShortBytes (not enough bytes in 'b') -// - TypeError{} (object not a complex128) -// - InvalidPrefixError -// - ExtensionTypeError{} (object an extension of the correct size, but not a complex128) -func ReadComplex128Bytes(b []byte) (c complex128, o []byte, err error) { - if len(b) < 18 { - err = ErrShortBytes - return - } - if b[0] != mfixext16 { - err = badPrefix(Complex128Type, b[0]) - return - } - if int8(b[1]) != Complex128Extension { - err = errExt(int8(b[1]), Complex128Extension) - return - } - c = complex(math.Float64frombits(big.Uint64(b[2:])), - math.Float64frombits(big.Uint64(b[10:]))) - o = b[18:] - return -} - -// ReadComplex64Bytes reads a complex64 -// extension object from 'b' and returns the -// remaining bytes. -// Possible errors: -// - ErrShortBytes (not enough bytes in 'b') -// - TypeError{} (object not a complex64) -// - ExtensionTypeError{} (object an extension of the correct size, but not a complex64) -func ReadComplex64Bytes(b []byte) (c complex64, o []byte, err error) { - if len(b) < 10 { - err = ErrShortBytes - return - } - if b[0] != mfixext8 { - err = badPrefix(Complex64Type, b[0]) - return - } - if b[1] != Complex64Extension { - err = errExt(int8(b[1]), Complex64Extension) - return - } - c = complex(math.Float32frombits(big.Uint32(b[2:])), - math.Float32frombits(big.Uint32(b[6:]))) - o = b[10:] - return -} - -// ReadTimeBytes reads a time.Time -// extension object from 'b' and returns the -// remaining bytes. -// Possible errors: -// - ErrShortBytes (not enough bytes in 'b') -// - TypeError{} (object not a complex64) -// - ExtensionTypeError{} (object an extension of the correct size, but not a time.Time) -func ReadTimeBytes(b []byte) (t time.Time, o []byte, err error) { - if len(b) < 15 { - err = ErrShortBytes - return - } - if b[0] != mext8 || b[1] != 12 { - err = badPrefix(TimeType, b[0]) - return - } - if int8(b[2]) != TimeExtension { - err = errExt(int8(b[2]), TimeExtension) - return - } - sec, nsec := getUnix(b[3:]) - t = time.Unix(sec, int64(nsec)).Local() - o = b[15:] - return -} - -// ReadMapStrIntfBytes reads a map[string]interface{} -// out of 'b' and returns the map and remaining bytes. -// If 'old' is non-nil, the values will be read into that map. -func ReadMapStrIntfBytes(b []byte, old map[string]interface{}) (v map[string]interface{}, o []byte, err error) { - var sz uint32 - o = b - sz, o, err = ReadMapHeaderBytes(o) - - if err != nil { - return - } - - if old != nil { - for key := range old { - delete(old, key) - } - v = old - } else { - v = make(map[string]interface{}, int(sz)) - } - - for z := uint32(0); z < sz; z++ { - if len(o) < 1 { - err = ErrShortBytes - return - } - var key []byte - key, o, err = ReadMapKeyZC(o) - if err != nil { - return - } - var val interface{} - val, o, err = ReadIntfBytes(o) - if err != nil { - return - } - v[string(key)] = val - } - return -} - -// ReadIntfBytes attempts to read -// the next object out of 'b' as a raw interface{} and -// return the remaining bytes. -func ReadIntfBytes(b []byte) (i interface{}, o []byte, err error) { - if len(b) < 1 { - err = ErrShortBytes - return - } - - k := NextType(b) - - switch k { - case MapType: - i, o, err = ReadMapStrIntfBytes(b, nil) - return - - case ArrayType: - var sz uint32 - sz, o, err = ReadArrayHeaderBytes(b) - if err != nil { - return - } - j := make([]interface{}, int(sz)) - i = j - for d := range j { - j[d], o, err = ReadIntfBytes(o) - if err != nil { - return - } - } - return - - case Float32Type: - i, o, err = ReadFloat32Bytes(b) - return - - case Float64Type: - i, o, err = ReadFloat64Bytes(b) - return - - case IntType: - i, o, err = ReadInt64Bytes(b) - return - - case UintType: - i, o, err = ReadUint64Bytes(b) - return - - case BoolType: - i, o, err = ReadBoolBytes(b) - return - - case TimeType: - i, o, err = ReadTimeBytes(b) - return - - case Complex64Type: - i, o, err = ReadComplex64Bytes(b) - return - - case Complex128Type: - i, o, err = ReadComplex128Bytes(b) - return - - case ExtensionType: - var t int8 - t, err = peekExtension(b) - if err != nil { - return - } - // use a user-defined extension, - // if it's been registered - f, ok := extensionReg[t] - if ok { - e := f() - o, err = ReadExtensionBytes(b, e) - i = e - return - } - // last resort is a raw extension - e := RawExtension{} - e.Type = int8(t) - o, err = ReadExtensionBytes(b, &e) - i = &e - return - - case NilType: - o, err = ReadNilBytes(b) - return - - case BinType: - i, o, err = ReadBytesBytes(b, nil) - return - - case StrType: - i, o, err = ReadStringBytes(b) - return - - default: - err = InvalidPrefixError(b[0]) - return - } -} - -// Skip skips the next object in 'b' and -// returns the remaining bytes. If the object -// is a map or array, all of its elements -// will be skipped. -// Possible Errors: -// - ErrShortBytes (not enough bytes in b) -// - InvalidPrefixError (bad encoding) -func Skip(b []byte) ([]byte, error) { - sz, asz, err := getSize(b) - if err != nil { - return b, err - } - if uintptr(len(b)) < sz { - return b, ErrShortBytes - } - b = b[sz:] - for asz > 0 { - b, err = Skip(b) - if err != nil { - return b, err - } - asz-- - } - return b, nil -} - -// returns (skip N bytes, skip M objects, error) -func getSize(b []byte) (uintptr, uintptr, error) { - l := len(b) - if l == 0 { - return 0, 0, ErrShortBytes - } - lead := b[0] - spec := &sizes[lead] // get type information - size, mode := spec.size, spec.extra - if size == 0 { - return 0, 0, InvalidPrefixError(lead) - } - if mode >= 0 { // fixed composites - return uintptr(size), uintptr(mode), nil - } - if l < int(size) { - return 0, 0, ErrShortBytes - } - switch mode { - case extra8: - return uintptr(size) + uintptr(b[1]), 0, nil - case extra16: - return uintptr(size) + uintptr(big.Uint16(b[1:])), 0, nil - case extra32: - return uintptr(size) + uintptr(big.Uint32(b[1:])), 0, nil - case map16v: - return uintptr(size), 2 * uintptr(big.Uint16(b[1:])), nil - case map32v: - return uintptr(size), 2 * uintptr(big.Uint32(b[1:])), nil - case array16v: - return uintptr(size), uintptr(big.Uint16(b[1:])), nil - case array32v: - return uintptr(size), uintptr(big.Uint32(b[1:])), nil - default: - return 0, 0, fatal - } -} diff --git a/internal/msgp/size.go b/internal/msgp/size.go deleted file mode 100644 index ce2f8b16ff..0000000000 --- a/internal/msgp/size.go +++ /dev/null @@ -1,38 +0,0 @@ -package msgp - -// The sizes provided -// are the worst-case -// encoded sizes for -// each type. For variable- -// length types ([]byte, string), -// the total encoded size is -// the prefix size plus the -// length of the object. -const ( - Int64Size = 9 - IntSize = Int64Size - UintSize = Int64Size - Int8Size = 2 - Int16Size = 3 - Int32Size = 5 - Uint8Size = 2 - ByteSize = Uint8Size - Uint16Size = 3 - Uint32Size = 5 - Uint64Size = Int64Size - Float64Size = 9 - Float32Size = 5 - Complex64Size = 10 - Complex128Size = 18 - - TimeSize = 15 - BoolSize = 1 - NilSize = 1 - - MapHeaderSize = 5 - ArrayHeaderSize = 5 - - BytesPrefixSize = 5 - StringPrefixSize = 5 - ExtensionPrefixSize = 6 -) diff --git a/internal/msgp/unsafe.go b/internal/msgp/unsafe.go deleted file mode 100644 index 92af0260d5..0000000000 --- a/internal/msgp/unsafe.go +++ /dev/null @@ -1,42 +0,0 @@ -//go:build !purego && !appengine -// +build !purego,!appengine - -package msgp - -import ( - "reflect" - "unsafe" -) - -// NOTE: -// all of the definition in this file -// should be repeated in appengine.go, -// but without using unsafe - -const ( - // spec says int and uint are always - // the same size, but that int/uint - // size may not be machine word size - smallint = unsafe.Sizeof(int(0)) == 4 -) - -// UnsafeString returns the byte slice as a volatile string -// THIS SHOULD ONLY BE USED BY THE CODE GENERATOR. -// THIS IS EVIL CODE. -// YOU HAVE BEEN WARNED. -func UnsafeString(b []byte) string { - sh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - return *(*string)(unsafe.Pointer(&reflect.StringHeader{Data: sh.Data, Len: sh.Len})) -} - -// UnsafeBytes returns the string as a byte slice -// THIS SHOULD ONLY BE USED BY THE CODE GENERATOR. -// THIS IS EVIL CODE. -// YOU HAVE BEEN WARNED. -func UnsafeBytes(s string) []byte { - return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ - Len: len(s), - Cap: len(s), - Data: (*(*reflect.StringHeader)(unsafe.Pointer(&s))).Data, - })) -} diff --git a/internal/msgp/write.go b/internal/msgp/write.go deleted file mode 100644 index 24fee4ba15..0000000000 --- a/internal/msgp/write.go +++ /dev/null @@ -1,863 +0,0 @@ -package msgp - -import ( - "errors" - "fmt" - "io" - "math" - "reflect" - "sync" - "time" -) - -const ( - // min buffer size for the writer - minWriterSize = 18 -) - -// Sizer is an interface implemented -// by types that can estimate their -// size when MessagePack encoded. -// This interface is optional, but -// encoding/marshaling implementations -// may use this as a way to pre-allocate -// memory for serialization. -type Sizer interface { - Msgsize() int -} - -var ( - // Nowhere is an io.Writer to nowhere - Nowhere io.Writer = nwhere{} - - btsType = reflect.TypeOf(([]byte)(nil)) - writerPool = sync.Pool{ - New: func() interface{} { - return &Writer{buf: make([]byte, 2048)} - }, - } -) - -func popWriter(w io.Writer) *Writer { - wr := writerPool.Get().(*Writer) - wr.Reset(w) - return wr -} - -func pushWriter(wr *Writer) { - wr.w = nil - wr.wloc = 0 - writerPool.Put(wr) -} - -// freeW frees a writer for use -// by other processes. It is not necessary -// to call freeW on a writer. However, maintaining -// a reference to a *Writer after calling freeW on -// it will cause undefined behavior. -func freeW(w *Writer) { pushWriter(w) } - -// Require ensures that cap(old)-len(old) >= extra. -func Require(old []byte, extra int) []byte { - l := len(old) - c := cap(old) - r := l + extra - if c >= r { - return old - } else if l == 0 { - return make([]byte, 0, extra) - } - // the new size is the greater - // of double the old capacity - // and the sum of the old length - // and the number of new bytes - // necessary. - c <<= 1 - if c < r { - c = r - } - n := make([]byte, l, c) - copy(n, old) - return n -} - -// nowhere writer -type nwhere struct{} - -func (n nwhere) Write(p []byte) (int, error) { return len(p), nil } - -// Marshaler is the interface implemented -// by types that know how to marshal themselves -// as MessagePack. MarshalMsg appends the marshalled -// form of the object to the provided -// byte slice, returning the extended -// slice and any errors encountered. -type Marshaler interface { - MarshalMsg([]byte) ([]byte, error) -} - -// Encodable is the interface implemented -// by types that know how to write themselves -// as MessagePack using a *msgp.Writer. -type Encodable interface { - EncodeMsg(*Writer) error -} - -// Writer is a buffered writer -// that can be used to write -// MessagePack objects to an io.Writer. -// You must call *Writer.Flush() in order -// to flush all of the buffered data -// to the underlying writer. -type Writer struct { - w io.Writer - buf []byte - wloc int -} - -// NewWriter returns a new *Writer. -func NewWriter(w io.Writer) *Writer { - if wr, ok := w.(*Writer); ok { - return wr - } - return popWriter(w) -} - -// NewWriterSize returns a writer with a custom buffer size. -func NewWriterSize(w io.Writer, sz int) *Writer { - // we must be able to require() 'minWriterSize' - // contiguous bytes, so that is the - // practical minimum buffer size - if sz < minWriterSize { - sz = minWriterSize - } - buf := make([]byte, sz) - return NewWriterBuf(w, buf) -} - -// NewWriterBuf returns a writer with a provided buffer. -// 'buf' is not used when the capacity is smaller than 18, -// custom buffer is allocated instead. -func NewWriterBuf(w io.Writer, buf []byte) *Writer { - if cap(buf) < minWriterSize { - buf = make([]byte, minWriterSize) - } - buf = buf[:cap(buf)] - return &Writer{ - w: w, - buf: buf, - } -} - -// Encode encodes an Encodable to an io.Writer. -func Encode(w io.Writer, e Encodable) error { - wr := NewWriter(w) - err := e.EncodeMsg(wr) - if err == nil { - err = wr.Flush() - } - freeW(wr) - return err -} - -func (mw *Writer) flush() error { - if mw.wloc == 0 { - return nil - } - n, err := mw.w.Write(mw.buf[:mw.wloc]) - if err != nil { - if n > 0 { - mw.wloc = copy(mw.buf, mw.buf[n:mw.wloc]) - } - return err - } - mw.wloc = 0 - return nil -} - -// Flush flushes all of the buffered -// data to the underlying writer. -func (mw *Writer) Flush() error { return mw.flush() } - -// Buffered returns the number bytes in the write buffer -func (mw *Writer) Buffered() int { return len(mw.buf) - mw.wloc } - -func (mw *Writer) avail() int { return len(mw.buf) - mw.wloc } - -func (mw *Writer) bufsize() int { return len(mw.buf) } - -// NOTE: this should only be called with -// a number that is guaranteed to be less than -// len(mw.buf). typically, it is called with a constant. -// -// NOTE: this is a hot code path -func (mw *Writer) require(n int) (int, error) { - c := len(mw.buf) - wl := mw.wloc - if c-wl < n { - if err := mw.flush(); err != nil { - return 0, err - } - wl = mw.wloc - } - mw.wloc += n - return wl, nil -} - -func (mw *Writer) Append(b ...byte) error { - if mw.avail() < len(b) { - err := mw.flush() - if err != nil { - return err - } - } - mw.wloc += copy(mw.buf[mw.wloc:], b) - return nil -} - -// push one byte onto the buffer -// -// NOTE: this is a hot code path -func (mw *Writer) push(b byte) error { - if mw.wloc == len(mw.buf) { - if err := mw.flush(); err != nil { - return err - } - } - mw.buf[mw.wloc] = b - mw.wloc++ - return nil -} - -func (mw *Writer) prefix8(b byte, u uint8) error { - const need = 2 - if len(mw.buf)-mw.wloc < need { - if err := mw.flush(); err != nil { - return err - } - } - prefixu8(mw.buf[mw.wloc:], b, u) - mw.wloc += need - return nil -} - -func (mw *Writer) prefix16(b byte, u uint16) error { - const need = 3 - if len(mw.buf)-mw.wloc < need { - if err := mw.flush(); err != nil { - return err - } - } - prefixu16(mw.buf[mw.wloc:], b, u) - mw.wloc += need - return nil -} - -func (mw *Writer) prefix32(b byte, u uint32) error { - const need = 5 - if len(mw.buf)-mw.wloc < need { - if err := mw.flush(); err != nil { - return err - } - } - prefixu32(mw.buf[mw.wloc:], b, u) - mw.wloc += need - return nil -} - -func (mw *Writer) prefix64(b byte, u uint64) error { - const need = 9 - if len(mw.buf)-mw.wloc < need { - if err := mw.flush(); err != nil { - return err - } - } - prefixu64(mw.buf[mw.wloc:], b, u) - mw.wloc += need - return nil -} - -// Write implements io.Writer, and writes -// data directly to the buffer. -func (mw *Writer) Write(p []byte) (int, error) { - l := len(p) - if mw.avail() < l { - if err := mw.flush(); err != nil { - return 0, err - } - if l > len(mw.buf) { - return mw.w.Write(p) - } - } - mw.wloc += copy(mw.buf[mw.wloc:], p) - return l, nil -} - -// implements io.WriteString -func (mw *Writer) writeString(s string) error { - l := len(s) - if mw.avail() < l { - if err := mw.flush(); err != nil { - return err - } - if l > len(mw.buf) { - _, err := io.WriteString(mw.w, s) - return err - } - } - mw.wloc += copy(mw.buf[mw.wloc:], s) - return nil -} - -// Reset changes the underlying writer used by the Writer -func (mw *Writer) Reset(w io.Writer) { - mw.buf = mw.buf[:cap(mw.buf)] - mw.w = w - mw.wloc = 0 -} - -// WriteMapHeader writes a map header of the given -// size to the writer -func (mw *Writer) WriteMapHeader(sz uint32) error { - switch { - case sz <= 15: - return mw.push(wfixmap(uint8(sz))) - case sz <= math.MaxUint16: - return mw.prefix16(mmap16, uint16(sz)) - default: - return mw.prefix32(mmap32, sz) - } -} - -// WriteArrayHeader writes an array header of the -// given size to the writer -func (mw *Writer) WriteArrayHeader(sz uint32) error { - switch { - case sz <= 15: - return mw.push(wfixarray(uint8(sz))) - case sz <= math.MaxUint16: - return mw.prefix16(marray16, uint16(sz)) - default: - return mw.prefix32(marray32, sz) - } -} - -// WriteNil writes a nil byte to the buffer -func (mw *Writer) WriteNil() error { - return mw.push(mnil) -} - -// WriteFloat64 writes a float64 to the writer -func (mw *Writer) WriteFloat64(f float64) error { - return mw.prefix64(mfloat64, math.Float64bits(f)) -} - -// WriteFloat32 writes a float32 to the writer -func (mw *Writer) WriteFloat32(f float32) error { - return mw.prefix32(mfloat32, math.Float32bits(f)) -} - -// WriteInt64 writes an int64 to the writer -func (mw *Writer) WriteInt64(i int64) error { - if i >= 0 { - switch { - case i <= math.MaxInt8: - return mw.push(wfixint(uint8(i))) - case i <= math.MaxInt16: - return mw.prefix16(mint16, uint16(i)) - case i <= math.MaxInt32: - return mw.prefix32(mint32, uint32(i)) - default: - return mw.prefix64(mint64, uint64(i)) - } - } - switch { - case i >= -32: - return mw.push(wnfixint(int8(i))) - case i >= math.MinInt8: - return mw.prefix8(mint8, uint8(i)) - case i >= math.MinInt16: - return mw.prefix16(mint16, uint16(i)) - case i >= math.MinInt32: - return mw.prefix32(mint32, uint32(i)) - default: - return mw.prefix64(mint64, uint64(i)) - } -} - -// WriteInt8 writes an int8 to the writer -func (mw *Writer) WriteInt8(i int8) error { return mw.WriteInt64(int64(i)) } - -// WriteInt16 writes an int16 to the writer -func (mw *Writer) WriteInt16(i int16) error { return mw.WriteInt64(int64(i)) } - -// WriteInt32 writes an int32 to the writer -func (mw *Writer) WriteInt32(i int32) error { return mw.WriteInt64(int64(i)) } - -// WriteInt writes an int to the writer -func (mw *Writer) WriteInt(i int) error { return mw.WriteInt64(int64(i)) } - -// WriteUint64 writes a uint64 to the writer -func (mw *Writer) WriteUint64(u uint64) error { - switch { - case u <= (1<<7)-1: - return mw.push(wfixint(uint8(u))) - case u <= math.MaxUint8: - return mw.prefix8(muint8, uint8(u)) - case u <= math.MaxUint16: - return mw.prefix16(muint16, uint16(u)) - case u <= math.MaxUint32: - return mw.prefix32(muint32, uint32(u)) - default: - return mw.prefix64(muint64, u) - } -} - -// WriteByte is analogous to WriteUint8 -func (mw *Writer) WriteByte(u byte) error { return mw.WriteUint8(uint8(u)) } - -// WriteUint8 writes a uint8 to the writer -func (mw *Writer) WriteUint8(u uint8) error { return mw.WriteUint64(uint64(u)) } - -// WriteUint16 writes a uint16 to the writer -func (mw *Writer) WriteUint16(u uint16) error { return mw.WriteUint64(uint64(u)) } - -// WriteUint32 writes a uint32 to the writer -func (mw *Writer) WriteUint32(u uint32) error { return mw.WriteUint64(uint64(u)) } - -// WriteUint writes a uint to the writer -func (mw *Writer) WriteUint(u uint) error { return mw.WriteUint64(uint64(u)) } - -// WriteBytes writes binary as 'bin' to the writer -func (mw *Writer) WriteBytes(b []byte) error { - sz := uint32(len(b)) - var err error - switch { - case sz <= math.MaxUint8: - err = mw.prefix8(mbin8, uint8(sz)) - case sz <= math.MaxUint16: - err = mw.prefix16(mbin16, uint16(sz)) - default: - err = mw.prefix32(mbin32, sz) - } - if err != nil { - return err - } - _, err = mw.Write(b) - return err -} - -// WriteBytesHeader writes just the size header -// of a MessagePack 'bin' object. The user is responsible -// for then writing 'sz' more bytes into the stream. -func (mw *Writer) WriteBytesHeader(sz uint32) error { - switch { - case sz <= math.MaxUint8: - return mw.prefix8(mbin8, uint8(sz)) - case sz <= math.MaxUint16: - return mw.prefix16(mbin16, uint16(sz)) - default: - return mw.prefix32(mbin32, sz) - } -} - -// WriteBool writes a bool to the writer -func (mw *Writer) WriteBool(b bool) error { - if b { - return mw.push(mtrue) - } - return mw.push(mfalse) -} - -// WriteString writes a messagepack string to the writer. -// (This is NOT an implementation of io.StringWriter) -func (mw *Writer) WriteString(s string) error { - sz := uint32(len(s)) - var err error - switch { - case sz <= 31: - err = mw.push(wfixstr(uint8(sz))) - case sz <= math.MaxUint8: - err = mw.prefix8(mstr8, uint8(sz)) - case sz <= math.MaxUint16: - err = mw.prefix16(mstr16, uint16(sz)) - default: - err = mw.prefix32(mstr32, sz) - } - if err != nil { - return err - } - return mw.writeString(s) -} - -// WriteStringHeader writes just the string size -// header of a MessagePack 'str' object. The user -// is responsible for writing 'sz' more valid UTF-8 -// bytes to the stream. -func (mw *Writer) WriteStringHeader(sz uint32) error { - switch { - case sz <= 31: - return mw.push(wfixstr(uint8(sz))) - case sz <= math.MaxUint8: - return mw.prefix8(mstr8, uint8(sz)) - case sz <= math.MaxUint16: - return mw.prefix16(mstr16, uint16(sz)) - default: - return mw.prefix32(mstr32, sz) - } -} - -// WriteStringFromBytes writes a 'str' object -// from a []byte. -func (mw *Writer) WriteStringFromBytes(str []byte) error { - sz := uint32(len(str)) - var err error - switch { - case sz <= 31: - err = mw.push(wfixstr(uint8(sz))) - case sz <= math.MaxUint8: - err = mw.prefix8(mstr8, uint8(sz)) - case sz <= math.MaxUint16: - err = mw.prefix16(mstr16, uint16(sz)) - default: - err = mw.prefix32(mstr32, sz) - } - if err != nil { - return err - } - _, err = mw.Write(str) - return err -} - -// WriteComplex64 writes a complex64 to the writer -func (mw *Writer) WriteComplex64(f complex64) error { - o, err := mw.require(10) - if err != nil { - return err - } - mw.buf[o] = mfixext8 - mw.buf[o+1] = Complex64Extension - big.PutUint32(mw.buf[o+2:], math.Float32bits(real(f))) - big.PutUint32(mw.buf[o+6:], math.Float32bits(imag(f))) - return nil -} - -// WriteComplex128 writes a complex128 to the writer -func (mw *Writer) WriteComplex128(f complex128) error { - o, err := mw.require(18) - if err != nil { - return err - } - mw.buf[o] = mfixext16 - mw.buf[o+1] = Complex128Extension - big.PutUint64(mw.buf[o+2:], math.Float64bits(real(f))) - big.PutUint64(mw.buf[o+10:], math.Float64bits(imag(f))) - return nil -} - -// WriteMapStrStr writes a map[string]string to the writer -func (mw *Writer) WriteMapStrStr(mp map[string]string) (err error) { - err = mw.WriteMapHeader(uint32(len(mp))) - if err != nil { - return - } - for key, val := range mp { - err = mw.WriteString(key) - if err != nil { - return - } - err = mw.WriteString(val) - if err != nil { - return - } - } - return nil -} - -// WriteMapStrIntf writes a map[string]interface to the writer -func (mw *Writer) WriteMapStrIntf(mp map[string]interface{}) (err error) { - err = mw.WriteMapHeader(uint32(len(mp))) - if err != nil { - return - } - for key, val := range mp { - err = mw.WriteString(key) - if err != nil { - return - } - err = mw.WriteIntf(val) - if err != nil { - return - } - } - return -} - -// WriteTime writes a time.Time object to the wire. -// -// Time is encoded as Unix time, which means that -// location (time zone) data is removed from the object. -// The encoded object itself is 12 bytes: 8 bytes for -// a big-endian 64-bit integer denoting seconds -// elapsed since "zero" Unix time, followed by 4 bytes -// for a big-endian 32-bit signed integer denoting -// the nanosecond offset of the time. This encoding -// is intended to ease portability across languages. -// (Note that this is *not* the standard time.Time -// binary encoding, because its implementation relies -// heavily on the internal representation used by the -// time package.) -func (mw *Writer) WriteTime(t time.Time) error { - t = t.UTC() - o, err := mw.require(15) - if err != nil { - return err - } - mw.buf[o] = mext8 - mw.buf[o+1] = 12 - mw.buf[o+2] = TimeExtension - putUnix(mw.buf[o+3:], t.Unix(), int32(t.Nanosecond())) - return nil -} - -// WriteIntf writes the concrete type of 'v'. -// WriteIntf will error if 'v' is not one of the following: -// - A bool, float, string, []byte, int, uint, or complex -// - A map of supported types (with string keys) -// - An array or slice of supported types -// - A pointer to a supported type -// - A type that satisfies the msgp.Encodable interface -// - A type that satisfies the msgp.Extension interface -func (mw *Writer) WriteIntf(v interface{}) error { - if v == nil { - return mw.WriteNil() - } - switch v := v.(type) { - - // preferred interfaces - - case Encodable: - return v.EncodeMsg(mw) - case Extension: - return mw.WriteExtension(v) - - // concrete types - - case bool: - return mw.WriteBool(v) - case float32: - return mw.WriteFloat32(v) - case float64: - return mw.WriteFloat64(v) - case complex64: - return mw.WriteComplex64(v) - case complex128: - return mw.WriteComplex128(v) - case uint8: - return mw.WriteUint8(v) - case uint16: - return mw.WriteUint16(v) - case uint32: - return mw.WriteUint32(v) - case uint64: - return mw.WriteUint64(v) - case uint: - return mw.WriteUint(v) - case int8: - return mw.WriteInt8(v) - case int16: - return mw.WriteInt16(v) - case int32: - return mw.WriteInt32(v) - case int64: - return mw.WriteInt64(v) - case int: - return mw.WriteInt(v) - case string: - return mw.WriteString(v) - case []byte: - return mw.WriteBytes(v) - case map[string]string: - return mw.WriteMapStrStr(v) - case map[string]interface{}: - return mw.WriteMapStrIntf(v) - case time.Time: - return mw.WriteTime(v) - } - - val := reflect.ValueOf(v) - if !isSupported(val.Kind()) || !val.IsValid() { - return fmt.Errorf("msgp: type %s not supported", val) - } - - switch val.Kind() { - case reflect.Ptr: - if val.IsNil() { - return mw.WriteNil() - } - return mw.WriteIntf(val.Elem().Interface()) - case reflect.Slice: - return mw.writeSlice(val) - case reflect.Map: - return mw.writeMap(val) - } - return &ErrUnsupportedType{T: val.Type()} -} - -func (mw *Writer) writeMap(v reflect.Value) (err error) { - if v.Type().Key().Kind() != reflect.String { - return errors.New("msgp: map keys must be strings") - } - ks := v.MapKeys() - err = mw.WriteMapHeader(uint32(len(ks))) - if err != nil { - return - } - for _, key := range ks { - val := v.MapIndex(key) - err = mw.WriteString(key.String()) - if err != nil { - return - } - err = mw.WriteIntf(val.Interface()) - if err != nil { - return - } - } - return -} - -func (mw *Writer) writeSlice(v reflect.Value) (err error) { - // is []byte - if v.Type().ConvertibleTo(btsType) { - return mw.WriteBytes(v.Bytes()) - } - - sz := uint32(v.Len()) - err = mw.WriteArrayHeader(sz) - if err != nil { - return - } - for i := uint32(0); i < sz; i++ { - err = mw.WriteIntf(v.Index(int(i)).Interface()) - if err != nil { - return - } - } - return -} - -func (mw *Writer) writeStruct(v reflect.Value) error { - if enc, ok := v.Interface().(Encodable); ok { - return enc.EncodeMsg(mw) - } - return fmt.Errorf("msgp: unsupported type: %s", v.Type()) -} - -func (mw *Writer) writeVal(v reflect.Value) error { - if !isSupported(v.Kind()) { - return fmt.Errorf("msgp: msgp/enc: type %q not supported", v.Type()) - } - - // shortcut for nil values - if v.IsNil() { - return mw.WriteNil() - } - switch v.Kind() { - case reflect.Bool: - return mw.WriteBool(v.Bool()) - - case reflect.Float32, reflect.Float64: - return mw.WriteFloat64(v.Float()) - - case reflect.Complex64, reflect.Complex128: - return mw.WriteComplex128(v.Complex()) - - case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8: - return mw.WriteInt64(v.Int()) - - case reflect.Interface, reflect.Ptr: - if v.IsNil() { - if err := mw.WriteNil(); err != nil { - return err - } - } - return mw.writeVal(v.Elem()) - - case reflect.Map: - return mw.writeMap(v) - - case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8: - return mw.WriteUint64(v.Uint()) - - case reflect.String: - return mw.WriteString(v.String()) - - case reflect.Slice, reflect.Array: - return mw.writeSlice(v) - - case reflect.Struct: - return mw.writeStruct(v) - - } - return fmt.Errorf("msgp: msgp/enc: type %q not supported", v.Type()) -} - -// is the reflect.Kind encodable? -func isSupported(k reflect.Kind) bool { - switch k { - case reflect.Func, reflect.Chan, reflect.Invalid, reflect.UnsafePointer: - return false - default: - return true - } -} - -// GuessSize guesses the size of the underlying -// value of 'i'. If the underlying value is not -// a simple builtin (or []byte), GuessSize defaults -// to 512. -func GuessSize(i interface{}) int { - if i == nil { - return NilSize - } - - switch i := i.(type) { - case Sizer: - return i.Msgsize() - case Extension: - return ExtensionPrefixSize + i.Len() - case float64: - return Float64Size - case float32: - return Float32Size - case uint8, uint16, uint32, uint64, uint: - return UintSize - case int8, int16, int32, int64, int: - return IntSize - case []byte: - return BytesPrefixSize + len(i) - case string: - return StringPrefixSize + len(i) - case complex64: - return Complex64Size - case complex128: - return Complex128Size - case bool: - return BoolSize - case map[string]interface{}: - s := MapHeaderSize - for key, val := range i { - s += StringPrefixSize + len(key) + GuessSize(val) - } - return s - case map[string]string: - s := MapHeaderSize - for key, val := range i { - s += 2*StringPrefixSize + len(key) + len(val) - } - return s - default: - return 512 - } -} diff --git a/internal/msgp/write_bytes.go b/internal/msgp/write_bytes.go deleted file mode 100644 index 8a55552c88..0000000000 --- a/internal/msgp/write_bytes.go +++ /dev/null @@ -1,411 +0,0 @@ -package msgp - -import ( - "math" - "reflect" - "time" -) - -// ensure 'sz' extra bytes in 'b' btw len(b) and cap(b) -func ensure(b []byte, sz int) ([]byte, int) { - l := len(b) - c := cap(b) - if c-l < sz { - o := make([]byte, (2*c)+sz) // exponential growth - n := copy(o, b) - return o[:n+sz], n - } - return b[:l+sz], l -} - -// AppendMapHeader appends a map header with the -// given size to the slice -func AppendMapHeader(b []byte, sz uint32) []byte { - switch { - case sz <= 15: - return append(b, wfixmap(uint8(sz))) - - case sz <= math.MaxUint16: - o, n := ensure(b, 3) - prefixu16(o[n:], mmap16, uint16(sz)) - return o - - default: - o, n := ensure(b, 5) - prefixu32(o[n:], mmap32, sz) - return o - } -} - -// AppendArrayHeader appends an array header with -// the given size to the slice -func AppendArrayHeader(b []byte, sz uint32) []byte { - switch { - case sz <= 15: - return append(b, wfixarray(uint8(sz))) - - case sz <= math.MaxUint16: - o, n := ensure(b, 3) - prefixu16(o[n:], marray16, uint16(sz)) - return o - - default: - o, n := ensure(b, 5) - prefixu32(o[n:], marray32, sz) - return o - } -} - -// AppendNil appends a 'nil' byte to the slice -func AppendNil(b []byte) []byte { return append(b, mnil) } - -// AppendFloat64 appends a float64 to the slice -func AppendFloat64(b []byte, f float64) []byte { - o, n := ensure(b, Float64Size) - prefixu64(o[n:], mfloat64, math.Float64bits(f)) - return o -} - -// AppendFloat32 appends a float32 to the slice -func AppendFloat32(b []byte, f float32) []byte { - o, n := ensure(b, Float32Size) - prefixu32(o[n:], mfloat32, math.Float32bits(f)) - return o -} - -// AppendInt64 appends an int64 to the slice -func AppendInt64(b []byte, i int64) []byte { - if i >= 0 { - switch { - case i <= math.MaxInt8: - return append(b, wfixint(uint8(i))) - case i <= math.MaxInt16: - o, n := ensure(b, 3) - putMint16(o[n:], int16(i)) - return o - case i <= math.MaxInt32: - o, n := ensure(b, 5) - putMint32(o[n:], int32(i)) - return o - default: - o, n := ensure(b, 9) - putMint64(o[n:], i) - return o - } - } - switch { - case i >= -32: - return append(b, wnfixint(int8(i))) - case i >= math.MinInt8: - o, n := ensure(b, 2) - putMint8(o[n:], int8(i)) - return o - case i >= math.MinInt16: - o, n := ensure(b, 3) - putMint16(o[n:], int16(i)) - return o - case i >= math.MinInt32: - o, n := ensure(b, 5) - putMint32(o[n:], int32(i)) - return o - default: - o, n := ensure(b, 9) - putMint64(o[n:], i) - return o - } -} - -// AppendInt appends an int to the slice -func AppendInt(b []byte, i int) []byte { return AppendInt64(b, int64(i)) } - -// AppendInt8 appends an int8 to the slice -func AppendInt8(b []byte, i int8) []byte { return AppendInt64(b, int64(i)) } - -// AppendInt16 appends an int16 to the slice -func AppendInt16(b []byte, i int16) []byte { return AppendInt64(b, int64(i)) } - -// AppendInt32 appends an int32 to the slice -func AppendInt32(b []byte, i int32) []byte { return AppendInt64(b, int64(i)) } - -// AppendUint64 appends a uint64 to the slice -func AppendUint64(b []byte, u uint64) []byte { - switch { - case u <= (1<<7)-1: - return append(b, wfixint(uint8(u))) - - case u <= math.MaxUint8: - o, n := ensure(b, 2) - putMuint8(o[n:], uint8(u)) - return o - - case u <= math.MaxUint16: - o, n := ensure(b, 3) - putMuint16(o[n:], uint16(u)) - return o - - case u <= math.MaxUint32: - o, n := ensure(b, 5) - putMuint32(o[n:], uint32(u)) - return o - - default: - o, n := ensure(b, 9) - putMuint64(o[n:], u) - return o - - } -} - -// AppendUint appends a uint to the slice -func AppendUint(b []byte, u uint) []byte { return AppendUint64(b, uint64(u)) } - -// AppendUint8 appends a uint8 to the slice -func AppendUint8(b []byte, u uint8) []byte { return AppendUint64(b, uint64(u)) } - -// AppendByte is analogous to AppendUint8 -func AppendByte(b []byte, u byte) []byte { return AppendUint8(b, uint8(u)) } - -// AppendUint16 appends a uint16 to the slice -func AppendUint16(b []byte, u uint16) []byte { return AppendUint64(b, uint64(u)) } - -// AppendUint32 appends a uint32 to the slice -func AppendUint32(b []byte, u uint32) []byte { return AppendUint64(b, uint64(u)) } - -// AppendBytes appends bytes to the slice as MessagePack 'bin' data -func AppendBytes(b, bts []byte) []byte { - sz := len(bts) - var o []byte - var n int - switch { - case sz <= math.MaxUint8: - o, n = ensure(b, 2+sz) - prefixu8(o[n:], mbin8, uint8(sz)) - n += 2 - case sz <= math.MaxUint16: - o, n = ensure(b, 3+sz) - prefixu16(o[n:], mbin16, uint16(sz)) - n += 3 - default: - o, n = ensure(b, 5+sz) - prefixu32(o[n:], mbin32, uint32(sz)) - n += 5 - } - return o[:n+copy(o[n:], bts)] -} - -// AppendBool appends a bool to the slice -func AppendBool(b []byte, t bool) []byte { - if t { - return append(b, mtrue) - } - return append(b, mfalse) -} - -// AppendString appends a string as a MessagePack 'str' to the slice -func AppendString(b []byte, s string) []byte { - sz := len(s) - var n int - var o []byte - switch { - case sz <= 31: - o, n = ensure(b, 1+sz) - o[n] = wfixstr(uint8(sz)) - n++ - case sz <= math.MaxUint8: - o, n = ensure(b, 2+sz) - prefixu8(o[n:], mstr8, uint8(sz)) - n += 2 - case sz <= math.MaxUint16: - o, n = ensure(b, 3+sz) - prefixu16(o[n:], mstr16, uint16(sz)) - n += 3 - default: - o, n = ensure(b, 5+sz) - prefixu32(o[n:], mstr32, uint32(sz)) - n += 5 - } - return o[:n+copy(o[n:], s)] -} - -// AppendStringFromBytes appends a []byte -// as a MessagePack 'str' to the slice 'b.' -func AppendStringFromBytes(b, str []byte) []byte { - sz := len(str) - var n int - var o []byte - switch { - case sz <= 31: - o, n = ensure(b, 1+sz) - o[n] = wfixstr(uint8(sz)) - n++ - case sz <= math.MaxUint8: - o, n = ensure(b, 2+sz) - prefixu8(o[n:], mstr8, uint8(sz)) - n += 2 - case sz <= math.MaxUint16: - o, n = ensure(b, 3+sz) - prefixu16(o[n:], mstr16, uint16(sz)) - n += 3 - default: - o, n = ensure(b, 5+sz) - prefixu32(o[n:], mstr32, uint32(sz)) - n += 5 - } - return o[:n+copy(o[n:], str)] -} - -// AppendComplex64 appends a complex64 to the slice as a MessagePack extension -func AppendComplex64(b []byte, c complex64) []byte { - o, n := ensure(b, Complex64Size) - o[n] = mfixext8 - o[n+1] = Complex64Extension - big.PutUint32(o[n+2:], math.Float32bits(real(c))) - big.PutUint32(o[n+6:], math.Float32bits(imag(c))) - return o -} - -// AppendComplex128 appends a complex128 to the slice as a MessagePack extension -func AppendComplex128(b []byte, c complex128) []byte { - o, n := ensure(b, Complex128Size) - o[n] = mfixext16 - o[n+1] = Complex128Extension - big.PutUint64(o[n+2:], math.Float64bits(real(c))) - big.PutUint64(o[n+10:], math.Float64bits(imag(c))) - return o -} - -// AppendTime appends a time.Time to the slice as a MessagePack extension -func AppendTime(b []byte, t time.Time) []byte { - o, n := ensure(b, TimeSize) - t = t.UTC() - o[n] = mext8 - o[n+1] = 12 - o[n+2] = TimeExtension - putUnix(o[n+3:], t.Unix(), int32(t.Nanosecond())) - return o -} - -// AppendMapStrStr appends a map[string]string to the slice -// as a MessagePack map with 'str'-type keys and values -func AppendMapStrStr(b []byte, m map[string]string) []byte { - sz := uint32(len(m)) - b = AppendMapHeader(b, sz) - for key, val := range m { - b = AppendString(b, key) - b = AppendString(b, val) - } - return b -} - -// AppendMapStrIntf appends a map[string]interface{} to the slice -// as a MessagePack map with 'str'-type keys. -func AppendMapStrIntf(b []byte, m map[string]interface{}) ([]byte, error) { - sz := uint32(len(m)) - b = AppendMapHeader(b, sz) - var err error - for key, val := range m { - b = AppendString(b, key) - b, err = AppendIntf(b, val) - if err != nil { - return b, err - } - } - return b, nil -} - -// AppendIntf appends the concrete type of 'i' to the -// provided []byte. 'i' must be one of the following: -// - 'nil' -// - A bool, float, string, []byte, int, uint, or complex -// - A map[string]interface{} or map[string]string -// - A []T, where T is another supported type -// - A *T, where T is another supported type -// - A type that satisfieds the msgp.Marshaler interface -// - A type that satisfies the msgp.Extension interface -func AppendIntf(b []byte, i interface{}) ([]byte, error) { - if i == nil { - return AppendNil(b), nil - } - - // all the concrete types - // for which we have methods - switch i := i.(type) { - case Marshaler: - return i.MarshalMsg(b) - case Extension: - return AppendExtension(b, i) - case bool: - return AppendBool(b, i), nil - case float32: - return AppendFloat32(b, i), nil - case float64: - return AppendFloat64(b, i), nil - case complex64: - return AppendComplex64(b, i), nil - case complex128: - return AppendComplex128(b, i), nil - case string: - return AppendString(b, i), nil - case []byte: - return AppendBytes(b, i), nil - case int8: - return AppendInt8(b, i), nil - case int16: - return AppendInt16(b, i), nil - case int32: - return AppendInt32(b, i), nil - case int64: - return AppendInt64(b, i), nil - case int: - return AppendInt64(b, int64(i)), nil - case uint: - return AppendUint64(b, uint64(i)), nil - case uint8: - return AppendUint8(b, i), nil - case uint16: - return AppendUint16(b, i), nil - case uint32: - return AppendUint32(b, i), nil - case uint64: - return AppendUint64(b, i), nil - case time.Time: - return AppendTime(b, i), nil - case map[string]interface{}: - return AppendMapStrIntf(b, i) - case map[string]string: - return AppendMapStrStr(b, i), nil - case []interface{}: - b = AppendArrayHeader(b, uint32(len(i))) - var err error - for _, k := range i { - b, err = AppendIntf(b, k) - if err != nil { - return b, err - } - } - return b, nil - } - - var err error - v := reflect.ValueOf(i) - switch v.Kind() { - case reflect.Array, reflect.Slice: - l := v.Len() - b = AppendArrayHeader(b, uint32(l)) - for i := 0; i < l; i++ { - b, err = AppendIntf(b, v.Index(i).Interface()) - if err != nil { - return b, err - } - } - return b, nil - case reflect.Ptr: - if v.IsNil() { - return AppendNil(b), err - } - b, err = AppendIntf(b, v.Elem().Interface()) - return b, err - default: - return b, &ErrUnsupportedType{T: v.Type()} - } -} diff --git a/internal/schema/cache.go b/internal/schema/cache.go index cb227a6969..bf21697cf1 100644 --- a/internal/schema/cache.go +++ b/internal/schema/cache.go @@ -163,7 +163,7 @@ func (c *cache) createField(field reflect.StructField, parentAlias string) *fiel } // Check if the type is supported and don't cache it if not. // First let's get the basic type. - var isSlice, isStruct bool + isSlice, isStruct := false, false ft := field.Type m := isTextUnmarshaler(reflect.Zero(ft)) if ft.Kind() == reflect.Ptr { diff --git a/internal/storage/memory/memory_test.go b/internal/storage/memory/memory_test.go index 7fc6b955d8..fb2b88a0e5 100644 --- a/internal/storage/memory/memory_test.go +++ b/internal/storage/memory/memory_test.go @@ -149,13 +149,13 @@ func Benchmark_Storage_Memory(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { for _, key := range keys { - _ = d.Set(key, value, ttl) + d.Set(key, value, ttl) } for _, key := range keys { _, _ = d.Get(key) } for _, key := range keys { - _ = d.Delete(key) + d.Delete(key) } } }) diff --git a/internal/template/html/html.go b/internal/template/html/html.go index 446431f7b2..71f6f02cb5 100644 --- a/internal/template/html/html.go +++ b/internal/template/html/html.go @@ -156,6 +156,7 @@ func (e *Engine) Load() error { name = strings.TrimSuffix(name, e.extension) // name = strings.Replace(name, e.extension, "", -1) // Read the file + // #gosec G304 buf, err := utils.ReadFile(path, e.fileSystem) if err != nil { return err diff --git a/internal/template/utils/utils.go b/internal/template/utils/utils.go index ee681b4f99..0ae8f22fd8 100644 --- a/internal/template/utils/utils.go +++ b/internal/template/utils/utils.go @@ -21,6 +21,7 @@ func Walk(fs http.FileSystem, root string, walkFn filepath.WalkFunc) error { return walk(fs, root, info, walkFn) } +// #nosec G304 // ReadFile returns the raw content of a file func ReadFile(path string, fs http.FileSystem) ([]byte, error) { if fs != nil { diff --git a/internal/uuid/CONTRIBUTORS b/internal/uuid/CONTRIBUTORS deleted file mode 100644 index b4bb97f6bc..0000000000 --- a/internal/uuid/CONTRIBUTORS +++ /dev/null @@ -1,9 +0,0 @@ -Paul Borman -bmatsuo -shawnps -theory -jboverfelt -dsymonds -cd1 -wallclockbuilder -dansouza diff --git a/internal/uuid/LICENSE b/internal/uuid/LICENSE deleted file mode 100644 index 5dc68268d9..0000000000 --- a/internal/uuid/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009,2014 Google Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/internal/uuid/dce.go b/internal/uuid/dce.go deleted file mode 100644 index d67ada3c9c..0000000000 --- a/internal/uuid/dce.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "encoding/binary" - "os" - "strconv" - "strings" -) - -// A Domain represents a Version 2 domain -type Domain byte - -// Domain constants for DCE Security (Version 2) UUIDs. -const ( - Person = Domain(0) - Group = Domain(1) - Org = Domain(2) -) - -// NewDCESecurity returns a DCE Security (Version 2) UUID. -// -// The domain should be one of Person, Group or Org. -// On a POSIX system the id should be the users UID for the Person -// domain and the users GID for the Group. The meaning of id for -// the domain Org or on non-POSIX systems is site defined. -// -// For a given domain/id pair the same token may be returned for up to -// 7 minutes and 10 seconds. -func NewDCESecurity(domain Domain, id uint32) (UUID, error) { - uuid, err := NewUUID() - if err == nil { - uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2 - uuid[9] = byte(domain) - binary.BigEndian.PutUint32(uuid[0:], id) - } - return uuid, err -} - -// NewDCEPerson returns a DCE Security (Version 2) UUID in the person -// domain with the id returned by os.Getuid. -// -// NewDCESecurity(Person, uint32(os.Getuid())) -func NewDCEPerson() (UUID, error) { - return NewDCESecurity(Person, uint32(os.Getuid())) -} - -// NewDCEGroup returns a DCE Security (Version 2) UUID in the group -// domain with the id returned by os.Getgid. -// -// NewDCESecurity(Group, uint32(os.Getgid())) -func NewDCEGroup() (UUID, error) { - return NewDCESecurity(Group, uint32(os.Getgid())) -} - -// Domain returns the domain for a Version 2 UUID. Domains are only defined -// for Version 2 UUIDs. -func (uuid UUID) Domain() Domain { - return Domain(uuid[9]) -} - -// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2 -// UUIDs. -func (uuid UUID) ID() uint32 { - return binary.BigEndian.Uint32(uuid[0:4]) -} - -func (d Domain) String() string { - switch d { - case Person: - return "Person" - case Group: - return "Group" - case Org: - return "Org" - } - return strings.Join([]string{"Domain", strconv.Itoa(int(d))}, "") -} diff --git a/internal/uuid/doc.go b/internal/uuid/doc.go deleted file mode 100644 index 5b8a4b9af8..0000000000 --- a/internal/uuid/doc.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package uuid generates and inspects UUIDs. -// -// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security -// Services. -// -// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to -// maps or compared directly. -package uuid diff --git a/internal/uuid/hash.go b/internal/uuid/hash.go deleted file mode 100644 index 0165f723b2..0000000000 --- a/internal/uuid/hash.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "crypto/md5" - "crypto/sha1" - "hash" -) - -// Well known namespace IDs and UUIDs -var ( - NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")) - NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")) - NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")) - NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")) - Nil UUID // empty UUID, all zeros -) - -// NewHash returns a new UUID derived from the hash of space concatenated with -// data generated by h. The hash should be at least 16 byte in length. The -// first 16 bytes of the hash are used to form the UUID. The version of the -// UUID will be the lower 4 bits of version. NewHash is used to implement -// NewMD5 and NewSHA1. -func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { - h.Reset() - h.Write(space[:]) - h.Write(data) - s := h.Sum(nil) - var uuid UUID - copy(uuid[:], s) - uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4) - uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant - return uuid -} - -// NewMD5 returns a new MD5 (Version 3) UUID based on the -// supplied name space and data. It is the same as calling: -// -// NewHash(md5.New(), space, data, 3) -func NewMD5(space UUID, data []byte) UUID { - return NewHash(md5.New(), space, data, 3) -} - -// NewSHA1 returns a new SHA1 (Version 5) UUID based on the -// supplied name space and data. It is the same as calling: -// -// NewHash(sha1.New(), space, data, 5) -func NewSHA1(space UUID, data []byte) UUID { - return NewHash(sha1.New(), space, data, 5) -} diff --git a/internal/uuid/marshal.go b/internal/uuid/marshal.go deleted file mode 100644 index b0f3337dff..0000000000 --- a/internal/uuid/marshal.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "errors" - "strconv" -) - -// MarshalText implements encoding.TextMarshaler. -func (uuid UUID) MarshalText() ([]byte, error) { - var js [36]byte - encodeHex(js[:], uuid) - return js[:], nil -} - -// UnmarshalText implements encoding.TextUnmarshaler. -func (uuid *UUID) UnmarshalText(data []byte) error { - id, err := ParseBytes(data) - if err != nil { - return err - } - *uuid = id - return nil -} - -// MarshalBinary implements encoding.BinaryMarshaler. -func (uuid UUID) MarshalBinary() ([]byte, error) { - return uuid[:], nil -} - -// UnmarshalBinary implements encoding.BinaryUnmarshaler. -func (uuid *UUID) UnmarshalBinary(data []byte) error { - if len(data) != 16 { - return errors.New("invalid UUID (got " + strconv.Itoa(len(data)) + " bytes)") - } - copy(uuid[:], data) - return nil -} diff --git a/internal/uuid/node.go b/internal/uuid/node.go deleted file mode 100644 index d651a2b061..0000000000 --- a/internal/uuid/node.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "sync" -) - -var ( - nodeMu sync.Mutex - ifname string // name of interface being used - nodeID [6]byte // hardware for version 1 UUIDs - zeroID [6]byte // nodeID with only 0's -) - -// NodeInterface returns the name of the interface from which the NodeID was -// derived. The interface "user" is returned if the NodeID was set by -// SetNodeID. -func NodeInterface() string { - defer nodeMu.Unlock() - nodeMu.Lock() - return ifname -} - -// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs. -// If name is "" then the first usable interface found will be used or a random -// Node ID will be generated. If a named interface cannot be found then false -// is returned. -// -// SetNodeInterface never fails when name is "". -func SetNodeInterface(name string) bool { - defer nodeMu.Unlock() - nodeMu.Lock() - return setNodeInterface(name) -} - -func setNodeInterface(name string) bool { - iname, addr := getHardwareInterface(name) // null implementation for js - if iname != "" && addr != nil { - ifname = iname - copy(nodeID[:], addr) - return true - } - - // We found no interfaces with a valid hardware address. If name - // does not specify a specific interface generate a random Node ID - // (section 4.1.6) - if name == "" { - ifname = "random" - randomBits(nodeID[:]) - return true - } - return false -} - -// NodeID returns a slice of a copy of the current Node ID, setting the Node ID -// if not already set. -func NodeID() []byte { - defer nodeMu.Unlock() - nodeMu.Lock() - if nodeID == zeroID { - setNodeInterface("") - } - nid := nodeID - return nid[:] -} - -// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes -// of id are used. If id is less than 6 bytes then false is returned and the -// Node ID is not set. -func SetNodeID(id []byte) bool { - if len(id) < 6 { - return false - } - defer nodeMu.Unlock() - nodeMu.Lock() - copy(nodeID[:], id) - ifname = "user" - return true -} - -// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is -// not valid. The NodeID is only well defined for version 1 and 2 UUIDs. -func (uuid UUID) NodeID() []byte { - var node [6]byte - copy(node[:], uuid[10:]) - return node[:] -} diff --git a/internal/uuid/node_js.go b/internal/uuid/node_js.go deleted file mode 100644 index 96090351a9..0000000000 --- a/internal/uuid/node_js.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build js -// +build js - -package uuid - -// getHardwareInterface returns nil values for the JS version of the code. -// This remvoves the "net" dependency, because it is not used in the browser. -// Using the "net" library inflates the size of the transpiled JS code by 673k bytes. -func getHardwareInterface(name string) (string, []byte) { return "", nil } diff --git a/internal/uuid/node_net.go b/internal/uuid/node_net.go deleted file mode 100644 index e91358f7d9..0000000000 --- a/internal/uuid/node_net.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2017 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:build !js -// +build !js - -package uuid - -import "net" - -var interfaces []net.Interface // cached list of interfaces - -// getHardwareInterface returns the name and hardware address of interface name. -// If name is "" then the name and hardware address of one of the system's -// interfaces is returned. If no interfaces are found (name does not exist or -// there are no interfaces) then "", nil is returned. -// -// Only addresses of at least 6 bytes are returned. -func getHardwareInterface(name string) (string, []byte) { - if interfaces == nil { - var err error - interfaces, err = net.Interfaces() - if err != nil { - return "", nil - } - } - for _, ifs := range interfaces { - if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) { - return ifs.Name, ifs.HardwareAddr - } - } - return "", nil -} diff --git a/internal/uuid/sql.go b/internal/uuid/sql.go deleted file mode 100644 index 10958abd2b..0000000000 --- a/internal/uuid/sql.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "database/sql/driver" - "errors" - "fmt" -) - -// Scan implements sql.Scanner so UUIDs can be read from databases transparently -// Currently, database types that map to string and []byte are supported. Please -// consult database-specific driver documentation for matching types. -func (uuid *UUID) Scan(src interface{}) error { - switch src := src.(type) { - case nil: - return nil - - case string: - // if an empty UUID comes from a table, we return a null UUID - if src == "" { - return nil - } - - // see Parse for required string format - u, err := Parse(src) - if err != nil { - return errors.New("Scan: " + err.Error()) - } - - *uuid = u - - case []byte: - // if an empty UUID comes from a table, we return a null UUID - if len(src) == 0 { - return nil - } - - // assumes a simple slice of bytes if 16 bytes - // otherwise attempts to parse - if len(src) != 16 { - return uuid.Scan(string(src)) - } - copy((*uuid)[:], src) - - default: - // here we use %T for type - return fmt.Errorf("Scan: unable to scan type %T into UUID", src) - } - - return nil -} - -// Value implements sql.Valuer so that UUIDs can be written to databases -// transparently. Currently, UUIDs map to strings. Please consult -// database-specific driver documentation for matching types. -func (uuid UUID) Value() (driver.Value, error) { - return uuid.String(), nil -} diff --git a/internal/uuid/time.go b/internal/uuid/time.go deleted file mode 100644 index e6ef06cdc8..0000000000 --- a/internal/uuid/time.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "encoding/binary" - "sync" - "time" -) - -// A Time represents a time as the number of 100's of nanoseconds since 15 Oct -// 1582. -type Time int64 - -const ( - lillian = 2299160 // Julian day of 15 Oct 1582 - unix = 2440587 // Julian day of 1 Jan 1970 - epoch = unix - lillian // Days between epochs - g1582 = epoch * 86400 // seconds between epochs - g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs -) - -var ( - timeMu sync.Mutex - lasttime uint64 // last time we returned - clockSeq uint16 // clock sequence for this run - - timeNow = time.Now // for testing -) - -// UnixTime converts t the number of seconds and nanoseconds using the Unix -// epoch of 1 Jan 1970. -func (t Time) UnixTime() (sec, nsec int64) { - sec = int64(t - g1582ns100) - nsec = (sec % 10000000) * 100 - sec /= 10000000 - return sec, nsec -} - -// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and -// clock sequence as well as adjusting the clock sequence as needed. An error -// is returned if the current time cannot be determined. -func GetTime() (Time, uint16, error) { - defer timeMu.Unlock() - timeMu.Lock() - return getTime() -} - -func getTime() (Time, uint16, error) { - t := timeNow() - - // If we don't have a clock sequence already, set one. - if clockSeq == 0 { - setClockSequence(-1) - } - now := uint64(t.UnixNano()/100) + g1582ns100 - - // If time has gone backwards with this clock sequence then we - // increment the clock sequence - if now <= lasttime { - clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000 - } - lasttime = now - return Time(now), clockSeq, nil -} - -// ClockSequence returns the current clock sequence, generating one if not -// already set. The clock sequence is only used for Version 1 UUIDs. -// -// The uuid package does not use global static storage for the clock sequence or -// the last time a UUID was generated. Unless SetClockSequence is used, a new -// random clock sequence is generated the first time a clock sequence is -// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) -func ClockSequence() int { - defer timeMu.Unlock() - timeMu.Lock() - return clockSequence() -} - -func clockSequence() int { - if clockSeq == 0 { - setClockSequence(-1) - } - return int(clockSeq & 0x3fff) -} - -// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to -// -1 causes a new sequence to be generated. -func SetClockSequence(seq int) { - defer timeMu.Unlock() - timeMu.Lock() - setClockSequence(seq) -} - -func setClockSequence(seq int) { - if seq == -1 { - var b [2]byte - randomBits(b[:]) // clock sequence - seq = int(b[0])<<8 | int(b[1]) - } - oldSeq := clockSeq - clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant - if oldSeq != clockSeq { - lasttime = 0 - } -} - -// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in -// uuid. The time is only defined for version 1 and 2 UUIDs. -func (uuid UUID) Time() Time { - time := int64(binary.BigEndian.Uint32(uuid[0:4])) - time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32 - time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48 - return Time(time) -} - -// ClockSequence returns the clock sequence encoded in uuid. -// The clock sequence is only well defined for version 1 and 2 UUIDs. -func (uuid UUID) ClockSequence() int { - return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff -} diff --git a/internal/uuid/util.go b/internal/uuid/util.go deleted file mode 100644 index 5ea6c73780..0000000000 --- a/internal/uuid/util.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "io" -) - -// randomBits completely fills slice b with random data. -func randomBits(b []byte) { - if _, err := io.ReadFull(rander, b); err != nil { - panic(err.Error()) // rand should never fail - } -} - -// xvalues returns the value of a byte as a hexadecimal digit or 255. -var xvalues = [256]byte{ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, - 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, -} - -// xtob converts hex characters x1 and x2 into a byte. -func xtob(x1, x2 byte) (byte, bool) { - b1 := xvalues[x1] - b2 := xvalues[x2] - return (b1 << 4) | b2, b1 != 255 && b2 != 255 -} diff --git a/internal/uuid/uuid.go b/internal/uuid/uuid.go deleted file mode 100644 index fadbb69a6c..0000000000 --- a/internal/uuid/uuid.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2018 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "bytes" - "crypto/rand" - "encoding/hex" - "errors" - "fmt" - "io" - "strconv" - "strings" -) - -// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC -// 4122. -type UUID [16]byte - -// A Version represents a UUID's version. -type Version byte - -// A Variant represents a UUID's variant. -type Variant byte - -// Constants returned by Variant. -const ( - Invalid = Variant(iota) // Invalid UUID - RFC4122 // The variant specified in RFC4122 - Reserved // Reserved, NCS backward compatibility. - Microsoft // Reserved, Microsoft Corporation backward compatibility. - Future // Reserved for future definition. -) - -var rander = rand.Reader // random function - -// Parse decodes s into a UUID or returns an error. Both the standard UUID -// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and -// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the -// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex -// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx. -func Parse(s string) (UUID, error) { - var uuid UUID - switch len(s) { - // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - case 36: - - // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - case 36 + 9: - if strings.ToLower(s[:9]) != "urn:uuid:" { - return uuid, errors.New(`invalid urn prefix: "` + s[:9] + `"`) - } - s = s[9:] - - // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} - case 36 + 2: - s = s[1:] - - // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - case 32: - var ok bool - for i := range uuid { - uuid[i], ok = xtob(s[i*2], s[i*2+1]) - if !ok { - return uuid, errors.New("invalid UUID format") - } - } - return uuid, nil - default: - return uuid, errors.New("invalid UUID length: " + strconv.Itoa(len(s))) - } - // s is now at least 36 bytes long - // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' { - return uuid, errors.New("invalid UUID format") - } - for i, x := range [16]int{ - 0, 2, 4, 6, - 9, 11, - 14, 16, - 19, 21, - 24, 26, 28, 30, 32, 34} { - v, ok := xtob(s[x], s[x+1]) - if !ok { - return uuid, errors.New("invalid UUID format") - } - uuid[i] = v - } - return uuid, nil -} - -// ParseBytes is like Parse, except it parses a byte slice instead of a string. -func ParseBytes(b []byte) (UUID, error) { - var uuid UUID - switch len(b) { - case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) { - return uuid, errors.New(`invalid urn prefix: "` + string(b[:9]) + `"`) - } - b = b[9:] - case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} - b = b[1:] - case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx - var ok bool - for i := 0; i < 32; i += 2 { - uuid[i/2], ok = xtob(b[i], b[i+1]) - if !ok { - return uuid, errors.New("invalid UUID format") - } - } - return uuid, nil - default: - return uuid, errors.New("invalid UUID length: " + strconv.Itoa(len(b))) - } - // s is now at least 36 bytes long - // it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' { - return uuid, errors.New("invalid UUID format") - } - for i, x := range [16]int{ - 0, 2, 4, 6, - 9, 11, - 14, 16, - 19, 21, - 24, 26, 28, 30, 32, 34} { - v, ok := xtob(b[x], b[x+1]) - if !ok { - return uuid, errors.New("invalid UUID format") - } - uuid[i] = v - } - return uuid, nil -} - -// MustParse is like Parse but panics if the string cannot be parsed. -// It simplifies safe initialization of global variables holding compiled UUIDs. -func MustParse(s string) UUID { - uuid, err := Parse(s) - if err != nil { - panic(`uuid: Parse(` + s + `): ` + err.Error()) - } - return uuid -} - -// FromBytes creates a new UUID from a byte slice. Returns an error if the slice -// does not have a length of 16. The bytes are copied from the slice. -func FromBytes(b []byte) (uuid UUID, err error) { - err = uuid.UnmarshalBinary(b) - return uuid, err -} - -// Must returns uuid if err is nil and panics otherwise. -func Must(uuid UUID, err error) UUID { - if err != nil { - panic(err) - } - return uuid -} - -// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx -// , or "" if uuid is invalid. -func (uuid UUID) String() string { - var buf [36]byte - encodeHex(buf[:], uuid) - return string(buf[:]) -} - -// URN returns the RFC 2141 URN form of uuid, -// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid. -func (uuid UUID) URN() string { - var buf [36 + 9]byte - copy(buf[:], "urn:uuid:") - encodeHex(buf[9:], uuid) - return string(buf[:]) -} - -func encodeHex(dst []byte, uuid UUID) { - hex.Encode(dst, uuid[:4]) - dst[8] = '-' - hex.Encode(dst[9:13], uuid[4:6]) - dst[13] = '-' - hex.Encode(dst[14:18], uuid[6:8]) - dst[18] = '-' - hex.Encode(dst[19:23], uuid[8:10]) - dst[23] = '-' - hex.Encode(dst[24:], uuid[10:]) -} - -// Variant returns the variant encoded in uuid. -func (uuid UUID) Variant() Variant { - switch { - case (uuid[8] & 0xc0) == 0x80: - return RFC4122 - case (uuid[8] & 0xe0) == 0xc0: - return Microsoft - case (uuid[8] & 0xe0) == 0xe0: - return Future - default: - return Reserved - } -} - -// Version returns the version of uuid. -func (uuid UUID) Version() Version { - return Version(uuid[6] >> 4) -} - -func (v Version) String() string { - if v > 15 { - return fmt.Sprintf("BAD_VERSION_%d", v) - } - return fmt.Sprintf("VERSION_%d", v) -} - -func (v Variant) String() string { - switch v { - case RFC4122: - return "RFC4122" - case Reserved: - return "Reserved" - case Microsoft: - return "Microsoft" - case Future: - return "Future" - case Invalid: - return "Invalid" - } - return fmt.Sprintf("BadVariant%d", int(v)) -} - -// SetRand sets the random number generator to r, which implements io.Reader. -// If r.Read returns an error when the package requests random data then -// a panic will be issued. -// -// Calling SetRand with nil sets the random number generator to the default -// generator. -func SetRand(r io.Reader) { - if r == nil { - rander = rand.Reader - return - } - rander = r -} diff --git a/internal/uuid/version1.go b/internal/uuid/version1.go deleted file mode 100644 index 463109629e..0000000000 --- a/internal/uuid/version1.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import ( - "encoding/binary" -) - -// NewUUID returns a Version 1 UUID based on the current NodeID and clock -// sequence, and the current time. If the NodeID has not been set by SetNodeID -// or SetNodeInterface then it will be set automatically. If the NodeID cannot -// be set NewUUID returns nil. If clock sequence has not been set by -// SetClockSequence then it will be set automatically. If GetTime fails to -// return the current NewUUID returns nil and an error. -// -// In most cases, New should be used. -func NewUUID() (UUID, error) { - var uuid UUID - now, seq, err := GetTime() - if err != nil { - return uuid, err - } - - timeLow := uint32(now & 0xffffffff) - timeMid := uint16((now >> 32) & 0xffff) - timeHi := uint16((now >> 48) & 0x0fff) - timeHi |= 0x1000 // Version 1 - - binary.BigEndian.PutUint32(uuid[0:], timeLow) - binary.BigEndian.PutUint16(uuid[4:], timeMid) - binary.BigEndian.PutUint16(uuid[6:], timeHi) - binary.BigEndian.PutUint16(uuid[8:], seq) - - nodeMu.Lock() - if nodeID == zeroID { - setNodeInterface("") - } - copy(uuid[10:], nodeID[:]) - nodeMu.Unlock() - - return uuid, nil -} diff --git a/internal/uuid/version4.go b/internal/uuid/version4.go deleted file mode 100644 index 3d09259011..0000000000 --- a/internal/uuid/version4.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package uuid - -import "io" - -// New creates a new random UUID or panics. New is equivalent to -// the expression -// -// uuid.Must(uuid.NewRandom()) -func New() UUID { - return Must(NewRandom()) -} - -// NewRandom returns a Random (Version 4) UUID. -// -// The strength of the UUIDs is based on the strength of the crypto/rand -// package. -// -// A note about uniqueness derived from the UUID Wikipedia entry: -// -// Randomly generated UUIDs have 122 random bits. One's annual risk of being -// hit by a meteorite is estimated to be one chance in 17 billion, that -// means the probability is about 0.00000000006 (6 × 10−11), -// equivalent to the odds of creating a few tens of trillions of UUIDs in a -// year and having one duplicate. -func NewRandom() (UUID, error) { - return NewRandomFromReader(rander) -} - -// NewRandomFromReader returns a UUID based on bytes read from a given io.Reader. -func NewRandomFromReader(r io.Reader) (UUID, error) { - var uuid UUID - _, err := io.ReadFull(r, uuid[:]) - if err != nil { - return Nil, err - } - uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4 - uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10 - return uuid, nil -} diff --git a/listen.go b/listen.go index 19dbae3621..80acd1edd2 100644 --- a/listen.go +++ b/listen.go @@ -305,8 +305,8 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { //nolint: r " │ Prefork .%s PID ....%s │\n"+ " └───────────────────────────────────────────────────┘"+ colors.Reset, - value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12), //nolint:gomnd // Using random padding lengths is fine here - value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14), //nolint:gomnd // Using random padding lengths is fine here + value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12), + value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14), ) var childPidsLogo string diff --git a/middleware/basicauth/config.go b/middleware/basicauth/config.go index b19d5087ea..3845e91538 100644 --- a/middleware/basicauth/config.go +++ b/middleware/basicauth/config.go @@ -53,8 +53,6 @@ type Config struct { } // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Users: map[string]string{}, diff --git a/middleware/cache/cache.go b/middleware/cache/cache.go index 8a733b7571..a4bfc4ddaf 100644 --- a/middleware/cache/cache.go +++ b/middleware/cache/cache.go @@ -34,7 +34,6 @@ const ( noStore = "no-store" ) -//nolint:gochecknoglobals // TODO: Do not use a global var here var ignoreHeaders = map[string]interface{}{ "Connection": nil, "Keep-Alive": nil, diff --git a/middleware/cache/config.go b/middleware/cache/config.go index 9c2d2e104d..98e9e94f5d 100644 --- a/middleware/cache/config.go +++ b/middleware/cache/config.go @@ -75,8 +75,6 @@ type Config struct { } // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Expiration: 1 * time.Minute, diff --git a/middleware/cache/manager.go b/middleware/cache/manager.go index 78660072e2..8770478b45 100644 --- a/middleware/cache/manager.go +++ b/middleware/cache/manager.go @@ -10,8 +10,6 @@ import ( // go:generate msgp // msgp -file="manager.go" -o="manager_msgp.go" -tests=false -unexported -// don't forget to replace the msgp import path to: -// "github.com/gofiber/fiber/v2/internal/msgp" type item struct { body []byte ctype []byte diff --git a/middleware/cache/manager_msgp.go b/middleware/cache/manager_msgp.go index c02d82546c..3d45903a55 100644 --- a/middleware/cache/manager_msgp.go +++ b/middleware/cache/manager_msgp.go @@ -5,7 +5,7 @@ package cache // DO NOT EDIT import ( - "github.com/gofiber/fiber/v2/internal/msgp" + "github.com/tinylib/msgp/msgp" ) // DecodeMsg implements msgp.Decodable diff --git a/middleware/compress/compress_test.go b/middleware/compress/compress_test.go index 28dd3f2a2f..4baf19c73d 100644 --- a/middleware/compress/compress_test.go +++ b/middleware/compress/compress_test.go @@ -12,10 +12,8 @@ import ( "github.com/gofiber/fiber/v2/utils" ) -//nolint:gochecknoglobals // Using a global var is fine here var filedata []byte -//nolint:gochecknoinits // init() is used to populate a global var from a README file func init() { dat, err := os.ReadFile("../../.github/README.md") if err != nil { diff --git a/middleware/compress/config.go b/middleware/compress/config.go index fb176c6083..5495ad4c42 100644 --- a/middleware/compress/config.go +++ b/middleware/compress/config.go @@ -33,8 +33,6 @@ const ( ) // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Level: LevelDefault, diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index 78dcae2dd3..d7325d5b2f 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -53,8 +53,6 @@ type Config struct { } // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, AllowOrigins: "*", diff --git a/middleware/csrf/config.go b/middleware/csrf/config.go index 7482b60a8f..18514a25af 100644 --- a/middleware/csrf/config.go +++ b/middleware/csrf/config.go @@ -105,8 +105,6 @@ type Config struct { const HeaderName = "X-Csrf-Token" // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ KeyLookup: "header:" + HeaderName, CookieName: "csrf_", diff --git a/middleware/csrf/manager.go b/middleware/csrf/manager.go index a16f3a213a..20d3d93e1a 100644 --- a/middleware/csrf/manager.go +++ b/middleware/csrf/manager.go @@ -11,8 +11,6 @@ import ( // go:generate msgp // msgp -file="manager.go" -o="manager_msgp.go" -tests=false -unexported -// don't forget to replace the msgp import path to: -// "github.com/gofiber/fiber/v2/internal/msgp" type item struct{} //msgp:ignore manager diff --git a/middleware/csrf/manager_msgp.go b/middleware/csrf/manager_msgp.go index 05b7bb00d4..337870bc93 100644 --- a/middleware/csrf/manager_msgp.go +++ b/middleware/csrf/manager_msgp.go @@ -3,7 +3,7 @@ package csrf // Code generated by github.com/tinylib/msgp DO NOT EDIT. import ( - "github.com/gofiber/fiber/v2/internal/msgp" + "github.com/tinylib/msgp/msgp" ) // DecodeMsg implements msgp.Decodable diff --git a/middleware/earlydata/config.go b/middleware/earlydata/config.go index 9ec223a8b7..2fffa4667a 100644 --- a/middleware/earlydata/config.go +++ b/middleware/earlydata/config.go @@ -33,8 +33,6 @@ type Config struct { } // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ IsEarlyData: func(c *fiber.Ctx) bool { return c.Get(DefaultHeaderName) == DefaultHeaderTrueValue diff --git a/middleware/encryptcookie/config.go b/middleware/encryptcookie/config.go index d8e4ba21da..c49fc16ebb 100644 --- a/middleware/encryptcookie/config.go +++ b/middleware/encryptcookie/config.go @@ -34,8 +34,6 @@ type Config struct { } // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Except: []string{"csrf_"}, diff --git a/middleware/encryptcookie/encryptcookie_test.go b/middleware/encryptcookie/encryptcookie_test.go index aed45d6903..47d988990b 100644 --- a/middleware/encryptcookie/encryptcookie_test.go +++ b/middleware/encryptcookie/encryptcookie_test.go @@ -11,7 +11,6 @@ import ( "github.com/valyala/fasthttp" ) -//nolint:gochecknoglobals // Using a global var is fine here var testKey = GenerateKey() func Test_Middleware_Encrypt_Cookie(t *testing.T) { diff --git a/middleware/etag/config.go b/middleware/etag/config.go index efc31d86c5..57a7c787ab 100644 --- a/middleware/etag/config.go +++ b/middleware/etag/config.go @@ -23,8 +23,6 @@ type Config struct { } // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Weak: false, Next: nil, diff --git a/middleware/etag/etag.go b/middleware/etag/etag.go index 98955833f8..13148fc63b 100644 --- a/middleware/etag/etag.go +++ b/middleware/etag/etag.go @@ -104,7 +104,7 @@ func appendUint(dst []byte, n uint32) []byte { var q uint32 for n >= 10 { i-- - q = n / 10 //nolint:gomnd // TODO: Explain why we divide by 10 here + q = n / 10 buf[i] = '0' + byte(n-q*10) n = q } diff --git a/middleware/expvar/config.go b/middleware/expvar/config.go index c31cebcf3d..8d9caa4650 100644 --- a/middleware/expvar/config.go +++ b/middleware/expvar/config.go @@ -12,7 +12,6 @@ type Config struct { Next func(c *fiber.Ctx) bool } -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, } diff --git a/middleware/favicon/favicon.go b/middleware/favicon/favicon.go index e7ef5d4dbb..66b688093c 100644 --- a/middleware/favicon/favicon.go +++ b/middleware/favicon/favicon.go @@ -34,8 +34,6 @@ type Config struct { } // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, File: "", diff --git a/middleware/filesystem/filesystem.go b/middleware/filesystem/filesystem.go index 1913ef59e5..02169d57c9 100644 --- a/middleware/filesystem/filesystem.go +++ b/middleware/filesystem/filesystem.go @@ -56,8 +56,6 @@ type Config struct { } // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Root: nil, diff --git a/middleware/idempotency/config.go b/middleware/idempotency/config.go index 21aeb6909c..f6dd8b4917 100644 --- a/middleware/idempotency/config.go +++ b/middleware/idempotency/config.go @@ -49,15 +49,13 @@ type Config struct { } // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here 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, //nolint:gomnd // No magic number, just the default config + Lifetime: 30 * time.Minute, KeyHeader: "X-Idempotency-Key", KeyHeaderValidate: func(k string) error { @@ -112,7 +110,7 @@ func configDefault(config ...Config) Config { if cfg.Storage == nil { cfg.Storage = memory.New(memory.Config{ - GCInterval: cfg.Lifetime / 2, //nolint:gomnd // Half the lifetime interval + GCInterval: cfg.Lifetime / 2, // Half the lifetime interval }) } diff --git a/middleware/idempotency/response_msgp.go b/middleware/idempotency/response_msgp.go index 255d96f06d..4eb4d7fcb0 100644 --- a/middleware/idempotency/response_msgp.go +++ b/middleware/idempotency/response_msgp.go @@ -3,7 +3,7 @@ package idempotency // Code generated by github.com/tinylib/msgp DO NOT EDIT. import ( - "github.com/gofiber/fiber/v2/internal/msgp" + "github.com/tinylib/msgp/msgp" ) // MarshalMsg implements msgp.Marshaler diff --git a/middleware/idempotency/response_msgp_test.go b/middleware/idempotency/response_msgp_test.go index a932efb866..cf41da4939 100644 --- a/middleware/idempotency/response_msgp_test.go +++ b/middleware/idempotency/response_msgp_test.go @@ -5,7 +5,7 @@ package idempotency import ( "testing" - "github.com/gofiber/fiber/v2/internal/msgp" + "github.com/tinylib/msgp/msgp" ) func TestMarshalUnmarshalresponse(t *testing.T) { diff --git a/middleware/limiter/config.go b/middleware/limiter/config.go index a123ea227b..92d078623d 100644 --- a/middleware/limiter/config.go +++ b/middleware/limiter/config.go @@ -69,10 +69,8 @@ type Config struct { } // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ - Max: 5, //nolint:gomnd // No magic number, just the default config + Max: 5, Expiration: 1 * time.Minute, KeyGenerator: func(c *fiber.Ctx) string { return c.IP() diff --git a/middleware/limiter/manager.go b/middleware/limiter/manager.go index 6b257f4593..374d3a1691 100644 --- a/middleware/limiter/manager.go +++ b/middleware/limiter/manager.go @@ -10,8 +10,6 @@ import ( // go:generate msgp // msgp -file="manager.go" -o="manager_msgp.go" -tests=false -unexported -// don't forget to replace the msgp import path to: -// "github.com/gofiber/fiber/v2/internal/msgp" type item struct { currHits int prevHits int diff --git a/middleware/limiter/manager_msgp.go b/middleware/limiter/manager_msgp.go index 0ed5d939e4..a0d81ec91d 100644 --- a/middleware/limiter/manager_msgp.go +++ b/middleware/limiter/manager_msgp.go @@ -3,7 +3,7 @@ package limiter // Code generated by github.com/tinylib/msgp DO NOT EDIT. import ( - "github.com/gofiber/fiber/v2/internal/msgp" + "github.com/tinylib/msgp/msgp" ) // DecodeMsg implements msgp.Decodable diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 0d45468230..21f34aad7c 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -79,15 +79,13 @@ type Buffer interface { type LogFunc func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Done: nil, Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", TimeFormat: "15:04:05", TimeZone: "Local", - TimeInterval: 500 * time.Millisecond, //nolint:gomnd // No magic number, just the default config + TimeInterval: 500 * time.Millisecond, Output: os.Stdout, enableColors: true, } diff --git a/middleware/monitor/config.go b/middleware/monitor/config.go index 10889707f9..3c56ada9f9 100644 --- a/middleware/monitor/config.go +++ b/middleware/monitor/config.go @@ -41,17 +41,16 @@ type Config struct { // 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 + ChartJsURL string // TODO: Rename to "ChartJSURL" in v3 index string } -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Title: defaultTitle, Refresh: defaultRefresh, FontURL: defaultFontURL, - ChartJSURL: defaultChartJSURL, + ChartJsURL: defaultChartJSURL, CustomHead: defaultCustomHead, APIOnly: false, Next: nil, @@ -70,7 +69,7 @@ func configDefault(config ...Config) Config { if ConfigDefault.Title != defaultTitle || ConfigDefault.Refresh != defaultRefresh || ConfigDefault.FontURL != defaultFontURL || - ConfigDefault.ChartJSURL != defaultChartJSURL || + ConfigDefault.ChartJsURL != defaultChartJSURL || ConfigDefault.CustomHead != defaultCustomHead { if ConfigDefault.Refresh < minRefresh { ConfigDefault.Refresh = minRefresh @@ -80,7 +79,7 @@ func configDefault(config ...Config) Config { ConfigDefault.Title, ConfigDefault.Refresh, ConfigDefault.FontURL, - ConfigDefault.ChartJSURL, + ConfigDefault.ChartJsURL, ConfigDefault.CustomHead, }) } @@ -105,8 +104,8 @@ func configDefault(config ...Config) Config { cfg.FontURL = defaultFontURL } - if cfg.ChartJSURL == "" { - cfg.ChartJSURL = defaultChartJSURL + if cfg.ChartJsURL == "" { + cfg.ChartJsURL = defaultChartJSURL } if cfg.Refresh < minRefresh { cfg.Refresh = minRefresh @@ -125,7 +124,7 @@ func configDefault(config ...Config) Config { title: cfg.Title, refresh: cfg.Refresh, fontURL: cfg.FontURL, - chartJSURL: cfg.ChartJSURL, + chartJSURL: cfg.ChartJsURL, customHead: cfg.CustomHead, }) diff --git a/middleware/monitor/config_test.go b/middleware/monitor/config_test.go index 60bf09ca70..fadc17e94b 100644 --- a/middleware/monitor/config_test.go +++ b/middleware/monitor/config_test.go @@ -18,7 +18,7 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJsURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) @@ -35,7 +35,7 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, title, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJsURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) @@ -51,7 +51,7 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, minRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJsURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) @@ -68,7 +68,7 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, refresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJsURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) @@ -85,7 +85,7 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, fontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJsURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) @@ -96,13 +96,13 @@ func Test_Config_Default(t *testing.T) { t.Parallel() chartURL := "http://example.com" cfg := configDefault(Config{ - ChartJSURL: chartURL, + ChartJsURL: chartURL, }) utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, chartURL, cfg.ChartJSURL) + utils.AssertEqual(t, chartURL, cfg.ChartJsURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) @@ -119,7 +119,7 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJsURL) utils.AssertEqual(t, head, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) @@ -135,7 +135,7 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJsURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, true, cfg.APIOnly) utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) @@ -154,7 +154,7 @@ func Test_Config_Default(t *testing.T) { utils.AssertEqual(t, defaultTitle, cfg.Title) utils.AssertEqual(t, defaultRefresh, cfg.Refresh) utils.AssertEqual(t, defaultFontURL, cfg.FontURL) - utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + utils.AssertEqual(t, defaultChartJSURL, cfg.ChartJsURL) utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) utils.AssertEqual(t, false, cfg.APIOnly) utils.AssertEqual(t, f(nil), cfg.Next(nil)) diff --git a/middleware/monitor/monitor.go b/middleware/monitor/monitor.go index e3e39f06fe..5eff16b2f8 100644 --- a/middleware/monitor/monitor.go +++ b/middleware/monitor/monitor.go @@ -33,7 +33,6 @@ type statsOS struct { Conns int `json:"conns"` } -//nolint:gochecknoglobals // TODO: Do not use a global var here var ( monitPIDCPU atomic.Value monitPIDRAM atomic.Value @@ -46,7 +45,6 @@ var ( monitOSConns atomic.Value ) -//nolint:gochecknoglobals // TODO: Do not use a global var here var ( mutex sync.RWMutex once sync.Once @@ -105,34 +103,34 @@ func New(config ...Config) fiber.Handler { func updateStatistics(p *process.Process) { pidCPU, err := p.CPUPercent() - if err != nil { - monitPIDCPU.Store(pidCPU / 10) //nolint:gomnd // TODO: Explain why we divide by 10 here + if err == nil { + monitPIDCPU.Store(pidCPU / 10) } - if osCPU, err := cpu.Percent(0, false); err != nil && len(osCPU) > 0 { + if osCPU, err := cpu.Percent(0, false); err == nil && len(osCPU) > 0 { monitOSCPU.Store(osCPU[0]) } - if pidRAM, err := p.MemoryInfo(); err != nil && pidRAM != nil { + if pidRAM, err := p.MemoryInfo(); err == nil && pidRAM != nil { monitPIDRAM.Store(pidRAM.RSS) } - if osRAM, err := mem.VirtualMemory(); err != nil && osRAM != nil { + if osRAM, err := mem.VirtualMemory(); err == nil && osRAM != nil { monitOSRAM.Store(osRAM.Used) monitOSTotalRAM.Store(osRAM.Total) } - if loadAvg, err := load.Avg(); err != nil && loadAvg != nil { + if loadAvg, err := load.Avg(); err == nil && loadAvg != nil { monitOSLoadAvg.Store(loadAvg.Load1) } pidConns, err := net.ConnectionsPid("tcp", p.Pid) - if err != nil { + if err == nil { monitPIDConns.Store(len(pidConns)) } osConns, err := net.Connections("tcp") - if err != nil { + if err == nil { monitOSConns.Store(len(osConns)) } } diff --git a/middleware/monitor/monitor_test.go b/middleware/monitor/monitor_test.go index f6e63fd9d4..f12c505d91 100644 --- a/middleware/monitor/monitor_test.go +++ b/middleware/monitor/monitor_test.go @@ -87,7 +87,7 @@ func Test_Monitor_Html_CustomCodes(t *testing.T) { conf := Config{ Title: "New " + defaultTitle, Refresh: defaultRefresh + time.Second, - ChartJSURL: "https://cdnjs.com/libraries/Chart.js", + ChartJsURL: "https://cdnjs.com/libraries/Chart.js", FontURL: "/public/my-font.css", CustomHead: ``, } diff --git a/middleware/pprof/config.go b/middleware/pprof/config.go index fb3e0978ae..ef8f05e34d 100644 --- a/middleware/pprof/config.go +++ b/middleware/pprof/config.go @@ -19,7 +19,6 @@ type Config struct { Prefix string } -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, } diff --git a/middleware/proxy/config.go b/middleware/proxy/config.go index 094b2c32a0..ca0f8940be 100644 --- a/middleware/proxy/config.go +++ b/middleware/proxy/config.go @@ -58,8 +58,6 @@ type Config struct { } // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, ModifyRequest: nil, diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index 356ebc10cb..830d71f985 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -104,13 +104,11 @@ func Balancer(config Config) fiber.Handler { } } -//nolint:gochecknoglobals // TODO: Do not use a global var here var client = &fasthttp.Client{ NoDefaultUserAgentHeader: true, DisablePathNormalizing: true, } -//nolint:gochecknoglobals // TODO: Do not use a global var here var lock sync.RWMutex // WithTlsConfig update http client with a user specified tls.config diff --git a/middleware/recover/config.go b/middleware/recover/config.go index 56b805d2e0..b29e750259 100644 --- a/middleware/recover/config.go +++ b/middleware/recover/config.go @@ -23,8 +23,6 @@ type Config struct { } // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, EnableStackTrace: false, diff --git a/middleware/requestid/config.go b/middleware/requestid/config.go index ca27b47f75..b3b605e590 100644 --- a/middleware/requestid/config.go +++ b/middleware/requestid/config.go @@ -33,8 +33,6 @@ type Config struct { // It 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. -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ Next: nil, Header: fiber.HeaderXRequestID, diff --git a/middleware/session/config.go b/middleware/session/config.go index c7c8662704..bee6c2d384 100644 --- a/middleware/session/config.go +++ b/middleware/session/config.go @@ -69,10 +69,8 @@ const ( ) // ConfigDefault is the default config -// -//nolint:gochecknoglobals // Using a global var is fine here var ConfigDefault = Config{ - Expiration: 24 * time.Hour, //nolint:gomnd // No magic number, just the default config + Expiration: 24 * time.Hour, KeyLookup: "cookie:session_id", KeyGenerator: utils.UUIDv4, source: "cookie", diff --git a/middleware/session/data.go b/middleware/session/data.go index f213b252f5..75024f80bc 100644 --- a/middleware/session/data.go +++ b/middleware/session/data.go @@ -6,14 +6,11 @@ import ( // go:generate msgp // msgp -file="data.go" -o="data_msgp.go" -tests=false -unexported -// don't forget to replace the msgp import path to: -// "github.com/gofiber/fiber/v2/internal/msgp" type data struct { sync.RWMutex Data map[string]interface{} } -//nolint:gochecknoglobals // TODO: Do not use a global var here var dataPool = sync.Pool{ New: func() interface{} { d := new(data) diff --git a/middleware/session/session.go b/middleware/session/session.go index f18abbf8b2..34ff67da98 100644 --- a/middleware/session/session.go +++ b/middleware/session/session.go @@ -23,7 +23,6 @@ type Session struct { exp time.Duration // expiration of this session } -//nolint:gochecknoglobals // TODO: Do not use a global var here var sessionPool = sync.Pool{ New: func() interface{} { return new(Session) diff --git a/middleware/session/store.go b/middleware/session/store.go index fd3b08d029..bda5db7f45 100644 --- a/middleware/session/store.go +++ b/middleware/session/store.go @@ -16,7 +16,6 @@ type Store struct { Config } -//nolint:gochecknoglobals // TODO: Do not use a global var here var mux sync.Mutex func New(config ...Config) *Store { diff --git a/path.go b/path.go index ba357328e8..f7e726f8c2 100644 --- a/path.go +++ b/path.go @@ -13,8 +13,8 @@ import ( "time" "unicode" - "github.com/gofiber/fiber/v2/internal/uuid" "github.com/gofiber/fiber/v2/utils" + "github.com/google/uuid" ) // routeParser holds the path segments and param names @@ -88,8 +88,6 @@ const ( ) // list of possible parameter and segment delimiter -// -//nolint:gochecknoglobals // TODO: Do not use a global var here var ( // slash has a special role, unlike the other parameters it must not be interpreted as a parameter routeDelimiter = []byte{slashDelimiter, '-', '.'} @@ -341,7 +339,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r constraint.Data = splitNonEscaped(c[start+1:end], string(parameterConstraintDataSeparatorChars)) if len(constraint.Data) == 1 { constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) - } else if len(constraint.Data) == 2 { //nolint:gomnd // This is fine, we simply expect two parts + } else if len(constraint.Data) == 2 { // This is fine, we simply expect two parts constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) constraint.Data[1] = RemoveEscapeChar(constraint.Data[1]) } diff --git a/prefork.go b/prefork.go index 7eebd50636..63dc6ce240 100644 --- a/prefork.go +++ b/prefork.go @@ -21,7 +21,6 @@ const ( envPreforkChildVal = "1" ) -//nolint:gochecknoglobals // TODO: Do not use global vars here var ( testPreforkMaster = false testOnPrefork = false @@ -157,7 +156,6 @@ func watchMaster() { } } -//nolint:gochecknoglobals // TODO: Do not use global vars here var ( dummyPid = 1 dummyChildCmd atomic.Value diff --git a/router_test.go b/router_test.go index 0fbb7c8f46..0cc8e766ea 100644 --- a/router_test.go +++ b/router_test.go @@ -20,10 +20,8 @@ import ( "github.com/valyala/fasthttp" ) -//nolint:gochecknoglobals // TODO: Do not use a global var here var routesFixture routeJSON -//nolint:gochecknoinits // init() is used to populate a global struct from a JSON file func init() { dat, err := os.ReadFile("./.github/testdata/testRoutes.json") if err != nil { diff --git a/utils/common.go b/utils/common.go index 1789a239b0..6c1dd1e911 100644 --- a/utils/common.go +++ b/utils/common.go @@ -19,7 +19,7 @@ import ( "sync/atomic" "unicode" - googleuuid "github.com/gofiber/fiber/v2/internal/uuid" + googleuuid "github.com/google/uuid" ) const ( @@ -35,7 +35,6 @@ const ( emptyUUID = "00000000-0000-0000-0000-000000000000" ) -//nolint:gochecknoglobals // TODO: Do not use a global var here var ( uuidSeed [24]byte uuidCounter uint64 @@ -44,8 +43,6 @@ var ( ) // UUID generates an universally unique identifier (UUID) -// -//nolint:gomnd // Those are not random numbers, it's the fastuuid algorithm func UUID() string { // Setup seed & counter once uuidSetup.Do(func() { diff --git a/utils/convert.go b/utils/convert.go index 4a91ead995..233dfbce31 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -49,7 +49,7 @@ func CopyBytes(b []byte) []byte { } const ( - uByte = 1 << (10 * iota) //nolint:gomnd // 1 << 10 == 1024 + uByte = 1 << (10 * iota) // 1 << 10 == 1024 uKilobyte uMegabyte uGigabyte diff --git a/utils/http.go b/utils/http.go index 69f25ef193..22da906826 100644 --- a/utils/http.go +++ b/utils/http.go @@ -71,8 +71,6 @@ func StatusMessage(status int) string { } // NOTE: Keep this in sync with the status code list -// -//nolint:gochecknoglobals // Using a global var is fine here var statusMessage = []string{ 100: "Continue", // StatusContinue 101: "Switching Protocols", // StatusSwitchingProtocols @@ -146,8 +144,6 @@ var statusMessage = []string{ // MIME types were copied from https://github.com/nginx/nginx/blob/67d2a9541826ecd5db97d604f23460210fd3e517/conf/mime.types with the following updates: // - Use "application/xml" instead of "text/xml" as recommended per https://datatracker.ietf.org/doc/html/rfc7303#section-4.1 // - Use "text/javascript" instead of "application/javascript" as recommended per https://www.rfc-editor.org/rfc/rfc9239#name-text-javascript -// -//nolint:gochecknoglobals // Using a global var is fine here var mimeExtensions = map[string]string{ "html": "text/html", "htm": "text/html", diff --git a/utils/ips.go b/utils/ips.go index adac9d4255..4886c117f7 100644 --- a/utils/ips.go +++ b/utils/ips.go @@ -6,8 +6,6 @@ import ( // IsIPv4 works the same way as net.ParseIP, // but without check for IPv6 case and without returning net.IP slice, whereby IsIPv4 makes no allocations. -// -//nolint:gomnd // Magic numbers used for a faster algorithm than net.ParseIP func IsIPv4(s string) bool { for i := 0; i < net.IPv4len; i++ { if len(s) == 0 { @@ -42,8 +40,6 @@ func IsIPv4(s string) bool { // IsIPv6 works the same way as net.ParseIP, // but without check for IPv4 case and without returning net.IP slice, whereby IsIPv6 makes no allocations. -// -//nolint:gomnd // Magic numbers used for a faster algorithm than net.ParseIP func IsIPv6(s string) bool { ellipsis := -1 // position of ellipsis in ip diff --git a/utils/time.go b/utils/time.go index 93fe88087e..8ea13c2262 100644 --- a/utils/time.go +++ b/utils/time.go @@ -6,7 +6,6 @@ import ( "time" ) -//nolint:gochecknoglobals // TODO: Do not use global vars here var ( timestampTimer sync.Once // Timestamp please start the timer function before you use this value From 2820aef585a976caa8a3026c4774efb0fe5d1410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Thu, 2 Feb 2023 23:01:37 +0300 Subject: [PATCH 041/212] :broom: chore: add go 1.20 to ci and readmes (#2322) * :white_check_mark: chore: add go 1.20 to ci and readmes * :broom: chore: add go 1.20 to ci and readmes * update linter * fix linter * fix benchmarks * fix benchmarks * fix benchmarks --- .github/README.md | 2 +- .github/README_de.md | 2 +- .github/README_es.md | 2 +- .github/README_fa.md | 2 +- .github/README_fr.md | 2 +- .github/README_he.md | 2 +- .github/README_id.md | 2 +- .github/README_it.md | 2 +- .github/README_ja.md | 2 +- .github/README_ko.md | 2 +- .github/README_nl.md | 2 +- .github/README_pt.md | 2 +- .github/README_ru.md | 2 +- .github/README_sa.md | 2 +- .github/README_tr.md | 2 +- .github/README_uk.md | 2 +- .github/README_zh-CN.md | 2 +- .github/README_zh-TW.md | 2 +- .github/testdata/testRoutes.json | 8 +++----- .github/workflows/benchmark.yml | 2 +- .github/workflows/linter.yml | 4 ++-- .github/workflows/test.yml | 2 +- .github/workflows/vulncheck.yml | 2 +- .golangci.yml | 3 ++- app_test.go | 2 +- go.mod | 2 +- middleware/encryptcookie/encryptcookie.go | 8 ++++---- path.go | 2 +- utils/http.go | 1 - 29 files changed, 35 insertions(+), 37 deletions(-) diff --git a/.github/README.md b/.github/README.md index ccb9d28a86..6c26f34c75 100644 --- a/.github/README.md +++ b/.github/README.md @@ -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.19. +* 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. * 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_de.md b/.github/README_de.md index f5a6ec8ca0..e80ece3571 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -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.19. +* 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. * 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 686b9f6c23..a584448d90 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -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.19. +* 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. * 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 c07124da49..558922ec59 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -185,7 +185,7 @@ Fiber از Express الهام گرفته, که محبوب ترین فری

## ⚠️ محدودیت ها -* به دلیل استفاده ناامن از Fiber, ممکن است کتابخانه همیشه با آخرین نسخه Go سازگار نباشد. Fiber 2.40.0 با زبان گو نسخه 1.16 تا 1.19 تست شده است. +* به دلیل استفاده ناامن از Fiber, ممکن است کتابخانه همیشه با آخرین نسخه Go سازگار نباشد. Fiber 2.40.0 با زبان گو نسخه 1.16 تا 1.20 تست شده است. * فریمورک Fiber با پکیج net/http سازگار نیست. این بدان معناست شما نمی توانید از پکیج های مانند go-swagger, gqlgen یا سایر پروژه هایی که بخشی از اکوسیستم net/http هستند استفاده کنید.
diff --git a/.github/README_fr.md b/.github/README_fr.md index a2fff61d34..7bffea6b75 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -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.19. +* 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. * 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 fe952f4045..5e54b71b20 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -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.19. +* 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. * 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 3ca1fb4d7c..b297c36c8a 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -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.19. +* 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. * 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 a9371d5f49..bb13792972 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -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.19 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.16 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 137768c04b..ca0f97f770 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -150,7 +150,7 @@ Fiber は人気の高い Web フレームワークである Expressjs に**イ ## ⚠️ 制限事項 -- Fiber は unsafe パッケージを使用しているため、最新の Go バージョンと互換性がない場合があります。Fiber 2.40.0 は、Go のバージョン 1.16 から 1.19 でテストされています。 +- Fiber は unsafe パッケージを使用しているため、最新の Go バージョンと互換性がない場合があります。Fiber 2.40.0 は、Go のバージョン 1.16 から 1.20 でテストされています。 - Fiber は net/http インターフェースと互換性がありません。つまり、gqlgen や go-swagger など、net/http のエコシステムの一部であるプロジェクトを使用することができません。 ## 👀 例 diff --git a/.github/README_ko.md b/.github/README_ko.md index 240a660ffb..1d8b9ec222 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -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.19로 테스트되고 있습니다. +* Fiber는 unsafe 패키지를 사용하기 때문에 최신 Go버전과 호환되지 않을 수 있습니다.Fiber 2.40.0은 Go 버전 1.16에서 1.20로 테스트되고 있습니다. * Fiber는 net/http 인터페이스와 호환되지 않습니다.즉, gqlgen이나 go-swagger 등 net/http 생태계의 일부인 프로젝트를 사용할 수 없습니다. ## 👀 예제 diff --git a/.github/README_nl.md b/.github/README_nl.md index c67bf32d92..250021d202 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -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.19. +* 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. * 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 37c38d7137..a08b6c1e55 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -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.19. +* 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. * 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 0fd1725975..3afeeef752 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -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.19. +* 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. * 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 fec84d47c5..2ebfee3938 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -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.19. +* 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. * 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 f1b0bea488..6bc5aacf54 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -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.19 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.16 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 diff --git a/.github/README_uk.md b/.github/README_uk.md index f19388490a..82fcb23525 100644 --- a/.github/README_uk.md +++ b/.github/README_uk.md @@ -160,7 +160,7 @@ Fiber **натхненний** Express, найпопулярнішим веб-ф ## ⚠️ Обмеження -- Через те, що Fiber використовує unsafe, бібліотека не завжди може бути сумісною з останньою версією Go. Fiber 2.40.0 було протестовано з Go версій 1.16 до 1.19. +- Через те, що Fiber використовує unsafe, бібліотека не завжди може бути сумісною з останньою версією Go. Fiber 2.40.0 було протестовано з Go версій 1.16 до 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 9d4e4e1cfe..b8aeb07e8e 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -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.19 上测试过。 +* 由于 Fiber 使用了 unsafe 特性,导致其可能与最新的 Go 版本不兼容。Fiber 2.40.0 已经在 Go 1.16 到 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 bb29d21e03..2188f81834 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -152,7 +152,7 @@ Fiber **啟發自** Express——網際網路上最知名的 Web 框架,我們 ## ⚠️ 限制 -- 由於 Fiber 有用到 Unsafe,本函式庫有時可能無法相容最新版的 Go 語言。Fiber 2.40.0 已在 Go 1.16 至 1.19 的版本測試過。 +- 由於 Fiber 有用到 Unsafe,本函式庫有時可能無法相容最新版的 Go 語言。Fiber 2.40.0 已在 Go 1.16 至 1.20 的版本測試過。 - Fiber 不相容 net/http 的介面,意味著您無法使用像是 gqlgen、go-swagger 或其他任何屬於 net/http 生態系統的專案。 ## 👀 範例 diff --git a/.github/testdata/testRoutes.json b/.github/testdata/testRoutes.json index 8e08aef162..0503d1802e 100644 --- a/.github/testdata/testRoutes.json +++ b/.github/testdata/testRoutes.json @@ -1,6 +1,5 @@ { - "testRoutes": [ - { + "test_routes": [{ "method": "GET", "path": "/authorizations" }, @@ -957,8 +956,7 @@ "path": "/user/keys/1337" } ], - "githubAPI": [ - { + "github_api": [{ "method": "GET", "path": "/authorizations" }, @@ -1915,4 +1913,4 @@ "path": "/user/keys/:id" } ] -} \ No newline at end of file +} diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index d375be446a..b960d9cc47 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -7,7 +7,7 @@ jobs: - name: Install Go uses: actions/setup-go@v3 with: - go-version: 1.18.x + go-version: 1.20.x - name: Fetch Repository uses: actions/checkout@v3 - name: Run Benchmark diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index b259fccfd7..ee4b1f3f8b 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -19,10 +19,10 @@ jobs: - uses: actions/setup-go@v3 with: # NOTE: Keep this in sync with the version from go.mod - go-version: 1.19 + go-version: 1.20.x - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: # NOTE: Keep this in sync with the version from .golangci.yml - version: v1.50.1 + version: v1.51.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c83f89e8c4..4c28b27c9f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: Build: strategy: matrix: - go-version: [1.16.x, 1.17.x, 1.19.x] + go-version: [1.16.x, 1.18.x, 1.20.x] platform: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: diff --git a/.github/workflows/vulncheck.yml b/.github/workflows/vulncheck.yml index bddf020f91..06477dccbb 100644 --- a/.github/workflows/vulncheck.yml +++ b/.github/workflows/vulncheck.yml @@ -12,7 +12,7 @@ jobs: - name: Install Go uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: 1.20.x check-latest: true - name: Fetch Repository uses: actions/checkout@v3 diff --git a/.golangci.yml b/.golangci.yml index f41b559f47..b44f788eb7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,4 +1,4 @@ -# Created based on v1.50.1 +# Created based on v1.51.0 # NOTE: Keep this in sync with the version in .github/workflows/linter.yml run: @@ -49,6 +49,7 @@ linters-settings: disable: - shadow - fieldalignment + - loopclosure grouper: import-require-single-import: true diff --git a/app_test.go b/app_test.go index 5f4fbc8767..aeb946b813 100644 --- a/app_test.go +++ b/app_test.go @@ -844,7 +844,7 @@ func Test_App_Static_Direct(t *testing.T) { body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, strings.Contains(string(body), "testRoutes")) + utils.AssertEqual(t, true, strings.Contains(string(body), "test_routes")) } // go test -run Test_App_Static_MaxAge diff --git a/go.mod b/go.mod index 869be78451..92bd65d4b0 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/gofiber/fiber/v2 -go 1.19 +go 1.20 require ( github.com/google/uuid v1.3.0 diff --git a/middleware/encryptcookie/encryptcookie.go b/middleware/encryptcookie/encryptcookie.go index 9e323ce0e1..96047a48d1 100644 --- a/middleware/encryptcookie/encryptcookie.go +++ b/middleware/encryptcookie/encryptcookie.go @@ -42,12 +42,12 @@ func New(config ...Config) fiber.Handler { cookieValue.SetKeyBytes(key) if c.Response().Header.Cookie(&cookieValue) { encryptedValue, err := cfg.Encryptor(string(cookieValue.Value()), cfg.Key) - if err == nil { - cookieValue.SetValue(encryptedValue) - c.Response().Header.SetCookie(&cookieValue) - } else { + if err != nil { panic(err) } + + cookieValue.SetValue(encryptedValue) + c.Response().Header.SetCookie(&cookieValue) } } }) diff --git a/path.go b/path.go index f7e726f8c2..83f7c237fc 100644 --- a/path.go +++ b/path.go @@ -572,7 +572,7 @@ func findGreedyParamLen(s string, searchCount int, segment *routeSegment) int { // check all from right to left segments for i := segment.PartCount; i > 0 && searchCount > 0; i-- { searchCount-- - if constPosition := strings.LastIndex(s, segment.ComparePart); constPosition != -1 { + if constPosition := strings.LastIndex(s, segment.ComparePart); constPosition != -1 { //nolint:revive // Actually not simpler s = s[:constPosition] } else { break diff --git a/utils/http.go b/utils/http.go index 22da906826..02498aede3 100644 --- a/utils/http.go +++ b/utils/http.go @@ -38,7 +38,6 @@ func ParseVendorSpecificContentType(cType string) string { } var parsableType string - //nolint:revive // Actually not simpler if semiColonIndex := strings.Index(cType, ";"); semiColonIndex == -1 { parsableType = cType[plusIndex+1:] } else if plusIndex < semiColonIndex { From 21cd45b750a723a5fc87a8bfb9d4b12016a901fa 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=BB=D0=B5=D1=81=D0=BD=D0=B8=D0=BA=D0=BE=D0=B2?= Date: Fri, 3 Feb 2023 19:14:24 +1000 Subject: [PATCH 042/212] =?UTF-8?q?PR:=20add=20url=20for=20favicon=20middl?= =?UTF-8?q?eware,=20for=20correct=20handling=20different=20of=E2=80=A6=20(?= =?UTF-8?q?#2231)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * PR: add url for favicon middleware, for correct handling different of ico formats * pr: efectn > URL would be better naming i think * pr: add test case * apply reviews * remove json annotinos, since they are unnecessary * readme fixes * linting fixes --------- Co-authored-by: koalan Co-authored-by: Muhammed Efe Çetin --- middleware/favicon/README.md | 23 +++++++++++++++++++++-- middleware/favicon/favicon.go | 17 +++++++++++++---- middleware/favicon/favicon_test.go | 20 ++++++++++++++++++++ 3 files changed, 54 insertions(+), 6 deletions(-) diff --git a/middleware/favicon/README.md b/middleware/favicon/README.md index 5c678fddb7..521d021d42 100644 --- a/middleware/favicon/README.md +++ b/middleware/favicon/README.md @@ -2,7 +2,7 @@ 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. +**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) @@ -13,6 +13,7 @@ Favicon middleware for [Fiber](https://github.com/gofiber/fiber) that ignores fa - [Custom Config](#custom-config) - [Config](#config) - [Default Config](#default-config-1) + ## Signatures ```go @@ -42,6 +43,7 @@ app.Use(favicon.New()) ```go app.Use(favicon.New(favicon.Config{ File: "./favicon.ico", + URL: "/favicon.ico", })) ``` @@ -59,6 +61,22 @@ type Config struct { // // 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 } ``` @@ -67,6 +85,7 @@ type Config struct { ```go var ConfigDefault = Config{ Next: nil, - File: "" + File: "", + URL: "/favicon.ico", } ``` diff --git a/middleware/favicon/favicon.go b/middleware/favicon/favicon.go index 66b688093c..ba192f9423 100644 --- a/middleware/favicon/favicon.go +++ b/middleware/favicon/favicon.go @@ -19,24 +19,30 @@ type Config struct { // File holds the path to an actual favicon that will be cached // // Optional. Default: "" - File string `json:"file"` + 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 `json:"-"` + FileSystem http.FileSystem // CacheControl defines how the Cache-Control header in the response should be set // // Optional. Default: "public, max-age=31536000" - CacheControl string `json:"cache_control"` + CacheControl string } // ConfigDefault is the default config var ConfigDefault = Config{ Next: nil, File: "", + URL: fPath, CacheControl: "public, max-age=31536000", } @@ -60,6 +66,9 @@ func New(config ...Config) fiber.Handler { if cfg.Next == nil { cfg.Next = ConfigDefault.Next } + if cfg.URL == "" { + cfg.URL = ConfigDefault.URL + } if cfg.File == "" { cfg.File = ConfigDefault.File } @@ -99,7 +108,7 @@ func New(config ...Config) fiber.Handler { } // Only respond to favicon requests - if c.Path() != fPath { + if c.Path() != cfg.URL { return c.Next() } diff --git a/middleware/favicon/favicon_test.go b/middleware/favicon/favicon_test.go index 8597b7ec47..9e628cf73e 100644 --- a/middleware/favicon/favicon_test.go +++ b/middleware/favicon/favicon_test.go @@ -79,6 +79,26 @@ func Test_Middleware_Favicon_Found(t *testing.T) { utils.AssertEqual(t, "public, max-age=31536000", resp.Header.Get(fiber.HeaderCacheControl), "CacheControl Control") } +// go test -run Test_Custom_Favicon_Url +func Test_Custom_Favicon_Url(t *testing.T) { + app := fiber.New() + const customURL = "/favicon.svg" + app.Use(New(Config{ + File: "../../.github/testdata/favicon.ico", + URL: customURL, + })) + + app.Get("/", func(c *fiber.Ctx) error { + return nil + }) + + resp, err := app.Test(httptest.NewRequest(http.MethodGet, customURL, nil)) + + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") + utils.AssertEqual(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) +} + // mockFS wraps local filesystem for the purposes of // Test_Middleware_Favicon_FileSystem located below // TODO use os.Dir if fiber upgrades to 1.16 From 675f5181ce303056719720ea8338cfed3b5fc171 Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 3 Feb 2023 13:01:33 +0100 Subject: [PATCH 043/212] prepare release for v2.42.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 4cc2334a2b..b4dd071e2d 100644 --- a/app.go +++ b/app.go @@ -30,7 +30,7 @@ import ( ) // Version of current fiber package -const Version = "2.41.0" +const Version = "2.42.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From 028d821beae35dac01a795269e58db5ab5a9cc50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Fri, 3 Feb 2023 13:59:47 +0100 Subject: [PATCH 044/212] prepare release --- .golangci.yml | 3 +++ middleware/favicon/favicon.go | 8 ++++---- path.go | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index b44f788eb7..c58d52511e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -86,6 +86,9 @@ linters-settings: disabled: true - name: cyclomatic disabled: true + - name: early-return + severity: warning + disabled: true - name: exported disabled: true - name: file-header diff --git a/middleware/favicon/favicon.go b/middleware/favicon/favicon.go index ba192f9423..84572b6d0f 100644 --- a/middleware/favicon/favicon.go +++ b/middleware/favicon/favicon.go @@ -19,23 +19,23 @@ type Config struct { // File holds the path to an actual favicon that will be cached // // Optional. Default: "" - File string + File string `json:"file"` // URL for favicon handler // // Optional. Default: "/favicon.ico" - URL string + URL string `json:"url"` // 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 + FileSystem http.FileSystem `json:"-"` // CacheControl defines how the Cache-Control header in the response should be set // // Optional. Default: "public, max-age=31536000" - CacheControl string + CacheControl string `json:"cache_control"` } // ConfigDefault is the default config diff --git a/path.go b/path.go index 83f7c237fc..f7e726f8c2 100644 --- a/path.go +++ b/path.go @@ -572,7 +572,7 @@ func findGreedyParamLen(s string, searchCount int, segment *routeSegment) int { // check all from right to left segments for i := segment.PartCount; i > 0 && searchCount > 0; i-- { searchCount-- - if constPosition := strings.LastIndex(s, segment.ComparePart); constPosition != -1 { //nolint:revive // Actually not simpler + if constPosition := strings.LastIndex(s, segment.ComparePart); constPosition != -1 { s = s[:constPosition] } else { break From 61a3336119fa4bde5911d9af34950e1ae3144dbb Mon Sep 17 00:00:00 2001 From: ACHMAD IRIANTO EKA PUTRA Date: Fri, 3 Feb 2023 21:45:27 +0800 Subject: [PATCH 045/212] add forward from domain (#2323) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add forward from domain * add balancer forward * add unittest and readme * add short description new feature * add short description on signature * golangci-lint fix --------- Co-authored-by: René Werner --- middleware/proxy/README.md | 165 ++++++++++++++++++--------------- middleware/proxy/proxy.go | 50 ++++++++++ middleware/proxy/proxy_test.go | 57 ++++++++++++ 3 files changed, 198 insertions(+), 74 deletions(-) diff --git a/middleware/proxy/README.md b/middleware/proxy/README.md index 0c68b1d86a..6cc03e8b9d 100644 --- a/middleware/proxy/README.md +++ b/middleware/proxy/README.md @@ -12,9 +12,16 @@ Proxy middleware for [Fiber](https://github.com/gofiber/fiber) that allows you t ### 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 @@ -23,8 +30,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/proxy" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/proxy" ) ``` @@ -39,54 +46,64 @@ proxy.WithTlsConfig(&tls.Config{ // if you need to use global self-custom client, you should use proxy.WithClient. proxy.WithClient(&fasthttp.Client{ - NoDefaultUserAgentHeader: true, - DisablePathNormalizing: true, + 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, + 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 + 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", - }, + 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 - }, + 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", })) ``` @@ -95,50 +112,50 @@ app.Use(proxy.Balancer(proxy.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 + // 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 + // 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 } ``` diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index 830d71f985..008342631f 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -178,3 +178,53 @@ func getScheme(uri []byte) []byte { } return uri[:i-1] } + +// DomainForward performs an http request based on the given domain and populates the given http response. +// This method will return an fiber.Handler +func DomainForward(hostname, addr string, clients ...*fasthttp.Client) fiber.Handler { + return func(c *fiber.Ctx) error { + host := string(c.Request().Host()) + if host == hostname { + return Do(c, addr+c.OriginalURL(), clients...) + } + return nil + } +} + +type roundrobin struct { + sync.Mutex + + current int + pool []string +} + +// this method will return a string of addr server from list server. +func (r *roundrobin) get() string { + r.Lock() + defer r.Unlock() + + if r.current >= len(r.pool) { + r.current %= len(r.pool) + } + + result := r.pool[r.current] + r.current++ + return result +} + +// BalancerForward Forward performs the given http request with round robin algorithm to server and fills the given http response. +// This method will return an fiber.Handler +func BalancerForward(servers []string, clients ...*fasthttp.Client) fiber.Handler { + r := &roundrobin{ + current: 0, + pool: servers, + } + return func(c *fiber.Ctx) error { + server := r.get() + if !strings.HasPrefix(server, "http") { + server = "http://" + server + } + c.Request().Header.Add("X-Real-IP", c.IP()) + return Do(c, server+c.OriginalURL(), clients...) + } +} diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index d87145514a..6ed169ed69 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -473,3 +473,60 @@ func Test_ProxyBalancer_Custom_Client(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) } + +// go test -run Test_Proxy_Domain_Forward_Local +func Test_Proxy_Domain_Forward_Local(t *testing.T) { + t.Parallel() + ln, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0") + utils.AssertEqual(t, nil, err) + app := fiber.New(fiber.Config{DisableStartupMessage: true}) + + // target server + ln1, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0") + utils.AssertEqual(t, nil, err) + app1 := fiber.New(fiber.Config{DisableStartupMessage: true}) + + app1.Get("/test", func(c *fiber.Ctx) error { + return c.SendString("test_local_client:" + c.Query("query_test")) + }) + + proxyAddr := ln.Addr().String() + targetAddr := ln1.Addr().String() + localDomain := strings.Replace(proxyAddr, "127.0.0.1", "localhost", 1) + app.Use(DomainForward(localDomain, "http://"+targetAddr, &fasthttp.Client{ + NoDefaultUserAgentHeader: true, + DisablePathNormalizing: true, + + Dial: fasthttp.Dial, + })) + + go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { utils.AssertEqual(t, nil, app1.Listener(ln1)) }() + + code, body, errs := fiber.Get("http://" + localDomain + "/test?query_test=true").String() + utils.AssertEqual(t, 0, len(errs)) + utils.AssertEqual(t, fiber.StatusOK, code) + utils.AssertEqual(t, "test_local_client:true", body) +} + +// go test -run Test_Proxy_Balancer_Forward_Local +func Test_Proxy_Balancer_Forward_Local(t *testing.T) { + t.Parallel() + + app := fiber.New() + + _, addr := createProxyTestServer(t, func(c *fiber.Ctx) error { + return c.SendString("forwarded") + }) + + app.Use(BalancerForward([]string{addr})) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + + b, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + + utils.AssertEqual(t, string(b), "forwarded") +} From 7899176d8e0d565d8c5b068f27f65fe60b39cdff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Sun, 5 Feb 2023 23:46:46 +0300 Subject: [PATCH 046/212] :construction_worker: v3 (ci): fix linter, remove gosec worklow --- .github/workflows/security.yml | 16 ---------------- .golangci.yml | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 .github/workflows/security.yml diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml deleted file mode 100644 index b5c495747e..0000000000 --- a/.github/workflows/security.yml +++ /dev/null @@ -1,16 +0,0 @@ -on: - push: - branches: - - v3-beta - pull_request: -name: Security -jobs: - Gosec: - runs-on: ubuntu-latest - steps: - - name: Fetch Repository - uses: actions/checkout@v3 - - name: Run Gosec - uses: securego/gosec@master - with: - args: -exclude-dir=internal/*/ ./... diff --git a/.golangci.yml b/.golangci.yml index af3bfb8d72..30b28dc8b7 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -48,7 +48,7 @@ linters-settings: packages: - flag - io/ioutil - packages-with-error-message: + packages-with-error-message: - flag: '`flag` package is only allowed in main.go' - io/ioutil: '`io/ioutil` package is deprecated, use the `io` and `os` package instead' From 54439a5bde246577331b6e345c9d640b289568c2 Mon Sep 17 00:00:00 2001 From: Iliya Date: Thu, 9 Feb 2023 09:14:13 +0330 Subject: [PATCH 047/212] =?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 048/212] =?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 c2749c36c2a17c6f06866ef45d5c43c3d43bd21a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Thu, 9 Feb 2023 22:33:45 +0300 Subject: [PATCH 049/212] :construction_worker: v3 (ci): fix some linter warnings --- app.go | 2 ++ app_test.go | 18 ++++++++----- bind.go | 12 ++++----- bind_test.go | 39 ++++++++++++++-------------- binder/binder.go | 18 +++++++------ binder/cookie.go | 1 - binder/json.go | 2 +- binder/mapping.go | 5 ++-- binder/query.go | 1 - binder/xml.go | 2 +- ctx.go | 6 ++--- ctx_interface.go | 2 +- ctx_test.go | 38 +++++++++++++++------------ go.mod | 1 - go.sum | 2 -- listen_test.go | 6 ++--- middleware/adaptor/adopter_test.go | 1 - middleware/helmet/helmet_test.go | 12 ++++----- middleware/keyauth/keyauth.go | 2 +- middleware/logger/default_logger.go | 2 +- middleware/logger/template_chain.go | 2 +- middleware/redirect/redirect.go | 2 +- middleware/redirect/redirect_test.go | 6 +++-- middleware/rewrite/rewrite.go | 2 +- middleware/session/session_test.go | 3 ++- redirect.go | 2 +- router.go | 15 +++++------ router_test.go | 2 +- 28 files changed, 107 insertions(+), 99 deletions(-) diff --git a/app.go b/app.go index 0c328a203e..06800e0d3e 100644 --- a/app.go +++ b/app.go @@ -1044,6 +1044,8 @@ func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) { c := app.AcquireCtx().(*DefaultCtx) c.Reset(fctx) + defer app.ReleaseCtx(c) + var errNetOP *net.OpError switch { diff --git a/app_test.go b/app_test.go index 853cb84741..046acad977 100644 --- a/app_test.go +++ b/app_test.go @@ -605,17 +605,23 @@ func Test_App_Order(t *testing.T) { app := New() app.Get("/test", func(c Ctx) error { - c.Write([]byte("1")) + _, err := c.Write([]byte("1")) + require.NoError(t, err) + return c.Next() }) app.All("/test", func(c Ctx) error { - c.Write([]byte("2")) + _, err := c.Write([]byte("2")) + require.NoError(t, err) + return c.Next() }) app.Use(func(c Ctx) error { - c.Write([]byte("3")) + _, err := c.Write([]byte("3")) + require.NoError(t, err) + return nil }) @@ -865,9 +871,9 @@ func Test_App_Static_Custom_CacheControl(t *testing.T) { require.Equal(t, nil, err, "app.Test(req)") require.Equal(t, "no-cache, no-store, must-revalidate", resp.Header.Get(HeaderCacheControl), "CacheControl Control") - normal_resp, normal_err := app.Test(httptest.NewRequest(MethodGet, "/config.yml", nil)) - require.Equal(t, nil, normal_err, "app.Test(req)") - require.Equal(t, "", normal_resp.Header.Get(HeaderCacheControl), "CacheControl Control") + normalResp, normalErr := app.Test(httptest.NewRequest(MethodGet, "/config.yml", nil)) + require.Equal(t, nil, normalErr, "app.Test(req)") + require.Equal(t, "", normalResp.Header.Get(HeaderCacheControl), "CacheControl Control") } // go test -run Test_App_Static_Download diff --git a/bind.go b/bind.go index 512a4afbf5..ab3594944f 100644 --- a/bind.go +++ b/bind.go @@ -66,9 +66,9 @@ func (b *Bind) validateStruct(out any) error { // NOTE: Should/Must is still valid for Custom binders. func (b *Bind) Custom(name string, dest any) error { binders := b.ctx.App().customBinders - for _, binder := range binders { - if binder.Name() == name { - return b.returnErr(binder.Parse(b.ctx, dest)) + for _, customBinder := range binders { + if customBinder.Name() == name { + return b.returnErr(customBinder.Parse(b.ctx, dest)) } } @@ -181,10 +181,10 @@ func (b *Bind) Body(out any) error { // Check custom binders binders := b.ctx.App().customBinders - for _, binder := range binders { - for _, mime := range binder.MIMETypes() { + for _, customBinder := range binders { + for _, mime := range customBinder.MIMETypes() { if mime == ctype { - return b.returnErr(binder.Parse(b.ctx, out)) + return b.returnErr(customBinder.Parse(b.ctx, out)) } } } diff --git a/bind_test.go b/bind_test.go index 51101859f4..4931eec864 100644 --- a/bind_test.go +++ b/bind_test.go @@ -16,6 +16,8 @@ import ( "github.com/valyala/fasthttp" ) +const helloWorld = "hello world" + // go test -run Test_Bind_Query -v func Test_Bind_Query(t *testing.T) { t.Parallel() @@ -63,7 +65,7 @@ func Test_Bind_Query(t *testing.T) { c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football&favouriteDrinks=milo,coke,pepsi&alloc=&no=1") q2 := new(Query2) q2.Bool = true - q2.Name = "hello world" + q2.Name = helloWorld require.Nil(t, c.Bind().Query(q2)) require.Equal(t, "basketball,football", q2.Hobby) require.True(t, q2.Bool) @@ -133,7 +135,7 @@ func Test_Bind_Query_Map(t *testing.T) { func Test_Bind_Query_WithSetParserDecoder(t *testing.T) { type NonRFCTime time.Time - NonRFCConverter := func(value string) reflect.Value { + nonRFCConverter := func(value string) reflect.Value { if v, err := time.Parse("2006-01-02", value); err == nil { return reflect.ValueOf(v) } @@ -142,7 +144,7 @@ func Test_Bind_Query_WithSetParserDecoder(t *testing.T) { nonRFCTime := binder.ParserType{ Customtype: NonRFCTime{}, - Converter: NonRFCConverter, + Converter: nonRFCConverter, } binder.SetParserDecoder(binder.ParserConfig{ @@ -167,7 +169,6 @@ func Test_Bind_Query_WithSetParserDecoder(t *testing.T) { c.Request().URI().SetQueryString("date=2021-04-10&title=CustomDateTest&Body=October") require.Nil(t, c.Bind().Query(q)) - fmt.Println(q.Date, "q.Date") require.Equal(t, "CustomDateTest", q.Title) date := fmt.Sprintf("%v", q.Date) require.Equal(t, "{0 63753609600 }", date) @@ -336,7 +337,7 @@ func Test_Bind_Header(t *testing.T) { h2 := new(Header2) h2.Bool = true - h2.Name = "hello world" + h2.Name = helloWorld require.Nil(t, c.Bind().Header(h2)) require.Equal(t, "go,fiber", h2.Hobby) require.True(t, h2.Bool) @@ -388,7 +389,7 @@ func Test_Bind_Header_Map(t *testing.T) { func Test_Bind_Header_WithSetParserDecoder(t *testing.T) { type NonRFCTime time.Time - NonRFCConverter := func(value string) reflect.Value { + nonRFCConverter := func(value string) reflect.Value { if v, err := time.Parse("2006-01-02", value); err == nil { return reflect.ValueOf(v) } @@ -397,7 +398,7 @@ func Test_Bind_Header_WithSetParserDecoder(t *testing.T) { nonRFCTime := binder.ParserType{ Customtype: NonRFCTime{}, - Converter: NonRFCConverter, + Converter: nonRFCConverter, } binder.SetParserDecoder(binder.ParserConfig{ @@ -425,7 +426,6 @@ func Test_Bind_Header_WithSetParserDecoder(t *testing.T) { c.Request().Header.Add("Body", "October") require.Nil(t, c.Bind().Header(r)) - fmt.Println(r.Date, "q.Date") require.Equal(t, "CustomDateTest", r.Title) date := fmt.Sprintf("%v", r.Date) require.Equal(t, "{0 63753609600 }", date) @@ -578,7 +578,7 @@ func Test_Bind_RespHeader(t *testing.T) { h2 := new(Header2) h2.Bool = true - h2.Name = "hello world" + h2.Name = helloWorld require.Nil(t, c.Bind().RespHeader(h2)) require.Equal(t, "go,fiber", h2.Hobby) require.True(t, h2.Bool) @@ -1214,7 +1214,7 @@ func Test_Bind_Cookie(t *testing.T) { h2 := new(Cookie2) h2.Bool = true - h2.Name = "hello world" + h2.Name = helloWorld require.Nil(t, c.Bind().Cookie(h2)) require.Equal(t, "go,fiber", h2.Hobby) require.True(t, h2.Bool) @@ -1266,7 +1266,7 @@ func Test_Bind_Cookie_Map(t *testing.T) { func Test_Bind_Cookie_WithSetParserDecoder(t *testing.T) { type NonRFCTime time.Time - NonRFCConverter := func(value string) reflect.Value { + nonRFCConverter := func(value string) reflect.Value { if v, err := time.Parse("2006-01-02", value); err == nil { return reflect.ValueOf(v) } @@ -1275,7 +1275,7 @@ func Test_Bind_Cookie_WithSetParserDecoder(t *testing.T) { nonRFCTime := binder.ParserType{ Customtype: NonRFCTime{}, - Converter: NonRFCConverter, + Converter: nonRFCConverter, } binder.SetParserDecoder(binder.ParserConfig{ @@ -1303,7 +1303,6 @@ func Test_Bind_Cookie_WithSetParserDecoder(t *testing.T) { c.Request().Header.SetCookie("Body", "October") require.Nil(t, c.Bind().Cookie(r)) - fmt.Println(r.Date, "q.Date") require.Equal(t, "CustomDateTest", r.Title) date := fmt.Sprintf("%v", r.Date) require.Equal(t, "{0 63753609600 }", date) @@ -1456,15 +1455,15 @@ func Benchmark_Bind_Cookie_Map(b *testing.B) { // custom binder for testing type customBinder struct{} -func (b *customBinder) Name() string { +func (*customBinder) Name() string { return "custom" } -func (b *customBinder) MIMETypes() []string { +func (*customBinder) MIMETypes() []string { return []string{"test", "test2"} } -func (b *customBinder) Parse(c Ctx, out any) error { +func (*customBinder) Parse(c Ctx, out any) error { return json.Unmarshal(c.Body(), out) } @@ -1474,8 +1473,8 @@ func Test_Bind_CustomBinder(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) // Register binder - binder := &customBinder{} - app.RegisterCustomBinder(binder) + customBinder := &customBinder{} + app.RegisterCustomBinder(customBinder) type Demo struct { Name string `json:"name"` @@ -1510,11 +1509,11 @@ func Test_Bind_Must(t *testing.T) { // simple struct validator for testing type structValidator struct{} -func (v *structValidator) Engine() any { +func (*structValidator) Engine() any { return "" } -func (v *structValidator) ValidateStruct(out any) error { +func (*structValidator) ValidateStruct(out any) error { out = reflect.ValueOf(out).Elem().Interface() sq := out.(simpleQuery) diff --git a/binder/binder.go b/binder/binder.go index 03b5935add..fb7ac12dab 100644 --- a/binder/binder.go +++ b/binder/binder.go @@ -11,11 +11,13 @@ var ( ) // Init default binders for Fiber -var HeaderBinder = &headerBinding{} -var RespHeaderBinder = &respHeaderBinding{} -var CookieBinder = &cookieBinding{} -var QueryBinder = &queryBinding{} -var FormBinder = &formBinding{} -var URIBinder = &uriBinding{} -var XMLBinder = &xmlBinding{} -var JSONBinder = &jsonBinding{} +var ( + HeaderBinder = &headerBinding{} + RespHeaderBinder = &respHeaderBinding{} + CookieBinder = &cookieBinding{} + QueryBinder = &queryBinding{} + FormBinder = &formBinding{} + URIBinder = &uriBinding{} + XMLBinder = &xmlBinding{} + JSONBinder = &jsonBinding{} +) diff --git a/binder/cookie.go b/binder/cookie.go index 2c107af882..0f5c650c33 100644 --- a/binder/cookie.go +++ b/binder/cookie.go @@ -34,7 +34,6 @@ func (b *cookieBinding) Bind(reqCtx *fasthttp.RequestCtx, out any) error { } else { data[k] = append(data[k], v) } - }) if err != nil { diff --git a/binder/json.go b/binder/json.go index 4eed4e6883..6c0d80c89b 100644 --- a/binder/json.go +++ b/binder/json.go @@ -10,6 +10,6 @@ func (*jsonBinding) Name() string { return "json" } -func (b *jsonBinding) Bind(body []byte, jsonDecoder utils.JSONUnmarshal, out any) error { +func (*jsonBinding) Bind(body []byte, jsonDecoder utils.JSONUnmarshal, out any) error { return jsonDecoder(body, out) } diff --git a/binder/mapping.go b/binder/mapping.go index 317755a23d..c4a793c171 100644 --- a/binder/mapping.go +++ b/binder/mapping.go @@ -134,10 +134,9 @@ func parseParamSquareBrackets(k string) (string, error) { kbytes := []byte(k) for i, b := range kbytes { - if b == '[' && kbytes[i+1] != ']' { if err := bb.WriteByte('.'); err != nil { - return "", err + return "", err //nolint:wrapchec } } @@ -146,7 +145,7 @@ func parseParamSquareBrackets(k string) (string, error) { } if err := bb.WriteByte(b); err != nil { - return "", err + return "", err //nolint:wrapchec } } diff --git a/binder/query.go b/binder/query.go index 440b587806..25b69f5bc3 100644 --- a/binder/query.go +++ b/binder/query.go @@ -38,7 +38,6 @@ func (b *queryBinding) Bind(reqCtx *fasthttp.RequestCtx, out any) error { } else { data[k] = append(data[k], v) } - }) if err != nil { diff --git a/binder/xml.go b/binder/xml.go index 29401abb77..af6b19e97d 100644 --- a/binder/xml.go +++ b/binder/xml.go @@ -10,6 +10,6 @@ func (*xmlBinding) Name() string { return "xml" } -func (b *xmlBinding) Bind(body []byte, out any) error { +func (*xmlBinding) Bind(body []byte, out any) error { return xml.Unmarshal(body, out) } diff --git a/ctx.go b/ctx.go index d6b94675a6..f8bcc0cf2c 100644 --- a/ctx.go +++ b/ctx.go @@ -1147,12 +1147,12 @@ func (c *DefaultCtx) Route() *Route { } // SaveFile saves any multipart file to disk. -func (c *DefaultCtx) SaveFile(fileheader *multipart.FileHeader, path string) error { +func (*DefaultCtx) SaveFile(fileheader *multipart.FileHeader, path string) error { return fasthttp.SaveMultipartFile(fileheader, path) } // SaveFileToStorage saves any multipart file to an external storage system. -func (c *DefaultCtx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, storage Storage) error { +func (*DefaultCtx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, storage Storage) error { file, err := fileheader.Open() if err != nil { return fmt.Errorf("failed to open: %w", err) @@ -1442,7 +1442,7 @@ func (c *DefaultCtx) IsProxyTrusted() bool { } // IsLocalHost will return true if address is a localhost address. -func (c *DefaultCtx) isLocalHost(address string) bool { +func (*DefaultCtx) isLocalHost(address string) bool { localHosts := []string{"127.0.0.1", "0.0.0.0", "::1"} for _, h := range localHosts { if strings.Contains(address, h) { diff --git a/ctx_interface.go b/ctx_interface.go index 816a3c00b4..c71175674b 100644 --- a/ctx_interface.go +++ b/ctx_interface.go @@ -185,7 +185,7 @@ type Ctx interface { // Next executes the next method in the stack that matches the current route. Next() (err error) - // RestartRouting instead of going to the next handler. This may be usefull after + // RestartRouting instead of going to the next handler. This may be useful after // changing the request path. Note that handlers might be executed again. RestartRouting() error diff --git a/ctx_test.go b/ctx_test.go index 08889023cc..25197ec78b 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -78,7 +78,7 @@ type customCtx struct { DefaultCtx } -func (c *customCtx) Params(key string, defaultValue ...string) string { +func (c *customCtx) Params(key string, defaultValue ...string) string { //nolint:revive return "prefix_" + c.DefaultCtx.Params(key) } @@ -97,7 +97,7 @@ func Test_Ctx_CustomCtx(t *testing.T) { app.Get("/:id", func(c Ctx) error { return c.SendString(c.Params("id")) }) - resp, err := app.Test(httptest.NewRequest("GET", "/v3", &bytes.Buffer{})) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v3", &bytes.Buffer{})) require.NoError(t, err, "app.Test(req)") body, err := io.ReadAll(resp.Body) require.NoError(t, err, "io.ReadAll(resp.Body)") @@ -531,27 +531,32 @@ func Test_Ctx_Format(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAccept, MIMETextPlain) - c.Format([]byte("Hello, World!")) + err := c.Format([]byte("Hello, World!")) + require.NoError(t, err) require.Equal(t, "Hello, World!", string(c.Response().Body())) c.Request().Header.Set(HeaderAccept, MIMETextHTML) - c.Format("Hello, World!") + err = c.Format("Hello, World!") + require.NoError(t, err) require.Equal(t, "

Hello, World!

", string(c.Response().Body())) c.Request().Header.Set(HeaderAccept, MIMEApplicationJSON) - c.Format("Hello, World!") + err = c.Format("Hello, World!") + require.NoError(t, err) require.Equal(t, `"Hello, World!"`, string(c.Response().Body())) c.Request().Header.Set(HeaderAccept, MIMETextPlain) - c.Format(complex(1, 1)) + err = c.Format(complex(1, 1)) + require.NoError(t, err) require.Equal(t, "(1+1i)", string(c.Response().Body())) c.Request().Header.Set(HeaderAccept, MIMEApplicationXML) - c.Format("Hello, World!") + err = c.Format("Hello, World!") + require.NoError(t, err) require.Equal(t, `Hello, World!`, string(c.Response().Body())) - err := c.Format(complex(1, 1)) - require.True(t, err != nil) + err = c.Format(complex(1, 1)) + require.Error(t, err) c.Request().Header.Set(HeaderAccept, MIMETextPlain) c.Format(Map{}) @@ -1079,7 +1084,7 @@ func Test_Ctx_IP(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - // default behaviour will return the remote IP from the stack + // default behavior will return the remote IP from the stack require.Equal(t, "0.0.0.0", c.IP()) // X-Forwarded-For is set, but it is ignored because proxyHeader is not set @@ -2569,11 +2574,12 @@ func Test_Ctx_RenderWithBindVars(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - c.BindVars(Map{ + err := c.BindVars(Map{ "Title": "Hello, World!", }) + require.NoError(t, err) - err := c.Render("./.github/testdata/index.tmpl", Map{}) + err = c.Render("./.github/testdata/index.tmpl", Map{}) require.NoError(t, err) buf := bytebufferpool.Get() _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail @@ -2659,10 +2665,10 @@ func Benchmark_Ctx_RenderWithLocalsAndBindVars(b *testing.B) { }) c := app.NewCtx(&fasthttp.RequestCtx{}) - c.BindVars(Map{ + err = c.BindVars(Map{ "Title": "Hello, World!", }) - require.Equal(b, nil, err) + require.NoError(b, err) c.Locals("Summary", "Test") b.ReportAllocs() @@ -2707,10 +2713,10 @@ func Benchmark_Ctx_RenderBindVars(b *testing.B) { app.config.Views = engine c := app.NewCtx(&fasthttp.RequestCtx{}) - c.BindVars(Map{ + err = c.BindVars(Map{ "Title": "Hello, World!", }) - require.Equal(b, nil, err) + require.NoError(b, err) b.ReportAllocs() b.ResetTimer() diff --git a/go.mod b/go.mod index 7c330458c8..4fa1ce8955 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/gofiber/fiber/v3 go 1.20 require ( - github.com/gofiber/fiber/v2 v2.40.1 github.com/gofiber/utils/v2 v2.0.0-beta.1 github.com/google/uuid v1.3.0 github.com/mattn/go-colorable v0.1.13 diff --git a/go.sum b/go.sum index 075184ead7..42d1b82354 100644 --- a/go.sum +++ b/go.sum @@ -3,8 +3,6 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gofiber/fiber/v2 v2.40.1 h1:pc7n9VVpGIqNsvg9IPLQhyFEMJL8gCs1kneH5D1pIl4= -github.com/gofiber/fiber/v2 v2.40.1/go.mod h1:Gko04sLksnHbzLSRBFWPFdzM9Ws9pRxvvIaohJK1dsk= github.com/gofiber/utils/v2 v2.0.0-beta.1 h1:ACfPdqeclx+BFIja19UjkKx7k3r5tmpILpNgzrfPLKs= github.com/gofiber/utils/v2 v2.0.0-beta.1/go.mod h1:CG89nDoIkEFIJaw5LdLO9AmBM11odse/LC79KQujm74= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= diff --git a/listen_test.go b/listen_test.go index ab6b565dd9..c677ddede3 100644 --- a/listen_test.go +++ b/listen_test.go @@ -118,7 +118,6 @@ func Test_Listen_TLS(t *testing.T) { CertFile: "./.github/testdata/ssl.pem", CertKeyFile: "./.github/testdata/ssl.key", })) - } // go test -run Test_Listen_TLS_Prefork @@ -146,7 +145,6 @@ func Test_Listen_TLS_Prefork(t *testing.T) { CertFile: "./.github/testdata/ssl.pem", CertKeyFile: "./.github/testdata/ssl.key", })) - } // go test -run Test_Listen_MutualTLS @@ -419,7 +417,7 @@ func Test_Listen_Print_Route(t *testing.T) { app.printRoutesMessage() }) fmt.Println(printRoutesMessage) - require.True(t, strings.Contains(printRoutesMessage, "GET")) + require.True(t, strings.Contains(printRoutesMessage, MethodGet)) require.True(t, strings.Contains(printRoutesMessage, "/")) require.True(t, strings.Contains(printRoutesMessage, "emptyHandler")) require.True(t, strings.Contains(printRoutesMessage, "routeName")) @@ -439,7 +437,7 @@ func Test_Listen_Print_Route_With_Group(t *testing.T) { app.printRoutesMessage() }) - require.True(t, strings.Contains(printRoutesMessage, "GET")) + require.True(t, strings.Contains(printRoutesMessage, MethodGet)) require.True(t, strings.Contains(printRoutesMessage, "/")) require.True(t, strings.Contains(printRoutesMessage, "emptyHandler")) require.True(t, strings.Contains(printRoutesMessage, "/v1/test")) diff --git a/middleware/adaptor/adopter_test.go b/middleware/adaptor/adopter_test.go index 8e9531e7b1..a8c7f4a243 100644 --- a/middleware/adaptor/adopter_test.go +++ b/middleware/adaptor/adopter_test.go @@ -141,7 +141,6 @@ func Test_HTTPHandler(t *testing.T) { } func Test_HTTPMiddleware(t *testing.T) { - tests := []struct { name string url string diff --git a/middleware/helmet/helmet_test.go b/middleware/helmet/helmet_test.go index 0b13b5a0d9..622ca9b4c8 100644 --- a/middleware/helmet/helmet_test.go +++ b/middleware/helmet/helmet_test.go @@ -21,7 +21,7 @@ func Test_Default(t *testing.T) { return c.SendString("Hello, World!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) require.NoError(t, err) require.Equal(t, "1; mode=block", resp.Header.Get(fiber.HeaderXXSSProtection)) require.Equal(t, "nosniff", resp.Header.Get(fiber.HeaderXContentTypeOptions)) @@ -48,11 +48,11 @@ func Test_Filter(t *testing.T) { return c.SendString("Skipped!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) require.NoError(t, err) require.Equal(t, "no-referrer", resp.Header.Get(fiber.HeaderReferrerPolicy)) - resp, err = app.Test(httptest.NewRequest("GET", "/filter", nil)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/filter", nil)) require.NoError(t, err) require.Equal(t, "", resp.Header.Get(fiber.HeaderReferrerPolicy)) } @@ -68,7 +68,7 @@ func Test_ContentSecurityPolicy(t *testing.T) { return c.SendString("Hello, World!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) require.NoError(t, err) require.Equal(t, "default-src 'none'", resp.Header.Get(fiber.HeaderContentSecurityPolicy)) } @@ -85,7 +85,7 @@ func Test_ContentSecurityPolicyReportOnly(t *testing.T) { return c.SendString("Hello, World!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) require.NoError(t, err) require.Equal(t, "default-src 'none'", resp.Header.Get(fiber.HeaderContentSecurityPolicyReportOnly)) require.Equal(t, "", resp.Header.Get(fiber.HeaderContentSecurityPolicy)) @@ -102,7 +102,7 @@ func Test_PermissionsPolicy(t *testing.T) { return c.SendString("Hello, World!") }) - resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) require.NoError(t, err) require.Equal(t, "microphone=()", resp.Header.Get(fiber.HeaderPermissionsPolicy)) } diff --git a/middleware/keyauth/keyauth.go b/middleware/keyauth/keyauth.go index bf02dfdd9b..efd7cf9e8e 100644 --- a/middleware/keyauth/keyauth.go +++ b/middleware/keyauth/keyauth.go @@ -69,7 +69,7 @@ func New(config ...Config) fiber.Handler { } if cfg.ErrorHandler == nil { cfg.ErrorHandler = func(c fiber.Ctx, err error) error { - if err == ErrMissingOrMalformedAPIKey { + if errors.Is(err, ErrMissingOrMalformedAPIKey) { return c.Status(fiber.StatusBadRequest).SendString(err.Error()) } return c.Status(fiber.StatusUnauthorized).SendString("Invalid or expired API Key") diff --git a/middleware/logger/default_logger.go b/middleware/logger/default_logger.go index a6dee891a1..da01129d7f 100644 --- a/middleware/logger/default_logger.go +++ b/middleware/logger/default_logger.go @@ -6,8 +6,8 @@ import ( "sync" "time" - "github.com/gofiber/fiber/v2/utils" "github.com/gofiber/fiber/v3" + "github.com/gofiber/utils/v2" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/valyala/bytebufferpool" diff --git a/middleware/logger/template_chain.go b/middleware/logger/template_chain.go index 1a5dd448f6..00015bf33d 100644 --- a/middleware/logger/template_chain.go +++ b/middleware/logger/template_chain.go @@ -4,7 +4,7 @@ import ( "bytes" "errors" - "github.com/gofiber/fiber/v2/utils" + "github.com/gofiber/utils/v2" ) // buildLogFuncChain analyzes the template and creates slices with the functions for execution and diff --git a/middleware/redirect/redirect.go b/middleware/redirect/redirect.go index 0dec5263a9..8433ddcba4 100644 --- a/middleware/redirect/redirect.go +++ b/middleware/redirect/redirect.go @@ -48,7 +48,7 @@ func New(config ...Config) fiber.Handler { // Initialize for k, v := range cfg.Rules { k = strings.Replace(k, "*", "(.*)", -1) - k = k + "$" + k += "$" cfg.rulesRegex[regexp.MustCompile(k)] = v } // Middleware function diff --git a/middleware/redirect/redirect_test.go b/middleware/redirect/redirect_test.go index 3b2491ca33..e47cc4b672 100644 --- a/middleware/redirect/redirect_test.go +++ b/middleware/redirect/redirect_test.go @@ -1,3 +1,4 @@ +//nolint:bodyclose // Much easier to just ignore memory leaks in tests // 🚀 Fiber is an Express inspired web framework written in Go with 💖 // 📌 API Documentation: https://fiber.wiki // 📝 Github Repository: https://github.com/gofiber/fiber @@ -9,6 +10,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" + "github.com/stretchr/testify/require" ) func Test_Redirect(t *testing.T) { @@ -108,7 +110,8 @@ func Test_Redirect(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - req, _ := http.NewRequest("GET", tt.url, nil) + req, err := http.NewRequest(fiber.MethodGet, tt.url, nil) + require.NoError(t, err) req.Header.Set("Location", "github.com/gofiber/redirect") resp, err := app.Test(req) if err != nil { @@ -122,5 +125,4 @@ func Test_Redirect(t *testing.T) { } }) } - } diff --git a/middleware/rewrite/rewrite.go b/middleware/rewrite/rewrite.go index 4824586cba..9c229d5111 100644 --- a/middleware/rewrite/rewrite.go +++ b/middleware/rewrite/rewrite.go @@ -51,7 +51,7 @@ func New(config ...Config) fiber.Handler { // Initialize for k, v := range cfg.Rules { k = strings.Replace(k, "*", "(.*)", -1) - k = k + "$" + k += "$" cfg.rulesRegex[regexp.MustCompile(k)] = v } // Middleware function diff --git a/middleware/session/session_test.go b/middleware/session/session_test.go index 652b7dd658..7a125415d5 100644 --- a/middleware/session/session_test.go +++ b/middleware/session/session_test.go @@ -359,7 +359,8 @@ func Test_Session_Reset(t *testing.T) { // set value & save sess.Set("name", "fenny") require.NoError(t, sess.Save()) - sess, _ = store.Get(ctx) + sess, err = store.Get(ctx) + require.NoError(t, err) err = sess.Destroy() require.NoError(t, err) diff --git a/redirect.go b/redirect.go index bcb709c4ce..aff9f516c7 100644 --- a/redirect.go +++ b/redirect.go @@ -287,7 +287,7 @@ func (r *Redirect) setFlash() { r.c.ClearCookie(FlashCookieName) } -func parseMessage(raw string) (key, value string) { +func parseMessage(raw string) (string, string) { if i := findNextNonEscapedCharsetPosition(raw, []byte(CookieDataAssigner)); i != -1 { return RemoveEscapeChar(raw[:i]), RemoveEscapeChar(raw[i+1:]) } diff --git a/router.go b/router.go index 04672c7715..5bae26e770 100644 --- a/router.go +++ b/router.go @@ -96,7 +96,7 @@ func (r *Route) match(detectionPath, path string, params *[maxParams]string) boo return false } -func (app *App) nextCustom(c CustomCtx) (match bool, err error) { +func (app *App) nextCustom(c CustomCtx) (bool, error) { // Get stack length tree, ok := app.treeStack[c.getMethodINT()][c.getTreePath()] if !ok { @@ -113,7 +113,7 @@ func (app *App) nextCustom(c CustomCtx) (match bool, err error) { route := tree[c.getIndexRoute()] // Check if it matches the request path - match = route.match(c.getDetectionPath(), c.Path(), c.getValues()) + match := route.match(c.getDetectionPath(), c.Path(), c.getValues()) // No match, next route if !match { @@ -129,22 +129,22 @@ func (app *App) nextCustom(c CustomCtx) (match bool, err error) { // Execute first handler of route c.setIndexHandler(0) - err = route.Handlers[0](c) + err := route.Handlers[0](c) return match, err // Stop scanning the stack } // If c.Next() does not match, return 404 - err = NewError(StatusNotFound, "Cannot "+c.Method()+" "+c.getPathOriginal()) + err := NewError(StatusNotFound, "Cannot "+c.Method()+" "+c.getPathOriginal()) // If no match, scan stack again if other methods match the request // Moved from app.handler because middleware may break the route chain if !c.getMatched() && app.methodExistCustom(c) { err = ErrMethodNotAllowed } - return + return false, err } -func (app *App) next(c *DefaultCtx) (match bool, err error) { +func (app *App) next(c *DefaultCtx) (bool, error) { // Get stack length tree, ok := app.treeStack[c.methodINT][c.treePath] if !ok { @@ -181,7 +181,7 @@ func (app *App) next(c *DefaultCtx) (match bool, err error) { } // If c.Next() does not match, return 404 - err = NewError(StatusNotFound, "Cannot "+c.method+" "+c.pathOriginal) + err := NewError(StatusNotFound, "Cannot "+c.method+" "+c.pathOriginal) if !c.matched && app.methodExist(c) { // If no match, scan stack again if other methods match the request // Moved from app.handler because middleware may break the route chain @@ -274,7 +274,6 @@ func (app *App) register(methods []string, pathRaw string, group *Group, handler } for _, method := range methods { - // Uppercase HTTP methods method = utils.ToUpper(method) // Check if the HTTP method is valid unless it's USE diff --git a/router_test.go b/router_test.go index 421d138956..e5d6830479 100644 --- a/router_test.go +++ b/router_test.go @@ -489,7 +489,7 @@ func Benchmark_App_MethodNotAllowed(b *testing.B) { } b.StopTimer() require.Equal(b, 405, c.Response.StatusCode()) - require.Equal(b, "GET", string(c.Response.Header.Peek("Allow"))) + require.Equal(b, MethodGet, string(c.Response.Header.Peek("Allow"))) require.Equal(b, utils.StatusMessage(StatusMethodNotAllowed), string(c.Response.Body())) } From 3168a6060591135216266fed77f8aa832dd39bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Thu, 9 Feb 2023 23:15:21 +0300 Subject: [PATCH 050/212] :construction_worker: v3 (ci): fix some linter warnings --- app_test.go | 1 - client_test.go | 1 - ctx_test.go | 29 ++++++++++++++-------- helpers.go | 2 +- helpers_test.go | 2 -- listen.go | 4 +-- listen_test.go | 7 ------ middleware/adaptor/adopter_test.go | 6 +++-- middleware/etag/etag.go | 2 +- middleware/filesystem/utils.go | 4 +-- middleware/helmet/helmet.go | 1 - middleware/logger/logger.go | 2 +- middleware/redirect/redirect.go | 2 +- middleware/redirect/redirect_test.go | 3 ++- mount_test.go | 7 +++--- path.go | 2 +- path_test.go | 1 - redirect.go | 13 +++++----- redirect_test.go | 37 +++++++++++++++++----------- router.go | 2 +- 20 files changed, 68 insertions(+), 60 deletions(-) diff --git a/app_test.go b/app_test.go index 046acad977..0598fd1ffc 100644 --- a/app_test.go +++ b/app_test.go @@ -1241,7 +1241,6 @@ func Test_App_Route(t *testing.T) { resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1/v2/v3", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") - } func Test_App_Deep_Group(t *testing.T) { diff --git a/client_test.go b/client_test.go index 38a0a309e5..ee784b2140 100644 --- a/client_test.go +++ b/client_test.go @@ -63,7 +63,6 @@ func Test_Client_Unsupported_Protocol(t *testing.T) { require.Equal(t, 1, len(errs)) require.Equal(t, `unsupported protocol "ftp". http and https are supported`, errs[0].Error()) - } func Test_Client_Get(t *testing.T) { diff --git a/ctx_test.go b/ctx_test.go index 25197ec78b..fb03644535 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -559,12 +559,15 @@ func Test_Ctx_Format(t *testing.T) { require.Error(t, err) c.Request().Header.Set(HeaderAccept, MIMETextPlain) - c.Format(Map{}) + err = c.Format(Map{}) + require.NoError(t, err) require.Equal(t, "map[]", string(c.Response().Body())) type broken string c.Request().Header.Set(HeaderAccept, "broken/accept") - c.Format(broken("Hello, World!")) + require.NoError(t, err) + err = c.Format(broken("Hello, World!")) + require.NoError(t, err) require.Equal(t, `Hello, World!`, string(c.Response().Body())) } @@ -2565,7 +2568,6 @@ func Test_Ctx_RenderWithLocals(t *testing.T) { defer bytebufferpool.Put(buf) require.Equal(t, "

Hello, World!

", string(c.Response().Body())) - } func Test_Ctx_RenderWithBindVars(t *testing.T) { @@ -3073,13 +3075,16 @@ func Test_Ctx_SendStream(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - c.SendStream(bytes.NewReader([]byte("Don't crash please"))) + err := c.SendStream(bytes.NewReader([]byte("Don't crash please"))) + require.NoError(t, err) require.Equal(t, "Don't crash please", string(c.Response().Body())) - c.SendStream(bytes.NewReader([]byte("Don't crash please")), len([]byte("Don't crash please"))) + err = c.SendStream(bytes.NewReader([]byte("Don't crash please")), len([]byte("Don't crash please"))) + require.NoError(t, err) require.Equal(t, "Don't crash please", string(c.Response().Body())) - c.SendStream(bufio.NewReader(bytes.NewReader([]byte("Hello bufio")))) + err = c.SendStream(bufio.NewReader(bytes.NewReader([]byte("Hello bufio")))) + require.NoError(t, err) require.Equal(t, "Hello bufio", string(c.Response().Body())) } @@ -3215,8 +3220,10 @@ func Test_Ctx_Write(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Write([]byte("Hello, ")) - c.Write([]byte("World!")) + _, err := c.Write([]byte("Hello, ")) + require.NoError(t, err) + _, err = c.Write([]byte("World!")) + require.NoError(t, err) require.Equal(t, "Hello, World!", string(c.Response().Body())) } @@ -3270,8 +3277,10 @@ func Test_Ctx_WriteString(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - c.WriteString("Hello, ") - c.WriteString("World!") + _, err := c.WriteString("Hello, ") + require.NoError(t, err) + _, err = c.WriteString("World!") + require.NoError(t, err) require.Equal(t, "Hello, World!", string(c.Response().Body())) } diff --git a/helpers.go b/helpers.go index 7754abf9df..7945a6df0f 100644 --- a/helpers.go +++ b/helpers.go @@ -714,7 +714,7 @@ const ( ConstraintBool = "bool" ConstraintFloat = "float" ConstraintAlpha = "alpha" - ConstraintGuid = "guid" //nolint:revive,stylecheck // TODO: Rename to "ConstraintGUID" in v3 + ConstraintGUID = "guid" ConstraintMinLen = "minLen" ConstraintMaxLen = "maxLen" ConstraintLen = "len" diff --git a/helpers_test.go b/helpers_test.go index b5f607be0b..b48f2fdb81 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -41,7 +41,6 @@ func Test_Utils_UniqueRouteStack(t *testing.T) { route2, route3, })) - } func Test_Utils_getGroupPath(t *testing.T) { @@ -143,7 +142,6 @@ func Test_Utils_IsNoCache(t *testing.T) { ok := isNoCache(c.string) require.Equal(t, c.bool, ok, fmt.Sprintf("want %t, got isNoCache(%s)=%t", c.bool, c.string, ok)) - } } diff --git a/listen.go b/listen.go index d05659276a..34860d5e1a 100644 --- a/listen.go +++ b/listen.go @@ -145,7 +145,7 @@ func (app *App) Listen(addr string, config ...ListenConfig) error { if cfg.CertFile != "" && cfg.CertKeyFile != "" { cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.CertKeyFile) if err != nil { - return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", cfg.CertFile, cfg.CertKeyFile, err) + return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %w", cfg.CertFile, cfg.CertKeyFile, err) } tlsHandler := &TLSHandler{} @@ -241,7 +241,7 @@ func (app *App) Listener(ln net.Listener, config ...ListenConfig) error { // Prefork is not supported for custom listeners if cfg.EnablePrefork { - fmt.Println("[Warning] Prefork isn't supported for custom listeners.") + log.Print("[Warning] Prefork isn't supported for custom listeners.") } return app.server.Serve(ln) diff --git a/listen_test.go b/listen_test.go index c677ddede3..e0dcd67238 100644 --- a/listen_test.go +++ b/listen_test.go @@ -168,7 +168,6 @@ func Test_Listen_MutualTLS(t *testing.T) { CertKeyFile: "./.github/testdata/ssl.key", CertClientFile: "./.github/testdata/ca-chain.cert.pem", })) - } // go test -run Test_Listen_MutualTLS_Prefork @@ -198,7 +197,6 @@ func Test_Listen_MutualTLS_Prefork(t *testing.T) { CertKeyFile: "./.github/testdata/ssl.key", CertClientFile: "./.github/testdata/ca-chain.cert.pem", })) - } // go test -run Test_Listener @@ -351,7 +349,6 @@ func Test_Listen_Master_Process_Show_Startup_Message(t *testing.T) { startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10), cfg) }) colors := Colors{} - fmt.Println(startupMessage) require.True(t, strings.Contains(startupMessage, "https://127.0.0.1:3000")) require.True(t, strings.Contains(startupMessage, "(bound on host 0.0.0.0 and port 3000)")) require.True(t, strings.Contains(startupMessage, "Child PIDs")) @@ -369,7 +366,6 @@ func Test_Listen_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { startupMessage := captureOutput(func() { app.startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10), cfg) }) - fmt.Println(startupMessage) require.Equal(t, "Test App v3.0.0", app.Config().AppName) require.True(t, strings.Contains(startupMessage, app.Config().AppName)) } @@ -386,7 +382,6 @@ func Test_Listen_Master_Process_Show_Startup_MessageWithAppNameNonAscii(t *testi startupMessage := captureOutput(func() { app.startupMessage(":3000", false, "", cfg) }) - fmt.Println(startupMessage) require.True(t, strings.Contains(startupMessage, "Serveur de vérification des données")) } @@ -402,7 +397,6 @@ func Test_Listen_Master_Process_Show_Startup_MessageWithDisabledPreforkAndCustom app.startupMessage("server.com:8081", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 5), cfg) }) colors := Colors{} - fmt.Println(startupMessage) require.True(t, strings.Contains(startupMessage, fmt.Sprintf("%sINFO%s", colors.Green, colors.Reset))) require.True(t, strings.Contains(startupMessage, fmt.Sprintf("%s%s%s", colors.Blue, appName, colors.Reset))) require.True(t, strings.Contains(startupMessage, fmt.Sprintf("%s%s%s", colors.Blue, "https://server.com:8081", colors.Reset))) @@ -416,7 +410,6 @@ func Test_Listen_Print_Route(t *testing.T) { printRoutesMessage := captureOutput(func() { app.printRoutesMessage() }) - fmt.Println(printRoutesMessage) require.True(t, strings.Contains(printRoutesMessage, MethodGet)) require.True(t, strings.Contains(printRoutesMessage, "/")) require.True(t, strings.Contains(printRoutesMessage, "emptyHandler")) diff --git a/middleware/adaptor/adopter_test.go b/middleware/adaptor/adopter_test.go index a8c7f4a243..7eed491813 100644 --- a/middleware/adaptor/adopter_test.go +++ b/middleware/adaptor/adopter_test.go @@ -14,6 +14,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -94,7 +95,8 @@ func Test_HTTPHandler(t *testing.T) { w.Header().Set("Header1", "value1") w.Header().Set("Header2", "value2") w.WriteHeader(http.StatusBadRequest) - fmt.Fprintf(w, "request body is %q", body) + _, err = fmt.Fprintf(w, "request body is %q", body) + require.NoError(t, err) } fiberH := HTTPHandlerFunc(http.HandlerFunc(nethttpH)) fiberH = setFiberContextValueMiddleware(fiberH, expectedContextKey, expectedContextValue) @@ -211,7 +213,7 @@ func Test_FiberAppDefaultPort(t *testing.T) { testFiberToHandlerFunc(t, true, fiber.New()) } -func testFiberToHandlerFunc(t *testing.T, checkDefaultPort bool, app ...*fiber.App) { +func testFiberToHandlerFunc(t *testing.T, checkDefaultPort bool, app ...*fiber.App) { //revive:disable-line:flag-parameter expectedMethod := fiber.MethodPost expectedRequestURI := "/foo/bar?baz=123" expectedBody := "body 123 foo bar baz" diff --git a/middleware/etag/etag.go b/middleware/etag/etag.go index 38fdc0165d..881e18c5e7 100644 --- a/middleware/etag/etag.go +++ b/middleware/etag/etag.go @@ -22,7 +22,7 @@ func New(config ...Config) fiber.Handler { crc32q := crc32.MakeTable(crcPol) // Return new handler - return func(c fiber.Ctx) (err error) { + return func(c fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() diff --git a/middleware/filesystem/utils.go b/middleware/filesystem/utils.go index 00247a91bf..b9d60bd6a0 100644 --- a/middleware/filesystem/utils.go +++ b/middleware/filesystem/utils.go @@ -70,8 +70,8 @@ func dirList(c fiber.Ctx, f fs.File) error { return nil } -func openFile(fs fs.FS, name string) (fs.File, error) { +func openFile(filesystem fs.FS, name string) (fs.File, error) { name = filepath.ToSlash(name) - return fs.Open(name) + return filesystem.Open(name) } diff --git a/middleware/helmet/helmet.go b/middleware/helmet/helmet.go index f966a73131..052f5d8da6 100644 --- a/middleware/helmet/helmet.go +++ b/middleware/helmet/helmet.go @@ -103,7 +103,6 @@ func New(config ...Config) fiber.Handler { } if cfg.PermissionPolicy != "" { c.Set(fiber.HeaderPermissionsPolicy, cfg.PermissionPolicy) - } return c.Next() } diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index bcbd047e74..5d0c85e09c 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -68,7 +68,7 @@ func New(config ...Config) fiber.Handler { } // Return new handler - return func(c fiber.Ctx) (err error) { + return func(c fiber.Ctx) error { // Don't execute middleware if Next returns true if cfg.Next != nil && cfg.Next(c) { return c.Next() diff --git a/middleware/redirect/redirect.go b/middleware/redirect/redirect.go index 8433ddcba4..68ff79b9b3 100644 --- a/middleware/redirect/redirect.go +++ b/middleware/redirect/redirect.go @@ -47,7 +47,7 @@ func New(config ...Config) fiber.Handler { cfg.rulesRegex = map[*regexp.Regexp]string{} // Initialize for k, v := range cfg.Rules { - k = strings.Replace(k, "*", "(.*)", -1) + k = strings.ReplaceAll(k, "*", "(.*)") k += "$" cfg.rulesRegex[regexp.MustCompile(k)] = v } diff --git a/middleware/redirect/redirect_test.go b/middleware/redirect/redirect_test.go index e47cc4b672..d2c00f016e 100644 --- a/middleware/redirect/redirect_test.go +++ b/middleware/redirect/redirect_test.go @@ -6,6 +6,7 @@ package redirect import ( + "context" "net/http" "testing" @@ -110,7 +111,7 @@ func Test_Redirect(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - req, err := http.NewRequest(fiber.MethodGet, tt.url, nil) + req, err := http.NewRequestWithContext(context.Background(), fiber.MethodGet, tt.url, nil) require.NoError(t, err) req.Header.Set("Location", "github.com/gofiber/redirect") resp, err := app.Test(req) diff --git a/mount_test.go b/mount_test.go index c374c350e6..ee0e3b5df7 100644 --- a/mount_test.go +++ b/mount_test.go @@ -282,7 +282,8 @@ func Test_Ctx_Render_Mount(t *testing.T) { t.Parallel() engine := &testTemplateEngine{} - engine.Load() + err := engine.Load() + require.NoError(t, err) sub := New(Config{ Views: engine, @@ -376,14 +377,14 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { body, err = io.ReadAll(resp.Body) require.Equal(t, nil, err) require.Equal(t, "

I'm Bruh

", string(body)) - } func Test_Ctx_Render_MountGroup(t *testing.T) { t.Parallel() engine := &testTemplateEngine{} - engine.Load() + err := engine.Load() + require.NoError(t, err) micro := New(Config{ Views: engine, diff --git a/path.go b/path.go index c7a41dc994..9fdf2e9022 100644 --- a/path.go +++ b/path.go @@ -617,7 +617,7 @@ func getParamConstraintType(constraintPart string) TypeConstraint { return floatConstraint case ConstraintAlpha: return alphaConstraint - case ConstraintGuid: + case ConstraintGUID: return guidConstraint case ConstraintMinLen, ConstraintMinLenLower: return minLenConstraint diff --git a/path_test.go b/path_test.go index 26b11cffdb..3582f7ee2f 100644 --- a/path_test.go +++ b/path_test.go @@ -133,7 +133,6 @@ func Test_Path_parseRoute(t *testing.T) { params: []string{"*1", "*2"}, wildCardCount: 2, }, rp) - } // go test -race -run Test_Path_matchParams diff --git a/redirect.go b/redirect.go index aff9f516c7..57f0bdfd06 100644 --- a/redirect.go +++ b/redirect.go @@ -171,7 +171,6 @@ func (r *Redirect) OldInput(key string) string { } } return "" - } // Redirect to the URL derived from the specified path, with specified status. @@ -204,10 +203,10 @@ func (r *Redirect) Route(name string, config ...RedirectConfig) error { // flash messages for i, message := range r.messages { - _, _ = messageText.WriteString(message) + _, _ = messageText.WriteString(message) //nolint:errcheck // Always return nil // when there are more messages or oldInput -> add a comma if len(r.messages)-1 != i || (len(r.messages)-1 == i && len(r.oldInput) > 0) { - _, _ = messageText.WriteString(CookieDataSeparator) + _, _ = messageText.WriteString(CookieDataSeparator) //nolint:errcheck // Always return nil } } r.messages = r.messages[:0] @@ -215,9 +214,9 @@ func (r *Redirect) Route(name string, config ...RedirectConfig) error { // old input data i := 1 for k, v := range r.oldInput { - _, _ = messageText.WriteString(OldInputDataPrefix + k + CookieDataAssigner + v) + _, _ = messageText.WriteString(OldInputDataPrefix + k + CookieDataAssigner + v) //nolint:errcheck // Always return nil if len(r.oldInput) != i { - _, _ = messageText.WriteString(CookieDataSeparator) + _, _ = messageText.WriteString(CookieDataSeparator) //nolint:errcheck // Always return nil } i++ } @@ -236,10 +235,10 @@ func (r *Redirect) Route(name string, config ...RedirectConfig) error { i := 1 for k, v := range cfg.Queries { - _, _ = queryText.WriteString(k + "=" + v) + _, _ = queryText.WriteString(k + "=" + v) //nolint:errcheck // Always return nil if i != len(cfg.Queries) { - _, _ = queryText.WriteString("&") + _, _ = queryText.WriteString("&") //nolint:errcheck // Always return nil } i++ } diff --git a/redirect_test.go b/redirect_test.go index f603c6fc43..85ac1cd45b 100644 --- a/redirect_test.go +++ b/redirect_test.go @@ -22,11 +22,13 @@ func Test_Redirect_To(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Redirect().To("http://default.com") + err := c.Redirect().To("http://default.com") + require.NoError(t, err) require.Equal(t, 302, c.Response().StatusCode()) require.Equal(t, "http://default.com", string(c.Response().Header.Peek(HeaderLocation))) - c.Redirect().Status(301).To("http://example.com") + err = c.Redirect().Status(301).To("http://example.com") + require.NoError(t, err) require.Equal(t, 301, c.Response().StatusCode()) require.Equal(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) } @@ -40,11 +42,12 @@ func Test_Redirect_Route_WithParams(t *testing.T) { }).Name("user") c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Redirect().Route("user", RedirectConfig{ + err := c.Redirect().Route("user", RedirectConfig{ Params: Map{ "name": "fiber", }, }) + require.NoError(t, err) require.Equal(t, 302, c.Response().StatusCode()) require.Equal(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } @@ -58,12 +61,13 @@ func Test_Redirect_Route_WithParams_WithQueries(t *testing.T) { }).Name("user") c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Redirect().Route("user", RedirectConfig{ + err := c.Redirect().Route("user", RedirectConfig{ Params: Map{ "name": "fiber", }, Queries: map[string]string{"data[0][name]": "john", "data[0][age]": "10", "test": "doe"}, }) + require.NoError(t, err) require.Equal(t, 302, c.Response().StatusCode()) // analysis of query parameters with url parsing, since a map pass is always randomly ordered location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) @@ -81,11 +85,12 @@ func Test_Redirect_Route_WithOptionalParams(t *testing.T) { }).Name("user") c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Redirect().Route("user", RedirectConfig{ + err := c.Redirect().Route("user", RedirectConfig{ Params: Map{ "name": "fiber", }, }) + require.NoError(t, err) require.Equal(t, 302, c.Response().StatusCode()) require.Equal(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } @@ -99,7 +104,8 @@ func Test_Redirect_Route_WithOptionalParamsWithoutValue(t *testing.T) { }).Name("user") c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Redirect().Route("user") + err := c.Redirect().Route("user") + require.NoError(t, err) require.Equal(t, 302, c.Response().StatusCode()) require.Equal(t, "/user/", string(c.Response().Header.Peek(HeaderLocation))) } @@ -113,11 +119,12 @@ func Test_Redirect_Route_WithGreedyParameters(t *testing.T) { }).Name("user") c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Redirect().Route("user", RedirectConfig{ + err := c.Redirect().Route("user", RedirectConfig{ Params: Map{ "+": "test/routes", }, }) + require.NoError(t, err) require.Equal(t, 302, c.Response().StatusCode()) require.Equal(t, "/user/test/routes", string(c.Response().Header.Peek(HeaderLocation))) } @@ -131,11 +138,12 @@ func Test_Redirect_Back(t *testing.T) { }).Name("home") c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Redirect().Back("/") + err := c.Redirect().Back("/") + require.NoError(t, err) require.Equal(t, 302, c.Response().StatusCode()) require.Equal(t, "/", string(c.Response().Header.Peek(HeaderLocation))) - err := c.Redirect().Back() + err = c.Redirect().Back() require.Equal(t, 500, c.Response().StatusCode()) require.ErrorAs(t, ErrRedirectBackNoFallback, &err) } @@ -153,7 +161,8 @@ func Test_Redirect_Back_WithReferer(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderReferer, "/back") - c.Redirect().Back("/") + err := c.Redirect().Back("/") + require.NoError(t, err) require.Equal(t, 302, c.Response().StatusCode()) require.Equal(t, "/back", c.Get(HeaderReferer)) require.Equal(t, "/back", string(c.Response().Header.Peek(HeaderLocation))) @@ -170,8 +179,8 @@ func Test_Redirect_Route_WithFlashMessages(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) - c.Redirect().With("success", "1").With("message", "test").Route("user") - + err := c.Redirect().With("success", "1").With("message", "test").Route("user") + require.NoError(t, err) require.Equal(t, 302, c.Response().StatusCode()) require.Equal(t, "/user", string(c.Response().Header.Peek(HeaderLocation))) @@ -194,8 +203,8 @@ func Test_Redirect_Route_WithOldInput(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) c.Request().URI().SetQueryString("id=1&name=tom") - c.Redirect().With("success", "1").With("message", "test").WithInput().Route("user") - + err := c.Redirect().With("success", "1").With("message", "test").WithInput().Route("user") + require.NoError(t, err) require.Equal(t, 302, c.Response().StatusCode()) require.Equal(t, "/user", string(c.Response().Header.Peek(HeaderLocation))) diff --git a/router.go b/router.go index 5bae26e770..e5f2ad9a2d 100644 --- a/router.go +++ b/router.go @@ -203,7 +203,7 @@ func (app *App) handler(rctx *fasthttp.RequestCtx) { // handle invalid http method directly if app.methodInt(c.Method()) == -1 { - _ = c.SendStatus(StatusNotImplemented) + _ = c.SendStatus(StatusNotImplemented) //nolint:errcheck // Always return nil return } From 497eb02b48190484d842748a08b2a84aba2ced62 Mon Sep 17 00:00:00 2001 From: Michail Safronov Date: Tue, 14 Feb 2023 02:48:55 +0500 Subject: [PATCH 051/212] 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 052/212] 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 053/212] 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 054/212] 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 055/212] :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 056/212] 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 057/212] :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 058/212] 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 059/212] 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 060/212] 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 061/212] 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 062/212] 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 063/212] =?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 064/212] =?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 065/212] 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 066/212] 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 067/212] 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 068/212] :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 41866cd3dd370826162f58d3e16e5e8751cf5110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Mon, 6 Mar 2023 17:35:39 +0300 Subject: [PATCH 069/212] :construction_worker: v3 (ci): fix some linter warnings --- addon/retry/config.go | 4 +- app.go | 9 +- app_test.go | 4 +- bind_test.go | 138 ++++++++++++++++--------- binder/mapping.go | 6 +- binder/xml.go | 7 +- client_test.go | 3 +- ctx.go | 6 +- ctx_interface.go | 7 +- ctx_test.go | 24 ++--- internal/memory/memory_test.go | 3 +- internal/storage/memory/memory_test.go | 8 +- listen.go | 23 +++-- listen_test.go | 1 + middleware/adaptor/adopter.go | 6 +- middleware/adaptor/adopter_test.go | 22 +++- middleware/basicauth/basicauth_test.go | 4 +- middleware/earlydata/earlydata_test.go | 9 +- middleware/encryptcookie/config.go | 4 +- middleware/expvar/config.go | 4 +- middleware/favicon/favicon_test.go | 1 - middleware/filesystem/utils.go | 14 ++- middleware/idempotency/config.go | 4 +- middleware/keyauth/keyauth.go | 6 +- middleware/logger/logger.go | 6 +- middleware/pprof/config.go | 4 +- middleware/redirect/redirect_test.go | 2 +- middleware/rewrite/rewrite.go | 2 +- middleware/skip/skip.go | 4 +- redirect.go | 36 ++++--- redirect_test.go | 7 +- router.go | 2 +- 32 files changed, 237 insertions(+), 143 deletions(-) diff --git a/addon/retry/config.go b/addon/retry/config.go index a2dcd27122..2a8bfabc19 100644 --- a/addon/retry/config.go +++ b/addon/retry/config.go @@ -1,6 +1,8 @@ package retry -import "time" +import ( + "time" +) // Config defines the config for addon. type Config struct { diff --git a/app.go b/app.go index 06800e0d3e..552b994028 100644 --- a/app.go +++ b/app.go @@ -821,7 +821,7 @@ func (app *App) Config() Config { func (app *App) Handler() fasthttp.RequestHandler { //revive:disable-line:confusing-naming // Having both a Handler() (uppercase) and a handler() (lowercase) is fine. TODO: Use nolint:revive directive instead. See https://github.com/golangci/golangci-lint/issues/3476 // prepare the server for the start app.startupProcess() - return app.handler + return app.requestHandler } // Stack returns the raw router stack. @@ -981,7 +981,7 @@ func (app *App) init() *App { } // fasthttp server settings - app.server.Handler = app.handler + app.server.Handler = app.requestHandler app.server.Name = app.config.ServerHeader app.server.Concurrency = app.config.Concurrency app.server.NoDefaultDate = app.config.DisableDefaultDate @@ -1041,7 +1041,10 @@ func (app *App) ErrorHandler(ctx Ctx, err error) error { // errors before calling the application's error handler method. func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) { // Acquire Ctx with fasthttp request from pool - c := app.AcquireCtx().(*DefaultCtx) + c, ok := app.AcquireCtx().(*DefaultCtx) + if !ok { + panic(fmt.Errorf("failed to type-assert to *DefaultCtx")) + } c.Reset(fctx) defer app.ReleaseCtx(c) diff --git a/app_test.go b/app_test.go index 0598fd1ffc..e7b3bcef4b 100644 --- a/app_test.go +++ b/app_test.go @@ -257,7 +257,7 @@ func Test_App_serverErrorHandler_Internal_Error(t *testing.T) { t.Parallel() app := New() msg := "test err" - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed app.serverErrorHandler(c.fasthttp, errors.New(msg)) require.Equal(t, string(c.fasthttp.Response.Body()), msg) @@ -1294,7 +1294,7 @@ func Benchmark_AcquireCtx(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_NewError -benchmem -count=4 func Benchmark_NewError(b *testing.B) { for n := 0; n < b.N; n++ { - NewError(200, "test") + NewError(200, "test") //nolint:errcheck // not needed } } diff --git a/bind_test.go b/bind_test.go index 4931eec864..d9c9d06aa4 100644 --- a/bind_test.go +++ b/bind_test.go @@ -1,3 +1,4 @@ +//nolint:wrapcheck,tagliatelle,bodyclose // We must not wrap errors in tests package fiber import ( @@ -628,6 +629,8 @@ func Test_Bind_RespHeader_Map(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Bind_Query -benchmem -count=4 func Benchmark_Bind_Query(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -643,13 +646,15 @@ func Benchmark_Bind_Query(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - c.Bind().Query(q) + err = c.Bind().Query(q) } - require.Nil(b, c.Bind().Query(q)) + require.Nil(b, err) } // go test -v -run=^$ -bench=Benchmark_Bind_Query_Map -benchmem -count=4 func Benchmark_Bind_Query_Map(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -660,13 +665,15 @@ func Benchmark_Bind_Query_Map(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - c.Bind().Query(&q) + err = c.Bind().Query(&q) } - require.Nil(b, c.Bind().Query(&q)) + require.Nil(b, err) } // go test -v -run=^$ -bench=Benchmark_Bind_Query_WithParseParam -benchmem -count=4 func Benchmark_Bind_Query_WithParseParam(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -687,14 +694,16 @@ func Benchmark_Bind_Query_WithParseParam(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - c.Bind().Query(cq) + err = c.Bind().Query(cq) } - require.Nil(b, c.Bind().Query(cq)) + require.Nil(b, err) } // go test -v -run=^$ -bench=Benchmark_Bind_Query_Comma -benchmem -count=4 func Benchmark_Bind_Query_Comma(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -711,13 +720,15 @@ func Benchmark_Bind_Query_Comma(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - c.Bind().Query(q) + err = c.Bind().Query(q) } - require.Nil(b, c.Bind().Query(q)) + require.Nil(b, err) } // go test -v -run=^$ -bench=Benchmark_Bind_Header -benchmem -count=4 func Benchmark_Bind_Header(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -737,13 +748,14 @@ func Benchmark_Bind_Header(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - c.Bind().Header(q) + err = c.Bind().Header(q) } - require.Nil(b, c.Bind().Header(q)) + require.Nil(b, err) } // go test -v -run=^$ -bench=Benchmark_Bind_Header_Map -benchmem -count=4 func Benchmark_Bind_Header_Map(b *testing.B) { + var err error app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -758,13 +770,15 @@ func Benchmark_Bind_Header_Map(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - c.Bind().Header(&q) + err = c.Bind().Header(&q) } - require.Nil(b, c.Bind().Header(&q)) + require.Nil(b, err) } // go test -v -run=^$ -bench=Benchmark_Bind_RespHeader -benchmem -count=4 func Benchmark_Bind_RespHeader(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -784,13 +798,14 @@ func Benchmark_Bind_RespHeader(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - c.Bind().RespHeader(q) + err = c.Bind().RespHeader(q) } - require.Nil(b, c.Bind().RespHeader(q)) + require.Nil(b, err) } // go test -v -run=^$ -bench=Benchmark_Bind_RespHeader_Map -benchmem -count=4 func Benchmark_Bind_RespHeader_Map(b *testing.B) { + var err error app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -805,9 +820,9 @@ func Benchmark_Bind_RespHeader_Map(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { - c.Bind().RespHeader(&q) + err = c.Bind().RespHeader(&q) } - require.Nil(b, c.Bind().RespHeader(&q)) + require.Nil(b, err) } // go test -run Test_Bind_Body @@ -823,8 +838,10 @@ func Test_Bind_Body(t *testing.T) { { var gzipJSON bytes.Buffer w := gzip.NewWriter(&gzipJSON) - _, _ = w.Write([]byte(`{"name":"john"}`)) - _ = w.Close() + _, err := w.Write([]byte(`{"name":"john"}`)) + require.NoError(t, err) + err = w.Close() + require.NoError(t, err) c.Request().Header.SetContentType(MIMEApplicationJSON) c.Request().Header.Set(HeaderContentEncoding, "gzip") @@ -938,6 +955,8 @@ func Test_Bind_Body_WithSetParserDecoder(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Bind_Body_JSON -benchmem -count=4 func Benchmark_Bind_Body_JSON(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -954,14 +973,16 @@ func Benchmark_Bind_Body_JSON(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.Bind().Body(d) + err = c.Bind().Body(d) } - require.Nil(b, c.Bind().Body(d)) + require.Nil(b, err) require.Equal(b, "john", d.Name) } // go test -v -run=^$ -bench=Benchmark_Bind_Body_XML -benchmem -count=4 func Benchmark_Bind_Body_XML(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -978,14 +999,16 @@ func Benchmark_Bind_Body_XML(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.Bind().Body(d) + err = c.Bind().Body(d) } - require.Nil(b, c.Bind().Body(d)) + require.Nil(b, err) require.Equal(b, "john", d.Name) } // go test -v -run=^$ -bench=Benchmark_Bind_Body_Form -benchmem -count=4 func Benchmark_Bind_Body_Form(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -1002,14 +1025,16 @@ func Benchmark_Bind_Body_Form(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.Bind().Body(d) + err = c.Bind().Body(d) } - require.Nil(b, c.Bind().Body(d)) + require.Nil(b, err) require.Equal(b, "john", d.Name) } // go test -v -run=^$ -bench=Benchmark_Bind_Body_MultipartForm -benchmem -count=4 func Benchmark_Bind_Body_MultipartForm(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -1027,14 +1052,16 @@ func Benchmark_Bind_Body_MultipartForm(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.Bind().Body(d) + err = c.Bind().Body(d) } - require.Nil(b, c.Bind().Body(d)) + require.Nil(b, err) require.Equal(b, "john", d.Name) } // go test -v -run=^$ -bench=Benchmark_Bind_Body_Form_Map -benchmem -count=4 func Benchmark_Bind_Body_Form_Map(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -1048,9 +1075,9 @@ func Benchmark_Bind_Body_Form_Map(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - _ = c.Bind().Body(&d) + err = c.Bind().Body(&d) } - require.Nil(b, c.Bind().Body(&d)) + require.Nil(b, err) require.Equal(b, "john", d["name"]) } @@ -1064,9 +1091,7 @@ func Test_Bind_URI(t *testing.T) { UserID uint `uri:"userId"` RoleID uint `uri:"roleId"` } - var ( - d = new(Demo) - ) + d := new(Demo) if err := c.Bind().URI(d); err != nil { t.Fatal(err) } @@ -1074,8 +1099,10 @@ func Test_Bind_URI(t *testing.T) { require.Equal(t, uint(222), d.RoleID) return nil }) - app.Test(httptest.NewRequest(MethodGet, "/test1/111/role/222", nil)) - app.Test(httptest.NewRequest(MethodGet, "/test2/111/role/222", nil)) + _, err := app.Test(httptest.NewRequest(MethodGet, "/test1/111/role/222", nil)) + require.NoError(t, err) + _, err = app.Test(httptest.NewRequest(MethodGet, "/test2/111/role/222", nil)) + require.NoError(t, err) } // go test -run Test_Bind_URI_Map @@ -1093,14 +1120,18 @@ func Test_Bind_URI_Map(t *testing.T) { require.Equal(t, uint(222), d["roleId"]) return nil }) - app.Test(httptest.NewRequest(MethodGet, "/test1/111/role/222", nil)) - app.Test(httptest.NewRequest(MethodGet, "/test2/111/role/222", nil)) + _, err := app.Test(httptest.NewRequest(MethodGet, "/test1/111/role/222", nil)) + require.NoError(t, err) + _, err = app.Test(httptest.NewRequest(MethodGet, "/test2/111/role/222", nil)) + require.NoError(t, err) } // go test -v -run=^$ -bench=Benchmark_Bind_URI -benchmem -count=4 func Benchmark_Bind_URI(b *testing.B) { + var err error + app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.route = &Route{ Params: []string{ @@ -1122,9 +1153,10 @@ func Benchmark_Bind_URI(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - c.Bind().URI(&res) + err = c.Bind().URI(&res) } + require.NoError(b, err) require.Equal(b, "john", res.Param1) require.Equal(b, "doe", res.Param2) require.Equal(b, "is", res.Param3) @@ -1133,8 +1165,10 @@ func Benchmark_Bind_URI(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Bind_URI_Map -benchmem -count=4 func Benchmark_Bind_URI_Map(b *testing.B) { + var err error + app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.route = &Route{ Params: []string{ @@ -1151,9 +1185,10 @@ func Benchmark_Bind_URI_Map(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - c.Bind().URI(&res) + err = c.Bind().URI(&res) } + require.NoError(b, err) require.Equal(b, "john", res["param1"]) require.Equal(b, "doe", res["param2"]) require.Equal(b, "is", res["param3"]) @@ -1405,6 +1440,8 @@ func Test_Bind_Cookie_Schema(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Bind_Cookie -benchmem -count=4 func Benchmark_Bind_Cookie(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -1425,13 +1462,15 @@ func Benchmark_Bind_Cookie(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - c.Bind().Cookie(q) + err = c.Bind().Cookie(q) } - require.Nil(b, c.Bind().Cookie(q)) + require.Nil(b, err) } // go test -v -run=^$ -bench=Benchmark_Bind_Cookie_Map -benchmem -count=4 func Benchmark_Bind_Cookie_Map(b *testing.B) { + var err error + app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -1447,9 +1486,9 @@ func Benchmark_Bind_Cookie_Map(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - c.Bind().Cookie(&q) + err = c.Bind().Cookie(&q) } - require.Nil(b, c.Bind().Cookie(&q)) + require.Nil(b, err) } // custom binder for testing @@ -1515,10 +1554,13 @@ func (*structValidator) Engine() any { func (*structValidator) ValidateStruct(out any) error { out = reflect.ValueOf(out).Elem().Interface() - sq := out.(simpleQuery) + sq, ok := out.(simpleQuery) + if !ok { + return fmt.Errorf("failed to type-assert to simpleQuery") + } if sq.Name != "john" { - return errors.New("you should have entered right name!") + return errors.New("you should have entered right name") } return nil @@ -1567,8 +1609,10 @@ func Test_Bind_RepeatParserWithSameStruct(t *testing.T) { var gzipJSON bytes.Buffer w := gzip.NewWriter(&gzipJSON) - _, _ = w.Write([]byte(`{"body_param":"body_param"}`)) - _ = w.Close() + _, err := w.Write([]byte(`{"body_param":"body_param"}`)) + require.NoError(t, err) + err = w.Close() + require.NoError(t, err) c.Request().Header.SetContentType(MIMEApplicationJSON) c.Request().Header.Set(HeaderContentEncoding, "gzip") c.Request().SetBody(gzipJSON.Bytes()) diff --git a/binder/mapping.go b/binder/mapping.go index c4a793c171..e37d06c643 100644 --- a/binder/mapping.go +++ b/binder/mapping.go @@ -86,7 +86,7 @@ func parse(aliasTag string, out any, data map[string][]string) error { // Parse data into the struct with gorilla/schema func parseToStruct(aliasTag string, out any, data map[string][]string) error { // Get decoder from pool - schemaDecoder := decoderPoolMap[aliasTag].Get().(*schema.Decoder) + schemaDecoder := decoderPoolMap[aliasTag].Get().(*schema.Decoder) //nolint:errcheck,forcetypeassert // not needed defer decoderPoolMap[aliasTag].Put(schemaDecoder) // Set alias tag @@ -136,7 +136,7 @@ func parseParamSquareBrackets(k string) (string, error) { for i, b := range kbytes { if b == '[' && kbytes[i+1] != ']' { if err := bb.WriteByte('.'); err != nil { - return "", err //nolint:wrapchec + return "", err //nolint:wrapchec,wrapcheck // unnecessary to wrap it } } @@ -145,7 +145,7 @@ func parseParamSquareBrackets(k string) (string, error) { } if err := bb.WriteByte(b); err != nil { - return "", err //nolint:wrapchec + return "", err //nolint:wrapchec,wrapcheck // unnecessary to wrap it } } diff --git a/binder/xml.go b/binder/xml.go index af6b19e97d..35d1de86bc 100644 --- a/binder/xml.go +++ b/binder/xml.go @@ -2,6 +2,7 @@ package binder import ( "encoding/xml" + "fmt" ) type xmlBinding struct{} @@ -11,5 +12,9 @@ func (*xmlBinding) Name() string { } func (*xmlBinding) Bind(body []byte, out any) error { - return xml.Unmarshal(body, out) + if err := xml.Unmarshal(body, out); err != nil { + return fmt.Errorf("failed to unmarshal xml: %w", err) + } + + return nil } diff --git a/client_test.go b/client_test.go index ee784b2140..97a7696d7d 100644 --- a/client_test.go +++ b/client_test.go @@ -5,6 +5,7 @@ import ( "bytes" "crypto/tls" "encoding/base64" + "encoding/json" "errors" "fmt" "io" @@ -17,8 +18,6 @@ import ( "testing" "time" - "encoding/json" - "github.com/gofiber/fiber/v3/internal/tlstest" "github.com/stretchr/testify/require" "github.com/valyala/fasthttp/fasthttputil" diff --git a/ctx.go b/ctx.go index f8bcc0cf2c..32f56d0b0c 100644 --- a/ctx.go +++ b/ctx.go @@ -1109,7 +1109,11 @@ func (c *DefaultCtx) Render(name string, bind Map, layouts ...string) error { func (c *DefaultCtx) renderExtensions(bind Map) { // Bind view map c.viewBindMap.Range(func(key, value any) bool { - bind[key.(string)] = value + keyS, ok := key.(string) + if !ok { + panic(fmt.Errorf("failed to type-assert to string")) + } + bind[keyS] = value return true }) diff --git a/ctx_interface.go b/ctx_interface.go index c71175674b..6bb49332dd 100644 --- a/ctx_interface.go +++ b/ctx_interface.go @@ -7,6 +7,7 @@ package fiber import ( "context" "crypto/tls" + "fmt" "io" "mime/multipart" "sync" @@ -408,7 +409,11 @@ func (app *App) NewCtx(fctx *fasthttp.RequestCtx) Ctx { // AcquireCtx retrieves a new Ctx from the pool. func (app *App) AcquireCtx() Ctx { - return app.pool.Get().(Ctx) + ctx, ok := app.pool.Get().(Ctx) + if !ok { + panic(fmt.Errorf("failed to type-assert to Ctx")) + } + return ctx } // ReleaseCtx releases the ctx back into the pool. diff --git a/ctx_test.go b/ctx_test.go index fb03644535..f80a33504d 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -62,7 +62,7 @@ func Test_Ctx_Accepts(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Accepts -benchmem -count=4 func Benchmark_Ctx_Accepts(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9") var res string @@ -78,7 +78,7 @@ type customCtx struct { DefaultCtx } -func (c *customCtx) Params(key string, defaultValue ...string) string { //nolint:revive +func (c *customCtx) Params(key string, defaultValue ...string) string { //revive:disable-line:unused-parameter // We need defaultValue for some cases return "prefix_" + c.DefaultCtx.Params(key) } @@ -140,7 +140,7 @@ func Test_Ctx_AcceptsCharsets(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsCharsets -benchmem -count=4 func Benchmark_Ctx_AcceptsCharsets(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set("Accept-Charset", "utf-8, iso-8859-1;q=0.5") var res string @@ -166,7 +166,7 @@ func Test_Ctx_AcceptsEncodings(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsEncodings -benchmem -count=4 func Benchmark_Ctx_AcceptsEncodings(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderAcceptEncoding, "deflate, gzip;q=1.0, *;q=0.5") var res string @@ -191,7 +191,7 @@ func Test_Ctx_AcceptsLanguages(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsLanguages -benchmem -count=4 func Benchmark_Ctx_AcceptsLanguages(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderAcceptLanguage, "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5") var res string @@ -252,7 +252,7 @@ func Test_Ctx_Append(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Append -benchmem -count=4 func Benchmark_Ctx_Append(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -285,7 +285,7 @@ func Test_Ctx_Attachment(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Attachment -benchmem -count=4 func Benchmark_Ctx_Attachment(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -311,7 +311,7 @@ func Test_Ctx_BaseURL(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_BaseURL -benchmem func Benchmark_Ctx_BaseURL(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().SetHost("google.com:1337") c.Request().URI().SetPath("/haha/oke/lol") @@ -500,7 +500,7 @@ func Test_Ctx_Cookie(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Cookie -benchmem -count=4 func Benchmark_Ctx_Cookie(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -591,7 +591,7 @@ func Benchmark_Ctx_Format(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_Format_HTML -benchmem -count=4 func Benchmark_Ctx_Format_HTML(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.NewCtx(&fasthttp.RequestCtx{}) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set("Accept", "text/html") b.ReportAllocs() @@ -608,7 +608,7 @@ func Benchmark_Ctx_Format_HTML(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_Format_JSON -benchmem -count=4 func Benchmark_Ctx_Format_JSON(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.NewCtx(&fasthttp.RequestCtx{}) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set("Accept", "application/json") b.ReportAllocs() @@ -625,7 +625,7 @@ func Benchmark_Ctx_Format_JSON(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_Format_XML -benchmem -count=4 func Benchmark_Ctx_Format_XML(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.NewCtx(&fasthttp.RequestCtx{}) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set("Accept", "application/xml") b.ReportAllocs() diff --git a/internal/memory/memory_test.go b/internal/memory/memory_test.go index 41183b9c9d..76a751b78d 100644 --- a/internal/memory/memory_test.go +++ b/internal/memory/memory_test.go @@ -12,7 +12,7 @@ import ( func Test_Memory(t *testing.T) { t.Parallel() - var store = New() + store := New() var ( key = "john" val any = []byte("doe") @@ -76,7 +76,6 @@ func Benchmark_Memory(b *testing.B) { } for _, key := range keys { d.Delete(key) - } } }) diff --git a/internal/storage/memory/memory_test.go b/internal/storage/memory/memory_test.go index b0a5b23c9f..ad2ee21ef3 100644 --- a/internal/storage/memory/memory_test.go +++ b/internal/storage/memory/memory_test.go @@ -65,9 +65,7 @@ func Test_Storage_Memory_Set_Expiration(t *testing.T) { } func Test_Storage_Memory_Get_Expired(t *testing.T) { - var ( - key = "john" - ) + key := "john" result, err := testStore.Get(key) require.NoError(t, err) @@ -102,9 +100,7 @@ func Test_Storage_Memory_Delete(t *testing.T) { func Test_Storage_Memory_Reset(t *testing.T) { t.Parallel() - var ( - val = []byte("doe") - ) + val := []byte("doe") err := testStore.Set("john1", val, 0) require.NoError(t, err) diff --git a/listen.go b/listen.go index 34860d5e1a..f79ea94ee9 100644 --- a/listen.go +++ b/listen.go @@ -63,7 +63,7 @@ type ListenConfig struct { // GracefulContext is a field to shutdown Fiber by given context gracefully. // // Default: nil - GracefulContext context.Context `json:"graceful_context"` + GracefulContext context.Context `json:"graceful_context"` //nolint:containedctx // It's needed to set context inside Listen. // TLSConfigFunc allows customizing tls.Config as you want. // @@ -112,7 +112,7 @@ func listenConfigDefault(config ...ListenConfig) ListenConfig { return ListenConfig{ ListenerNetwork: NetworkTCP4, OnShutdownError: func(err error) { - log.Fatalf("shutdown: %v", err) + log.Fatalf("shutdown: %v", err) //nolint:revive // It's an optipn }, } } @@ -124,7 +124,7 @@ func listenConfigDefault(config ...ListenConfig) ListenConfig { if cfg.OnShutdownError == nil { cfg.OnShutdownError = func(err error) { - log.Fatalf("shutdown: %v", err) + log.Fatalf("shutdown: %v", err) //nolint:revive // It's an optipn } } @@ -141,7 +141,7 @@ func (app *App) Listen(addr string, config ...ListenConfig) error { cfg := listenConfigDefault(config...) // Configure TLS - var tlsConfig *tls.Config = nil + var tlsConfig *tls.Config if cfg.CertFile != "" && cfg.CertKeyFile != "" { cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.CertKeyFile) if err != nil { @@ -160,7 +160,7 @@ func (app *App) Listen(addr string, config ...ListenConfig) error { if cfg.CertClientFile != "" { clientCACert, err := os.ReadFile(filepath.Clean(cfg.CertClientFile)) if err != nil { - return err + return fmt.Errorf("failed to read file: %w", err) } clientCertPool := x509.NewCertPool() @@ -248,7 +248,7 @@ func (app *App) Listener(ln net.Listener, config ...ListenConfig) error { } // Create listener function. -func (app *App) createListener(addr string, tlsConfig *tls.Config, cfg ListenConfig) (net.Listener, error) { +func (*App) createListener(addr string, tlsConfig *tls.Config, cfg ListenConfig) (net.Listener, error) { var listener net.Listener var err error @@ -262,6 +262,11 @@ func (app *App) createListener(addr string, tlsConfig *tls.Config, cfg ListenCon cfg.ListenerAddrFunc(listener.Addr()) } + // Wrap error comes from tls.Listen/net.Listen + if err != nil { + err = fmt.Errorf("failed to listen: %w", err) + } + return listener, err } @@ -278,7 +283,7 @@ func (app *App) printMessages(cfg ListenConfig, ln net.Listener) { } // startupMessage prepares the startup message with the handler number, port, address and other information -func (app *App) startupMessage(addr string, tls bool, pids string, cfg ListenConfig) { +func (app *App) startupMessage(addr string, enableTLS bool, pids string, cfg ListenConfig) { //nolint:revive TODO: Check CertKeyFile instead of control-flag. // ignore child processes if IsChild() { return @@ -297,7 +302,7 @@ func (app *App) startupMessage(addr string, tls bool, pids string, cfg ListenCon } scheme := schemeHTTP - if tls { + if enableTLS { scheme = schemeHTTPS } @@ -424,7 +429,7 @@ func (app *App) printRoutesMessage() { func (app *App) gracefulShutdown(ctx context.Context, cfg ListenConfig) { <-ctx.Done() - if err := app.Shutdown(); err != nil { + if err := app.Shutdown(); err != nil { //nolint:contextcheck // TODO: Implement it cfg.OnShutdownError(err) } diff --git a/listen_test.go b/listen_test.go index e0dcd67238..8c8cc0dd68 100644 --- a/listen_test.go +++ b/listen_test.go @@ -1,3 +1,4 @@ +//nolint:wrapcheck // We must not wrap errors in tests package fiber import ( diff --git a/middleware/adaptor/adopter.go b/middleware/adaptor/adopter.go index 2fe2a46536..bd76a21322 100644 --- a/middleware/adaptor/adopter.go +++ b/middleware/adaptor/adopter.go @@ -45,7 +45,7 @@ func HTTPMiddleware(mw func(http.Handler) http.Handler) fiber.Handler { } } }) - _ = HTTPHandler(mw(nextHandler))(c) + _ = HTTPHandler(mw(nextHandler))(c) //nolint:errcheck // TODO if next { return c.Next() } @@ -81,7 +81,7 @@ func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc { return } req.Header.SetContentLength(len(body)) - _, _ = req.BodyWriter().Write(body) + _, _ = req.BodyWriter().Write(body) //nolint:errcheck // TODO } req.Header.SetMethod(r.Method) req.SetRequestURI(r.RequestURI) @@ -121,6 +121,6 @@ func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc { w.Header().Add(string(k), string(v)) }) w.WriteHeader(fctx.Response.StatusCode()) - _, _ = w.Write(fctx.Response.Body()) + _, _ = w.Write(fctx.Response.Body()) //nolint:errcheck // not needed } } diff --git a/middleware/adaptor/adopter_test.go b/middleware/adaptor/adopter_test.go index 7eed491813..96b9e09ba8 100644 --- a/middleware/adaptor/adopter_test.go +++ b/middleware/adaptor/adopter_test.go @@ -5,6 +5,7 @@ package adaptor import ( + "context" "fmt" "io" "net" @@ -71,10 +72,13 @@ func Test_HTTPHandler(t *testing.T) { t.Fatalf("unexpected remoteAddr %q. Expecting %q", r.RemoteAddr, expectedRemoteAddr) } body, err := io.ReadAll(r.Body) - r.Body.Close() if err != nil { t.Fatalf("unexpected error when reading request body: %s", err) } + err = r.Body.Close() + if err != nil { + t.Fatalf("unexpected error when closing request body: %s", err) + } if string(body) != expectedBody { t.Fatalf("unexpected body %q. Expecting %q", body, expectedBody) } @@ -107,7 +111,10 @@ func Test_HTTPHandler(t *testing.T) { req.Header.SetMethod(expectedMethod) req.SetRequestURI(expectedRequestURI) req.Header.SetHost(expectedHost) - req.BodyWriter().Write([]byte(expectedBody)) // nolint:errcheck + _, err = req.BodyWriter().Write([]byte(expectedBody)) + if err != nil { + t.Fatalf("unexpected error when writing the request body: %s", err) + } for k, v := range expectedHeader { req.Header.Set(k, v) } @@ -186,7 +193,10 @@ func Test_HTTPMiddleware(t *testing.T) { }) for _, tt := range tests { - req, _ := http.NewRequest(tt.method, tt.url, nil) + 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) @@ -194,6 +204,10 @@ func Test_HTTPMiddleware(t *testing.T) { if resp.StatusCode != tt.statusCode { t.Fatalf(`%s: StatusCode: got %v - expected %v`, t.Name(), resp.StatusCode, tt.statusCode) } + err = resp.Body.Close() + if err != nil { + t.Fatalf("unexpected error when closing request body: %s", err) + } } } @@ -214,6 +228,8 @@ func Test_FiberAppDefaultPort(t *testing.T) { } func testFiberToHandlerFunc(t *testing.T, checkDefaultPort bool, app ...*fiber.App) { //revive:disable-line:flag-parameter + t.Helper() + expectedMethod := fiber.MethodPost expectedRequestURI := "/foo/bar?baz=123" expectedBody := "body 123 foo bar baz" diff --git a/middleware/basicauth/basicauth_test.go b/middleware/basicauth/basicauth_test.go index 8754333ece..526007bb3c 100644 --- a/middleware/basicauth/basicauth_test.go +++ b/middleware/basicauth/basicauth_test.go @@ -39,8 +39,8 @@ func Test_Middleware_BasicAuth(t *testing.T) { })) app.Get("/testauth", func(c fiber.Ctx) error { - username := c.Locals("username").(string) - password := c.Locals("password").(string) + username := c.Locals("username").(string) //nolint:errcheck, forcetypeassert // not needed + password := c.Locals("password").(string) //nolint:errcheck, forcetypeassert // not needed return c.SendString(username + password) }) diff --git a/middleware/earlydata/earlydata_test.go b/middleware/earlydata/earlydata_test.go index 619df60023..7e3c6d3940 100644 --- a/middleware/earlydata/earlydata_test.go +++ b/middleware/earlydata/earlydata_test.go @@ -38,8 +38,7 @@ func appWithConfig(t *testing.T, c *fiber.Config) *fiber.App { isEarly := earlydata.IsEarly(c) switch h := c.Get(headerName); h { - case "", - headerValOff: + case "", headerValOff: if isEarly { return errors.New("is early-data even though it's not") } @@ -69,7 +68,11 @@ func appWithConfig(t *testing.T, c *fiber.Config) *fiber.App { fiber.MethodGet, fiber.MethodPost, }, "/", func(c fiber.Ctx) error { - if !c.Locals(localsKeyTestValid).(bool) { + valid, ok := c.Locals(localsKeyTestValid).(bool) + if !ok { + panic(fmt.Errorf("failed to type-assert to bool")) + } + if !valid { return errors.New("handler called even though validation failed") } diff --git a/middleware/encryptcookie/config.go b/middleware/encryptcookie/config.go index 79067abb61..a68877308d 100644 --- a/middleware/encryptcookie/config.go +++ b/middleware/encryptcookie/config.go @@ -1,6 +1,8 @@ package encryptcookie -import "github.com/gofiber/fiber/v3" +import ( + "github.com/gofiber/fiber/v3" +) // Config defines the config for middleware. type Config struct { diff --git a/middleware/expvar/config.go b/middleware/expvar/config.go index 28ac231af2..1def8f2ca8 100644 --- a/middleware/expvar/config.go +++ b/middleware/expvar/config.go @@ -1,6 +1,8 @@ package expvar -import "github.com/gofiber/fiber/v3" +import ( + "github.com/gofiber/fiber/v3" +) // Config defines the config for middleware. type Config struct { diff --git a/middleware/favicon/favicon_test.go b/middleware/favicon/favicon_test.go index 918b4a3ffd..a39330b074 100644 --- a/middleware/favicon/favicon_test.go +++ b/middleware/favicon/favicon_test.go @@ -1,4 +1,3 @@ -//nolint:bodyclose // Much easier to just ignore memory leaks in tests package favicon import ( diff --git a/middleware/filesystem/utils.go b/middleware/filesystem/utils.go index b9d60bd6a0..3cf7fddc1f 100644 --- a/middleware/filesystem/utils.go +++ b/middleware/filesystem/utils.go @@ -21,7 +21,10 @@ func getFileExtension(p string) string { } func dirList(c fiber.Ctx, f fs.File) error { - ff := f.(fs.ReadDirFile) + ff, ok := f.(fs.ReadDirFile) + if !ok { + return fmt.Errorf("failed to type-assert to fs.ReadDirFile") + } fileinfos, err := ff.ReadDir(-1) if err != nil { return fmt.Errorf("failed to read dir: %w", err) @@ -33,7 +36,7 @@ func dirList(c fiber.Ctx, f fs.File) error { name := fi.Name() info, err := fi.Info() if err != nil { - return err + return fmt.Errorf("failed to get file info: %w", err) } fm[name] = info @@ -73,5 +76,10 @@ func dirList(c fiber.Ctx, f fs.File) error { func openFile(filesystem fs.FS, name string) (fs.File, error) { name = filepath.ToSlash(name) - return filesystem.Open(name) + file, err := filesystem.Open(name) + if err != nil { + return nil, fmt.Errorf("failed to open file: %w", err) + } + + return file, nil } diff --git a/middleware/idempotency/config.go b/middleware/idempotency/config.go index d5c11b962c..1856b42b02 100644 --- a/middleware/idempotency/config.go +++ b/middleware/idempotency/config.go @@ -9,9 +9,7 @@ import ( "github.com/gofiber/fiber/v3/internal/storage/memory" ) -var ( - ErrInvalidIdempotencyKey = errors.New("invalid idempotency key") -) +var ErrInvalidIdempotencyKey = errors.New("invalid idempotency key") // Config defines the config for middleware. type Config struct { diff --git a/middleware/keyauth/keyauth.go b/middleware/keyauth/keyauth.go index efd7cf9e8e..6d13ae754d 100644 --- a/middleware/keyauth/keyauth.go +++ b/middleware/keyauth/keyauth.go @@ -11,10 +11,8 @@ import ( "github.com/gofiber/fiber/v3" ) -var ( - // When there is no request of the key thrown ErrMissingOrMalformedAPIKey - ErrMissingOrMalformedAPIKey = errors.New("missing or malformed API Key") -) +// When there is no request of the key thrown ErrMissingOrMalformedAPIKey +var ErrMissingOrMalformedAPIKey = errors.New("missing or malformed API Key") type Config struct { // Filter defines a function to skip middleware. diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 5d0c85e09c..6d46ceb062 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -123,10 +123,6 @@ func New(config ...Config) fiber.Handler { } // Logger instance & update some logger data fields - if err = cfg.LoggerFunc(c, data, cfg); err != nil { - return err - } - - return nil + return cfg.LoggerFunc(c, data, cfg) } } diff --git a/middleware/pprof/config.go b/middleware/pprof/config.go index 618f5b658d..f763cc2481 100644 --- a/middleware/pprof/config.go +++ b/middleware/pprof/config.go @@ -1,6 +1,8 @@ package pprof -import "github.com/gofiber/fiber/v3" +import ( + "github.com/gofiber/fiber/v3" +) // Config defines the config for middleware. type Config struct { diff --git a/middleware/redirect/redirect_test.go b/middleware/redirect/redirect_test.go index d2c00f016e..dee35c5f3c 100644 --- a/middleware/redirect/redirect_test.go +++ b/middleware/redirect/redirect_test.go @@ -1,8 +1,8 @@ -//nolint:bodyclose // Much easier to just ignore memory leaks in tests // 🚀 Fiber is an Express inspired web framework written in Go with 💖 // 📌 API Documentation: https://fiber.wiki // 📝 Github Repository: https://github.com/gofiber/fiber +//nolint:bodyclose // Much easier to just ignore memory leaks in tests package redirect import ( diff --git a/middleware/rewrite/rewrite.go b/middleware/rewrite/rewrite.go index 9c229d5111..8992f99dbd 100644 --- a/middleware/rewrite/rewrite.go +++ b/middleware/rewrite/rewrite.go @@ -50,7 +50,7 @@ func New(config ...Config) fiber.Handler { cfg.rulesRegex = map[*regexp.Regexp]string{} // Initialize for k, v := range cfg.Rules { - k = strings.Replace(k, "*", "(.*)", -1) + k = strings.ReplaceAll(k, "*", "(.*)") k += "$" cfg.rulesRegex[regexp.MustCompile(k)] = v } diff --git a/middleware/skip/skip.go b/middleware/skip/skip.go index c41504e3e3..debc10c17b 100644 --- a/middleware/skip/skip.go +++ b/middleware/skip/skip.go @@ -1,6 +1,8 @@ package skip -import "github.com/gofiber/fiber/v3" +import ( + "github.com/gofiber/fiber/v3" +) // New creates a middleware handler which skips the wrapped handler // if the exclude predicate returns true. diff --git a/redirect.go b/redirect.go index 57f0bdfd06..b9c4a4b436 100644 --- a/redirect.go +++ b/redirect.go @@ -5,6 +5,7 @@ package fiber import ( + "fmt" "strings" "sync" @@ -13,17 +14,15 @@ import ( "github.com/valyala/bytebufferpool" ) -var ( - // Pool for redirection - redirectPool = sync.Pool{ - New: func() any { - return &Redirect{ - status: StatusFound, - oldInput: make(map[string]string, 0), - } - }, - } -) +// Pool for redirection +var redirectPool = sync.Pool{ + New: func() any { + return &Redirect{ + status: StatusFound, + oldInput: make(map[string]string, 0), + } + }, +} // Cookie name to send flash messages when to use redirection. const ( @@ -52,7 +51,12 @@ type RedirectConfig struct { // AcquireRedirect return default Redirect reference from the redirect pool func AcquireRedirect() *Redirect { - return redirectPool.Get().(*Redirect) + redirect, ok := redirectPool.Get().(*Redirect) + if !ok { + panic(fmt.Errorf("failed to type-assert to *Redirect")) + } + + return redirect } // ReleaseRedirect returns c acquired via Redirect to redirect pool. @@ -103,11 +107,11 @@ func (r *Redirect) WithInput() *Redirect { switch ctype { case MIMEApplicationForm: - _ = r.c.Bind().Form(r.oldInput) + _ = r.c.Bind().Form(r.oldInput) //nolint:errcheck // not needed case MIMEMultipartForm: - _ = r.c.Bind().MultipartForm(r.oldInput) + _ = r.c.Bind().MultipartForm(r.oldInput) //nolint:errcheck // not needed default: - _ = r.c.Bind().Query(r.oldInput) + _ = r.c.Bind().Query(r.oldInput) //nolint:errcheck // not needed } return r @@ -286,7 +290,7 @@ func (r *Redirect) setFlash() { r.c.ClearCookie(FlashCookieName) } -func parseMessage(raw string) (string, string) { +func parseMessage(raw string) (key string, value string) { if i := findNextNonEscapedCharsetPosition(raw, []byte(CookieDataAssigner)); i != -1 { return RemoveEscapeChar(raw[:i]), RemoveEscapeChar(raw[i+1:]) } diff --git a/redirect_test.go b/redirect_test.go index 85ac1cd45b..f809fb88b0 100644 --- a/redirect_test.go +++ b/redirect_test.go @@ -2,6 +2,7 @@ // 📝 Github Repository: https://github.com/gofiber/fiber // 📌 API Documentation: https://docs.gofiber.io +//nolint:wrapcheck // We must not wrap errors in tests package fiber import ( @@ -341,7 +342,7 @@ func Benchmark_Redirect_Route(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - c.Redirect().Route("user", RedirectConfig{ + c.Redirect().Route("user", RedirectConfig{ //nolint:errcheck,gosec,revive // we don't need to handle error here Params: Map{ "name": "fiber", }, @@ -365,7 +366,7 @@ func Benchmark_Redirect_Route_WithQueries(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - c.Redirect().Route("user", RedirectConfig{ + c.Redirect().Route("user", RedirectConfig{ //nolint:errcheck,gosec,revive // we don't need to handle error here Params: Map{ "name": "fiber", }, @@ -394,7 +395,7 @@ func Benchmark_Redirect_Route_WithFlashMessages(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { - c.Redirect().With("success", "1").With("message", "test").Route("user") + c.Redirect().With("success", "1").With("message", "test").Route("user") //nolint:errcheck,gosec,revive // we don't need to handle error here } require.Equal(b, 302, c.Response().StatusCode()) diff --git a/router.go b/router.go index e5f2ad9a2d..a623cf5b83 100644 --- a/router.go +++ b/router.go @@ -190,7 +190,7 @@ func (app *App) next(c *DefaultCtx) (bool, error) { return false, err } -func (app *App) handler(rctx *fasthttp.RequestCtx) { +func (app *App) requestHandler(rctx *fasthttp.RequestCtx) { // Handler for default ctxs var c CustomCtx if app.newCtxFunc != 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 070/212] 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 071/212] 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 072/212] 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 073/212] =?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 074/212] 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 075/212] 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 076/212] =?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 077/212] :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 078/212] =?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 079/212] :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 080/212] 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 081/212] 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 082/212] 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 083/212] 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 084/212] 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 085/212] 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 086/212] 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 087/212] 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 088/212] 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 089/212] 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 090/212] 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 091/212] =?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 092/212] =?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 093/212] 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 094/212] 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 095/212] 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 096/212] 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 097/212] 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 098/212] 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 099/212] 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 100/212] 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 101/212] 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 102/212] 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 103/212] 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 104/212] 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 105/212] 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 106/212] 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 107/212] 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 108/212] 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 109/212] 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 110/212] 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 111/212] 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 112/212] 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 113/212] 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 114/212] 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 115/212] 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 116/212] 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 117/212] 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 118/212] :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 119/212] :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 120/212] :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 121/212] 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 122/212] 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 123/212] =?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 124/212] :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 125/212] =?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 126/212] =?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 127/212] 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 128/212] 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 129/212] 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 130/212] 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 131/212] =?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 132/212] =?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 133/212] =?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 134/212] 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 135/212] 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 136/212] :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 137/212] =?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 138/212] [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 139/212] 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 140/212] 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 141/212] 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 142/212] 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 143/212] :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 144/212] :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 145/212] 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 146/212] 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 147/212] =?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 148/212] =?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 149/212] 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 150/212] 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 151/212] =?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 152/212] 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 153/212] 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 154/212] :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 155/212] 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 156/212] :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 157/212] :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 158/212] :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 159/212] =?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 160/212] =?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 161/212] 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 162/212] 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 163/212] 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 164/212] :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 165/212] :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 166/212] =?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 167/212] 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 168/212] 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 169/212] 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 170/212] 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 171/212] 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 172/212] 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 173/212] 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 174/212] =?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 175/212] =?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 176/212] 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 177/212] 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 178/212] 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 179/212] :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 180/212] 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 181/212] 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 182/212] 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 183/212] :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 184/212] =?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 185/212] =?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 186/212] 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 187/212] =?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 188/212] - 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 189/212] 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 190/212] 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 191/212] :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 192/212] 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 193/212] 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 194/212] 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 195/212] 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 196/212] 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 197/212] 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 198/212] :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 199/212] =?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 200/212] 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 201/212] 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 202/212] =?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 203/212] 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 204/212] 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 205/212] 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 206/212] 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 207/212] =?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 208/212] =?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 209/212] =?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 210/212] 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 211/212] :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**. From 44acb06c02188f34d6e8bc0bb3e04de27c46cbd4 Mon Sep 17 00:00:00 2001 From: Muhammed Efe Cetin Date: Sat, 5 Aug 2023 22:25:21 +0300 Subject: [PATCH 212/212] :broom: v3 (chore): fix linter warnings --- ctx_interface.go | 2 +- ctx_test.go | 24 ++++++++++++------------ listen.go | 6 +++--- middleware/filesystem/filesystem.go | 2 -- middleware/logger/default_logger.go | 1 - middleware/logger/logger_test.go | 1 - middleware/proxy/proxy_test.go | 4 ++-- redirect.go | 5 +++-- redirect_test.go | 22 +++++++++++----------- router.go | 13 ++++++++++--- router_test.go | 4 ++-- 11 files changed, 44 insertions(+), 40 deletions(-) diff --git a/ctx_interface.go b/ctx_interface.go index bdcbda08d7..4478a041dd 100644 --- a/ctx_interface.go +++ b/ctx_interface.go @@ -313,7 +313,7 @@ type Ctx interface { SendStream(stream io.Reader, size ...int) error // Set sets the response's HTTP header field to the specified key, value. - Set(key string, val string) + Set(key, val string) // Subdomains returns a string slice of subdomains in the domain name of the request. // The subdomain offset, which defaults to 2, is used for determining the beginning of the subdomain segments. diff --git a/ctx_test.go b/ctx_test.go index 76c6c086b3..a317a114ae 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -616,7 +616,7 @@ func Benchmark_Ctx_Format(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_Format_HTML -benchmem -count=4 func Benchmark_Ctx_Format_HTML(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) //nolint:errcheck, forcetypeassert // not needed + c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("Accept", "text/html") b.ReportAllocs() @@ -633,7 +633,7 @@ func Benchmark_Ctx_Format_HTML(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_Format_JSON -benchmem -count=4 func Benchmark_Ctx_Format_JSON(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) //nolint:errcheck, forcetypeassert // not needed + c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("Accept", "application/json") b.ReportAllocs() @@ -650,7 +650,7 @@ func Benchmark_Ctx_Format_JSON(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_Format_XML -benchmem -count=4 func Benchmark_Ctx_Format_XML(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) //nolint:errcheck, forcetypeassert // not needed + c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("Accept", "application/xml") b.ReportAllocs() @@ -1679,7 +1679,7 @@ func Test_Ctx_Params_Case_Sensitive(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Params -benchmem -count=4 func Benchmark_Ctx_Params(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.route = &Route{ Params: []string{ @@ -2412,7 +2412,7 @@ func Test_Ctx_JSONP(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_JSONP -benchmem -count=4 func Benchmark_Ctx_JSONP(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed type SomeStruct struct { Name string @@ -2437,7 +2437,7 @@ func Benchmark_Ctx_JSONP(b *testing.B) { func Test_Ctx_XML(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed require.True(t, c.JSON(complex(1, 1)) != nil) @@ -2470,7 +2470,7 @@ func Test_Ctx_XML(t *testing.T) { // go test -run=^$ -bench=Benchmark_Ctx_XML -benchmem -count=4 func Benchmark_Ctx_XML(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed type SomeStruct struct { Name string `xml:"Name"` Age uint8 `xml:"Age"` @@ -2509,7 +2509,7 @@ func Test_Ctx_Links(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Links -benchmem -count=4 func Benchmark_Ctx_Links(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -2935,7 +2935,7 @@ func Benchmark_Ctx_Render_Engine(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_Get_Location_From_Route -benchmem -count=4 func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed app.Get("/user/:name", func(c Ctx) error { return c.SendString(c.Params("name")) @@ -3235,7 +3235,7 @@ func Benchmark_Ctx_Type(b *testing.B) { // go test -v -run=^$ -bench=Benchmark_Ctx_Type_Charset -benchmem -count=4 func Benchmark_Ctx_Type_Charset(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -3260,7 +3260,7 @@ func Test_Ctx_Vary(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Vary -benchmem -count=4 func Benchmark_Ctx_Vary(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -3313,7 +3313,7 @@ func Test_Ctx_Writef(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Ctx_Writef -benchmem -count=4 func Benchmark_Ctx_Writef(b *testing.B) { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed world := "World!" b.ReportAllocs() diff --git a/listen.go b/listen.go index 609c418bb1..feea47e73b 100644 --- a/listen.go +++ b/listen.go @@ -293,7 +293,7 @@ func (app *App) printMessages(cfg ListenConfig, ln net.Listener) { } // prepareListenData create an slice of ListenData -func (app *App) prepareListenData(addr string, isTLS bool, cfg ListenConfig) ListenData { //revive:disable-line:flag-parameter // Accepting a bool param named isTLS if fine here +func (*App) prepareListenData(addr string, isTLS bool, cfg ListenConfig) ListenData { //revive:disable-line:flag-parameter // Accepting a bool param named isTLS if fine here host, port := parseAddr(addr) if host == "" { if cfg.ListenerNetwork == NetworkTCP6 { @@ -311,7 +311,7 @@ func (app *App) prepareListenData(addr string, isTLS bool, cfg ListenConfig) Lis } // startupMessage prepares the startup message with the handler number, port, address and other information -func (app *App) startupMessage(addr string, enableTLS bool, pids string, cfg ListenConfig) { //nolint:revive TODO: Check CertKeyFile instead of control-flag. +func (app *App) startupMessage(addr string, isTLS bool, pids string, cfg ListenConfig) { //nolint: revive // Accepting a bool param named isTLS if fine here // ignore child processes if IsChild() { return @@ -330,7 +330,7 @@ func (app *App) startupMessage(addr string, enableTLS bool, pids string, cfg Lis } scheme := schemeHTTP - if enableTLS { + if isTLS { scheme = schemeHTTPS } diff --git a/middleware/filesystem/filesystem.go b/middleware/filesystem/filesystem.go index 67797f2727..055cb9ae64 100644 --- a/middleware/filesystem/filesystem.go +++ b/middleware/filesystem/filesystem.go @@ -169,8 +169,6 @@ func New(config ...Config) fiber.Handler { } if err != nil { - fmt.Print("test") - fmt.Print(err) if errors.Is(err, fs.ErrNotExist) { return c.Status(fiber.StatusNotFound).Next() } diff --git a/middleware/logger/default_logger.go b/middleware/logger/default_logger.go index dcc8d533f6..1517c089a3 100644 --- a/middleware/logger/default_logger.go +++ b/middleware/logger/default_logger.go @@ -112,7 +112,6 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error { bytebufferpool.Put(buf) return nil - } // run something before returning the handler diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 722ccd01a1..e9196ab995 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -145,7 +145,6 @@ func Test_Logger_ErrorTimeZone(t *testing.T) { type fakeOutput int func (o *fakeOutput) Write([]byte) (int, error) { - fmt.Print(*o) *o++ return 0, errors.New("fake output") } diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index 796a1d7125..86dc2153e4 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -415,7 +415,7 @@ func Test_Proxy_Do_WithRedirect(t *testing.T) { return Do(c, "https://google.com") }) - resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil), 1500) require.Equal(t, nil, err1) body, err := io.ReadAll(resp.Body) require.NoError(t, err) @@ -447,7 +447,7 @@ func Test_Proxy_DoRedirects_TooManyRedirects(t *testing.T) { return DoRedirects(c, "http://google.com", 0) }) - resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil)) + resp, err1 := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil), 1500) require.Equal(t, nil, err1) body, err := io.ReadAll(resp.Body) require.NoError(t, err) diff --git a/redirect.go b/redirect.go index b9c4a4b436..bfa8ab7756 100644 --- a/redirect.go +++ b/redirect.go @@ -90,7 +90,7 @@ func (r *Redirect) Status(code int) *Redirect { // They will be sent as a cookie. // You can get them by using: Redirect().Messages(), Redirect().Message() // Note: You must use escape char before using ',' and ':' chars to avoid wrong parsing. -func (r *Redirect) With(key string, value string) *Redirect { +func (r *Redirect) With(key, value string) *Redirect { r.messages = append(r.messages, key+CookieDataAssigner+value) return r @@ -290,9 +290,10 @@ func (r *Redirect) setFlash() { r.c.ClearCookie(FlashCookieName) } -func parseMessage(raw string) (key string, value string) { +func parseMessage(raw string) (string, string) { //nolint: revive // not necessary if i := findNextNonEscapedCharsetPosition(raw, []byte(CookieDataAssigner)); i != -1 { return RemoveEscapeChar(raw[:i]), RemoveEscapeChar(raw[i+1:]) } + return RemoveEscapeChar(raw), "" } diff --git a/redirect_test.go b/redirect_test.go index f809fb88b0..dee9b4193c 100644 --- a/redirect_test.go +++ b/redirect_test.go @@ -178,7 +178,7 @@ func Test_Redirect_Route_WithFlashMessages(t *testing.T) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed err := c.Redirect().With("success", "1").With("message", "test").Route("user") require.NoError(t, err) @@ -201,7 +201,7 @@ func Test_Redirect_Route_WithOldInput(t *testing.T) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().URI().SetQueryString("id=1&name=tom") err := c.Redirect().With("success", "1").With("message", "test").WithInput().Route("user") @@ -229,7 +229,7 @@ func Test_Redirect_setFlash(t *testing.T) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") @@ -336,7 +336,7 @@ func Benchmark_Redirect_Route(b *testing.B) { return c.JSON(c.Params("name")) }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -360,7 +360,7 @@ func Benchmark_Redirect_Route_WithQueries(b *testing.B) { return c.JSON(c.Params("name")) }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -389,7 +389,7 @@ func Benchmark_Redirect_Route_WithFlashMessages(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ReportAllocs() b.ResetTimer() @@ -415,7 +415,7 @@ func Benchmark_Redirect_setFlash(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") @@ -444,7 +444,7 @@ func Benchmark_Redirect_Messages(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") c.Redirect().setFlash() @@ -469,7 +469,7 @@ func Benchmark_Redirect_OldInputs(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") c.Redirect().setFlash() @@ -494,7 +494,7 @@ func Benchmark_Redirect_Message(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") c.Redirect().setFlash() @@ -519,7 +519,7 @@ func Benchmark_Redirect_OldInput(b *testing.B) { return c.SendString("user") }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") c.Redirect().setFlash() diff --git a/router.go b/router.go index cdf19cbb1c..87429d6213 100644 --- a/router.go +++ b/router.go @@ -98,7 +98,7 @@ func (r *Route) match(detectionPath, path string, params *[maxParams]string) boo return false } -func (app *App) nextCustom(c CustomCtx) (bool, error) { +func (app *App) nextCustom(c CustomCtx) (bool, error) { //nolint: unparam // bool param might be useful for testing // Get stack length tree, ok := app.treeStack[c.getMethodINT()][c.getTreePath()] if !ok { @@ -204,10 +204,17 @@ func (app *App) next(c *DefaultCtx) (bool, error) { func (app *App) requestHandler(rctx *fasthttp.RequestCtx) { // Handler for default ctxs var c CustomCtx + var ok bool if app.newCtxFunc != nil { - c = app.AcquireCtx().(CustomCtx) + c, ok = app.AcquireCtx().(CustomCtx) + if !ok { + panic(fmt.Errorf("failed to type-assert to CustomCtx")) + } } else { - c = app.AcquireCtx().(*DefaultCtx) + c, ok = app.AcquireCtx().(*DefaultCtx) + if !ok { + panic(fmt.Errorf("failed to type-assert to *DefaultCtx")) + } } c.Reset(rctx) defer app.ReleaseCtx(c) diff --git a/router_test.go b/router_test.go index e5d6830479..4c28cc841d 100644 --- a/router_test.go +++ b/router_test.go @@ -618,7 +618,7 @@ func Benchmark_Router_Next(b *testing.B) { var res bool var err error - c := app.NewCtx(request).(*DefaultCtx) + c := app.NewCtx(request).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed b.ResetTimer() for n := 0; n < b.N; n++ { @@ -799,7 +799,7 @@ func Benchmark_Router_Github_API(b *testing.B) { for n := 0; n < b.N; n++ { c.URI().SetPath(routesFixture.TestRoutes[i].Path) - ctx := app.AcquireCtx().(*DefaultCtx) + ctx := app.AcquireCtx().(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed ctx.Reset(c) match, err = app.next(ctx)