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 01/44] 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 02/44] :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 03/44] 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 04/44] =?UTF-8?q?=F0=9F=A9=B9=20Fix:=20Unintended=20overwr?= =?UTF-8?q?itten=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 05/44] 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 06/44] 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 07/44] :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 08/44] :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 09/44] 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 10/44] :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 11/44] 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 12/44] 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 13/44] 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 14/44] 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 15/44] 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 16/44] 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 17/44] :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 18/44] =?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 19/44] 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 20/44] =?UTF-8?q?=F0=9F=93=9Ddocs(middleware):=20fix=20ali?= =?UTF-8?q?gnment=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 21/44] 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 22/44] =?UTF-8?q?=F0=9F=9A=80=20Feature:=20Add=20idempoten?= =?UTF-8?q?cy=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 23/44] 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 24/44] :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 25/44] :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 26/44] :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 27/44] =?UTF-8?q?=F0=9F=93=9D=20docs(filesystem):=20fix=20?= =?UTF-8?q?statik=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 28/44] =?UTF-8?q?=F0=9F=93=9D=20Docs:=20Add=20discord=20ch?= =?UTF-8?q?annel=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 29/44] 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 30/44] =?UTF-8?q?=F0=9F=94=A5=20write=20integer=20Query=20?= =?UTF-8?q?Parser.=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 31/44] 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 32/44] =?UTF-8?q?=F0=9F=9A=A8=20added=20testcases=20and=20?= =?UTF-8?q?minor=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 33/44] =?UTF-8?q?=F0=9F=93=9A=20Doc:=20Correct=20the=20fig?= =?UTF-8?q?ure=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 34/44] =?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 35/44] =?UTF-8?q?=F0=9F=9A=80=20Feature:=20Add=20and=20app?= =?UTF-8?q?ly=20more=20stricter=20golangci-lint=20linting=20rules=20(#2286?= =?UTF-8?q?)?= 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 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 36/44] =?UTF-8?q?=F0=9F=93=9D=20docs(filesystem):=20clean?= =?UTF-8?q?=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 37/44] 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 38/44] =?UTF-8?q?=F0=9F=9A=80=20Feature:=20Add=20earlydata?= =?UTF-8?q?=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 39/44] =?UTF-8?q?=F0=9F=90=9B=20Bug:=20Fix=20issues=20intr?= =?UTF-8?q?oduced=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 40/44] :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 41/44] =?UTF-8?q?PR:=20add=20url=20for=20favicon=20middlew?= =?UTF-8?q?are,=20for=20correct=20handling=20different=20of=E2=80=A6=20(#2?= =?UTF-8?q?231)?= 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 42/44] 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 43/44] 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 44/44] 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") +}