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/README.md b/.github/README.md index 45f428f037..1374d8c3f6 100644 --- a/.github/README.md +++ b/.github/README.md @@ -1,6 +1,6 @@
-
+
@@ -58,6 +58,9 @@
+
+
+
@@ -69,10 +72,10 @@
-
+
-
+
@@ -80,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. @@ -109,13 +112,13 @@ 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).
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+ Fiber — це веб фреймворк, який був натхненний Express
+ і заснований на Fasthttp, найшвидшому HTTP-двигунові написаному на
+ Go. Фреймворк розроблено з метою спростити процес швидкої розробки
+ високопродуктивних веб-додатків з нульовим розподілом пам'яті.
+
+
-
-
-
- Fiber是移植NodeJS的Express框架改以Go語言編寫。本套件基於Fasthttp,Fasthttp有不分配記憶體空間和Request Pool的特性,在網路效能方面有著顯著的效能。
+ Fiber 是款啟發自 Express 的 Web 框架,建基於 Fasthttp——Go 上最快的 HTTP 引擎。設計旨在 減輕 快速開發的負擔,兼顧 零記憶體分配 和 效能。
-
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -105,13 +108,13 @@ 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) ببینیت.
-
+
+
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -105,13 +108,13 @@ 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).
-
+
+
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -105,13 +108,13 @@ 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).
-
+
+
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -120,8 +123,8 @@ func main() {
-
+
+
+مطمئن شوید Go را نصب (دانلود) کرده اید. نسخه 1.16 یا بیشتر مورد نیاز است.
پروژه خود را با ساختن یک پوشه و سپس اجرای go mod init github.com/your/repo
داخل پوشه (یادگیری بیشتر) راه اندازی کنید. سپس Fiber را با دستور go get
نصب کنید :
@@ -182,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 هستند استفاده کنید.
@@ -617,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 11d3db369d..8503c9a297 100644
--- a/.github/README_fr.md
+++ b/.github/README_fr.md
@@ -1,6 +1,6 @@
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -105,13 +108,13 @@ 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).
-
+
+
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -122,15 +125,15 @@ func main() {
-
+
+
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -105,13 +108,13 @@ 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.
-
+
+
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -105,13 +108,13 @@ 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).
-
+
+
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -106,13 +109,13 @@ 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)にアクセスしてください。
-
+
+
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -105,13 +108,13 @@ 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)를 확인해 주세요.
-
+
+
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -105,13 +108,13 @@ 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.
-
+
+
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -105,13 +108,13 @@ 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) .
-
+
+
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -105,13 +108,13 @@ 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).
-
+
+
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -111,13 +114,13 @@ 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).
-
+
+
+
@@ -52,6 +52,9 @@
+
+
+
@@ -63,10 +66,10 @@
-
+
-
+
@@ -105,13 +108,13 @@ 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.
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
📚 Показати більше прикладів коду
+
+### Двигуни перегляду
+
+📖 [Конфігурація](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"))
+}
+```
+
+
+
+## ⭐️ Звіздарі
+
+
+
+## ⚠️ Ліцензія
+
+Авторське право (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 4c7fd347e9..b69a521d09 100644
--- a/.github/README_zh-CN.md
+++ b/.github/README_zh-CN.md
@@ -1,6 +1,6 @@
+
@@ -55,6 +55,9 @@
+
+
+
@@ -66,10 +69,10 @@
-
+
-
+
@@ -107,13 +110,13 @@ 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) 。
-
+
+
+
@@ -55,6 +55,12 @@
+
+
+
+
+
+
@@ -66,10 +72,10 @@
-
+
-
+
@@ -77,9 +83,10 @@
+
-
+
+
📚 顯示更多範例
+ 📚 展示更多程式碼範例
-### 界面引擎
+### 檢視引擎
-📖 [設定](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/)。
-如果你想要執行部份或用別的樣板引擎[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)套件。
+如果您想執行部分檢視 (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) 等等不同的引擎……
+
+請參考我們的 [Template](https://github.com/gofiber/template) 套件——它支援多種檢視引擎。
```go
package main
@@ -297,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",
@@ -312,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()
}
@@ -332,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
@@ -350,9 +357,9 @@ func main() {
```
-### 中介器 logger
+### 中介模組記錄器
-📖 [Logger](https://docs.gofiber.io/api/middleware/logger)
+📖 [記錄器](https://docs.gofiber.io/api/middleware/logger)
```go
package main
@@ -375,7 +382,7 @@ func main() {
}
```
-### 跨網域資源共享 (CORS)
+### 跨原始來源資源共用 (CORS)
📖 [CORS](https://docs.gofiber.io/api/middleware/cors)
@@ -398,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() {
@@ -422,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"
@@ -462,7 +469,7 @@ func main() {
}
```
-### WebSocket 升級
+### WebSocket 升級 (Upgrade)
📖 [Websocket](https://github.com/gofiber/websocket)
@@ -498,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 (
@@ -518,11 +525,11 @@ 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())
- 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()
@@ -537,125 +544,151 @@ func main() {
}
```
-### Recover 中介器
+### Recover 中介模組
📖 [Recover](https://docs.gofiber.io/api/middleware/recover)
```go
import (
- "github.com/gofiber/fiber/v3"
- "github.com/gofiber/fiber/recover"
+ "github.com/gofiber/fiber/v3"
+ "github.com/gofiber/fiber/v3/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"))
+
-## ⭐️ Stargazers
+## ⭐️ 收藏數
-## ⚠️ 授權
+## ⚠️ 授權條款
-版權所有 (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)
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 9cc328eed5..9d1fc3f6a2 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.19.x
+ go-version: 1.20.x
- name: Fetch Repository
uses: actions/checkout@v3
- name: Run Benchmark
@@ -24,6 +24,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
# TODO: reactivate it later -> when v3 is the stable one
diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml
index e40a67086c..db9a2bb8e5 100644
--- a/.github/workflows/linter.yml
+++ b/.github/workflows/linter.yml
@@ -1,16 +1,29 @@
+# Adapted from https://github.com/golangci/golangci-lint-action/blob/b56f6f529003f1c81d4d759be6bd5f10bf9a0fa0/README.md#how-to-use
+
+name: golangci-lint
on:
- push:
- branches:
- - v3-beta
- pull_request:
-name: Linter
+ push:
+ tags:
+ - v*
+ branches:
+ #- master
+ #- main
+ - v3-beta
+ 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.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.51.0
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 1ffb54108b..99adde703d 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -8,7 +8,7 @@ jobs:
Build:
strategy:
matrix:
- go-version: [1.18.x, 1.19.x]
+ go-version: [1.19.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
new file mode 100644
index 0000000000..af3bfb8d72
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,207 @@
+# Created based on v1.51.0
+# 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
+
+output:
+ sort-results: true
+
+linters-settings:
+ 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'
+
+ gocritic:
+ disabled-checks:
+ - ifElseChain
+
+ gofumpt:
+ module-path: github.com/gofiber/fiber
+ extra-rules: true
+
+ gosec:
+ config:
+ global:
+ audit: true
+
+ depguard:
+ include-go-root: true
+ packages:
+ - flag
+ - io/ioutil
+ 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'
+
+ govet:
+ check-shadowing: true
+ enable-all: true
+ disable:
+ - shadow
+ - fieldalignment
+ - loopclosure
+
+ 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: early-return
+ severity: warning
+ 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
+ - depguard
+ - dogsled
+ - durationcheck
+ - errcheck
+ - errchkjson
+ - errname
+ - errorlint
+ - execinquery
+ - exhaustive
+ - exportloopref
+ - forbidigo
+ - forcetypeassert
+ - goconst
+ - gocritic
+ - gofmt
+ - gofumpt
+ - goimports
+ - gomoddirectives
+ - goprintffuncname
+ - gosec
+ - gosimple
+ - govet
+ - grouper
+ - loggercheck
+ - misspell
+ - nakedret
+ - nilerr
+ - nilnil
+ - noctx
+ - nolintlint
+ - nonamedreturns
+ - nosprintfhostport
+ - predeclared
+ - promlinter
+ - reassign
+ - revive
+ - rowserrcheck
+ - sqlclosecheck
+ - staticcheck
+ - stylecheck
+ - tagliatelle
+ # - 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
+ - wastedassign
+ - whitespace
+ - wrapcheck
+ - tenv
diff --git a/app.go b/app.go
index 6afe10af8c..0c328a203e 100644
--- a/app.go
+++ b/app.go
@@ -9,10 +9,12 @@ package fiber
import (
"bufio"
+ "context"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
+ "log"
"net"
"net/http"
"net/http/httputil"
@@ -293,7 +295,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
@@ -450,7 +452,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) {
@@ -562,12 +564,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)
+ log.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{}{}
}
@@ -817,7 +818,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
@@ -834,12 +835,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 {
// TODO: check should be defered?
app.hooks.executeOnShutdownHooks()
@@ -850,7 +869,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
@@ -865,7 +884,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, timeout ...time.Duration) (resp *http.Response, err error) {
+func (app *App) Test(req *http.Request, timeout ...time.Duration) (*http.Response, error) {
// Set timeout
to := 1 * time.Second
if len(timeout) > 0 {
@@ -880,15 +899,15 @@ func (app *App) Test(req *http.Request, timeout ...time.Duration) (resp *http.Re
// 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()
@@ -921,7 +940,7 @@ func (app *App) Test(req *http.Request, timeout ...time.Duration) (resp *http.Re
}
// Check for errors
- if err != nil && err != fasthttp.ErrGetOnly {
+ if err != nil && !errors.Is(err, fasthttp.ErrGetOnly) {
return nil, err
}
@@ -929,12 +948,17 @@ func (app *App) Test(req *http.Request, timeout ...time.Duration) (resp *http.Re
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, _ ...any) {
+func (*disableLogger) Printf(_ string, _ ...any) {
// fmt.Println(fmt.Sprintf(format, args...))
}
@@ -945,7 +969,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)
}
}
@@ -1020,25 +1044,28 @@ func (app *App) serverErrorHandler(fctx *fasthttp.RequestCtx, err error) {
c := app.AcquireCtx().(*DefaultCtx)
c.Reset(fctx)
- if _, ok := err.(*fasthttp.ErrSmallBuffer); ok {
+ 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 {
- err = ErrBadRequest
+ 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 8044bf8e86..853cb84741 100644
--- a/app_test.go
+++ b/app_test.go
@@ -2,10 +2,12 @@
// 🤖 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 (
"bytes"
+ "context"
"crypto/tls"
"errors"
"fmt"
@@ -23,13 +25,14 @@ import (
"github.com/stretchr/testify/require"
"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)
@@ -40,6 +43,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()
+
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 500, resp.StatusCode, "Status code")
@@ -49,6 +54,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 +106,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 +131,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: -+`,
)
@@ -137,7 +145,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)")
}
@@ -146,6 +153,7 @@ func Test_App_ServerErrorHandler_SmallReadBuffer(t *testing.T) {
}
func Test_App_Errors(t *testing.T) {
+ t.Parallel()
app := New(Config{
BodyLimit: 4,
})
@@ -169,6 +177,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")
@@ -189,6 +198,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 {
require.Equal(t, "1: USE error", err.Error())
@@ -218,6 +228,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 {
require.Equal(t, "1: USE error", err.Error())
@@ -242,7 +253,19 @@ func Test_App_ErrorHandler_RouteStack(t *testing.T) {
require.Equal(t, "1: USE error", string(body))
}
+func Test_App_serverErrorHandler_Internal_Error(t *testing.T) {
+ t.Parallel()
+ app := New()
+ msg := "test err"
+ c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx)
+
+ app.serverErrorHandler(c.fasthttp, errors.New(msg))
+ require.Equal(t, string(c.fasthttp.Response.Body()), msg)
+ require.Equal(t, c.fasthttp.Response.StatusCode(), StatusBadRequest)
+}
+
func Test_App_Nested_Params(t *testing.T) {
+ t.Parallel()
app := New()
app.Get("/test", func(c Ctx) error {
@@ -266,6 +289,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 {
@@ -308,6 +332,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 {
@@ -336,6 +361,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 {
@@ -366,6 +392,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 {
@@ -399,6 +426,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 {
@@ -441,10 +469,10 @@ func Test_App_Use_MultiplePrefix(t *testing.T) {
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, "/test/doe", string(body))
-
}
func Test_App_Use_StrictRouting(t *testing.T) {
+ t.Parallel()
app := New(Config{StrictRouting: true})
app.Get("/abc", func(c Ctx) error {
@@ -478,13 +506,14 @@ 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 {
require.Equal(t, "add: invalid http method JANE\n", fmt.Sprintf("%v", err))
}
}()
- methods := append(DefaultMethods, "JOHN")
+ methods := append(DefaultMethods, "JOHN") //nolint:gocritic // We want a new slice here
app := New(Config{
RequestMethods: methods,
})
@@ -508,6 +537,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,
})
@@ -523,6 +553,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/*")
@@ -541,6 +572,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()
}
@@ -569,6 +601,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 {
@@ -598,6 +631,7 @@ func Test_App_Order(t *testing.T) {
}
func Test_App_Methods(t *testing.T) {
+ t.Parallel()
dummyHandler := testEmptyHandler
app := New()
@@ -637,6 +671,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)
@@ -667,6 +702,7 @@ func Test_App_Route_Naming(t *testing.T) {
}
func Test_App_New(t *testing.T) {
+ t.Parallel()
app := New()
app.Get("/", testEmptyHandler)
@@ -677,6 +713,7 @@ func Test_App_New(t *testing.T) {
}
func Test_App_Config(t *testing.T) {
+ t.Parallel()
app := New(Config{
StrictRouting: true,
})
@@ -684,12 +721,15 @@ 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()
require.True(t, app.Shutdown() == nil)
})
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" {
@@ -699,6 +739,46 @@ func Test_App_Shutdown(t *testing.T) {
})
}
+func Test_App_ShutdownWithTimeout(t *testing.T) {
+ t.Parallel()
+ app := New()
+ app.Get("/", func(c Ctx) error {
+ time.Sleep(5 * time.Second)
+ return c.SendString("body")
+ })
+ ln := fasthttputil.NewInmemoryListener()
+ go func() {
+ require.NoError(t, 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 || !errors.Is(err, context.DeadlineExceeded) {
+ t.Fatalf("unexpected err %v. Expecting %v", err, context.DeadlineExceeded)
+ }
+ }
+}
+
// go test -run Test_App_Static_Index_Default
func Test_App_Static_Index_Default(t *testing.T) {
app := New()
@@ -753,7 +833,7 @@ func Test_App_Static_Direct(t *testing.T) {
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
- require.True(t, strings.Contains(string(body), "testRoutes"))
+ require.True(t, strings.Contains(string(body), "test_routes"))
}
// go test -run Test_App_Static_MaxAge
@@ -762,7 +842,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))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.False(t, resp.Header.Get(HeaderContentLength) == "")
@@ -775,17 +855,17 @@ 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))
require.Equal(t, nil, err, "app.Test(req)")
require.Equal(t, "no-cache, no-store, must-revalidate", resp.Header.Get(HeaderCacheControl), "CacheControl Control")
- normal_resp, normal_err := app.Test(httptest.NewRequest("GET", "/config.yml", nil))
+ normal_resp, normal_err := app.Test(httptest.NewRequest(MethodGet, "/config.yml", nil))
require.Equal(t, nil, normal_err, "app.Test(req)")
require.Equal(t, "", normal_resp.Header.Get(HeaderCacheControl), "CacheControl Control")
}
@@ -796,7 +876,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))
require.NoError(t, err, "app.Test(req)")
require.Equal(t, 200, resp.StatusCode, "Status code")
require.False(t, resp.Header.Get(HeaderContentLength) == "")
@@ -966,7 +1046,7 @@ func Test_App_Static_Next(t *testing.T) {
})
t.Run("app.Static is skipped: invoking Get handler", func(t *testing.T) {
- req := httptest.NewRequest("GET", "/", nil)
+ req := httptest.NewRequest(MethodGet, "/", nil)
req.Header.Set("X-Custom-Header", "skip")
resp, err := app.Test(req)
require.NoError(t, err)
@@ -980,7 +1060,7 @@ func Test_App_Static_Next(t *testing.T) {
})
t.Run("app.Static is not skipped: serving index.html", func(t *testing.T) {
- req := httptest.NewRequest("GET", "/", nil)
+ req := httptest.NewRequest(MethodGet, "/", nil)
req.Header.Set("X-Custom-Header", "don't skip")
resp, err := app.Test(req)
require.NoError(t, err)
@@ -1039,6 +1119,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 {
require.Equal(t, "use: invalid handler int\n", fmt.Sprintf("%v", err))
@@ -1048,6 +1129,7 @@ func Test_App_Group_Invalid(t *testing.T) {
}
func Test_App_Group(t *testing.T) {
+ t.Parallel()
dummyHandler := testEmptyHandler
app := New()
@@ -1108,6 +1190,7 @@ func Test_App_Group(t *testing.T) {
}
func Test_App_Route(t *testing.T) {
+ t.Parallel()
dummyHandler := testEmptyHandler
app := New()
@@ -1156,6 +1239,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++
@@ -1176,6 +1260,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.Use(func(c Ctx) error {
@@ -1210,6 +1295,7 @@ func Benchmark_NewError(b *testing.B) {
// go test -run Test_NewError
func Test_NewError(t *testing.T) {
+ t.Parallel()
e := NewError(StatusForbidden, "permission denied")
require.Equal(t, StatusForbidden, e.Code)
require.Equal(t, "permission denied", e.Message)
@@ -1217,6 +1303,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.Get("/", testEmptyHandler)
@@ -1242,17 +1329,19 @@ func (errorReader) Read([]byte) (int, error) {
// go test -run Test_Test_DumpError
func Test_Test_DumpError(t *testing.T) {
+ t.Parallel()
app := New()
app.Get("/", testEmptyHandler)
resp, err := app.Test(httptest.NewRequest(MethodGet, "/", errorReader(0)))
require.True(t, resp == nil)
- require.Equal(t, "errorReader", err.Error())
+ require.Equal(t, "failed to dump request: errorReader", err.Error())
}
// go test -run Test_App_Handler
func Test_App_Handler(t *testing.T) {
+ t.Parallel()
h := New().Handler()
require.Equal(t, "fasthttp.RequestHandler", reflect.TypeOf(h).String())
}
@@ -1261,10 +1350,11 @@ type invalidView struct{}
func (invalidView) Load() error { return errors.New("invalid view") }
-func (i invalidView) Render(io.Writer, string, any, ...string) error { panic("implement me") }
+func (invalidView) Render(io.Writer, string, any, ...string) error { panic("implement me") }
// 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() {
@@ -1272,11 +1362,14 @@ func Test_App_Init_Error_View(t *testing.T) {
require.Equal(t, "implement me", fmt.Sprintf("%v", err))
}
}()
- _ = app.config.Views.Render(nil, "", nil)
+
+ err := app.config.Views.Render(nil, "", nil)
+ require.NoError(t, err)
}
// go test -run Test_App_Stack
func Test_App_Stack(t *testing.T) {
+ t.Parallel()
app := New()
app.Use("/path0", testEmptyHandler)
@@ -1300,6 +1393,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)
@@ -1312,6 +1406,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,
@@ -1350,6 +1445,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()
app.Get("/bad-request", func(c Ctx) error {
@@ -1383,6 +1479,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,
})
@@ -1393,11 +1490,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 {
- require.Equal(t, 431, resp.StatusCode)
- }
+ req, err := http.NewRequestWithContext(context.Background(), MethodGet, "http://127.0.0.1:4006/small-read-buffer", http.NoBody)
+ require.NoError(t, err)
+ var client http.Client
+ resp, err := client.Do(req)
require.NoError(t, err)
+ require.Equal(t, 431, resp.StatusCode)
require.Nil(t, app.Shutdown())
}()
@@ -1405,12 +1503,14 @@ func Test_App_SmallReadBuffer(t *testing.T) {
}
func Test_App_Server(t *testing.T) {
+ t.Parallel()
app := New()
require.False(t, app.Server() == nil)
}
func Test_App_Error_In_Fasthttp_Server(t *testing.T) {
+ t.Parallel()
app := New()
app.config.ErrorHandler = func(c Ctx, err error) error {
return errors.New("fake error")
@@ -1424,28 +1524,30 @@ 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})
- _, err := app.Test(httptest.NewRequest("GET", "/", nil))
+ _, err := app.Test(httptest.NewRequest(MethodGet, "/", nil))
require.Equal(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))
require.Equal(t, nil, err)
})
}
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.
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)))
require.NoError(t, err, "app.Test(req)")
body, err := io.ReadAll(resp.Body)
require.NoError(t, err, "io.ReadAll(resp.Body)")
@@ -1453,6 +1555,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"
@@ -1468,12 +1571,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")
@@ -1489,7 +1592,7 @@ func Test_App_DisablePreParseMultipartForm(t *testing.T) {
require.Equal(t, len(testString), n, "writer n")
require.Nil(t, 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)
require.NoError(t, err, "app.Test(req)")
@@ -1500,6 +1603,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)
@@ -1511,7 +1615,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)
}()
@@ -1532,6 +1636,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",
}}
@@ -1546,7 +1651,8 @@ func Test_App_SetTLSHandler(t *testing.T) {
}
func Test_App_AddCustomRequestMethod(t *testing.T) {
- methods := append(DefaultMethods, "TEST")
+ t.Parallel()
+ methods := append(DefaultMethods, "TEST") //nolint:gocritic // We want a new slice here
app := New(Config{
RequestMethods: methods,
})
@@ -1559,6 +1665,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.go b/client.go
index b217f39b8f..734f677d3c 100644
--- a/client.go
+++ b/client.go
@@ -8,11 +8,9 @@ import (
"fmt"
"io"
"mime/multipart"
- "net"
"os"
"path/filepath"
"strconv"
- "strings"
"sync"
"time"
@@ -188,11 +186,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)
}
@@ -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.
@@ -255,7 +241,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
@@ -295,7 +281,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
@@ -666,10 +652,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
}
@@ -742,14 +726,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 (
@@ -774,7 +758,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)
@@ -784,25 +768,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.
@@ -811,6 +795,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
}
@@ -819,12 +804,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 any) (code int, body []byte, errs []error) {
+func (a *Agent) Struct(v any) (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
}
@@ -833,7 +821,7 @@ func (a *Agent) Struct(v any) (code int, body []byte, errs []error) {
errs = append(errs, err)
}
- return
+ return code, body, errs
}
func (a *Agent) release() {
@@ -891,7 +879,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.
@@ -913,7 +905,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.
@@ -936,7 +932,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.
@@ -959,7 +959,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.
@@ -980,7 +984,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.
@@ -995,9 +1003,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 3b95a94da7..38a0a309e5 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 (
@@ -289,6 +290,7 @@ func Test_Client_UserAgent(t *testing.T) {
}()
t.Run("default", func(t *testing.T) {
+ t.Parallel()
for i := 0; i < 5; i++ {
a := Get("http://example.com")
@@ -303,6 +305,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"
@@ -322,11 +325,14 @@ 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" {
- _, _ = c.Write(key)
- _, _ = c.Write(value)
+ _, err := c.Write(key)
+ require.NoError(t, err)
+ _, err = c.Write(value)
+ require.NoError(t, err)
}
})
return nil
@@ -347,6 +353,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")
@@ -362,6 +369,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())
}
@@ -375,6 +383,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"))
@@ -392,6 +401,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())
}
@@ -405,6 +415,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())
}
@@ -450,6 +461,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())
}
@@ -463,6 +475,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)
@@ -482,6 +495,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())
}
@@ -494,6 +508,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())
}
@@ -506,6 +521,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())
}
@@ -576,6 +592,7 @@ func Test_Client_Agent_Dest(t *testing.T) {
}()
t.Run("small dest", func(t *testing.T) {
+ t.Parallel()
dest := []byte("de")
a := Get("http://example.com")
@@ -591,6 +608,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")
@@ -611,25 +629,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()
@@ -671,6 +690,7 @@ func Test_Client_Agent_RetryIf(t *testing.T) {
}
func Test_Client_Agent_Json(t *testing.T) {
+ t.Parallel()
handler := func(c Ctx) error {
require.Equal(t, MIMEApplicationJSON, string(c.Request().Header.ContentType()))
@@ -685,6 +705,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))
@@ -697,6 +718,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 {
require.Equal(t, MIMEApplicationXML, string(c.Request().Header.ContentType()))
@@ -711,6 +733,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))
@@ -722,6 +745,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 {
require.Equal(t, MIMEApplicationForm, string(c.Request().Header.ContentType()))
@@ -816,7 +840,10 @@ func Test_Client_Agent_MultipartForm_SendFiles(t *testing.T) {
buf := make([]byte, fh1.Size)
f, err := fh1.Open()
require.NoError(t, err)
- defer func() { _ = f.Close() }()
+ defer func() {
+ err := f.Close()
+ require.NoError(t, err)
+ }()
_, err = f.Read(buf)
require.NoError(t, err)
require.Equal(t, "form file", string(buf))
@@ -868,13 +895,16 @@ func checkFormFile(t *testing.T, fh *multipart.FileHeader, filename string) {
basename := filepath.Base(filename)
require.Equal(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
require.NoError(t, err)
b2 := make([]byte, fh.Size)
f, err := fh.Open()
require.NoError(t, err)
- defer func() { _ = f.Close() }()
+ defer func() {
+ err := f.Close()
+ require.NoError(t, err)
+ }()
_, err = f.Read(b2)
require.NoError(t, err)
require.Equal(t, b1, b2)
@@ -913,6 +943,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")
}
@@ -927,7 +958,7 @@ func Test_Client_Debug(t *testing.T) {
str := output.String()
- require.True(t, strings.Contains(str, "Connected to example.com(pipe)"))
+ require.True(t, strings.Contains(str, "Connected to example.com(InmemoryListener)"))
require.True(t, strings.Contains(str, "GET / HTTP/1.1"))
require.True(t, strings.Contains(str, "User-Agent: fiber"))
require.True(t, strings.Contains(str, "Host: example.com\r\n\r\n"))
@@ -1006,6 +1037,7 @@ func Test_Client_Agent_InsecureSkipVerify(t *testing.T) {
cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")
require.NoError(t, err)
+ //nolint:gosec // We're in a test so using old ciphers is fine
serverTLSConf := &tls.Config{
Certificates: []tls.Certificate{cer},
}
@@ -1093,6 +1125,7 @@ func Test_Client_Agent_MaxRedirectsCount(t *testing.T) {
}()
t.Run("success", func(t *testing.T) {
+ t.Parallel()
a := Get("http://example.com?foo").
MaxRedirectsCount(1)
@@ -1106,6 +1139,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)
@@ -1174,6 +1208,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() }
@@ -1189,11 +1224,12 @@ 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()
request := a.Request()
- request.Header.SetMethod("GET")
+ request.Header.SetMethod(MethodGet)
request.SetRequestURI("http://example.com")
err := a.Parse()
require.NoError(t, err)
@@ -1215,13 +1251,8 @@ func Test_Client_Agent_Parse(t *testing.T) {
require.Nil(t, a.Parse())
}
-func Test_AddMissingPort_TLS(t *testing.T) {
- addr := addMissingPort("example.com", true)
- require.Equal(t, "example.com:443", addr)
-}
-
func testAgent(t *testing.T, handler Handler, wrapAgent func(agent *Agent), excepted string, count ...int) {
- t.Parallel()
+ t.Helper()
ln := fasthttputil.NewInmemoryListener()
@@ -1263,8 +1294,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++
@@ -1272,8 +1303,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/ctx.go b/ctx.go
index e56f47df89..d6b94675a6 100644
--- a/ctx.go
+++ b/ctx.go
@@ -26,6 +26,11 @@ import (
"github.com/valyala/fasthttp"
)
+const (
+ schemeHTTP = "http"
+ schemeHTTPS = "https"
+)
+
// maxParams defines the maximum number of parameters per route.
const maxParams = 30
@@ -61,9 +66,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
@@ -224,8 +230,8 @@ func (c *DefaultCtx) 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)
}
})
@@ -393,6 +399,7 @@ func (c *DefaultCtx) 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.
@@ -498,8 +505,11 @@ func (c *DefaultCtx) Hostname() string {
// Port returns the remote port of the request.
func (c *DefaultCtx) 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.
@@ -516,13 +526,16 @@ func (c *DefaultCtx) 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 *DefaultCtx) 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)
@@ -532,8 +545,7 @@ func (c *DefaultCtx) 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
@@ -583,8 +595,9 @@ func (c *DefaultCtx) extractIPFromHeader(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
if j > len(headerValue) {
@@ -618,14 +631,14 @@ func (c *DefaultCtx) 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 *DefaultCtx) IPs() (ips []string) {
+func (c *DefaultCtx) IPs() []string {
return c.extractIPsFromHeader(HeaderXForwardedFor)
}
@@ -664,7 +677,7 @@ func (c *DefaultCtx) JSON(data any) error {
func (c *DefaultCtx) JSONP(data any, 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
@@ -702,11 +715,11 @@ func (c *DefaultCtx) 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, strings.TrimRight(c.app.getString(bb.Bytes()), ","))
@@ -715,7 +728,7 @@ func (c *DefaultCtx) Links(link ...string) {
// Locals makes it possible to pass any values under keys scoped to the request
// and therefore available to all following routes that match the request.
-func (c *DefaultCtx) Locals(key any, value ...any) (val any) {
+func (c *DefaultCtx) Locals(key any, value ...any) any {
if len(value) == 0 {
return c.fasthttp.UserValue(key)
}
@@ -758,9 +771,10 @@ func (c *DefaultCtx) ClientHelloInfo() *tls.ClientHelloInfo {
}
// Next executes the next method in the stack that matches the current route.
-func (c *DefaultCtx) Next() (err error) {
+func (c *DefaultCtx) 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
@@ -776,7 +790,7 @@ func (c *DefaultCtx) 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 *DefaultCtx) RestartRouting() error {
var err error
@@ -832,9 +846,8 @@ func (c *DefaultCtx) 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
@@ -859,15 +872,16 @@ func (c *DefaultCtx) Path(override ...string) string {
// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy.
func (c *DefaultCtx) Scheme() 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 {
@@ -882,7 +896,7 @@ func (c *DefaultCtx) Scheme() 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)):
@@ -906,25 +920,45 @@ func (c *DefaultCtx) 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 *DefaultCtx) 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]
+ }
+ return 0
+ }
+
+ return value
+}
+
// Range returns a struct containing the type and a slice of ranges.
-func (c *DefaultCtx) Range(size int) (rangeData Range, err error) {
+func (c *DefaultCtx) 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])
@@ -949,11 +983,10 @@ func (c *DefaultCtx) Range(size int) (rangeData Range, err error) {
})
}
if len(rangeData.Ranges) < 1 {
- err = ErrRangeUnsatisfiable
- return
+ return rangeData, ErrRangeUnsatisfiable
}
- return
+ return rangeData, nil
}
// Redirect returns the Redirect reference.
@@ -987,7 +1020,7 @@ func (c *DefaultCtx) getLocationFromRoute(route Route, params Map) (string, erro
if !segment.IsParam {
_, err := buf.WriteString(segment.Const)
if err != nil {
- return "", err
+ return "", fmt.Errorf("failed to write string: %w", err)
}
continue
}
@@ -998,7 +1031,7 @@ func (c *DefaultCtx) getLocationFromRoute(route Route, params Map) (string, erro
if isSame || isGreedy {
_, err := buf.WriteString(utils.ToString(val))
if err != nil {
- return "", err
+ return "", fmt.Errorf("failed to write string: %w", err)
}
}
}
@@ -1017,7 +1050,6 @@ func (c *DefaultCtx) GetRouteURL(routeName string, params Map) (string, error) {
// Render a template with data and sends a text/html response.
// We support the following engines: https://github.com/gofiber/template
func (c *DefaultCtx) Render(name string, bind Map, layouts ...string) error {
- var err error
// Get new buffer from pool
buf := bytebufferpool.Get()
defer bytebufferpool.Put(buf)
@@ -1039,7 +1071,7 @@ func (c *DefaultCtx) Render(name string, bind Map, 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
@@ -1051,17 +1083,18 @@ func (c *DefaultCtx) Render(name string, bind Map, 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)
}
}
@@ -1069,8 +1102,8 @@ func (c *DefaultCtx) Render(name string, bind Map, 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 *DefaultCtx) renderExtensions(bind Map) {
@@ -1122,20 +1155,24 @@ func (c *DefaultCtx) SaveFile(fileheader *multipart.FileHeader, path string) err
func (c *DefaultCtx) 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 *DefaultCtx) Secure() bool {
- return c.Protocol() == "https"
+ return c.Protocol() == schemeHTTPS
}
// Send sets the HTTP response body without copying it.
@@ -1161,6 +1198,7 @@ func (c *DefaultCtx) 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,
@@ -1168,7 +1206,7 @@ func (c *DefaultCtx) 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)
@@ -1192,7 +1230,7 @@ func (c *DefaultCtx) 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 += "/"
@@ -1257,11 +1295,11 @@ func (c *DefaultCtx) SendStream(stream io.Reader, size ...int) error {
}
// Set sets the response's HTTP header field to the specified key, value.
-func (c *DefaultCtx) Set(key string, val string) {
+func (c *DefaultCtx) Set(key, val string) {
c.fasthttp.Response.Header.Set(key, val)
}
-func (c *DefaultCtx) setCanonical(key string, val string) {
+func (c *DefaultCtx) setCanonical(key, val string) {
c.fasthttp.Response.Header.SetCanonical(utils.UnsafeBytes(key), utils.UnsafeBytes(val))
}
@@ -1332,6 +1370,7 @@ func (c *DefaultCtx) Write(p []byte) (int, error) {
// Writef appends f & a into response body writer.
func (c *DefaultCtx) Writef(f string, a ...any) (int, error) {
+ //nolint:wrapcheck // This must not be wrapped
return fmt.Fprintf(c.fasthttp.Response.BodyWriter(), f, a...)
}
@@ -1344,7 +1383,7 @@ func (c *DefaultCtx) 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 *DefaultCtx) XHR() bool {
- return utils.EqualFold(c.Get(HeaderXRequestedWith), "xmlhttprequest")
+ return utils.EqualFold(c.app.getBytes(c.Get(HeaderXRequestedWith)), []byte("xmlhttprequest"))
}
// configDependentPaths set paths for route recognition and prepared paths for the user,
@@ -1373,8 +1412,9 @@ func (c *DefaultCtx) 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]
}
}
@@ -1386,13 +1426,14 @@ func (c *DefaultCtx) 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_interface.go b/ctx_interface.go
index de3d16cbf3..816a3c00b4 100644
--- a/ctx_interface.go
+++ b/ctx_interface.go
@@ -226,6 +226,16 @@ type Ctx interface {
// Make copies or use the Immutable setting to use the value outside the Handler.
Query(key string, defaultValue ...string) string
+ // 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
+ QueryInt(key string, defaultValue ...int) int
+
// Range returns a struct containing the type and a slice of ranges.
Range(size int) (rangeData Range, err error)
diff --git a/ctx_test.go b/ctx_test.go
index b4c0c55d38..08889023cc 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 (
@@ -404,6 +405,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.NewCtx(&fasthttp.RequestCtx{})
@@ -416,6 +418,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"
@@ -641,12 +644,13 @@ func Test_Ctx_FormFile(t *testing.T) {
f, err := fh.Open()
require.NoError(t, err)
+ defer func() {
+ require.Equal(t, nil, f.Close())
+ }()
b := new(bytes.Buffer)
_, err = io.Copy(b, f)
require.NoError(t, err)
-
- f.Close()
require.Equal(t, "hello world", b.String())
return nil
})
@@ -659,8 +663,7 @@ func Test_Ctx_FormFile(t *testing.T) {
_, err = ioWriter.Write([]byte("hello world"))
require.NoError(t, err)
-
- writer.Close()
+ require.NoError(t, writer.Close())
req := httptest.NewRequest(MethodPost, "/test", body)
req.Header.Set(HeaderContentType, writer.FormDataContentType())
@@ -683,10 +686,9 @@ func Test_Ctx_FormValue(t *testing.T) {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
-
require.Nil(t, writer.WriteField("name", "john"))
+ require.Nil(t, 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())))
@@ -867,6 +869,97 @@ func Benchmark_Ctx_Host(b *testing.B) {
require.Equal(b, "google.com", host)
}
+// go test -run Test_Ctx_IsProxyTrusted
+func Test_Ctx_IsProxyTrusted(t *testing.T) {
+ t.Parallel()
+
+ {
+ app := New()
+ c := app.NewCtx(&fasthttp.RequestCtx{})
+ defer app.ReleaseCtx(c)
+ require.True(t, c.IsProxyTrusted())
+ }
+ {
+ app := New(Config{
+ EnableTrustedProxyCheck: false,
+ })
+ c := app.NewCtx(&fasthttp.RequestCtx{})
+ require.True(t, c.IsProxyTrusted())
+ }
+
+ {
+ app := New(Config{
+ EnableTrustedProxyCheck: true,
+ })
+ c := app.NewCtx(&fasthttp.RequestCtx{})
+ require.False(t, c.IsProxyTrusted())
+ }
+ {
+ app := New(Config{
+ EnableTrustedProxyCheck: true,
+
+ TrustedProxies: []string{},
+ })
+ c := app.NewCtx(&fasthttp.RequestCtx{})
+ require.False(t, c.IsProxyTrusted())
+ }
+ {
+ app := New(Config{
+ EnableTrustedProxyCheck: true,
+
+ TrustedProxies: []string{
+ "127.0.0.1",
+ },
+ })
+ c := app.NewCtx(&fasthttp.RequestCtx{})
+ require.False(t, c.IsProxyTrusted())
+ }
+ {
+ app := New(Config{
+ EnableTrustedProxyCheck: true,
+
+ TrustedProxies: []string{
+ "127.0.0.1/8",
+ },
+ })
+ c := app.NewCtx(&fasthttp.RequestCtx{})
+ require.False(t, c.IsProxyTrusted())
+ }
+ {
+ app := New(Config{
+ EnableTrustedProxyCheck: true,
+
+ TrustedProxies: []string{
+ "0.0.0.0",
+ },
+ })
+ c := app.NewCtx(&fasthttp.RequestCtx{})
+ require.True(t, c.IsProxyTrusted())
+ }
+ {
+ app := New(Config{
+ EnableTrustedProxyCheck: true,
+
+ TrustedProxies: []string{
+ "0.0.0.1/31",
+ },
+ })
+ c := app.NewCtx(&fasthttp.RequestCtx{})
+ require.True(t, c.IsProxyTrusted())
+ }
+ {
+ app := New(Config{
+ EnableTrustedProxyCheck: true,
+
+ TrustedProxies: []string{
+ "0.0.0.1/31junk",
+ },
+ })
+ c := app.NewCtx(&fasthttp.RequestCtx{})
+ require.False(t, c.IsProxyTrusted())
+ }
+}
+
// go test -run Test_Ctx_Hostname
func Test_Ctx_Hostname(t *testing.T) {
t.Parallel()
@@ -998,7 +1091,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 {
@@ -1030,7 +1123,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 {
@@ -1297,6 +1390,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")
@@ -1357,35 +1451,45 @@ 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))
+ require.NoError(t, err)
+ body, err := io.ReadAll(resp.Body)
+ require.NoError(t, err)
require.Equal(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))
+ require.NoError(t, err)
+
+ body, err = io.ReadAll(resp.Body)
+ require.NoError(t, err)
require.Equal(t, []byte("example.golang"), body)
// Test SignatureSchemes
- resp, _ = app.Test(httptest.NewRequest(MethodGet, "/SignatureSchemes", nil))
- body, _ = io.ReadAll(resp.Body)
- require.Equal(t, "["+strconv.Itoa(PSSWithSHA256)+"]", string(body))
+ resp, err = app.Test(httptest.NewRequest(MethodGet, "/SignatureSchemes", nil))
+ require.NoError(t, err)
+
+ body, err = io.ReadAll(resp.Body)
+ require.NoError(t, err)
+ require.Equal(t, "["+strconv.Itoa(pssWithSHA256)+"]", string(body))
// Test SupportedVersions
- resp, _ = app.Test(httptest.NewRequest(MethodGet, "/SupportedVersions", nil))
- body, _ = io.ReadAll(resp.Body)
- require.Equal(t, "["+strconv.Itoa(VersionTLS13)+"]", string(body))
+ resp, err = app.Test(httptest.NewRequest(MethodGet, "/SupportedVersions", nil))
+ require.NoError(t, err)
+ body, err = io.ReadAll(resp.Body)
+ require.NoError(t, err)
+ require.Equal(t, "["+strconv.Itoa(versionTLS13)+"]", string(body))
}
// go test -run Test_Ctx_InvalidMethod
@@ -1422,8 +1526,8 @@ func Test_Ctx_MultipartForm(t *testing.T) {
writer := multipart.NewWriter(body)
require.Nil(t, writer.WriteField("name", "john"))
+ require.NoError(t, 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())))
@@ -1438,8 +1542,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{}
@@ -1590,6 +1694,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()
c := app.NewCtx(&fasthttp.RequestCtx{})
@@ -1625,23 +1730,31 @@ func Test_Ctx_Scheme(t *testing.T) {
c := app.NewCtx(freq)
- c.Request().Header.Set(HeaderXForwardedProto, "https")
- require.Equal(t, "https", c.Scheme())
+ c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS)
+ require.Equal(t, schemeHTTPS, c.Scheme())
c.Request().Header.Reset()
- c.Request().Header.Set(HeaderXForwardedProtocol, "https")
- require.Equal(t, "https", c.Scheme())
+ c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS)
+ require.Equal(t, schemeHTTPS, c.Scheme())
+ c.Request().Header.Reset()
+
+ c.Request().Header.Set(HeaderXForwardedProto, "https, http")
+ require.Equal(t, schemeHTTPS, c.Scheme())
+ c.Request().Header.Reset()
+
+ c.Request().Header.Set(HeaderXForwardedProtocol, "https, http")
+ require.Equal(t, schemeHTTPS, c.Scheme())
c.Request().Header.Reset()
c.Request().Header.Set(HeaderXForwardedSsl, "on")
- require.Equal(t, "https", c.Scheme())
+ require.Equal(t, schemeHTTPS, c.Scheme())
c.Request().Header.Reset()
- c.Request().Header.Set(HeaderXUrlScheme, "https")
- require.Equal(t, "https", c.Scheme())
+ c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS)
+ require.Equal(t, schemeHTTPS, c.Scheme())
c.Request().Header.Reset()
- require.Equal(t, "http", c.Scheme())
+ require.Equal(t, schemeHTTP, c.Scheme())
}
// go test -v -run=^$ -bench=Benchmark_Ctx_Scheme -benchmem -count=4
@@ -1664,23 +1777,23 @@ func Test_Ctx_Scheme_TrustedProxy(t *testing.T) {
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0"}})
c := app.NewCtx(&fasthttp.RequestCtx{})
- c.Request().Header.Set(HeaderXForwardedProto, "https")
- require.Equal(t, "https", c.Scheme())
+ c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS)
+ require.Equal(t, schemeHTTPS, c.Scheme())
c.Request().Header.Reset()
- c.Request().Header.Set(HeaderXForwardedProtocol, "https")
- require.Equal(t, "https", c.Scheme())
+ c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS)
+ require.Equal(t, schemeHTTPS, c.Scheme())
c.Request().Header.Reset()
c.Request().Header.Set(HeaderXForwardedSsl, "on")
- require.Equal(t, "https", c.Scheme())
+ require.Equal(t, schemeHTTPS, c.Scheme())
c.Request().Header.Reset()
- c.Request().Header.Set(HeaderXUrlScheme, "https")
- require.Equal(t, "https", c.Scheme())
+ c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS)
+ require.Equal(t, schemeHTTPS, c.Scheme())
c.Request().Header.Reset()
- require.Equal(t, "http", c.Scheme())
+ require.Equal(t, schemeHTTP, c.Scheme())
}
// go test -run Test_Ctx_Scheme_TrustedProxyRange
@@ -1689,23 +1802,23 @@ func Test_Ctx_Scheme_TrustedProxyRange(t *testing.T) {
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0/30"}})
c := app.NewCtx(&fasthttp.RequestCtx{})
- c.Request().Header.Set(HeaderXForwardedProto, "https")
- require.Equal(t, "https", c.Scheme())
+ c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS)
+ require.Equal(t, schemeHTTPS, c.Scheme())
c.Request().Header.Reset()
- c.Request().Header.Set(HeaderXForwardedProtocol, "https")
- require.Equal(t, "https", c.Scheme())
+ c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS)
+ require.Equal(t, schemeHTTPS, c.Scheme())
c.Request().Header.Reset()
c.Request().Header.Set(HeaderXForwardedSsl, "on")
- require.Equal(t, "https", c.Scheme())
+ require.Equal(t, schemeHTTPS, c.Scheme())
c.Request().Header.Reset()
- c.Request().Header.Set(HeaderXUrlScheme, "https")
- require.Equal(t, "https", c.Scheme())
+ c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS)
+ require.Equal(t, schemeHTTPS, c.Scheme())
c.Request().Header.Reset()
- require.Equal(t, "http", c.Scheme())
+ require.Equal(t, schemeHTTP, c.Scheme())
}
// go test -run Test_Ctx_Scheme_UntrustedProxyRange
@@ -1714,23 +1827,23 @@ func Test_Ctx_Scheme_UntrustedProxyRange(t *testing.T) {
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"1.1.1.1/30"}})
c := app.NewCtx(&fasthttp.RequestCtx{})
- c.Request().Header.Set(HeaderXForwardedProto, "https")
- require.Equal(t, "http", c.Scheme())
+ c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS)
+ require.Equal(t, schemeHTTP, c.Scheme())
c.Request().Header.Reset()
- c.Request().Header.Set(HeaderXForwardedProtocol, "https")
- require.Equal(t, "http", c.Scheme())
+ c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS)
+ require.Equal(t, schemeHTTP, c.Scheme())
c.Request().Header.Reset()
c.Request().Header.Set(HeaderXForwardedSsl, "on")
- require.Equal(t, "http", c.Scheme())
+ require.Equal(t, schemeHTTP, c.Scheme())
c.Request().Header.Reset()
- c.Request().Header.Set(HeaderXUrlScheme, "https")
- require.Equal(t, "http", c.Scheme())
+ c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS)
+ require.Equal(t, schemeHTTP, c.Scheme())
c.Request().Header.Reset()
- require.Equal(t, "http", c.Scheme())
+ require.Equal(t, schemeHTTP, c.Scheme())
}
// go test -run Test_Ctx_Scheme_UnTrustedProxy
@@ -1739,23 +1852,23 @@ func Test_Ctx_Scheme_UnTrustedProxy(t *testing.T) {
app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.8.0.1"}})
c := app.NewCtx(&fasthttp.RequestCtx{})
- c.Request().Header.Set(HeaderXForwardedProto, "https")
- require.Equal(t, "http", c.Scheme())
+ c.Request().Header.Set(HeaderXForwardedProto, schemeHTTPS)
+ require.Equal(t, schemeHTTP, c.Scheme())
c.Request().Header.Reset()
- c.Request().Header.Set(HeaderXForwardedProtocol, "https")
- require.Equal(t, "http", c.Scheme())
+ c.Request().Header.Set(HeaderXForwardedProtocol, schemeHTTPS)
+ require.Equal(t, schemeHTTP, c.Scheme())
c.Request().Header.Reset()
c.Request().Header.Set(HeaderXForwardedSsl, "on")
- require.Equal(t, "http", c.Scheme())
+ require.Equal(t, schemeHTTP, c.Scheme())
c.Request().Header.Reset()
- c.Request().Header.Set(HeaderXUrlScheme, "https")
- require.Equal(t, "http", c.Scheme())
+ c.Request().Header.Set(HeaderXUrlScheme, schemeHTTPS)
+ require.Equal(t, schemeHTTP, c.Scheme())
c.Request().Header.Reset()
- require.Equal(t, "http", c.Scheme())
+ require.Equal(t, schemeHTTP, c.Scheme())
}
// go test -run Test_Ctx_Query
@@ -1770,6 +1883,20 @@ func Test_Ctx_Query(t *testing.T) {
require.Equal(t, "default", c.Query("unknown", "default"))
}
+func Test_Ctx_QueryInt(t *testing.T) {
+ t.Parallel()
+ app := New()
+ c := app.NewCtx(&fasthttp.RequestCtx{})
+
+ c.Request().URI().SetQueryString("search=john&age=20&id=")
+ require.Equal(t, 0, c.QueryInt("foo"))
+ require.Equal(t, 20, c.QueryInt("age", 12))
+ require.Equal(t, 0, c.QueryInt("search"))
+ require.Equal(t, 1, c.QueryInt("search", 1))
+ require.Equal(t, 0, c.QueryInt("id"))
+ require.Equal(t, 2, c.QueryInt("id", 2))
+}
+
// go test -run Test_Ctx_Range
func Test_Ctx_Range(t *testing.T) {
t.Parallel()
@@ -1856,7 +1983,12 @@ func Test_Ctx_SaveFile(t *testing.T) {
tempFile, err := os.CreateTemp(os.TempDir(), "test-")
require.NoError(t, err)
- defer os.Remove(tempFile.Name())
+ defer func(file *os.File) {
+ err := file.Close()
+ require.NoError(t, err)
+ err = os.Remove(file.Name())
+ require.NoError(t, err)
+ }(tempFile)
err = c.SaveFile(fh, tempFile.Name())
require.NoError(t, err)
@@ -1874,7 +2006,7 @@ func Test_Ctx_SaveFile(t *testing.T) {
_, err = ioWriter.Write([]byte("hello world"))
require.NoError(t, err)
- writer.Close()
+ require.NoError(t, writer.Close())
req := httptest.NewRequest(MethodPost, "/test", body)
req.Header.Set("Content-Type", writer.FormDataContentType())
@@ -1916,7 +2048,7 @@ func Test_Ctx_SaveFileToStorage(t *testing.T) {
_, err = ioWriter.Write([]byte("hello world"))
require.NoError(t, err)
- writer.Close()
+ require.NoError(t, writer.Close())
req := httptest.NewRequest(MethodPost, "/test", body)
req.Header.Set("Content-Type", writer.FormDataContentType())
@@ -2001,7 +2133,9 @@ func Test_Ctx_Download(t *testing.T) {
f, err := os.Open("./ctx.go")
require.NoError(t, err)
- defer f.Close()
+ defer func() {
+ require.NoError(t, f.Close())
+ }()
expect, err := io.ReadAll(f)
require.NoError(t, err)
@@ -2020,7 +2154,9 @@ func Test_Ctx_SendFile(t *testing.T) {
// fetch file content
f, err := os.Open("./ctx.go")
require.NoError(t, err)
- defer f.Close()
+ defer func() {
+ require.NoError(t, nil, f.Close())
+ }()
expectFileContent, err := io.ReadAll(f)
require.NoError(t, err)
// fetch file info for the not modified test case
@@ -2066,7 +2202,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))
require.NoError(t, err)
require.Equal(t, StatusNotFound, resp.StatusCode)
}
@@ -2104,11 +2240,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))
require.NoError(t, err)
require.Equal(t, StatusOK, resp.StatusCode)
// 2nd try
- resp, err = app.Test(httptest.NewRequest("GET", endpoint, nil))
+ resp, err = app.Test(httptest.NewRequest(MethodGet, endpoint, nil))
require.NoError(t, err)
require.Equal(t, StatusOK, resp.StatusCode)
})
@@ -2126,9 +2262,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))
require.Nil(t, err1)
require.Nil(t, err2)
@@ -2334,6 +2470,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()
@@ -2350,6 +2487,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")
@@ -2371,12 +2509,12 @@ func Test_Ctx_Render(t *testing.T) {
err := c.Render("./.github/testdata/index.tmpl", Map{
"Title": "Hello, World!",
})
+ require.NoError(t, err)
buf := bytebufferpool.Get()
- _, _ = buf.WriteString("overwrite")
+ _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail
defer bytebufferpool.Put(buf)
- require.NoError(t, err)
require.Equal(t, "
Hello, World!
", string(c.Response().Body()))
err = c.Render("./.github/testdata/template-non-exists.html", nil)
@@ -2396,12 +2534,12 @@ func Test_Ctx_RenderWithoutLocals(t *testing.T) {
c.Locals("Title", "Hello, World!")
err := c.Render("./.github/testdata/index.tmpl", Map{})
+ require.NoError(t, err)
buf := bytebufferpool.Get()
- _, _ = buf.WriteString("overwrite")
+ _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail
defer bytebufferpool.Put(buf)
- require.NoError(t, err)
require.Equal(t, "
", string(c.Response().Body()))
}
@@ -2415,12 +2553,12 @@ func Test_Ctx_RenderWithLocals(t *testing.T) {
c.Locals("Title", "Hello, World!")
err := c.Render("./.github/testdata/index.tmpl", Map{})
+ require.NoError(t, err)
buf := bytebufferpool.Get()
- _, _ = buf.WriteString("overwrite")
+ _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail
defer bytebufferpool.Put(buf)
- require.NoError(t, err)
require.Equal(t, "Hello, World!
", string(c.Response().Body()))
}
@@ -2438,17 +2576,37 @@ func Test_Ctx_RenderWithBindVars(t *testing.T) {
err := c.Render("./.github/testdata/index.tmpl", Map{})
require.NoError(t, err)
buf := bytebufferpool.Get()
- _, _ = buf.WriteString("overwrite")
+ _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail
defer bytebufferpool.Put(buf)
require.NoError(t, err)
require.Equal(t, "Hello, World!
", string(c.Response().Body()))
+}
+
+func Test_Ctx_RenderWithOverwrittenBind(t *testing.T) {
+ t.Parallel()
+ app := New()
+ c := app.NewCtx(&fasthttp.RequestCtx{})
+
+ err := c.BindVars(Map{
+ "Title": "Hello, World!",
+ })
+ require.NoError(t, err)
+ err = c.Render("./.github/testdata/index.tmpl", Map{
+ "Title": "Hello from Fiber!",
+ })
+ require.NoError(t, err)
+
+ buf := bytebufferpool.Get()
+ _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail
+ defer bytebufferpool.Put(buf)
+
+ require.Equal(t, "Hello, World!
", string(c.Response().Body()))
}
func Test_Ctx_RenderWithBindVarsLocals(t *testing.T) {
t.Parallel()
-
app := New(Config{
PassLocalsToViews: true,
})
@@ -2466,6 +2624,7 @@ func Test_Ctx_RenderWithBindVarsLocals(t *testing.T) {
require.NoError(t, err)
require.Equal(t, "Hello, World! Test
", string(c.Response().Body()))
+ require.Equal(t, "Hello, World! Test
", string(c.Response().Body()))
}
func Test_Ctx_RenderWithLocalsAndBinding(t *testing.T) {
@@ -2566,6 +2725,7 @@ func Benchmark_Ctx_RenderBindVars(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 {
@@ -2583,9 +2743,9 @@ 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
+ var executedOldHandler, executedNewHandler bool
app.Get("/old", func(c Ctx) error {
c.Path("/new")
@@ -2609,6 +2769,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
@@ -2634,10 +2795,18 @@ type testTemplateEngine struct {
func (t *testTemplateEngine) Render(w io.Writer, name string, bind any, 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 {
@@ -2650,6 +2819,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{}
require.Equal(t, nil, engine.Load())
app := New()
@@ -2665,6 +2835,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{}
require.Equal(t, nil, engine.Load())
app := New(Config{ViewsLayout: "main.tmpl"})
@@ -2712,14 +2883,17 @@ func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) {
for n := 0; n < b.N; n++ {
location, err = c.getLocationFromRoute(app.GetRoute("User"), Map{"name": "fiber"})
}
+
require.Equal(b, "/user/fiber", location)
require.Equal(b, nil, err)
-
}
// 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.NewCtx(&fasthttp.RequestCtx{})
app.Get("/user/:name", func(c Ctx) error {
@@ -2736,6 +2910,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.NewCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
@@ -2755,6 +2930,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.NewCtx(&fasthttp.RequestCtx{})
@@ -2773,6 +2949,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.NewCtx(&fasthttp.RequestCtx{})
@@ -2790,14 +2967,15 @@ 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 any, layout ...string) error {
+func (errorTemplateEngine) Render(_ io.Writer, _ string, _ any, _ ...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) {
+ t.Parallel()
app := New()
app.config.Views = errorTemplateEngine{}
c := app.NewCtx(&fasthttp.RequestCtx{})
@@ -2809,10 +2987,12 @@ 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")
require.NoError(t, err)
- defer os.Remove(file.Name())
+ defer func() {
+ err := os.Remove(file.Name())
+ require.NoError(t, err)
+ }()
_, err = file.Write([]byte("template"))
require.NoError(t, err)
@@ -3134,7 +3314,6 @@ func Benchmark_Ctx_SendString_B(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) {
@@ -3185,7 +3364,6 @@ func Benchmark_Ctx_BodyStreamWriter(b *testing.B) {
func Test_Ctx_String(t *testing.T) {
t.Parallel()
-
app := New()
c := app.NewCtx(&fasthttp.RequestCtx{})
@@ -3194,8 +3372,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
@@ -3295,6 +3473,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.NewCtx(&fasthttp.RequestCtx{})
diff --git a/error_test.go b/error_test.go
index 7fce3c12aa..02096cd3db 100644
--- a/error_test.go
+++ b/error_test.go
@@ -1,67 +1,76 @@
package fiber
import (
+ "encoding/json"
"errors"
"testing"
- jerrors "encoding/json"
-
"github.com/gofiber/fiber/v3/internal/schema"
"github.com/stretchr/testify/require"
)
func TestConversionError(t *testing.T) {
+ t.Parallel()
ok := errors.As(ConversionError{}, &schema.ConversionError{})
require.True(t, ok)
}
func TestUnknownKeyError(t *testing.T) {
+ t.Parallel()
ok := errors.As(UnknownKeyError{}, &schema.UnknownKeyError{})
require.True(t, ok)
}
func TestEmptyFieldError(t *testing.T) {
+ t.Parallel()
ok := errors.As(EmptyFieldError{}, &schema.EmptyFieldError{})
require.True(t, ok)
}
func TestMultiError(t *testing.T) {
+ t.Parallel()
ok := errors.As(MultiError{}, &schema.MultiError{})
require.True(t, ok)
}
func TestInvalidUnmarshalError(t *testing.T) {
- var e *jerrors.InvalidUnmarshalError
+ t.Parallel()
+ var e *json.InvalidUnmarshalError
ok := errors.As(&InvalidUnmarshalError{}, &e)
require.True(t, ok)
}
func TestMarshalerError(t *testing.T) {
- var e *jerrors.MarshalerError
+ t.Parallel()
+ var e *json.MarshalerError
ok := errors.As(&MarshalerError{}, &e)
require.True(t, ok)
}
func TestSyntaxError(t *testing.T) {
- var e *jerrors.SyntaxError
+ t.Parallel()
+ var e *json.SyntaxError
ok := errors.As(&SyntaxError{}, &e)
require.True(t, ok)
}
func TestUnmarshalTypeError(t *testing.T) {
- var e *jerrors.UnmarshalTypeError
+ t.Parallel()
+ var e *json.UnmarshalTypeError
ok := errors.As(&UnmarshalTypeError{}, &e)
require.True(t, ok)
}
func TestUnsupportedTypeError(t *testing.T) {
- var e *jerrors.UnsupportedTypeError
+ t.Parallel()
+ var e *json.UnsupportedTypeError
ok := errors.As(&UnsupportedTypeError{}, &e)
require.True(t, ok)
}
func TestUnsupportedValeError(t *testing.T) {
- var e *jerrors.UnsupportedValueError
+ t.Parallel()
+ var e *json.UnsupportedValueError
ok := errors.As(&UnsupportedValueError{}, &e)
require.True(t, ok)
}
diff --git a/go.mod b/go.mod
index fec575483a..7c330458c8 100644
--- a/go.mod
+++ b/go.mod
@@ -1,26 +1,26 @@
module github.com/gofiber/fiber/v3
-go 1.19
+go 1.20
require (
github.com/gofiber/fiber/v2 v2.40.1
github.com/gofiber/utils/v2 v2.0.0-beta.1
github.com/google/uuid v1.3.0
github.com/mattn/go-colorable v0.1.13
- github.com/mattn/go-isatty v0.0.16
+ github.com/mattn/go-isatty v0.0.17
github.com/stretchr/testify v1.8.1
github.com/tinylib/msgp v1.1.6
github.com/valyala/bytebufferpool v1.0.0
- github.com/valyala/fasthttp v1.42.0
+ github.com/valyala/fasthttp v1.44.0
)
require (
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/klauspost/compress v1.15.12 // indirect
+ github.com/klauspost/compress v1.15.15 // indirect
github.com/philhofer/fwd v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
- golang.org/x/sys v0.2.0 // indirect
+ golang.org/x/sys v0.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index d1f071c00b..075184ead7 100644
--- a/go.sum
+++ b/go.sum
@@ -10,12 +10,13 @@ github.com/gofiber/utils/v2 v2.0.0-beta.1/go.mod h1:CG89nDoIkEFIJaw5LdLO9AmBM11o
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/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
-github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
-github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
+github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
+github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
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/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ=
github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -31,10 +32,8 @@ 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.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY=
-github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY=
-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.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=
@@ -58,10 +57,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
-golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
+golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/group.go b/group.go
index 1470e970d5..50753c177d 100644
--- a/group.go
+++ b/group.go
@@ -180,7 +180,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 ebf976f904..7754abf9df 100644
--- a/helpers.go
+++ b/helpers.go
@@ -7,6 +7,7 @@ package fiber
import (
"bytes"
"crypto/tls"
+ "fmt"
"io"
"log"
"net"
@@ -21,9 +22,8 @@ import (
"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)
@@ -34,12 +34,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
}
}
}
@@ -50,19 +54,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
@@ -75,7 +81,9 @@ func (app *App) quoteString(raw string) string {
}
// Scan stack if other methods match the request
-func (app *App) methodExist(c *DefaultCtx) (exist bool) {
+func (app *App) methodExist(c *DefaultCtx) bool {
+ var exists bool
+
methods := app.config.RequestMethods
for i := 0; i < len(methods); i++ {
// Skip original method
@@ -106,7 +114,7 @@ func (app *App) methodExist(c *DefaultCtx) (exist bool) {
// No match, next route
if match {
// We matched
- exist = true
+ exists = true
// Add method to Allow header
c.Append(HeaderAllow, methods[i])
// Break stack loop
@@ -114,11 +122,12 @@ func (app *App) methodExist(c *DefaultCtx) (exist bool) {
}
}
}
- return
+ return exists
}
// Scan stack if other methods match the request
-func (app *App) methodExistCustom(c CustomCtx) (exist bool) {
+func (app *App) methodExistCustom(c CustomCtx) bool {
+ var exists bool
methods := app.config.RequestMethods
for i := 0; i < len(methods); i++ {
// Skip original method
@@ -149,7 +158,7 @@ func (app *App) methodExistCustom(c CustomCtx) (exist bool) {
// No match, next route
if match {
// We matched
- exist = true
+ exists = true
// Add method to Allow header
c.Append(HeaderAllow, methods[i])
// Break stack loop
@@ -157,7 +166,7 @@ func (app *App) methodExistCustom(c CustomCtx) (exist bool) {
}
}
}
- return
+ return exists
}
// uniqueRouteStack drop all not unique routes from the slice
@@ -232,7 +241,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
}
@@ -266,7 +275,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:]
}
@@ -306,21 +315,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)
}
@@ -328,6 +337,7 @@ var getBytesImmutable = func(s string) (b []byte) {
func (app *App) methodInt(s string) int {
// For better performance
if len(app.configured.RequestMethods) == 0 {
+ // TODO: Use iota instead
switch s {
case MethodGet:
return 0
@@ -384,8 +394,7 @@ func IsMethodIdempotent(m string) bool {
}
switch m {
- case MethodPut,
- MethodDelete:
+ case MethodPut, MethodDelete:
return true
default:
return false
@@ -705,7 +714,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"
@@ -719,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 bba282b229..b5f607be0b 100644
--- a/helpers_test.go
+++ b/helpers_test.go
@@ -16,6 +16,7 @@ import (
)
func Test_Utils_UniqueRouteStack(t *testing.T) {
+ t.Parallel()
route1 := &Route{}
route2 := &Route{}
route3 := &Route{}
@@ -92,6 +93,7 @@ func Benchmark_Utils_Unescape(b *testing.B) {
}
func Test_Utils_Parse_Address(t *testing.T) {
+ t.Parallel()
testCases := []struct {
addr, host, port string
}{
@@ -114,6 +116,7 @@ func Test_Utils_GetOffset(t *testing.T) {
}
func Test_Utils_TestConn_Deadline(t *testing.T) {
+ t.Parallel()
conn := &testConn{}
require.Nil(t, conn.SetDeadline(time.Time{}))
require.Nil(t, conn.SetReadDeadline(time.Time{}))
@@ -121,6 +124,7 @@ func Test_Utils_TestConn_Deadline(t *testing.T) {
}
func Test_Utils_IsNoCache(t *testing.T) {
+ t.Parallel()
testCases := []struct {
string
bool
@@ -192,12 +196,3 @@ func Benchmark_SlashRecognition(b *testing.B) {
require.True(b, result)
})
}
-
-func IndexRune(str string, needle int32) bool {
- for _, b := range str {
- if b == needle {
- return true
- }
- }
- return false
-}
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 b4cb642014..91a9b4775f 100644
--- a/hooks_test.go
+++ b/hooks_test.go
@@ -10,13 +10,12 @@ import (
"github.com/valyala/bytebufferpool"
)
-var testSimpleHandler = func(c Ctx) error {
+func testSimpleHandler(c Ctx) error {
return c.SendString("simple")
}
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.Use("/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.Use("/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()
@@ -221,12 +212,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)
require.Nil(t, app.Shutdown())
@@ -242,7 +233,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/memory/memory.go b/internal/memory/memory.go
index fe23a2581e..829c2a98e6 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/memory/memory_test.go b/internal/memory/memory_test.go
index dfc1cb28c4..41183b9c9d 100644
--- a/internal/memory/memory_test.go
+++ b/internal/memory/memory_test.go
@@ -11,6 +11,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/schema/encoder.go b/internal/schema/encoder.go
index 4d03b154e8..849d7c0fec 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
}
diff --git a/internal/storage/memory/memory_test.go b/internal/storage/memory/memory_test.go
index 00dcf86b4a..b0a5b23c9f 100644
--- a/internal/storage/memory/memory_test.go
+++ b/internal/storage/memory/memory_test.go
@@ -11,6 +11,7 @@ import (
var testStore = New()
func Test_Storage_Memory_Set(t *testing.T) {
+ t.Parallel()
var (
key = "john"
val = []byte("doe")
@@ -21,6 +22,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")
@@ -34,6 +36,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")
@@ -48,6 +51,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")
@@ -71,6 +75,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")
require.NoError(t, err)
@@ -78,6 +83,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")
@@ -95,6 +101,7 @@ func Test_Storage_Memory_Delete(t *testing.T) {
}
func Test_Storage_Memory_Reset(t *testing.T) {
+ t.Parallel()
var (
val = []byte("doe")
)
@@ -118,10 +125,12 @@ func Test_Storage_Memory_Reset(t *testing.T) {
}
func Test_Storage_Memory_Close(t *testing.T) {
+ t.Parallel()
require.NoError(t, testStore.Close())
}
func Test_Storage_Memory_Conn(t *testing.T) {
+ t.Parallel()
require.True(t, testStore.Conn() != nil)
}
diff --git a/listen.go b/listen.go
index 1ecaa87318..d05659276a 100644
--- a/listen.go
+++ b/listen.go
@@ -194,7 +194,7 @@ func (app *App) Listen(addr string, config ...ListenConfig) error {
// Configure Listener
ln, err := app.createListener(addr, tlsConfig, cfg)
if err != nil {
- return err
+ return fmt.Errorf("failed to listen: %w", err)
}
// prepare the server for the start
@@ -268,7 +268,7 @@ func (app *App) createListener(addr string, tlsConfig *tls.Config, cfg ListenCon
func (app *App) printMessages(cfg ListenConfig, ln net.Listener) {
// Print startup message
if !cfg.DisableStartupMessage {
- app.startupMessage(ln.Addr().String(), getTlsConfig(ln) != nil, "", cfg)
+ app.startupMessage(ln.Addr().String(), getTLSConfig(ln) != nil, "", cfg)
}
// Print routes
@@ -296,9 +296,9 @@ func (app *App) startupMessage(addr string, tls bool, pids string, cfg ListenCon
}
}
- scheme := "http"
+ scheme := schemeHTTP
if tls {
- scheme = "https"
+ scheme = schemeHTTPS
}
isPrefork := "Disabled"
@@ -389,7 +389,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
@@ -417,7 +417,7 @@ 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
}
// shutdown goroutine
diff --git a/listen_test.go b/listen_test.go
index de482e4331..ab6b565dd9 100644
--- a/listen_test.go
+++ b/listen_test.go
@@ -217,13 +217,16 @@ func Test_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 {
require.NoError(t, 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)
require.NoError(t, err)
diff --git a/middleware/basicauth/basicauth_test.go b/middleware/basicauth/basicauth_test.go
index 7656c9eb6e..8754333ece 100644
--- a/middleware/basicauth/basicauth_test.go
+++ b/middleware/basicauth/basicauth_test.go
@@ -1,13 +1,12 @@
package basicauth
import (
+ "encoding/base64"
"fmt"
"io"
"net/http/httptest"
"testing"
- b64 "encoding/base64"
-
"github.com/gofiber/fiber/v3"
"github.com/stretchr/testify/require"
"github.com/valyala/fasthttp"
@@ -16,7 +15,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 {
@@ -24,7 +22,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))
require.NoError(t, err)
require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
}
@@ -75,9 +73,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)
require.NoError(t, err)
@@ -109,7 +107,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/cache/cache.go b/middleware/cache/cache.go
index 87712c1922..14ec063191 100644
--- a/middleware/cache/cache.go
+++ b/middleware/cache/cache.go
@@ -43,8 +43,8 @@ var ignoreHeaders = map[string]any{
"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 +69,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 +81,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 +205,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 5567f0b998..1da4496f58 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"
@@ -36,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))
require.NoError(t, err)
- resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
+ resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
require.NoError(t, err)
require.Equal(t, "public, max-age=10", resp.Header.Get(fiber.HeaderCacheControl))
}
@@ -54,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))
require.NoError(t, err)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
@@ -62,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))
require.NoError(t, err)
bodyCached, err := io.ReadAll(respCached.Body)
require.NoError(t, err)
@@ -72,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))
require.NoError(t, err)
bodyCachedNextRound, err := io.ReadAll(respCachedNextRound.Body)
require.NoError(t, err)
@@ -93,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)
require.NoError(t, err)
- cachedReq := httptest.NewRequest("GET", "/", nil)
+ cachedReq := httptest.NewRequest(fiber.MethodGet, "/", nil)
cachedResp, err := app.Test(cachedReq)
require.NoError(t, err)
@@ -121,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)
+ require.NoError(t, err)
+ body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(t, cacheMiss, resp.Header.Get("X-Cache"))
require.Equal(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)
+ require.NoError(t, err)
+ cachedBody, err := io.ReadAll(cachedResp.Body)
require.NoError(t, err)
require.Equal(t, cacheHit, cachedResp.Header.Get("X-Cache"))
require.Equal(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)
+ require.NoError(t, err)
+ noCacheBody, err := io.ReadAll(noCacheResp.Body)
require.NoError(t, err)
require.Equal(t, cacheMiss, noCacheResp.Header.Get("X-Cache"))
require.Equal(t, []byte("2"), noCacheBody)
@@ -153,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)
+ require.NoError(t, err)
+ noCacheBody1, err := io.ReadAll(noCacheResp1.Body)
require.NoError(t, err)
require.Equal(t, cacheMiss, noCacheResp1.Header.Get("X-Cache"))
require.Equal(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)
+ require.NoError(t, err)
+ cachedBody1, err := io.ReadAll(cachedResp1.Body)
require.NoError(t, err)
require.Equal(t, cacheHit, cachedResp1.Header.Get("X-Cache"))
require.Equal(t, []byte("2"), cachedBody1)
@@ -189,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)
require.NoError(t, err)
require.Equal(t, cacheMiss, resp.Header.Get("X-Cache"))
@@ -200,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)
require.NoError(t, err)
@@ -209,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)
@@ -222,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)
@@ -232,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)
require.NoError(t, err)
require.Equal(t, cacheHit, cachedResp1.Header.Get("X-Cache"))
@@ -252,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)
+ require.NoError(t, err)
+ noStoreBody, err := io.ReadAll(noStoreResp.Body)
require.NoError(t, err)
require.Equal(t, []byte("2"), noStoreBody)
// Response not cached, returns updated response
@@ -279,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))
require.NoError(t, err)
- defer func(Body io.ReadCloser) {
- err := Body.Close()
+ defer func(body io.ReadCloser) {
+ err := body.Close()
require.NoError(t, err)
}(rsp.Body)
@@ -312,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)
require.NoError(t, err)
- cachedReq := httptest.NewRequest("GET", "/", nil)
+ cachedReq := httptest.NewRequest(fiber.MethodGet, "/", nil)
cachedResp, err := app.Test(cachedReq)
require.NoError(t, err)
@@ -343,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))
require.NoError(t, err)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(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))
require.NoError(t, err)
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(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))
require.NoError(t, err)
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(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))
require.NoError(t, err)
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
@@ -385,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))
require.NoError(t, err)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(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))
require.NoError(t, err)
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(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))
require.NoError(t, err)
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
require.Equal(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))
require.NoError(t, err)
body, err = io.ReadAll(resp.Body)
require.NoError(t, err)
@@ -421,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))
require.NoError(t, err)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
time.Sleep(500 * time.Millisecond)
- respCached, err := app.Test(httptest.NewRequest("GET", "/", nil))
+ respCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
require.NoError(t, err)
bodyCached, err := io.ReadAll(respCached.Body)
require.NoError(t, err)
@@ -458,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))
require.NoError(t, err)
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
- respCached, err := app.Test(httptest.NewRequest("GET", "/", nil))
+ respCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
require.NoError(t, err)
bodyCached, err := io.ReadAll(respCached.Body)
require.NoError(t, err)
require.True(t, bytes.Equal(body, bodyCached))
require.True(t, respCached.Header.Get(fiber.HeaderCacheControl) != "")
- _, err = app.Test(httptest.NewRequest("GET", "/error", nil))
+ _, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/error", nil))
require.NoError(t, err)
- errRespCached, err := app.Test(httptest.NewRequest("GET", "/error", nil))
+ errRespCached, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/error", nil))
require.NoError(t, err)
require.True(t, errRespCached.Header.Get(fiber.HeaderCacheControl) == "")
}
@@ -492,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)
require.NoError(t, err)
require.True(t, called)
@@ -506,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"))
+ require.NoError(t, err)
return time.Second * time.Duration(newCacheTime)
}}))
@@ -516,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))
require.NoError(t, err)
require.True(t, called)
require.Equal(t, 1, newCacheTime)
@@ -524,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))
require.NoError(t, err)
body, err := io.ReadAll(resp.Body)
@@ -537,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))
require.NoError(t, err)
cachedBodyNextRound, err := io.ReadAll(cachedRespNextRound.Body)
require.NoError(t, err)
@@ -560,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)
require.NoError(t, err)
require.Equal(t, "foobar", resp.Header.Get("X-Foobar"))
- req = httptest.NewRequest("GET", "/", nil)
+ req = httptest.NewRequest(fiber.MethodGet, "/", nil)
resp, err = app.Test(req)
require.NoError(t, err)
require.Equal(t, "foobar", resp.Header.Get("X-Foobar"))
@@ -595,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))
require.NoError(t, err)
require.Equal(t, cacheMiss, resp.Header.Get("X-Cache"))
- resp, err = app.Test(httptest.NewRequest("GET", "/", nil))
+ resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
require.NoError(t, err)
require.Equal(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))
require.NoError(t, err)
require.Equal(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))
require.NoError(t, err)
require.Equal(t, cacheUnreachable, errRespCached.Header.Get("X-Cache"))
}
@@ -624,12 +625,12 @@ func Test_Cache_WithHead(t *testing.T) {
}
app.Route("/").Get(handler).Head(handler)
- req := httptest.NewRequest("HEAD", "/", nil)
+ req := httptest.NewRequest(fiber.MethodHead, "/", nil)
resp, err := app.Test(req)
require.NoError(t, err)
require.Equal(t, cacheMiss, resp.Header.Get("X-Cache"))
- cachedReq := httptest.NewRequest("HEAD", "/", nil)
+ cachedReq := httptest.NewRequest(fiber.MethodHead, "/", nil)
cachedResp, err := app.Test(cachedReq)
require.NoError(t, err)
require.Equal(t, cacheHit, cachedResp.Header.Get("X-Cache"))
@@ -653,28 +654,28 @@ func Test_Cache_WithHeadThenGet(t *testing.T) {
}
app.Route("/").Get(handler).Head(handler)
- headResp, err := app.Test(httptest.NewRequest("HEAD", "/?cache=123", nil))
+ headResp, err := app.Test(httptest.NewRequest(fiber.MethodHead, "/?cache=123", nil))
require.NoError(t, err)
headBody, err := io.ReadAll(headResp.Body)
require.NoError(t, err)
require.Equal(t, "", string(headBody))
require.Equal(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))
require.NoError(t, err)
headBody, err = io.ReadAll(headResp.Body)
require.NoError(t, err)
require.Equal(t, "", string(headBody))
require.Equal(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))
require.NoError(t, err)
getBody, err := io.ReadAll(getResp.Body)
require.NoError(t, err)
require.Equal(t, "123", string(getBody))
require.Equal(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))
require.NoError(t, err)
getBody, err = io.ReadAll(getResp.Body)
require.NoError(t, err)
@@ -695,7 +696,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))
require.NoError(t, err)
require.Equal(t, cacheMiss, resp.Header.Get("Cache-Status"))
}
@@ -706,7 +707,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)
}
}
@@ -742,7 +743,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))
require.NoError(t, err)
require.Equal(t, tcase[1], rsp.Header.Get("X-Cache"), fmt.Sprintf("Case %v", idx))
}
@@ -760,7 +761,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))
+ require.NoError(t, err)
return c.Send(make([]byte, size))
})
@@ -776,7 +778,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))
require.NoError(t, err)
require.Equal(t, tcase[1], rsp.Header.Get("X-Cache"), fmt.Sprintf("Case %v", idx))
}
@@ -789,14 +791,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()
@@ -819,14 +821,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()
@@ -854,7 +856,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()
@@ -886,7 +888,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/heap.go b/middleware/cache/heap.go
index bcc279c2c5..fa97871595 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 any) {
- h.pushInternal(x.(heapEntry))
+ h.pushInternal(x.(heapEntry)) //nolint:forcetypeassert // Forced type assertion required to implement the heap.Interface interface
}
func (h *indexedHeap) Pop() any {
@@ -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 617e0d8991..c6ae542805 100644
--- a/middleware/cache/manager.go
+++ b/middleware/cache/manager.go
@@ -8,7 +8,8 @@ import (
"github.com/gofiber/fiber/v3/internal/memory"
)
-//go:generate msgp -o=manager_msgp.go -io=false -unexported
+// go:generate msgp
+// msgp -file="manager.go" -o="manager_msgp.go" -tests=false -unexported
type item struct {
body []byte
ctype []byte
@@ -48,7 +49,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
@@ -66,38 +67,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)
}
@@ -106,16 +116,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_test.go b/middleware/compress/compress_test.go
index ced70df3c3..a4c3cd38b0 100644
--- a/middleware/compress/compress_test.go
+++ b/middleware/compress/compress_test.go
@@ -25,6 +25,7 @@ func init() {
// go test -run Test_Compress_Gzip
func Test_Compress_Gzip(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New())
@@ -34,7 +35,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)
@@ -50,9 +51,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}))
@@ -62,7 +65,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)
@@ -79,6 +82,7 @@ func Test_Compress_Different_Level(t *testing.T) {
}
func Test_Compress_Deflate(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New())
@@ -87,7 +91,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)
@@ -102,6 +106,7 @@ func Test_Compress_Deflate(t *testing.T) {
}
func Test_Compress_Brotli(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New())
@@ -110,7 +115,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, 10*time.Second)
@@ -125,6 +130,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}))
@@ -133,7 +139,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)
@@ -148,6 +154,7 @@ func Test_Compress_Disabled(t *testing.T) {
}
func Test_Compress_Next_Error(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New())
@@ -156,7 +163,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)
@@ -171,6 +178,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 {
@@ -178,7 +186,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))
require.NoError(t, err)
require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
}
diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go
index 0356f38d0c..0239223d24 100644
--- a/middleware/cors/cors.go
+++ b/middleware/cors/cors.go
@@ -1,7 +1,6 @@
package cors
import (
- "net/http"
"strconv"
"strings"
@@ -128,7 +127,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 84e7bd9b73..bc508a1186 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 {
@@ -231,7 +237,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))
require.NoError(t, err)
require.Equal(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 8c01c6c46f..c512082cbf 100644
--- a/middleware/csrf/config.go
+++ b/middleware/csrf/config.go
@@ -106,7 +106,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
}
@@ -143,7 +143,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 %s
", basePathEscaped)
- fmt.Fprint(c, "")
+ _, _ = fmt.Fprintf(c, "
")
c.Type("html")
diff --git a/middleware/idempotency/idempotency_test.go b/middleware/idempotency/idempotency_test.go
index d289124555..1873050ae2 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 (
@@ -171,5 +172,4 @@ func Benchmark_Idempotency(b *testing.B) {
h(c)
}
})
-
}
diff --git a/middleware/limiter/README.md b/middleware/limiter/README.md
index 919764bf14..9542515f85 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"
}
diff --git a/middleware/limiter/limiter_test.go b/middleware/limiter/limiter_test.go
index 9cae095c11..dd4ba87305 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"
@@ -16,6 +15,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()
@@ -33,7 +33,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))
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode)
@@ -49,19 +49,20 @@ 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))
require.NoError(t, err)
require.Equal(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))
require.NoError(t, err)
require.Equal(t, 200, resp.StatusCode)
}
// 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()
@@ -78,7 +79,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))
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode)
@@ -94,19 +95,20 @@ 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))
require.NoError(t, err)
require.Equal(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))
require.NoError(t, err)
require.Equal(t, 200, resp.StatusCode)
}
// 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{
@@ -117,27 +119,28 @@ 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))
require.NoError(t, err)
require.Equal(t, 400, resp.StatusCode)
- resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil))
+ resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
require.NoError(t, err)
require.Equal(t, 200, resp.StatusCode)
- resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil))
+ resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
require.NoError(t, err)
require.Equal(t, 429, resp.StatusCode)
}
// 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{
@@ -153,27 +156,28 @@ 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))
require.NoError(t, err)
require.Equal(t, 400, resp.StatusCode)
- resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil))
+ resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
require.NoError(t, err)
require.Equal(t, 200, resp.StatusCode)
- resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil))
+ resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/success", nil))
require.NoError(t, err)
require.Equal(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))
require.NoError(t, err)
require.Equal(t, 200, resp.StatusCode)
}
// 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()
@@ -191,21 +195,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))
require.NoError(t, err)
require.Equal(t, 200, resp.StatusCode)
- resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil))
+ resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
require.NoError(t, err)
require.Equal(t, 400, resp.StatusCode)
- resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil))
+ resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/fail", nil))
require.NoError(t, err)
require.Equal(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))
require.NoError(t, err)
require.Equal(t, 400, resp.StatusCode)
}
@@ -227,7 +231,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()
@@ -239,6 +243,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 {
@@ -246,12 +251,13 @@ func Test_Limiter_Next(t *testing.T) {
},
}))
- resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
+ resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
require.NoError(t, err)
require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
}
func Test_Limiter_Headers(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New(Config{
@@ -264,7 +270,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)
@@ -294,7 +300,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()
@@ -306,6 +312,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,
@@ -319,7 +326,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 {
require.NoError(t, err)
require.Equal(t, 429, resp.StatusCode)
diff --git a/middleware/limiter/manager.go b/middleware/limiter/manager.go
index 46d7c4b6af..4225cdd3ac 100644
--- a/middleware/limiter/manager.go
+++ b/middleware/limiter/manager.go
@@ -8,7 +8,8 @@ import (
"github.com/gofiber/fiber/v3/internal/memory"
)
-//go:generate msgp -o=manager_msgp.go -io=false -unexported
+// go:generate msgp
+// msgp -file="manager.go" -o="manager_msgp.go" -tests=false -unexported
type item struct {
currHits int
prevHits int
@@ -43,7 +44,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
@@ -55,37 +56,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)
@@ -93,21 +90,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 411dc06a81..70cbefdfd3 100644
--- a/middleware/logger/README.md
+++ b/middleware/logger/README.md
@@ -96,7 +96,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)
}
},
}))
@@ -246,7 +246,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 a30f3cd26c..ceae3dd445 100644
--- a/middleware/logger/config.go
+++ b/middleware/logger/config.go
@@ -161,7 +161,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
}
diff --git a/middleware/logger/data.go b/middleware/logger/data.go
index fc30238835..2d5955dc51 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() any { 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/default_logger.go b/middleware/logger/default_logger.go
index 624725ed6c..a6dee891a1 100644
--- a/middleware/logger/default_logger.go
+++ b/middleware/logger/default_logger.go
@@ -18,15 +18,14 @@ var mu sync.Mutex
// default logger for fiber
func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
+ var err error
+
// Alias colors
colors := c.App().Config().ColorScheme
// Get new buffer
buf := bytebufferpool.Get()
- // Put buffer back to pool
- defer bytebufferpool.Put(buf)
-
// Default output when no custom Format or io.Writer is given
if cfg.enableColors && cfg.Format == defaultFormat {
// Format error if exist
@@ -36,32 +35,36 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
}
// Format log to buffer
- _, _ = buf.WriteString(fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+data.ErrPaddingStr+"s %s\n",
- data.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| %-"+data.ErrPaddingStr+"s %s\n",
+ data.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())
}
+ // Put buffer back to pool
+ bytebufferpool.Put(buf)
+
// End chain
return nil
}
- var err error
// Loop over template parts execute dynamic parts and add fixed parts to the buffer
for i, logFunc := range data.LogFuncChain {
if logFunc == nil {
- _, _ = buf.Write(data.TemplateChain[i])
+ _, _ = buf.Write(data.TemplateChain[i]) //nolint:errcheck // This will never fail
} else if data.TemplateChain[i] == nil {
_, err = logFunc(buf, c, data, "")
} else {
@@ -74,7 +77,7 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
// 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
@@ -82,7 +85,7 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
// 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()
@@ -91,6 +94,9 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error {
cfg.Done(c, buf.Bytes())
}
+ // Put buffer back to pool
+ bytebufferpool.Put(buf)
+
return nil
}
diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go
index 489c52f5b2..bcbd047e74 100644
--- a/middleware/logger/logger.go
+++ b/middleware/logger/logger.go
@@ -48,6 +48,8 @@ func New(config ...Config) fiber.Handler {
var (
once sync.Once
errHandler fiber.ErrorHandler
+
+ dataPool = sync.Pool{New: func() interface{} { return new(Data) }}
)
// Err padding
@@ -89,7 +91,7 @@ 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
@@ -97,7 +99,7 @@ func New(config ...Config) fiber.Handler {
data.TemplateChain = templateChain
data.LogFuncChain = logFunChain
// put data back in the pool
- defer DataPool.Put(data)
+ defer dataPool.Put(data)
// Set latency start time
if cfg.enableLatency {
@@ -111,7 +113,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
}
}
diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go
index 76f485b650..536459c7e0 100644
--- a/middleware/logger/logger_test.go
+++ b/middleware/logger/logger_test.go
@@ -1,6 +1,8 @@
+//nolint:bodyclose // Much easier to just ignore memory leaks in tests
package logger
import (
+ "bufio"
"bytes"
"errors"
"fmt"
@@ -10,6 +12,7 @@ import (
"os"
"sync"
"testing"
+ "time"
"github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/middleware/requestid"
@@ -20,6 +23,7 @@ import (
// go test -run Test_Logger
func Test_Logger(t *testing.T) {
+ t.Parallel()
app := fiber.New()
buf := bytebufferpool.Get()
@@ -34,7 +38,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))
require.NoError(t, err)
require.Equal(t, fiber.StatusInternalServerError, resp.StatusCode)
require.Equal(t, "some random error", buf.String())
@@ -42,6 +46,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()
@@ -66,21 +71,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))
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode)
require.Equal(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))
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode)
require.Equal(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))
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode)
require.Equal(t, "", buf.String())
@@ -88,6 +93,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 {
@@ -95,26 +101,28 @@ func Test_Logger_Next(t *testing.T) {
},
}))
- resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
+ resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
require.NoError(t, err)
require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
}
// 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{
Done: func(c fiber.Ctx, logString []byte) {
if c.Response().StatusCode() == fiber.StatusOK {
- buf.Write(logString)
+ _, err := buf.Write(logString)
+ require.NoError(t, 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))
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode)
@@ -123,12 +131,13 @@ 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",
}))
- resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
+ resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
require.NoError(t, err)
require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
}
@@ -142,13 +151,14 @@ 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{
Output: o,
}))
- resp, err := app.Test(httptest.NewRequest("GET", "/", nil))
+ resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil))
require.NoError(t, err)
require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
@@ -157,6 +167,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)
@@ -169,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))
require.NoError(t, err)
require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
@@ -179,6 +190,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)
@@ -188,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))
require.NoError(t, err)
require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
@@ -198,6 +210,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)
@@ -215,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))
require.NoError(t, err)
expectedGetResponse := "Sample response body"
@@ -223,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))
require.NoError(t, err)
expectedPostResponse := "Post in test"
@@ -232,6 +245,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()
@@ -246,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))
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode)
require.Equal(t, "0 5 200", buf.String())
@@ -254,12 +268,13 @@ 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()
defer bytebufferpool.Put(buf)
- app.Use(New(ConfigDefault))
+ app.Use(New())
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("hello")
@@ -272,10 +287,10 @@ 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()
require.Nil(t, err1)
@@ -286,21 +301,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)
}
- require.Equal(bb, 200, fctx.Response.Header.StatusCode())
+ require.Equal(b, 200, fctx.Response.Header.StatusCode())
}
b.Run("Base", func(bb *testing.B) {
@@ -343,6 +360,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)
@@ -361,7 +379,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))
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode)
@@ -370,6 +388,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)
@@ -381,10 +400,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)
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode)
require.Equal(t, "Hello fiber!", buf.String())
@@ -392,6 +411,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)
@@ -403,10 +423,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)
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode)
require.Equal(t, "Hello fiber!", buf.String())
@@ -414,6 +434,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()
@@ -432,11 +453,51 @@ 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)
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode)
require.Equal(t, customTag, buf.String())
}
+
+// go test -run Test_Logger_ByteSent_Streaming
+func Test_Logger_ByteSent_Streaming(t *testing.T) {
+ t.Parallel()
+ 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(fiber.MethodGet, "/", nil))
+ require.NoError(t, err)
+ require.Equal(t, fiber.StatusOK, resp.StatusCode)
+ require.Equal(t, "0 0 200", buf.String())
+}
diff --git a/middleware/logger/tags.go b/middleware/logger/tags.go
index feb473ad87..afc0e34ad4 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/v3"
)
@@ -91,6 +90,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) {
@@ -193,11 +195,11 @@ 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) {
- 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/pprof/pprof.go b/middleware/pprof/pprof.go
index aa55dc0181..373e42d6f0 100644
--- a/middleware/pprof/pprof.go
+++ b/middleware/pprof/pprof.go
@@ -8,26 +8,26 @@ import (
"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)
-)
-
// New creates a new middleware handler
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/pprof/pprof_test.go b/middleware/pprof/pprof_test.go
index c4274f1a85..afb152f743 100644
--- a/middleware/pprof/pprof_test.go
+++ b/middleware/pprof/pprof_test.go
@@ -12,6 +12,7 @@ import (
)
func Test_Non_Pprof_Path(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New())
@@ -30,6 +31,7 @@ func Test_Non_Pprof_Path(t *testing.T) {
}
func Test_Non_Pprof_Path_WithPrefix(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New(Config{Prefix: "/federated-fiber"}))
@@ -48,6 +50,7 @@ func Test_Non_Pprof_Path_WithPrefix(t *testing.T) {
}
func Test_Pprof_Index(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New())
@@ -67,6 +70,7 @@ func Test_Pprof_Index(t *testing.T) {
}
func Test_Pprof_Index_WithPrefix(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New(Config{Prefix: "/federated-fiber"}))
@@ -86,6 +90,7 @@ func Test_Pprof_Index_WithPrefix(t *testing.T) {
}
func Test_Pprof_Subs(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New())
@@ -101,6 +106,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"
@@ -113,6 +119,7 @@ func Test_Pprof_Subs(t *testing.T) {
}
func Test_Pprof_Subs_WithPrefix(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New(Config{Prefix: "/federated-fiber"}))
@@ -128,6 +135,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"
@@ -140,6 +148,7 @@ func Test_Pprof_Subs_WithPrefix(t *testing.T) {
}
func Test_Pprof_Other(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New())
@@ -154,6 +163,7 @@ func Test_Pprof_Other(t *testing.T) {
}
func Test_Pprof_Other_WithPrefix(t *testing.T) {
+ t.Parallel()
app := fiber.New()
app.Use(New(Config{Prefix: "/federated-fiber"}))
@@ -170,7 +180,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{
@@ -187,7 +196,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/README.md b/middleware/proxy/README.md
index daa66245fc..f49acffe49 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
-func Do(c fiber.Ctx, addr string, clients ...*fasthttp.Client) error
+// Do performs the given http request and fills the given http response.
+func Do(c *iber.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/v3"
- "github.com/gofiber/fiber/v3/middleware/proxy"
+ "github.com/gofiber/fiber/v3"
+ "github.com/gofiber/fiber/v3/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 %s
", basePathEscaped)
+ _, _ = fmt.Fprint(c, "")
if len(basePathEscaped) > 1 {
parentPathEscaped := html.EscapeString(strings.TrimRight(c.Path(), "/") + "/..")
- fmt.Fprintf(c, `
")
+ _, _ = fmt.Fprint(c, "