From 002d9b5db443dd8e15844d158e3d8b6ef69b610a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Wed, 29 Jun 2022 08:54:34 +0300 Subject: [PATCH 001/141] :memo: docs: flat-square style for readme badges (#1949) --- .github/README.md | 51 +++++++++++++++++++++-------------------- .github/README_ckb.md | 48 +++++++++++++++++++------------------- .github/README_de.md | 48 +++++++++++++++++++------------------- .github/README_es.md | 48 +++++++++++++++++++------------------- .github/README_fa.md | 48 +++++++++++++++++++------------------- .github/README_fr.md | 48 +++++++++++++++++++------------------- .github/README_he.md | 48 +++++++++++++++++++------------------- .github/README_id.md | 48 +++++++++++++++++++------------------- .github/README_it.md | 48 +++++++++++++++++++------------------- .github/README_ja.md | 48 +++++++++++++++++++------------------- .github/README_ko.md | 48 +++++++++++++++++++------------------- .github/README_nl.md | 48 +++++++++++++++++++------------------- .github/README_pt.md | 48 +++++++++++++++++++------------------- .github/README_ru.md | 48 +++++++++++++++++++------------------- .github/README_sa.md | 48 +++++++++++++++++++------------------- .github/README_tr.md | 48 +++++++++++++++++++------------------- .github/README_zh-CN.md | 48 +++++++++++++++++++------------------- .github/README_zh-TW.md | 48 +++++++++++++++++++------------------- 18 files changed, 434 insertions(+), 433 deletions(-) diff --git a/.github/README.md b/.github/README.md index 6fa94e3c9e..3f88da06c0 100644 --- a/.github/README.md +++ b/.github/README.md @@ -5,81 +5,82 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - + +

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. diff --git a/.github/README_ckb.md b/.github/README_ckb.md index 0129a7f6d9..8fd7ae8dbb 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_de.md b/.github/README_de.md index 47fe7a4cae..094dd85aa1 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_es.md b/.github/README_es.md index b7932b860f..b5098d739a 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_fa.md b/.github/README_fa.md index 63512f3be1..f9e8ffd187 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +


diff --git a/.github/README_fr.md b/.github/README_fr.md index dfb46a3a9c..e2d3caae37 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_he.md b/.github/README_he.md index f5338121b1..4aa0dd28e1 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_id.md b/.github/README_id.md index bab65920bb..3e0d9b63b3 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_it.md b/.github/README_it.md index 98971185b5..f42bba9f46 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_ja.md b/.github/README_ja.md index ba8788598a..b9a437b60e 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_ko.md b/.github/README_ko.md index 9fdca2863a..11463c4e3c 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_nl.md b/.github/README_nl.md index 701d97ef30..9bc3dd228c 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_pt.md b/.github/README_pt.md index b8529b239f..714f553eb4 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_ru.md b/.github/README_ru.md index 499d637a74..5fc5191d33 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_sa.md b/.github/README_sa.md index 07d26e5ed0..34199e355a 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_tr.md b/.github/README_tr.md index 87c7d9515b..7103dd2316 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - + - +

diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index b9a55db9f6..8024583e82 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index 4f18a708e1..38249ab47b 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -5,77 +5,77 @@
- + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +
- + - + - + - + - + - + - +

From 83d87612227e79afaf5380fa8cccc1c06ac96953 Mon Sep 17 00:00:00 2001 From: hamidreza ghahremani <97837767+hamidreza01@users.noreply.github.com> Date: Wed, 29 Jun 2022 13:17:01 +0430 Subject: [PATCH 002/141] Update README_fa.md (#1953) --- .github/README_fa.md | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/README_fa.md b/.github/README_fa.md index f9e8ffd187..90332c3c75 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -676,30 +676,30 @@ func main() {
-

+
| Middleware | توضیحات | | :------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Basic auth middleware provides an HTTP basic authentication. It calls the next handler for valid credentials and 401 Unauthorized for missing or invalid credentials. | -| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Intercept and cache responses | -| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Compression middleware for Fiber, it supports `deflate`, `gzip` and `brotli` by default. | -| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | -| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | -| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | -| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | -| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | -| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | -| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | FileSystem middleware for Fiber, special thanks and credits to Alireza Salary | -| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Rate-limiting middleware for Fiber. Use to limit repeated requests to public APIs and/or endpoints such as password reset. | -| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP request/response logger. | -| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Monitor middleware that reports server metrics, inspired by express-status-monitor | -| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Special thanks to Matthew Lee \(@mthli\) | -| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Allows you to proxy requests to a multiple servers | -| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Recover middleware recovers from panics anywhere in the stack chain and handles the control to the centralized[ ErrorHandler](https://docs.gofiber.io/guide/error-handling). | -| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Adds a requestid to every request. | -| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | -| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | -| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) |یک میدلور پایه که سیستم احراز هویت پایه ای را فراهم میکند. در صورت معتبر بودن درخواست روتر بعدی صدا زده شده و در صورت نامعتبر بودن خطای ۴۰۱ نمایش داده میشود.| +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) |پاسخ هارا رهگیری کرده و انها را به صورت موقت ذخیره میکند.| +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | یک میدلور فشرده سازی برای Fiber که به طور پیشفرض از `deflate`, `gzip` و `brotli`. پشتیبانی میکند.| | +| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) |فعال سازی هدر های cross-origin با گزینه های مختلف.| +| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) |در برابر حملات CSRF ایمنی ایجاد میکند.| +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) |مقادیر کوکی هارا رمزنگاری میکند.| +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | میدلور ETag به کش ها اجازه میدهد کارآمد تر عمل کرده و در پهنای باند صرفه جویی کنند. به عنوان یک وب سرور نیازی به دادن پاسخ کامل نیست اگر محتوا تغییر نکرده باشد. | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | میدلور Expvar میتواند متغیر هایی را تعریف کرده و مقادیر انها را در زمان اجرا با فرمت JSON به شما نشان دهد. | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | جلوگیری و یا کش کردن درخواست های favicon در صورتی که مسیر یک فایل را داده باشید.| +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | میدلور FileSystem به شما اجازه میدهد فایل های یک مسیر را عمومی کنید. | +| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) |میدلور محدود کننده تعداد درخواست برای Fiber.| +| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) |لاگ گرفتن از درخواست و پاسخ های HTTP.| +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) |وضعیت سرور را مانیتور و گزارش میکند، از express-status-monitor الهام گرفته شده است.| +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | تشکر ویژه از Matthew Lee \(@mthli\)| +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | اجازه میدهد درخواست هارا بر روی چند سرور پروکسی کنید. | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) |خطا های زمان اجرا را در وب سرور HTTP شما مدیریت میکنند[ ErrorHandler](https://docs.gofiber.io/guide/error-handling). | +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | به تمامی درخواست ها شناسه ای را اختصاص میدهد.| +| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) |برای ذخیره و مدیریت شناسه کاربری یا session بازدید کنندگان استفاده .میشود| +| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) |این میدلور میتواند با استفاده از شرط های تعیین شده درخواست هایی را نادیده بگیرد.| +| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) |این میدلور محدودیت زمانی ای را برای درخواست ها تنظیم میکند، در صورتی که محدودیت به پایان برسد ErrorHandler صدا زده میشود.|


From ab3874c634d1ffb0331aac9e28266cef628ac193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Wed, 29 Jun 2022 12:41:33 +0300 Subject: [PATCH 003/141] :memo: docs: translate some lines to turkish (#1954) --- .github/README_tr.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/README_tr.md b/.github/README_tr.md index 7103dd2316..ed7e7337cb 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -565,25 +565,25 @@ Fiber'a dahil edilen middlewareların bir listesi aşağıda verilmiştir. | Middleware | Açıklama | | :------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Basic auth middleware'i, bir HTTP Basic auth sağlar. Geçerli kimlik bilgileri için sonraki handlerı ve eksik veya geçersiz kimlik bilgileri için 401 döndürür. | -| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Reponseları durdur ve önbelleğe al | +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Basic auth middleware'ı, bir HTTP Basic auth sağlar. Geçerli kimlik bilgileri için sonraki handlerı ve eksik veya geçersiz kimlik bilgileri için 401 döndürür. | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Reponseları durdur ve önbelleğe al. | | [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Fiber için sıkıştırma middleware, varsayılan olarak `deflate`, `gzip` ve `brotli`yi destekler. | | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Çeşitli seçeneklerle başlangıçlar arası kaynak paylaşımını \(CORS\) etkinleştirin. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | CSRF exploitlerinden korunun. | -| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | -| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | -| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware'ı cookie değerlerini şifreler. | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware'ı sayfa içeriği değişmediyse bant genişliğini daha verimli kullanmak için tam sayfa içeriğini tekrar göndermez. | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware, HTTP serverinin bazı runtime değişkenlerini JSON formatında sunar. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Bir dosya yolu sağlanmışsa, loglardaki favicon'u yoksayar veya bellekten sunar. | -| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Fiber için FileSystem middleware, Alireza Salary'e özel teşekkürler | +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Fiber için FileSystem middleware, Alireza Salary'e özel teşekkürler. | | [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Fiber için hız sınırlayıcı middleware'i. Açık API'lere ve/veya parola sıfırlama gibi endpointlere yönelik tekrarlanan istekleri sınırlamak için kullanın. | | [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP istek/yanıt logger'ı. | -| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Monitor middleware that reports server metrics, inspired by express-status-monitor | -| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Matthew Lee'ye özel teşekkürler \(@mthli\) | -| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Birden çok sunucuya proxy istekleri yapmanızı sağlar | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Monitor middleware'ı sunucu metriklerini rapor eder, express-status-monitor'den esinlenildi. | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Matthew Lee'ye özel teşekkürler \(@mthli\). | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Birden çok sunucuya proxy istekleri yapmanızı sağlar. | | [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Recover middleware'i, stack chain'ini herhangi bir yerindeki paniklerden kurtulur ve kontrolü merkezileştirilmiş [ErrorHandler'e](https://docs.gofiber.io/guide/error-handling) verir. | -| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Her requeste id verir | -| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | -| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Her requeste id verir. | +| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session için middleware. NOTE: Bu middleware Fiber'in Storage yapısını kullanır. | +| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware'ı verilen koşul `true` olduğunda handler'ı atlar ve işlemez. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Bir request için maksimum süre ekler ve aşılırsa ErrorHandler'a iletir. | ## 🧬 Harici Middlewarelar @@ -596,10 +596,10 @@ Harici olarak barındırılan middlewareların modüllerinin listesi. Bu middlew | [helmet](https://github.com/gofiber/helmet) | Çeşitli HTTP headerları ayarlayarak uygulamalarınızın güvenliğini sağlamaya yardımcı olur. | | [jwt](https://github.com/gofiber/jwt) | JWT, bir JSON Web Token \(JWT\) yetkilendirmesi döndüren middleware. | | [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware, key tabanlı bir authentication sağlar. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [redirect](https://github.com/gofiber/redirect) | Yönlendirme middleware 'ı. | | [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware, sağlanan kurallara göre URL yolunu yeniden yazar. Geriye dönük uyumluluk için veya yalnızca daha temiz ve daha açıklayıcı bağlantılar oluşturmak için yardımcı olabilir. | -| [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | -| [template](https://github.com/gofiber/template) | Bu paket, Fiber `v1.10.x`, Go sürüm 1.13 veya üzeri gerekli olduğunda kullanılabilecek 8 template engine içerir. | +| [storage](https://github.com/gofiber/storage) | Fiber'in Storage yapısını destekleyen birçok storage driver'ı verir. Bu sayede depolama gerektiren Fiber middlewarelarında kolaylıkla kullanılabilir. | +| [template](https://github.com/gofiber/template) | Bu paket, Fiber `v2.x.x`, Go sürüm 1.14 veya üzeri gerekli olduğunda kullanılabilecek 9 template motoru içerir. | | [websocket](https://github.com/gofiber/websocket) | Yereller desteğiyle Fiber için Fasthttp WebSocket'a dayalıdır! | ## 🕶️ Awesome Listesi From b8b5c8afedb3eeb4535574ed430c720777cac648 Mon Sep 17 00:00:00 2001 From: naoki kuroda <68233204+nnnkkk7@users.noreply.github.com> Date: Sat, 2 Jul 2022 17:32:09 +0900 Subject: [PATCH 004/141] delete a blank line (#1961) --- hooks.go | 1 - 1 file changed, 1 deletion(-) diff --git a/hooks.go b/hooks.go index b13d491b98..3ea7a00014 100644 --- a/hooks.go +++ b/hooks.go @@ -94,7 +94,6 @@ func (h *hooks) executeOnRouteHooks(route Route) error { } func (h *hooks) executeOnNameHooks(route Route) error { - for _, v := range h.onName { if err := v(route); err != nil { return err From 41d31a019061f8f3b7155b9c92b20a8e0750ddb0 Mon Sep 17 00:00:00 2001 From: John Li Date: Sat, 2 Jul 2022 19:02:41 +0800 Subject: [PATCH 005/141] fix middleware/session update cookie. (#1960) --- middleware/session/session.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/middleware/session/session.go b/middleware/session/session.go index c8be6625b3..33be2a1a7d 100644 --- a/middleware/session/session.go +++ b/middleware/session/session.go @@ -144,10 +144,8 @@ func (s *Session) Save() error { s.exp = s.config.Expiration } - // Create session with the session ID if fresh - if s.fresh { - s.setSession() - } + // Update client cookie + s.setSession() // Convert data to bytes mux.Lock() From a337e95463d5b41c86179f8392922dee6de40df3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Jul 2022 07:43:03 +0200 Subject: [PATCH 006/141] Bump github.com/valyala/fasthttp from 1.37.0 to 1.38.0 (#1948) Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.37.0 to 1.38.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.37.0...v1.38.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d48909c7ef..3a3ca5d952 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module github.com/gofiber/fiber/v2 go 1.16 require ( - github.com/valyala/fasthttp v1.37.0 + github.com/valyala/fasthttp v1.38.0 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 ) diff --git a/go.sum b/go.sum index 5c4ae8555c..2f5953c3dd 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwc github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 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.37.0 h1:7WHCyI7EAkQMVmrfBhWTCOaeROb1aCBiTopx63LkMbE= -github.com/valyala/fasthttp v1.37.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= +github.com/valyala/fasthttp v1.38.0 h1:yTjSSNjuDi2PPvXY2836bIwLmiTS2T4T9p1coQshpco= +github.com/valyala/fasthttp v1.38.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= From b6818c20be7d654dfba65755939c8ad31bd8260f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Mon, 4 Jul 2022 08:49:57 +0300 Subject: [PATCH 007/141] :memo: docs: new issue templates (#1945) * :memo: docs: new issue templates * :memo: docs: new issue templates * :memo: docs: new issue templates * :memo: docs: new issue templates * :memo: docs: new issue templates --- .github/ISSUE_TEMPLATE/---bug.md | 28 ------- .github/ISSUE_TEMPLATE/---feature.md | 16 ---- .github/ISSUE_TEMPLATE/---question.md | 23 ------ .github/ISSUE_TEMPLATE/bug-report.yaml | 85 +++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature-request.yaml | 60 +++++++++++++++ .github/ISSUE_TEMPLATE/question.yaml | 55 +++++++++++++ 6 files changed, 200 insertions(+), 67 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/---bug.md delete mode 100644 .github/ISSUE_TEMPLATE/---feature.md delete mode 100644 .github/ISSUE_TEMPLATE/---question.md create mode 100644 .github/ISSUE_TEMPLATE/bug-report.yaml create mode 100644 .github/ISSUE_TEMPLATE/feature-request.yaml create mode 100644 .github/ISSUE_TEMPLATE/question.yaml diff --git a/.github/ISSUE_TEMPLATE/---bug.md b/.github/ISSUE_TEMPLATE/---bug.md deleted file mode 100644 index a250292311..0000000000 --- a/.github/ISSUE_TEMPLATE/---bug.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -name: "\U0001F41B Bug" -about: Create a report to help us improve -title: "\U0001F41B " -labels: 'Type: Bug' -assignees: '' - ---- - -**Fiber version** - -**Issue description** - -**Code snippet** - -```go -package main - -import "github.com/gofiber/fiber/v2" - -func main() { - app := fiber.New() - - // Steps to reproduce - - log.Fatal(app.Listen(":3000")) -} -``` diff --git a/.github/ISSUE_TEMPLATE/---feature.md b/.github/ISSUE_TEMPLATE/---feature.md deleted file mode 100644 index c3baf503a2..0000000000 --- a/.github/ISSUE_TEMPLATE/---feature.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: "\U0001F680 Feature" -about: Suggest an idea for this project -title: "\U0001F680 " -labels: 'Type: Feature' -assignees: '' - ---- - -**Is your feature request related to a problem?** - -**Describe the solution you'd like** - -**Describe alternatives you've considered** - -**Additional context** diff --git a/.github/ISSUE_TEMPLATE/---question.md b/.github/ISSUE_TEMPLATE/---question.md deleted file mode 100644 index 68912c2b38..0000000000 --- a/.github/ISSUE_TEMPLATE/---question.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: "\U0001F917 Question" -about: Ask a question so we can help -title: "\U0001F917 " -labels: 'Type: Question' -assignees: '' - ---- - -**Question description** - -**Code snippet** _Optional_ - -```go -package main - -import "github.com/gofiber/fiber/v2" - -func main() { - app := fiber.New() - // .. -} -``` diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml new file mode 100644 index 0000000000..c4a902a86f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -0,0 +1,85 @@ +name: "\U0001F41B Bug Report" +title: "\U0001F41B [Bug]: " +description: Create a bug report to help us fix it. +labels: ["☢️ Bug"] + +body: + - type: markdown + id: notice + attributes: + value: | + ### Notice + **This repository is not related to external or third-part Fiber modules. If you have a problem with them, open an issue under their repos. If you think the problem is related to Fiber, open the issue here.** + - Dont't forget you can ask your questions on our [Discord server](https://gofiber.io/discord). + - If you think Fiber doesn't have a nice feature that you think, open the issue with **✏️ Feature Request** template. + - Write your issue with clear and understandable English. + - type: textarea + id: description + attributes: + label: "Bug Description" + description: "A clear and detailed description of what the bug is." + placeholder: "Explain your problem as clear and detailed." + validations: + required: true + - type: textarea + id: how-to-reproduce + attributes: + label: How to Reproduce + description: "Steps to reproduce the behavior and what should be observed in the end." + placeholder: "Tell us step by step how we can replicate your problem and what we should see in the end." + value: | + Steps to reproduce the behavior: + 1. Go to '....' + 2. Click on '....' + 3. Do '....' + 4. See '....' + validations: + required: true + - type: textarea + id: expected-behavior + attributes: + label: Expected Behavior + description: "A clear and detailed description of what you think should happens." + placeholder: "Tell us what Fiber should normally do." + validations: + required: true + - type: input + id: version + attributes: + label: "Fiber Version" + description: "Some bugs may be fixed in future Fiber releases, so we have to know your Fiber version." + placeholder: "Write your Fiber version. (v2.33.0, v2.34.1...)" + validations: + required: true + - type: textarea + id: snippet + attributes: + label: "Code Snippet (optional)" + description: "For some issues, we need to know some parts of your code." + placeholder: "Share a code you think related to the issue." + render: go + value: | + package main + + import "github.com/gofiber/fiber/v2" + import "log" + + func main() { + app := fiber.New() + + // Steps to reproduce + + log.Fatal(app.Listen(":3000")) + } + - type: checkboxes + id: terms + attributes: + label: "Checklist:" + description: "By submitting this issue, you confirm that:" + options: + - label: "I agree to follow Fiber's [Code of Conduct](../CODE_OF_CONDUCT.md)." + required: true + - label: "I have checked for existing issues that describe my problem prior to opening this one." + required: true + - label: "I understand that improperly formatted bug reports may be closed without explanation." + required: true diff --git a/.github/ISSUE_TEMPLATE/feature-request.yaml b/.github/ISSUE_TEMPLATE/feature-request.yaml new file mode 100644 index 0000000000..0e63adc060 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.yaml @@ -0,0 +1,60 @@ +name: "\U0001F680 Feature Request" +title: "\U0001F680 [Feature]: " +description: Suggest an idea to improve this project. +labels: ["✏️ Feature"] + +body: + - type: markdown + id: notice + attributes: + value: | + ### Notice + - Dont't forget you can ask your questions on our [Discord server](https://gofiber.io/discord). + - If you think this is just a bug, open the issue with **☢️ Bug Report** template. + - Write your issue with clear and understandable English. + - type: textarea + id: description + attributes: + label: "Feature Description" + description: "A clear and detailed description of the feature we need to do." + placeholder: "Explain your feature as clear and detailed." + validations: + required: true + - type: textarea + id: additional-context + attributes: + label: "Additional Context (optional)" + description: "If you have something else to describe, write them here." + placeholder: "Write here what you can describe differently." + - type: textarea + id: snippet + attributes: + label: "Code Snippet (optional)" + description: "Code snippet may be really helpful to describe some features." + placeholder: "Share a code to explain the feature better." + render: go + value: | + package main + + import "github.com/gofiber/fiber/v2" + import "log" + + func main() { + app := fiber.New() + + // An example to describe the feature + + log.Fatal(app.Listen(":3000")) + } + - type: checkboxes + id: terms + attributes: + label: "Checklist:" + description: "By submitting this issue, you confirm that:" + options: + - label: "I agree to follow Fiber's [Code of Conduct](../CODE_OF_CONDUCT.md)." + required: true + - label: "I have checked for existing issues that describe my suggestion prior to opening this one." + required: true + - label: "I understand that improperly formatted feature requests may be closed without explanation." + required: true diff --git a/.github/ISSUE_TEMPLATE/question.yaml b/.github/ISSUE_TEMPLATE/question.yaml new file mode 100644 index 0000000000..b1cdfa694c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yaml @@ -0,0 +1,55 @@ +name: "🤔 Question" +title: "\U0001F917 [Question]: " +description: Ask a question so we can help you easily. +labels: ["🤔 Question"] + +body: + - type: markdown + id: notice + attributes: + value: | + ### Notice + - Dont't forget you can ask your questions on our [Discord server](https://gofiber.io/discord). + - If you think this is just a bug, open the issue with **☢️ Bug Report** template. + - If you think Fiber doesn't have a nice feature that you think, open the issue with **✏️ Feature Request** template. + - Write your issue with clear and understandable English. + - type: textarea + id: description + attributes: + label: "Question Description" + description: "A clear and detailed description of the question." + placeholder: "Explain your question as clear and detailed." + validations: + required: true + - type: textarea + id: snippet + attributes: + label: "Code Snippet (optional)" + description: "Code snippet may be really helpful to describe some features." + placeholder: "Share a code to explain the feature better." + render: go + value: | + package main + + import "github.com/gofiber/fiber/v2" + import "log" + + func main() { + app := fiber.New() + + // An example to describe the question + + log.Fatal(app.Listen(":3000")) + } + - type: checkboxes + id: terms + attributes: + label: "Checklist:" + description: "By submitting this issue, you confirm that:" + options: + - label: "I agree to follow Fiber's [Code of Conduct](../CODE_OF_CONDUCT.md)." + required: true + - label: "I have checked for existing issues that describe my questions prior to opening this one." + required: true + - label: "I understand that improperly formatted questions may be closed without explanation." + required: true From dac929fdee341a487057f031d7c2b1172542beca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Mon, 4 Jul 2022 08:50:37 +0300 Subject: [PATCH 008/141] :broom: client: remove beta warning (#1951) * :broom: client: remove beta warning * Stabilize the Test_Test_Timeout unittest Co-authored-by: wernerr --- app_test.go | 2 +- client.go | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/app_test.go b/app_test.go index d1a40ea684..bbc816b14e 100644 --- a/app_test.go +++ b/app_test.go @@ -1299,7 +1299,7 @@ func Test_Test_Timeout(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode, "Status code") app.Get("timeout", func(c *Ctx) error { - time.Sleep(55 * time.Millisecond) + time.Sleep(100 * time.Millisecond) return nil }) diff --git a/client.go b/client.go index e5dd104367..0974b80f24 100644 --- a/client.go +++ b/client.go @@ -738,14 +738,9 @@ func (a *Agent) RetryIf(retryIf RetryIfFunc) *Agent { } /************************** End Agent Setting **************************/ -var warnOnce sync.Once // Bytes returns the status code, bytes body and errors of url. func (a *Agent) Bytes() (code int, body []byte, errs []error) { - warnOnce.Do(func() { - fmt.Println("[Warning] client is still in beta, API might change in the future!") - }) - defer a.release() if errs = append(errs, a.errs...); len(errs) > 0 { From e58ad6b69aa8b3b0de92e0edcd5f8f4c52c23ca2 Mon Sep 17 00:00:00 2001 From: marcelogamba Date: Mon, 4 Jul 2022 04:14:57 -0300 Subject: [PATCH 009/141] TagLatency doesn't have consistence format between default and custom format (#1943) Co-authored-by: Marcelo Gamba --- middleware/logger/logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index ad0ce03dbf..6cbffa1e5d 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -229,7 +229,7 @@ func New(config ...Config) fiber.Handler { case TagUA: return buf.WriteString(c.Get(fiber.HeaderUserAgent)) case TagLatency: - return buf.WriteString(stop.Sub(start).String()) + return buf.WriteString(fmt.Sprintf("%7v", stop.Sub(start).Round(time.Millisecond))) case TagBody: return buf.Write(c.Body()) case TagBytesReceived: From 29be2338e80dcca27938e5d23b1a4e5dafff9953 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Mon, 4 Jul 2022 10:15:17 +0300 Subject: [PATCH 010/141] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20defaul?= =?UTF-8?q?t=20error=20handler=20&=20example=20(#1941)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :recycle: refactor: default error handler & example * ♻️ refactor: default error handler & example --- app.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app.go b/app.go index 63957ab3ff..7bbc2d4586 100644 --- a/app.go +++ b/app.go @@ -76,7 +76,8 @@ type Storage interface { // cfg := fiber.Config{} // cfg.ErrorHandler = func(c *Ctx, err error) error { // code := StatusInternalServerError -// if e, ok := err.(*Error); ok { +// var e *fiber.Error +// if errors.As(err, &e) { // code = e.Code // } // c.Set(HeaderContentType, MIMETextPlainCharsetUTF8) @@ -433,7 +434,8 @@ const ( // DefaultErrorHandler that process return errors from handlers var DefaultErrorHandler = func(c *Ctx, err error) error { code := StatusInternalServerError - if e, ok := err.(*Error); ok { + var e *Error + if errors.As(err, &e) { code = e.Code } c.Set(HeaderContentType, MIMETextPlainCharsetUTF8) From 744e4da3ef6f934cd0acc87a1486d6f5daecb1b8 Mon Sep 17 00:00:00 2001 From: "Majid Taheri(I love optimization)" Date: Tue, 5 Jul 2022 16:04:32 +0430 Subject: [PATCH 011/141] add some parameters to config for supporting #1936 (#1956) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add some parameters to config for supporting #1936 * remove go:embed * apply code review - 1956#issuecomment-1169811117 * lint Co-authored-by: Muhammed Efe Çetin --- middleware/monitor/README.md | 23 ++++++++++++++- middleware/monitor/config.go | 46 ++++++++++++++++++++++++------ middleware/monitor/index.go | 37 +++++++++++++++++------- middleware/monitor/monitor_test.go | 44 ++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 21 deletions(-) diff --git a/middleware/monitor/README.md b/middleware/monitor/README.md index 8ac23e4211..5987aec00b 100644 --- a/middleware/monitor/README.md +++ b/middleware/monitor/README.md @@ -53,7 +53,7 @@ type Config struct { // Optional. Default: 3 seconds Refresh time.Duration - // To disable serving HTML, you can make true this option. + // Whether the service should expose only the monitoring API. // // Optional. Default: false APIOnly bool @@ -62,6 +62,23 @@ type Config struct { // // Optional. Default: nil Next func(c *fiber.Ctx) bool + + // Custom HTML Code to Head Section(Before End) + // + // Optional. Default: empty + CustomHead string + + // FontURL for specify font resource path or URL . also you can use relative path + // + // Optional. Default: https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap + + FontURL string + // ChartJsURL for specify ChartJS library path or URL . also you can use relative path + // + // Optional. Default: https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js + + ChartJsURL string + } ``` @@ -73,5 +90,9 @@ var ConfigDefault = Config{ Refresh: 3 * time.Second, APIOnly: false, Next: nil, + CustomHead:"", + FontURL:"https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap", + ChartJsURL:"https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js" + } ``` diff --git a/middleware/monitor/config.go b/middleware/monitor/config.go index dbd3186835..a5d08b65ca 100644 --- a/middleware/monitor/config.go +++ b/middleware/monitor/config.go @@ -28,28 +28,49 @@ type Config struct { // Optional. Default: nil Next func(c *fiber.Ctx) bool - // customized indexHtml + // Custom HTML Code to Head Section(Before End) + // + // Optional. Default: empty + CustomHead string + + // FontURL for specify font resource path or URL . also you can use relative path + // + // Optional. Default: https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap + FontURL string + + // ChartJsURL for specify ChartJS library path or URL . also you can use relative path + // + // Optional. Default: https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js + ChartJsURL string + index string } var ConfigDefault = Config{ - Title: defaultTitle, - Refresh: defaultRefresh, - APIOnly: false, - Next: nil, - index: newIndex(defaultTitle, defaultRefresh), + Title: defaultTitle, + Refresh: defaultRefresh, + FontURL: defaultFontURL, + ChartJsURL: defaultChartJsURL, + CustomHead: defaultCustomHead, + APIOnly: false, + Next: nil, + index: newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, + defaultCustomHead}), } func configDefault(config ...Config) Config { // Users can change ConfigDefault.Title/Refresh which then // become incompatible with ConfigDefault.index - if ConfigDefault.Title != defaultTitle || ConfigDefault.Refresh != defaultRefresh { + if ConfigDefault.Title != defaultTitle || ConfigDefault.Refresh != defaultRefresh || + ConfigDefault.FontURL != defaultFontURL || ConfigDefault.ChartJsURL != defaultChartJsURL || + ConfigDefault.CustomHead != defaultCustomHead { if ConfigDefault.Refresh < minRefresh { ConfigDefault.Refresh = minRefresh } // update default index with new default title/refresh - ConfigDefault.index = newIndex(ConfigDefault.Title, ConfigDefault.Refresh) + ConfigDefault.index = newIndex(viewBag{ConfigDefault.Title, + ConfigDefault.Refresh, ConfigDefault.FontURL, ConfigDefault.ChartJsURL, ConfigDefault.CustomHead}) } // Return default config if nothing provided @@ -68,7 +89,13 @@ func configDefault(config ...Config) Config { if cfg.Refresh == 0 { cfg.Refresh = ConfigDefault.Refresh } + if cfg.FontURL == "" { + cfg.FontURL = defaultFontURL + } + if cfg.ChartJsURL == "" { + cfg.ChartJsURL = defaultChartJsURL + } if cfg.Title == ConfigDefault.Title && cfg.Refresh == ConfigDefault.Refresh { cfg.index = ConfigDefault.index } else { @@ -76,7 +103,8 @@ func configDefault(config ...Config) Config { cfg.Refresh = minRefresh } // update cfg.index with custom title/refresh - cfg.index = newIndex(cfg.Title, cfg.Refresh) + cfg.index = newIndex(viewBag{cfg.Title, + cfg.Refresh, cfg.FontURL, cfg.ChartJsURL, cfg.CustomHead}) } if cfg.Next == nil { diff --git a/middleware/monitor/index.go b/middleware/monitor/index.go index 4267b7c4e2..b17c95f9e4 100644 --- a/middleware/monitor/index.go +++ b/middleware/monitor/index.go @@ -6,25 +6,37 @@ import ( "time" ) +type viewBag struct { + title string + refresh time.Duration + fontUrl string + chartJsUrl string + customHead string +} + // returns index with new title/refresh -func newIndex(title string, refresh time.Duration) string { +func newIndex(dat viewBag) string { - timeout := refresh.Milliseconds() - timeoutDiff + timeout := dat.refresh.Milliseconds() - timeoutDiff if timeout < timeoutDiff { timeout = timeoutDiff } ts := strconv.FormatInt(timeout, 10) - - index := strings.ReplaceAll(indexHtml, "$TITLE", title) - return strings.ReplaceAll(index, "$TIMEOUT", ts) + replacer := strings.NewReplacer("$TITLE", dat.title, "$TIMEOUT", ts, + "$FONT_URL", dat.fontUrl, "$CHART_JS_URL", dat.chartJsUrl, "$CUSTOM_HEAD", dat.customHead, + ) + return replacer.Replace(indexHtml) } const ( defaultTitle = "Fiber Monitor" - defaultRefresh = 3 * time.Second - timeoutDiff = 200 // timeout will be Refresh (in millisconds) - timeoutDiff - minRefresh = timeoutDiff * time.Millisecond + defaultRefresh = 3 * time.Second + timeoutDiff = 200 // timeout will be Refresh (in milliseconds) - timeoutDiff + minRefresh = timeoutDiff * time.Millisecond + defaultFontURL = `https://fonts.googleapis.com/css2?family=Roboto:wght@400;900&display=swap` + defaultChartJsURL = `https://cdn.jsdelivr.net/npm/chart.js@2.9/dist/Chart.bundle.min.js` + defaultCustomHead = `` // parametrized by $TITLE and $TIMEOUT indexHtml = ` @@ -32,8 +44,9 @@ const ( - - + + + $TITLE @@ -253,5 +267,6 @@ const ( fetchJSON() -` + +` ) diff --git a/middleware/monitor/monitor_test.go b/middleware/monitor/monitor_test.go index 6fd4d654c6..7b73e295ed 100644 --- a/middleware/monitor/monitor_test.go +++ b/middleware/monitor/monitor_test.go @@ -61,6 +61,50 @@ func Test_Monitor_Html(t *testing.T) { conf.Refresh.Milliseconds()-timeoutDiff) utils.AssertEqual(t, true, bytes.Contains(buf, []byte(timeoutLine))) } +func Test_Monitor_Html_CustomCodes(t *testing.T) { + t.Parallel() + + app := fiber.New() + + // defaults + app.Get("/", New()) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + utils.AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, + resp.Header.Get(fiber.HeaderContentType)) + buf, err := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, true, bytes.Contains(buf, []byte(""+defaultTitle+""))) + timeoutLine := fmt.Sprintf("setTimeout(fetchJSON, %d)", + defaultRefresh.Milliseconds()-timeoutDiff) + utils.AssertEqual(t, true, bytes.Contains(buf, []byte(timeoutLine))) + + // custom config + conf := Config{Title: "New " + defaultTitle, Refresh: defaultRefresh + time.Second, + ChartJsURL: "https://cdnjs.com/libraries/Chart.js", + FontURL: "/public/my-font.css", + CustomHead: ``, + } + app.Get("/custom", New(conf)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/custom", nil)) + + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + utils.AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, + resp.Header.Get(fiber.HeaderContentType)) + buf, err = ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, true, bytes.Contains(buf, []byte(""+conf.Title+""))) + utils.AssertEqual(t, true, bytes.Contains(buf, []byte("https://cdnjs.com/libraries/Chart.js"))) + utils.AssertEqual(t, true, bytes.Contains(buf, []byte("/public/my-font.css"))) + utils.AssertEqual(t, true, bytes.Contains(buf, []byte(conf.CustomHead))) + + timeoutLine = fmt.Sprintf("setTimeout(fetchJSON, %d)", + conf.Refresh.Milliseconds()-timeoutDiff) + utils.AssertEqual(t, true, bytes.Contains(buf, []byte(timeoutLine))) +} // go test -run Test_Monitor_JSON -race func Test_Monitor_JSON(t *testing.T) { From dfa24a09587731cdb3ee529b09df956dbd12e13f Mon Sep 17 00:00:00 2001 From: olongfen <824426699@qq.com> Date: Tue, 5 Jul 2022 19:45:11 +0800 Subject: [PATCH 012/141] add: params parse (#1964) --- ctx.go | 10 ++++++++++ ctx_test.go | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/ctx.go b/ctx.go index 81c62190c9..2487eb4214 100644 --- a/ctx.go +++ b/ctx.go @@ -39,6 +39,7 @@ const ( queryTag = "query" reqHeaderTag = "reqHeader" bodyTag = "form" + uriTag = "uri" ) // userContextKey define the key name for storing context.Context in *fasthttp.RequestCtx @@ -854,6 +855,15 @@ func (c *Ctx) AllParams() map[string]string { return params } +// ParamsParser binds the param string to a struct. +func (c *Ctx) ParamsParser(out interface{}) error { + params := make(map[string][]string, len(c.route.Params)) + for _, param := range c.route.Params { + params[param] = append(params[param], c.Params(param)) + } + return c.parseToStruct(uriTag, out, params) +} + // ParamsInt is used to get an integer from the route parameters // it defaults to zero if the parameter is not found or if the // parameter cannot be converted to an integer diff --git a/ctx_test.go b/ctx_test.go index e593af531d..9f092327b5 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -426,6 +426,29 @@ func Test_Ctx_BodyParser(t *testing.T) { utils.AssertEqual(t, "doe", cq.Data[1].Name) } +func Test_Ctx_ParamParser(t *testing.T) { + t.Parallel() + app := New() + app.Get("/test1/userId/role/:roleId", func(ctx *Ctx) error { + type Demo struct { + UserID uint `uri:"userId"` + RoleID uint `uri:"roleId"` + } + var ( + d = new(Demo) + ) + if err := ctx.ParamsParser(d); err != nil { + t.Fatal(err) + } + utils.AssertEqual(t, uint(111), d.UserID) + utils.AssertEqual(t, uint(222), d.RoleID) + return nil + }) + app.Test(httptest.NewRequest(MethodGet, "/test1/111/role/222", nil)) + app.Test(httptest.NewRequest(MethodGet, "/test2/111/role/222", nil)) + +} + // go test -run Test_Ctx_BodyParser_WithSetParserDecoder func Test_Ctx_BodyParser_WithSetParserDecoder(t *testing.T) { type CustomTime time.Time @@ -1433,6 +1456,36 @@ func Benchmark_Ctx_AllParams(b *testing.B) { res) } +// go test -v -run=^$ -bench=Benchmark_Ctx_ParamsParse -benchmem -count=4 +func Benchmark_Ctx_ParamsParse(b *testing.B) { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.route = &Route{ + Params: []string{ + "param1", "param2", "param3", "param4", + }, + } + c.values = [maxParams]string{ + "john", "doe", "is", "awesome", + } + var res struct { + Param1 string `uri:"param1"` + Param2 string `uri:"param2"` + Param3 string `uri:"param3"` + Param4 string `uri:"param4"` + } + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + c.ParamsParser(&res) + } + utils.AssertEqual(b, "john", res.Param1) + utils.AssertEqual(b, "doe", res.Param2) + utils.AssertEqual(b, "is", res.Param3) + utils.AssertEqual(b, "awesome", res.Param4) +} + // go test -run Test_Ctx_Path func Test_Ctx_Path(t *testing.T) { t.Parallel() @@ -2896,7 +2949,7 @@ func Test_Ctx_SendStream(t *testing.T) { file, err := os.Open("./.github/index.html") utils.AssertEqual(t, nil, err) c.SendStream(bufio.NewReader(file)) - utils.AssertEqual(t, true, (c.Response().Header.ContentLength() > 200)) + utils.AssertEqual(t, true, c.Response().Header.ContentLength() > 200) } // go test -run Test_Ctx_Set From bad700157046e4409b1b8e4e46a78e51f828a839 Mon Sep 17 00:00:00 2001 From: olongfen Date: Wed, 6 Jul 2022 18:42:54 +0800 Subject: [PATCH 013/141] binds the param string to a struct use params tag (#1968) * add: params parse * fix: binds the param string to a struct use params tag --- ctx.go | 4 ++-- ctx_test.go | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ctx.go b/ctx.go index 2487eb4214..004cf3ec7a 100644 --- a/ctx.go +++ b/ctx.go @@ -39,7 +39,7 @@ const ( queryTag = "query" reqHeaderTag = "reqHeader" bodyTag = "form" - uriTag = "uri" + paramsTag = "params" ) // userContextKey define the key name for storing context.Context in *fasthttp.RequestCtx @@ -861,7 +861,7 @@ func (c *Ctx) ParamsParser(out interface{}) error { for _, param := range c.route.Params { params[param] = append(params[param], c.Params(param)) } - return c.parseToStruct(uriTag, out, params) + return c.parseToStruct(paramsTag, out, params) } // ParamsInt is used to get an integer from the route parameters diff --git a/ctx_test.go b/ctx_test.go index 9f092327b5..325b67d78b 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -431,8 +431,8 @@ func Test_Ctx_ParamParser(t *testing.T) { app := New() app.Get("/test1/userId/role/:roleId", func(ctx *Ctx) error { type Demo struct { - UserID uint `uri:"userId"` - RoleID uint `uri:"roleId"` + UserID uint `params:"userId"` + RoleID uint `params:"roleId"` } var ( d = new(Demo) @@ -1470,10 +1470,10 @@ func Benchmark_Ctx_ParamsParse(b *testing.B) { "john", "doe", "is", "awesome", } var res struct { - Param1 string `uri:"param1"` - Param2 string `uri:"param2"` - Param3 string `uri:"param3"` - Param4 string `uri:"param4"` + Param1 string `params:"param1"` + Param2 string `params:"param2"` + Param3 string `params:"param3"` + Param4 string `params:"param4"` } b.ReportAllocs() b.ResetTimer() From 2a2337d83e2ba07879a514299570432765589185 Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 6 Jul 2022 12:51:08 +0200 Subject: [PATCH 014/141] prepare release v2.35.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 7bbc2d4586..fd9a313870 100644 --- a/app.go +++ b/app.go @@ -39,7 +39,7 @@ import ( ) // Version of current fiber package -const Version = "2.34.1" +const Version = "2.35.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From ad89ba4e1c42bb43287ab3e5faacf746b74c396a Mon Sep 17 00:00:00 2001 From: Raden Mohamad Rishwan <103234055+radenrishwan@users.noreply.github.com> Date: Thu, 21 Jul 2022 21:46:01 +0700 Subject: [PATCH 015/141] fix README_it.md (#1980) --- .github/README_id.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README_id.md b/.github/README_id.md index 3e0d9b63b3..e43d0e7491 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -607,7 +607,7 @@ Kumpulan `middleware` yang dihost external dan diurus oleh [Tim Fiber](https://g ## 🕶️ Awesome List -For more articles, middlewares, examples or tools check our [awesome list](https://github.com/gofiber/awesome-fiber). +Untuk artikel lainnya, middlewares, contoh atau tools check kami [awesome list](https://github.com/gofiber/awesome-fiber). ## 👍 Berkontribusi From ff1e0109a3be687f10661f3daf2bb462399f0e81 Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 27 Jul 2022 08:37:03 +0200 Subject: [PATCH 016/141] =?UTF-8?q?Closes=20#1931=20"=F0=9F=A4=97=20How=20?= =?UTF-8?q?to=20get=20path=20param=20before=20a=20custom=20verb=3F"=20(#19?= =?UTF-8?q?83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add possibility for parameters before custom verb FIX for "🤗 How to get path param before a custom verb? #1931" * try to stabilize the tests --- middleware/proxy/proxy_test.go | 2 +- path.go | 2 +- path_test.go | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index 4e2215c596..906e54669b 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -124,7 +124,7 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) { // go test -run Test_Proxy_Forward_WithTlsConfig_To_Http func Test_Proxy_Forward_WithTlsConfig_To_Http(t *testing.T) { - t.Parallel() + //t.Parallel() _, targetAddr := createProxyTestServer(func(c *fiber.Ctx) error { return c.SendString("hello from target") diff --git a/path.go b/path.go index 2f70c7f742..21a3d6c4d0 100644 --- a/path.go +++ b/path.go @@ -58,7 +58,7 @@ var ( // list of chars for the parameter recognising parameterStartChars = []byte{wildcardParam, plusParam, paramStarterChar} // list of chars of delimiters and the starting parameter name char - parameterDelimiterChars = append([]byte{paramStarterChar}, routeDelimiter...) + parameterDelimiterChars = append([]byte{paramStarterChar, escapeChar}, routeDelimiter...) // list of chars to find the end of a parameter parameterEndChars = append([]byte{optionalParam}, parameterDelimiterChars...) ) diff --git a/path_test.go b/path_test.go index 246cd58f5d..9a237520dd 100644 --- a/path_test.go +++ b/path_test.go @@ -47,6 +47,17 @@ func Test_Path_parseRoute(t *testing.T) { }, params: nil, }, rp) + + rp = parseRoute("/v1/some/resource/:name\\:customVerb") + utils.AssertEqual(t, routeParser{ + segs: []*routeSegment{ + {Const: "/v1/some/resource/", Length: 18}, + {IsParam: true, ParamName: "name", ComparePart: ":customVerb", PartCount: 1}, + {Const: ":customVerb", Length: 11, IsLast: true}, + }, + params: []string{"name"}, + }, rp) + // heavy test with escaped charaters rp = parseRoute("/v1/some/resource/name\\\\:customVerb?\\?/:param/*") utils.AssertEqual(t, routeParser{ @@ -170,6 +181,10 @@ func Test_Path_matchParams(t *testing.T) { {url: "/v1/some/resource/name:customVerb", params: nil, match: true}, {url: "/v1/some/resource/name:test", params: nil, match: false}, }) + testCase("/v1/some/resource/:name\\:customVerb", []testparams{ + {url: "/v1/some/resource/test:customVerb", params: []string{"test"}, match: true}, + {url: "/v1/some/resource/test:test", params: nil, match: false}, + }) testCase("/v1/some/resource/name\\\\:customVerb?\\?/:param/*", []testparams{ {url: "/v1/some/resource/name:customVerb??/test/optionalWildCard/character", params: []string{"test", "optionalWildCard/character"}, match: true}, {url: "/v1/some/resource/name:customVerb??/test", params: []string{"test", ""}, match: true}, From 4103f9463dce7fe658a4eaf360142426b9070615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Mon, 1 Aug 2022 09:24:37 +0300 Subject: [PATCH 017/141] :sparkles: feature: customizable colors (#1977) * :sparkles: feature: customizable colors * :sparkles: feature: customizable colors * :sparkles: feature: customizable colors --- app.go | 425 +------------------------------ app_test.go | 164 ------------ color.go | 99 +++++++ listen.go | 416 ++++++++++++++++++++++++++++++ listen_test.go | 181 +++++++++++++ middleware/logger/logger.go | 44 ++-- middleware/logger/logger_test.go | 5 +- middleware/logger/utils.go | 28 +- 8 files changed, 739 insertions(+), 623 deletions(-) create mode 100644 color.go create mode 100644 listen.go create mode 100644 listen_test.go diff --git a/app.go b/app.go index fd9a313870..f3b488891a 100644 --- a/app.go +++ b/app.go @@ -10,30 +10,20 @@ package fiber import ( "bufio" "bytes" - "crypto/tls" - "crypto/x509" "errors" "fmt" - "io/ioutil" "net" "net/http" "net/http/httputil" - "os" - "path/filepath" "reflect" - "runtime" - "sort" "strconv" "strings" "sync" "sync/atomic" - "text/tabwriter" "time" "encoding/json" - "github.com/gofiber/fiber/v2/internal/colorable" - "github.com/gofiber/fiber/v2/internal/isatty" "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" ) @@ -371,6 +361,11 @@ type Config struct { // If set to true, will print all routes with their method, path and handler. // Default: false EnablePrintRoutes bool `json:"enable_print_routes"` + + // You can define custom color scheme. They'll be used for startup message, route list and some middlewares. + // + // Optional. Default: DefaultColors + ColorScheme Colors `json:"color_scheme"` } // Static defines configuration options when defining static assets. @@ -523,6 +518,9 @@ func New(config ...Config) *App { app.handleTrustedProxy(ipAddress) } + // Override colors + app.config.ColorScheme = defaultColors(app.config.ColorScheme) + // Init appList app.appList[""] = app @@ -758,154 +756,6 @@ func NewError(code int, message ...string) *Error { return err } -// Listener can be used to pass a custom listener. -func (app *App) Listener(ln net.Listener) error { - // Prefork is supported for custom listeners - if app.config.Prefork { - addr, tlsConfig := lnMetadata(app.config.Network, ln) - return app.prefork(app.config.Network, addr, tlsConfig) - } - // prepare the server for the start - app.startupProcess() - // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), getTlsConfig(ln) != nil, "") - } - // Print routes - if app.config.EnablePrintRoutes { - app.printRoutesMessage() - } - // Start listening - return app.server.Serve(ln) -} - -// Listen serves HTTP requests from the given addr. -// -// app.Listen(":8080") -// app.Listen("127.0.0.1:8080") -func (app *App) Listen(addr string) error { - // Start prefork - if app.config.Prefork { - return app.prefork(app.config.Network, addr, nil) - } - // Setup listener - ln, err := net.Listen(app.config.Network, addr) - if err != nil { - return err - } - // prepare the server for the start - app.startupProcess() - // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), false, "") - } - // Print routes - if app.config.EnablePrintRoutes { - app.printRoutesMessage() - } - // Start listening - return app.server.Serve(ln) -} - -// ListenTLS serves HTTPS requests from the given addr. -// certFile and keyFile are the paths to TLS certificate and key file: -// app.ListenTLS(":8080", "./cert.pem", "./cert.key") -func (app *App) ListenTLS(addr, certFile, keyFile string) error { - // Check for valid cert/key path - if len(certFile) == 0 || len(keyFile) == 0 { - return errors.New("tls: provide a valid cert or key path") - } - // Prefork is supported - if app.config.Prefork { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) - } - config := &tls.Config{ - MinVersion: tls.VersionTLS12, - Certificates: []tls.Certificate{ - cert, - }, - } - return app.prefork(app.config.Network, addr, config) - } - // Setup listener - ln, err := net.Listen(app.config.Network, addr) - if err != nil { - return err - } - // prepare the server for the start - app.startupProcess() - // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), true, "") - } - // Print routes - if app.config.EnablePrintRoutes { - app.printRoutesMessage() - } - // Start listening - return app.server.ServeTLS(ln, certFile, keyFile) -} - -// ListenMutualTLS serves HTTPS requests from the given addr. -// certFile, keyFile and clientCertFile are the paths to TLS certificate and key file: -// app.ListenMutualTLS(":8080", "./cert.pem", "./cert.key", "./client.pem") -func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) error { - // Check for valid cert/key path - if len(certFile) == 0 || len(keyFile) == 0 { - return errors.New("tls: provide a valid cert or key path") - } - - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) - } - - clientCACert, err := ioutil.ReadFile(filepath.Clean(clientCertFile)) - if err != nil { - return err - } - clientCertPool := x509.NewCertPool() - clientCertPool.AppendCertsFromPEM(clientCACert) - - config := &tls.Config{ - MinVersion: tls.VersionTLS12, - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: clientCertPool, - Certificates: []tls.Certificate{ - cert, - }, - } - - // Prefork is supported - if app.config.Prefork { - return app.prefork(app.config.Network, addr, config) - } - - // Setup listener - ln, err := tls.Listen(app.config.Network, addr, config) - if err != nil { - return err - } - - // prepare the server for the start - app.startupProcess() - - // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), true, "") - } - - // Print routes - if app.config.EnablePrintRoutes { - app.printRoutesMessage() - } - - // Start listening - return app.server.Serve(ln) -} - // Config returns the app config as value ( read-only ). func (app *App) Config() Config { return app.config @@ -1137,262 +987,3 @@ func (app *App) startupProcess() *App { app.mutex.Unlock() return app } - -// startupMessage prepares the startup message with the handler number, port, address and other information -func (app *App) startupMessage(addr string, tls bool, pids string) { - // ignore child processes - if IsChild() { - return - } - - const ( - cBlack = "\u001b[90m" - // cRed = "\u001b[91m" - cCyan = "\u001b[96m" - // cGreen = "\u001b[92m" - // cYellow = "\u001b[93m" - // cBlue = "\u001b[94m" - // cMagenta = "\u001b[95m" - // cWhite = "\u001b[97m" - cReset = "\u001b[0m" - ) - - value := func(s string, width int) string { - pad := width - len(s) - str := "" - for i := 0; i < pad; i++ { - str += "." - } - if s == "Disabled" { - str += " " + s - } else { - str += fmt.Sprintf(" %s%s%s", cCyan, s, cBlack) - } - return str - } - - center := func(s string, width int) string { - pad := strconv.Itoa((width - len(s)) / 2) - str := fmt.Sprintf("%"+pad+"s", " ") - str += s - str += fmt.Sprintf("%"+pad+"s", " ") - if len(str) < width { - str += " " - } - return str - } - - centerValue := func(s string, width int) string { - pad := strconv.Itoa((width - len(s)) / 2) - str := fmt.Sprintf("%"+pad+"s", " ") - str += fmt.Sprintf("%s%s%s", cCyan, s, cBlack) - str += fmt.Sprintf("%"+pad+"s", " ") - if len(str)-10 < width { - str += " " - } - return str - } - - pad := func(s string, width int) (str string) { - toAdd := width - len(s) - str += s - for i := 0; i < toAdd; i++ { - str += " " - } - return - } - - host, port := parseAddr(addr) - if host == "" { - if app.config.Network == NetworkTCP6 { - host = "[::1]" - } else { - host = "0.0.0.0" - } - } - - scheme := "http" - if tls { - scheme = "https" - } - - isPrefork := "Disabled" - if app.config.Prefork { - isPrefork = "Enabled" - } - - procs := strconv.Itoa(runtime.GOMAXPROCS(0)) - if !app.config.Prefork { - procs = "1" - } - - mainLogo := cBlack + " ┌───────────────────────────────────────────────────┐\n" - if app.config.AppName != "" { - mainLogo += " │ " + centerValue(app.config.AppName, 49) + " │\n" - } - mainLogo += " │ " + centerValue(" Fiber v"+Version, 49) + " │\n" - - if host == "0.0.0.0" { - mainLogo += - " │ " + center(fmt.Sprintf("%s://127.0.0.1:%s", scheme, port), 49) + " │\n" + - " │ " + center(fmt.Sprintf("(bound on host 0.0.0.0 and port %s)", port), 49) + " │\n" - } else { - mainLogo += - " │ " + center(fmt.Sprintf("%s://%s:%s", scheme, host, port), 49) + " │\n" - } - - mainLogo += fmt.Sprintf( - " │ │\n"+ - " │ Handlers %s Processes %s │\n"+ - " │ Prefork .%s PID ....%s │\n"+ - " └───────────────────────────────────────────────────┘"+ - cReset, - value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12), - value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14), - ) - - var childPidsLogo string - if app.config.Prefork { - var childPidsTemplate string - childPidsTemplate += "%s" - childPidsTemplate += " ┌───────────────────────────────────────────────────┐\n%s" - childPidsTemplate += " └───────────────────────────────────────────────────┘" - childPidsTemplate += "%s" - - newLine := " │ %s%s%s │" - - // Turn the `pids` variable (in the form ",a,b,c,d,e,f,etc") into a slice of PIDs - var pidSlice []string - for _, v := range strings.Split(pids, ",") { - if v != "" { - pidSlice = append(pidSlice, v) - } - } - - var lines []string - thisLine := "Child PIDs ... " - var itemsOnThisLine []string - - addLine := func() { - lines = append(lines, - fmt.Sprintf( - newLine, - cBlack, - thisLine+cCyan+pad(strings.Join(itemsOnThisLine, ", "), 49-len(thisLine)), - cBlack, - ), - ) - } - - for _, pid := range pidSlice { - if len(thisLine+strings.Join(append(itemsOnThisLine, pid), ", ")) > 49 { - addLine() - thisLine = "" - itemsOnThisLine = []string{pid} - } else { - itemsOnThisLine = append(itemsOnThisLine, pid) - } - } - - // Add left over items to their own line - if len(itemsOnThisLine) != 0 { - addLine() - } - - // Form logo - childPidsLogo = fmt.Sprintf(childPidsTemplate, - cBlack, - strings.Join(lines, "\n")+"\n", - cReset, - ) - } - - // Combine both the child PID logo and the main Fiber logo - - // Pad the shorter logo to the length of the longer one - splitMainLogo := strings.Split(mainLogo, "\n") - splitChildPidsLogo := strings.Split(childPidsLogo, "\n") - - mainLen := len(splitMainLogo) - childLen := len(splitChildPidsLogo) - - if mainLen > childLen { - diff := mainLen - childLen - for i := 0; i < diff; i++ { - splitChildPidsLogo = append(splitChildPidsLogo, "") - } - } else { - diff := childLen - mainLen - for i := 0; i < diff; i++ { - splitMainLogo = append(splitMainLogo, "") - } - } - - // Combine the two logos, line by line - output := "\n" - for i := range splitMainLogo { - output += cBlack + splitMainLogo[i] + " " + splitChildPidsLogo[i] + "\n" - } - - out := colorable.NewColorableStdout() - if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) { - out = colorable.NewNonColorable(os.Stdout) - } - - _, _ = fmt.Fprintln(out, output) -} - -// printRoutesMessage print all routes with method, path, name and handlers -// in a format of table, like this: -// method | path | name | handlers -// GET | / | routeName | github.com/gofiber/fiber/v2.emptyHandler -// HEAD | / | | github.com/gofiber/fiber/v2.emptyHandler -func (app *App) printRoutesMessage() { - // ignore child processes - if IsChild() { - return - } - - const ( - // cBlack = "\u001b[90m" - // cRed = "\u001b[91m" - cCyan = "\u001b[96m" - cGreen = "\u001b[92m" - cYellow = "\u001b[93m" - cBlue = "\u001b[94m" - // cMagenta = "\u001b[95m" - cWhite = "\u001b[97m" - // cReset = "\u001b[0m" - ) - var routes []RouteMessage - for _, routeStack := range app.stack { - for _, route := range routeStack { - var newRoute = RouteMessage{} - newRoute.name = route.Name - newRoute.method = route.Method - newRoute.path = route.Path - for _, handler := range route.Handlers { - newRoute.handlers += runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name() + " " - } - routes = append(routes, newRoute) - } - } - - out := colorable.NewColorableStdout() - if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) { - out = colorable.NewNonColorable(os.Stdout) - } - - w := tabwriter.NewWriter(out, 1, 1, 1, ' ', 0) - // Sort routes by path - sort.Slice(routes, func(i, j int) bool { - return routes[i].path < routes[j].path - }) - _, _ = fmt.Fprintf(w, "%smethod\t%s| %spath\t%s| %sname\t%s| %shandlers\n", cBlue, cWhite, cGreen, cWhite, cCyan, cWhite, cYellow) - _, _ = fmt.Fprintf(w, "%s------\t%s| %s----\t%s| %s----\t%s| %s--------\n", cBlue, cWhite, cGreen, cWhite, cCyan, cWhite, cYellow) - for _, route := range routes { - _, _ = fmt.Fprintf(w, "%s%s\t%s| %s%s\t%s| %s%s\t%s| %s%s\n", cBlue, route.method, cWhite, cGreen, route.path, cWhite, cCyan, route.name, cWhite, cYellow, route.handlers) - } - - _ = w.Flush() -} diff --git a/app_test.go b/app_test.go index bbc816b14e..f64c32fbc4 100644 --- a/app_test.go +++ b/app_test.go @@ -6,7 +6,6 @@ package fiber import ( "bytes" - "crypto/tls" "errors" "fmt" "io" @@ -26,7 +25,6 @@ import ( "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" - "github.com/valyala/fasthttp/fasthttputil" ) var testEmptyHandler = func(c *Ctx) error { @@ -1122,131 +1120,6 @@ func Test_App_Next_Method(t *testing.T) { utils.AssertEqual(t, 404, resp.StatusCode, "Status code") } -// go test -run Test_App_Listen -func Test_App_Listen(t *testing.T) { - app := New(Config{DisableStartupMessage: true}) - - utils.AssertEqual(t, false, app.Listen(":99999") == nil) - - go func() { - time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) - }() - - utils.AssertEqual(t, nil, app.Listen(":4003")) -} - -// go test -run Test_App_Listen_Prefork -func Test_App_Listen_Prefork(t *testing.T) { - testPreforkMaster = true - - app := New(Config{DisableStartupMessage: true, Prefork: true}) - - utils.AssertEqual(t, nil, app.Listen(":99999")) -} - -// go test -run Test_App_ListenTLS -func Test_App_ListenTLS(t *testing.T) { - app := New() - - // invalid port - utils.AssertEqual(t, false, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") == nil) - // missing perm/cert file - utils.AssertEqual(t, false, app.ListenTLS(":0", "", "./.github/testdata/ssl.key") == nil) - - go func() { - time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) - }() - - utils.AssertEqual(t, nil, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) -} - -// go test -run Test_App_ListenTLS_Prefork -func Test_App_ListenTLS_Prefork(t *testing.T) { - testPreforkMaster = true - - app := New(Config{DisableStartupMessage: true, Prefork: true}) - - // invalid key file content - utils.AssertEqual(t, false, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.tmpl") == nil) - - utils.AssertEqual(t, nil, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) -} - -// go test -run Test_App_ListenMutualTLS -func Test_App_ListenMutualTLS(t *testing.T) { - app := New() - - // invalid port - utils.AssertEqual(t, false, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem") == nil) - // missing perm/cert file - utils.AssertEqual(t, false, app.ListenMutualTLS(":0", "", "./.github/testdata/ssl.key", "") == nil) - - go func() { - time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) - }() - - utils.AssertEqual(t, nil, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) -} - -// go test -run Test_App_ListenMutualTLS_Prefork -func Test_App_ListenMutualTLS_Prefork(t *testing.T) { - testPreforkMaster = true - - app := New(Config{DisableStartupMessage: true, Prefork: true}) - - // invalid key file content - utils.AssertEqual(t, false, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.html", "") == nil) - - utils.AssertEqual(t, nil, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) -} - -// go test -run Test_App_Listener -func Test_App_Listener(t *testing.T) { - app := New() - - go func() { - time.Sleep(500 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) - }() - - ln := fasthttputil.NewInmemoryListener() - utils.AssertEqual(t, nil, app.Listener(ln)) -} - -// go test -run Test_App_Listener_Prefork -func Test_App_Listener_Prefork(t *testing.T) { - testPreforkMaster = true - - app := New(Config{DisableStartupMessage: true, Prefork: true}) - - ln := fasthttputil.NewInmemoryListener() - utils.AssertEqual(t, nil, app.Listener(ln)) -} - -func Test_App_Listener_TLS_Listener(t *testing.T) { - // Create tls certificate - cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") - if err != nil { - utils.AssertEqual(t, nil, err) - } - config := &tls.Config{Certificates: []tls.Certificate{cer}} - - ln, err := tls.Listen(NetworkTCP4, ":0", config) - utils.AssertEqual(t, nil, err) - - app := New() - - go func() { - time.Sleep(time.Millisecond * 500) - utils.AssertEqual(t, nil, app.Shutdown()) - }() - - utils.AssertEqual(t, nil, app.Listener(ln)) -} - // go test -v -run=^$ -bench=Benchmark_AcquireCtx -benchmem -count=4 func Benchmark_AcquireCtx(b *testing.B) { app := New() @@ -1710,40 +1583,3 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") utils.AssertEqual(t, "hi, i'm a custom sub sub fiber error", string(b), "Third fiber Response body") } - -func emptyHandler(c *Ctx) error { - return nil -} -func Test_App_print_Route(t *testing.T) { - app := New(Config{EnablePrintRoutes: true}) - app.Get("/", emptyHandler).Name("routeName") - printRoutesMessage := captureOutput(func() { - app.printRoutesMessage() - }) - fmt.Println(printRoutesMessage) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "routeName")) -} - -func Test_App_print_Route_with_group(t *testing.T) { - app := New(Config{EnablePrintRoutes: true}) - app.Get("/", emptyHandler) - v1 := app.Group("v1") - v1.Get("/test", emptyHandler).Name("v1") - v1.Post("/test/fiber", emptyHandler) - v1.Put("/test/fiber/*", emptyHandler) - printRoutesMessage := captureOutput(func() { - app.printRoutesMessage() - }) - fmt.Println(printRoutesMessage) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "POST")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "PUT")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber/*")) -} diff --git a/color.go b/color.go new file mode 100644 index 0000000000..f982b379de --- /dev/null +++ b/color.go @@ -0,0 +1,99 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package fiber + +// Colors is a struct to define custom colors for Fiber app and middlewares. +type Colors struct { + // Black color. + // + // Optional. Default: "\u001b[90m" + Black string + + // Red color. + // + // Optional. Default: "\u001b[91m" + Red string + + // Green color. + // + // Optional. Default: "\u001b[92m" + Green string + + // Yellow color. + // + // Optional. Default: "\u001b[93m" + Yellow string + + // Blue color. + // + // Optional. Default: "\u001b[94m" + Blue string + + // Magenta color. + // + // Optional. Default: "\u001b[95m" + Magenta string + + // Cyan color. + // + // Optional. Default: "\u001b[96m" + Cyan string + + // White color. + // + // Optional. Default: "\u001b[97m" + White string + + // Reset color. + // + // Optional. Default: "\u001b[0m" + Reset string +} + +// Default color codes +var DefaultColors = Colors{ + Black: "\u001b[90m", + Red: "\u001b[91m", + Green: "\u001b[92m", + Yellow: "\u001b[93m", + Blue: "\u001b[94m", + Magenta: "\u001b[95m", + Cyan: "\u001b[96m", + White: "\u001b[97m", + Reset: "\u001b[0m", +} + +// defaultColors is a function to override default colors to config +func defaultColors(colors Colors) Colors { + if colors.Red == "" { + colors.Red = DefaultColors.Red + } + + if colors.Green == "" { + colors.Green = DefaultColors.Green + } + + if colors.Yellow == "" { + colors.Yellow = DefaultColors.Yellow + } + + if colors.Blue == "" { + colors.Blue = DefaultColors.Blue + } + + if colors.Magenta == "" { + colors.Magenta = DefaultColors.Magenta + } + + if colors.Cyan == "" { + colors.Cyan = DefaultColors.Cyan + } + + if colors.Reset == "" { + colors.Reset = DefaultColors.Reset + } + + return colors +} diff --git a/listen.go b/listen.go new file mode 100644 index 0000000000..8f92007b91 --- /dev/null +++ b/listen.go @@ -0,0 +1,416 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package fiber + +import ( + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io/ioutil" + "net" + "os" + "path/filepath" + "reflect" + "runtime" + "sort" + "strconv" + "strings" + "text/tabwriter" + + "github.com/gofiber/fiber/v2/internal/colorable" + "github.com/gofiber/fiber/v2/internal/isatty" +) + +// Listener can be used to pass a custom listener. +func (app *App) Listener(ln net.Listener) error { + // Prefork is supported for custom listeners + if app.config.Prefork { + addr, tlsConfig := lnMetadata(app.config.Network, ln) + return app.prefork(app.config.Network, addr, tlsConfig) + } + // prepare the server for the start + app.startupProcess() + // Print startup message + if !app.config.DisableStartupMessage { + app.startupMessage(ln.Addr().String(), getTlsConfig(ln) != nil, "") + } + // Print routes + if app.config.EnablePrintRoutes { + app.printRoutesMessage() + } + // Start listening + return app.server.Serve(ln) +} + +// Listen serves HTTP requests from the given addr. +// +// app.Listen(":8080") +// app.Listen("127.0.0.1:8080") +func (app *App) Listen(addr string) error { + // Start prefork + if app.config.Prefork { + return app.prefork(app.config.Network, addr, nil) + } + // Setup listener + ln, err := net.Listen(app.config.Network, addr) + if err != nil { + return err + } + // prepare the server for the start + app.startupProcess() + // Print startup message + if !app.config.DisableStartupMessage { + app.startupMessage(ln.Addr().String(), false, "") + } + // Print routes + if app.config.EnablePrintRoutes { + app.printRoutesMessage() + } + // Start listening + return app.server.Serve(ln) +} + +// ListenTLS serves HTTPS requests from the given addr. +// certFile and keyFile are the paths to TLS certificate and key file: +// app.ListenTLS(":8080", "./cert.pem", "./cert.key") +func (app *App) ListenTLS(addr, certFile, keyFile string) error { + // Check for valid cert/key path + if len(certFile) == 0 || len(keyFile) == 0 { + return errors.New("tls: provide a valid cert or key path") + } + // Prefork is supported + if app.config.Prefork { + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) + } + config := &tls.Config{ + MinVersion: tls.VersionTLS12, + Certificates: []tls.Certificate{ + cert, + }, + } + return app.prefork(app.config.Network, addr, config) + } + // Setup listener + ln, err := net.Listen(app.config.Network, addr) + if err != nil { + return err + } + // prepare the server for the start + app.startupProcess() + // Print startup message + if !app.config.DisableStartupMessage { + app.startupMessage(ln.Addr().String(), true, "") + } + // Print routes + if app.config.EnablePrintRoutes { + app.printRoutesMessage() + } + // Start listening + return app.server.ServeTLS(ln, certFile, keyFile) +} + +// ListenMutualTLS serves HTTPS requests from the given addr. +// certFile, keyFile and clientCertFile are the paths to TLS certificate and key file: +// app.ListenMutualTLS(":8080", "./cert.pem", "./cert.key", "./client.pem") +func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) error { + // Check for valid cert/key path + if len(certFile) == 0 || len(keyFile) == 0 { + return errors.New("tls: provide a valid cert or key path") + } + + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) + } + + clientCACert, err := ioutil.ReadFile(filepath.Clean(clientCertFile)) + if err != nil { + return err + } + clientCertPool := x509.NewCertPool() + clientCertPool.AppendCertsFromPEM(clientCACert) + + config := &tls.Config{ + MinVersion: tls.VersionTLS12, + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: clientCertPool, + Certificates: []tls.Certificate{ + cert, + }, + } + + // Prefork is supported + if app.config.Prefork { + return app.prefork(app.config.Network, addr, config) + } + + // Setup listener + ln, err := tls.Listen(app.config.Network, addr, config) + if err != nil { + return err + } + + // prepare the server for the start + app.startupProcess() + + // Print startup message + if !app.config.DisableStartupMessage { + app.startupMessage(ln.Addr().String(), true, "") + } + + // Print routes + if app.config.EnablePrintRoutes { + app.printRoutesMessage() + } + + // Start listening + return app.server.Serve(ln) +} + +// startupMessage prepares the startup message with the handler number, port, address and other information +func (app *App) startupMessage(addr string, tls bool, pids string) { + // ignore child processes + if IsChild() { + return + } + + // Alias colors + colors := app.config.ColorScheme + + value := func(s string, width int) string { + pad := width - len(s) + str := "" + for i := 0; i < pad; i++ { + str += "." + } + if s == "Disabled" { + str += " " + s + } else { + str += fmt.Sprintf(" %s%s%s", colors.Cyan, s, colors.Black) + } + return str + } + + center := func(s string, width int) string { + pad := strconv.Itoa((width - len(s)) / 2) + str := fmt.Sprintf("%"+pad+"s", " ") + str += s + str += fmt.Sprintf("%"+pad+"s", " ") + if len(str) < width { + str += " " + } + return str + } + + centerValue := func(s string, width int) string { + pad := strconv.Itoa((width - len(s)) / 2) + str := fmt.Sprintf("%"+pad+"s", " ") + str += fmt.Sprintf("%s%s%s", colors.Cyan, s, colors.Black) + str += fmt.Sprintf("%"+pad+"s", " ") + if len(str)-10 < width { + str += " " + } + return str + } + + pad := func(s string, width int) (str string) { + toAdd := width - len(s) + str += s + for i := 0; i < toAdd; i++ { + str += " " + } + return + } + + host, port := parseAddr(addr) + if host == "" { + if app.config.Network == NetworkTCP6 { + host = "[::1]" + } else { + host = "0.0.0.0" + } + } + + scheme := "http" + if tls { + scheme = "https" + } + + isPrefork := "Disabled" + if app.config.Prefork { + isPrefork = "Enabled" + } + + procs := strconv.Itoa(runtime.GOMAXPROCS(0)) + if !app.config.Prefork { + procs = "1" + } + + mainLogo := colors.Black + " ┌───────────────────────────────────────────────────┐\n" + if app.config.AppName != "" { + mainLogo += " │ " + centerValue(app.config.AppName, 49) + " │\n" + } + mainLogo += " │ " + centerValue(" Fiber v"+Version, 49) + " │\n" + + if host == "0.0.0.0" { + mainLogo += + " │ " + center(fmt.Sprintf("%s://127.0.0.1:%s", scheme, port), 49) + " │\n" + + " │ " + center(fmt.Sprintf("(bound on host 0.0.0.0 and port %s)", port), 49) + " │\n" + } else { + mainLogo += + " │ " + center(fmt.Sprintf("%s://%s:%s", scheme, host, port), 49) + " │\n" + } + + mainLogo += fmt.Sprintf( + " │ │\n"+ + " │ Handlers %s Processes %s │\n"+ + " │ Prefork .%s PID ....%s │\n"+ + " └───────────────────────────────────────────────────┘"+ + colors.Reset, + value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12), + value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14), + ) + + var childPidsLogo string + if app.config.Prefork { + var childPidsTemplate string + childPidsTemplate += "%s" + childPidsTemplate += " ┌───────────────────────────────────────────────────┐\n%s" + childPidsTemplate += " └───────────────────────────────────────────────────┘" + childPidsTemplate += "%s" + + newLine := " │ %s%s%s │" + + // Turn the `pids` variable (in the form ",a,b,c,d,e,f,etc") into a slice of PIDs + var pidSlice []string + for _, v := range strings.Split(pids, ",") { + if v != "" { + pidSlice = append(pidSlice, v) + } + } + + var lines []string + thisLine := "Child PIDs ... " + var itemsOnThisLine []string + + addLine := func() { + lines = append(lines, + fmt.Sprintf( + newLine, + colors.Black, + thisLine+colors.Cyan+pad(strings.Join(itemsOnThisLine, ", "), 49-len(thisLine)), + colors.Black, + ), + ) + } + + for _, pid := range pidSlice { + if len(thisLine+strings.Join(append(itemsOnThisLine, pid), ", ")) > 49 { + addLine() + thisLine = "" + itemsOnThisLine = []string{pid} + } else { + itemsOnThisLine = append(itemsOnThisLine, pid) + } + } + + // Add left over items to their own line + if len(itemsOnThisLine) != 0 { + addLine() + } + + // Form logo + childPidsLogo = fmt.Sprintf(childPidsTemplate, + colors.Black, + strings.Join(lines, "\n")+"\n", + colors.Reset, + ) + } + + // Combine both the child PID logo and the main Fiber logo + + // Pad the shorter logo to the length of the longer one + splitMainLogo := strings.Split(mainLogo, "\n") + splitChildPidsLogo := strings.Split(childPidsLogo, "\n") + + mainLen := len(splitMainLogo) + childLen := len(splitChildPidsLogo) + + if mainLen > childLen { + diff := mainLen - childLen + for i := 0; i < diff; i++ { + splitChildPidsLogo = append(splitChildPidsLogo, "") + } + } else { + diff := childLen - mainLen + for i := 0; i < diff; i++ { + splitMainLogo = append(splitMainLogo, "") + } + } + + // Combine the two logos, line by line + output := "\n" + for i := range splitMainLogo { + output += colors.Black + splitMainLogo[i] + " " + splitChildPidsLogo[i] + "\n" + } + + out := colorable.NewColorableStdout() + if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) { + out = colorable.NewNonColorable(os.Stdout) + } + + _, _ = fmt.Fprintln(out, output) +} + +// printRoutesMessage print all routes with method, path, name and handlers +// in a format of table, like this: +// method | path | name | handlers +// GET | / | routeName | github.com/gofiber/fiber/v2.emptyHandler +// HEAD | / | | github.com/gofiber/fiber/v2.emptyHandler +func (app *App) printRoutesMessage() { + // ignore child processes + if IsChild() { + return + } + + // Alias colors + colors := app.config.ColorScheme + + var routes []RouteMessage + for _, routeStack := range app.stack { + for _, route := range routeStack { + var newRoute = RouteMessage{} + newRoute.name = route.Name + newRoute.method = route.Method + newRoute.path = route.Path + for _, handler := range route.Handlers { + newRoute.handlers += runtime.FuncForPC(reflect.ValueOf(handler).Pointer()).Name() + " " + } + routes = append(routes, newRoute) + } + } + + out := colorable.NewColorableStdout() + if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) { + out = colorable.NewNonColorable(os.Stdout) + } + + w := tabwriter.NewWriter(out, 1, 1, 1, ' ', 0) + // Sort routes by path + sort.Slice(routes, func(i, j int) bool { + return routes[i].path < routes[j].path + }) + + _, _ = fmt.Fprintf(w, "%smethod\t%s| %spath\t%s| %sname\t%s| %shandlers\n", colors.Blue, colors.White, colors.Green, colors.White, colors.Cyan, colors.White, colors.Yellow) + _, _ = fmt.Fprintf(w, "%s------\t%s| %s----\t%s| %s----\t%s| %s--------\n", colors.Blue, colors.White, colors.Green, colors.White, colors.Cyan, colors.White, colors.Yellow) + for _, route := range routes { + _, _ = fmt.Fprintf(w, "%s%s\t%s| %s%s\t%s| %s%s\t%s| %s%s\n", colors.Blue, route.method, colors.White, colors.Green, route.path, colors.White, colors.Cyan, route.name, colors.White, colors.Yellow, route.handlers) + } + + _ = w.Flush() +} diff --git a/listen_test.go b/listen_test.go new file mode 100644 index 0000000000..5eb035f515 --- /dev/null +++ b/listen_test.go @@ -0,0 +1,181 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package fiber + +import ( + "crypto/tls" + "fmt" + "strings" + "testing" + "time" + + "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp/fasthttputil" +) + +// go test -run Test_App_Listen +func Test_App_Listen(t *testing.T) { + app := New(Config{DisableStartupMessage: true}) + + utils.AssertEqual(t, false, app.Listen(":99999") == nil) + + go func() { + time.Sleep(1000 * time.Millisecond) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + utils.AssertEqual(t, nil, app.Listen(":4003")) +} + +// go test -run Test_App_Listen_Prefork +func Test_App_Listen_Prefork(t *testing.T) { + testPreforkMaster = true + + app := New(Config{DisableStartupMessage: true, Prefork: true}) + + utils.AssertEqual(t, nil, app.Listen(":99999")) +} + +// go test -run Test_App_ListenTLS +func Test_App_ListenTLS(t *testing.T) { + app := New() + + // invalid port + utils.AssertEqual(t, false, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") == nil) + // missing perm/cert file + utils.AssertEqual(t, false, app.ListenTLS(":0", "", "./.github/testdata/ssl.key") == nil) + + go func() { + time.Sleep(1000 * time.Millisecond) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + utils.AssertEqual(t, nil, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) +} + +// go test -run Test_App_ListenTLS_Prefork +func Test_App_ListenTLS_Prefork(t *testing.T) { + testPreforkMaster = true + + app := New(Config{DisableStartupMessage: true, Prefork: true}) + + // invalid key file content + utils.AssertEqual(t, false, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.tmpl") == nil) + + utils.AssertEqual(t, nil, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) +} + +// go test -run Test_App_ListenMutualTLS +func Test_App_ListenMutualTLS(t *testing.T) { + app := New() + + // invalid port + utils.AssertEqual(t, false, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem") == nil) + // missing perm/cert file + utils.AssertEqual(t, false, app.ListenMutualTLS(":0", "", "./.github/testdata/ssl.key", "") == nil) + + go func() { + time.Sleep(1000 * time.Millisecond) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + utils.AssertEqual(t, nil, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) +} + +// go test -run Test_App_ListenMutualTLS_Prefork +func Test_App_ListenMutualTLS_Prefork(t *testing.T) { + testPreforkMaster = true + + app := New(Config{DisableStartupMessage: true, Prefork: true}) + + // invalid key file content + utils.AssertEqual(t, false, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.html", "") == nil) + + utils.AssertEqual(t, nil, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) +} + +// go test -run Test_App_Listener +func Test_App_Listener(t *testing.T) { + app := New() + + go func() { + time.Sleep(500 * time.Millisecond) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + ln := fasthttputil.NewInmemoryListener() + utils.AssertEqual(t, nil, app.Listener(ln)) +} + +// go test -run Test_App_Listener_Prefork +func Test_App_Listener_Prefork(t *testing.T) { + testPreforkMaster = true + + app := New(Config{DisableStartupMessage: true, Prefork: true}) + + ln := fasthttputil.NewInmemoryListener() + utils.AssertEqual(t, nil, app.Listener(ln)) +} + +func Test_App_Listener_TLS_Listener(t *testing.T) { + // Create tls certificate + cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") + if err != nil { + utils.AssertEqual(t, nil, err) + } + config := &tls.Config{Certificates: []tls.Certificate{cer}} + + ln, err := tls.Listen(NetworkTCP4, ":0", config) + utils.AssertEqual(t, nil, err) + + app := New() + + go func() { + time.Sleep(time.Millisecond * 500) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + utils.AssertEqual(t, nil, app.Listener(ln)) +} + +func Test_App_print_Route(t *testing.T) { + app := New(Config{EnablePrintRoutes: true}) + app.Get("/", emptyHandler).Name("routeName") + printRoutesMessage := captureOutput(func() { + app.printRoutesMessage() + }) + fmt.Println(printRoutesMessage) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "routeName")) +} + +func Test_App_print_Route_with_group(t *testing.T) { + app := New(Config{EnablePrintRoutes: true}) + app.Get("/", emptyHandler) + + v1 := app.Group("v1") + v1.Get("/test", emptyHandler).Name("v1") + v1.Post("/test/fiber", emptyHandler) + v1.Put("/test/fiber/*", emptyHandler) + + printRoutesMessage := captureOutput(func() { + app.printRoutesMessage() + }) + + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "POST")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "PUT")) + utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber/*")) +} + +func emptyHandler(c *Ctx) error { + return nil +} diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 6cbffa1e5d..776b050103 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -61,19 +61,6 @@ const ( TagReset = "reset" ) -// Color values -const ( - cBlack = "\u001b[90m" - cRed = "\u001b[91m" - cGreen = "\u001b[92m" - cYellow = "\u001b[93m" - cBlue = "\u001b[94m" - cMagenta = "\u001b[95m" - cCyan = "\u001b[96m" - cWhite = "\u001b[97m" - cReset = "\u001b[0m" -) - // New creates a new middleware handler func New(config ...Config) fiber.Handler { // Set default config @@ -133,6 +120,9 @@ func New(config ...Config) fiber.Handler { return c.Next() } + // Alias colors + colors := c.App().Config().ColorScheme + // Set error handler once once.Do(func() { // get longested possible path @@ -179,16 +169,16 @@ func New(config ...Config) fiber.Handler { // Format error if exist formatErr := "" if chainErr != nil { - formatErr = cRed + " | " + chainErr.Error() + cReset + formatErr = colors.Red + " | " + chainErr.Error() + colors.Reset } // Format log to buffer _, _ = buf.WriteString(fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n", timestamp.Load().(string), - statusColor(c.Response().StatusCode()), c.Response().StatusCode(), cReset, + statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset, stop.Sub(start).Round(time.Millisecond), c.IP(), - methodColor(c.Method()), c.Method(), cReset, + methodColor(c.Method(), colors), c.Method(), colors.Reset, c.Path(), formatErr, )) @@ -240,7 +230,7 @@ func New(config ...Config) fiber.Handler { return buf.WriteString(c.Route().Path) case TagStatus: if cfg.enableColors { - return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode()), c.Response().StatusCode(), cReset)) + return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset)) } return appendInt(buf, c.Response().StatusCode()) case TagResBody: @@ -255,27 +245,27 @@ func New(config ...Config) fiber.Handler { return buf.WriteString(c.Request().URI().QueryArgs().String()) case TagMethod: if cfg.enableColors { - return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method()), c.Method(), cReset)) + return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) } return buf.WriteString(c.Method()) case TagBlack: - return buf.WriteString(cBlack) + return buf.WriteString(colors.Black) case TagRed: - return buf.WriteString(cRed) + return buf.WriteString(colors.Red) case TagGreen: - return buf.WriteString(cGreen) + return buf.WriteString(colors.Green) case TagYellow: - return buf.WriteString(cYellow) + return buf.WriteString(colors.Yellow) case TagBlue: - return buf.WriteString(cBlue) + return buf.WriteString(colors.Blue) case TagMagenta: - return buf.WriteString(cMagenta) + return buf.WriteString(colors.Magenta) case TagCyan: - return buf.WriteString(cCyan) + return buf.WriteString(colors.Cyan) case TagWhite: - return buf.WriteString(cWhite) + return buf.WriteString(colors.White) case TagReset: - return buf.WriteString(cReset) + return buf.WriteString(colors.Reset) case TagError: if chainErr != nil { return buf.WriteString(chainErr.Error()) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 0e0ba1d63c..95f053a008 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -144,11 +144,14 @@ func Test_Logger_All(t *testing.T) { Output: buf, })) + // Alias colors + colors := app.Config().ColorScheme + resp, err := app.Test(httptest.NewRequest("GET", "/?foo=bar", nil)) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) - expected := fmt.Sprintf("%dHost=example.comhttp0.0.0.0example.com/?foo=bar/%s%s%s%s%s%s%s%s%sCannot GET /", os.Getpid(), cBlack, cRed, cGreen, cYellow, cBlue, cMagenta, cCyan, cWhite, cReset) + expected := fmt.Sprintf("%dHost=example.comhttp0.0.0.0example.com/?foo=bar/%s%s%s%s%s%s%s%s%sCannot GET /", os.Getpid(), colors.Black, colors.Red, colors.Green, colors.Yellow, colors.Blue, colors.Magenta, colors.Cyan, colors.White, colors.Reset) utils.AssertEqual(t, expected, buf.String()) } diff --git a/middleware/logger/utils.go b/middleware/logger/utils.go index f1616bdee6..2af456d492 100644 --- a/middleware/logger/utils.go +++ b/middleware/logger/utils.go @@ -4,36 +4,36 @@ import ( "github.com/gofiber/fiber/v2" ) -func methodColor(method string) string { +func methodColor(method string, colors fiber.Colors) string { switch method { case fiber.MethodGet: - return cCyan + return colors.Cyan case fiber.MethodPost: - return cGreen + return colors.Green case fiber.MethodPut: - return cYellow + return colors.Yellow case fiber.MethodDelete: - return cRed + return colors.Red case fiber.MethodPatch: - return cWhite + return colors.White case fiber.MethodHead: - return cMagenta + return colors.Magenta case fiber.MethodOptions: - return cBlue + return colors.Blue default: - return cReset + return colors.Reset } } -func statusColor(code int) string { +func statusColor(code int, colors fiber.Colors) string { switch { case code >= fiber.StatusOK && code < fiber.StatusMultipleChoices: - return cGreen + return colors.Green case code >= fiber.StatusMultipleChoices && code < fiber.StatusBadRequest: - return cBlue + return colors.Blue case code >= fiber.StatusBadRequest && code < fiber.StatusInternalServerError: - return cYellow + return colors.Yellow default: - return cRed + return colors.Red } } From 43133bec439d664f4d2cace7c1450691369f1dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=A8=E3=83=BC=E3=81=B5=E3=81=A8=E3=81=B5?= Date: Mon, 1 Aug 2022 15:25:56 +0900 Subject: [PATCH 018/141] =?UTF-8?q?=F0=9F=94=A5=20Add=20OnPrefork=20Hooks?= =?UTF-8?q?=20so=20you=20can=20get=20the=20PID=20of=20the=20child=20proces?= =?UTF-8?q?s.=20(#1974)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hooks.go | 16 ++++++++++++++++ hooks_test.go | 20 ++++++++++++++++++++ prefork.go | 12 ++++++++++++ 3 files changed, 48 insertions(+) diff --git a/hooks.go b/hooks.go index 3ea7a00014..571ac25210 100644 --- a/hooks.go +++ b/hooks.go @@ -7,6 +7,7 @@ type OnGroupHandler = func(Group) error type OnGroupNameHandler = OnGroupHandler type OnListenHandler = func() error type OnShutdownHandler = OnListenHandler +type OnForkHandler = func(int) error type hooks struct { // Embed app @@ -19,6 +20,7 @@ type hooks struct { onGroupName []OnGroupNameHandler onListen []OnListenHandler onShutdown []OnShutdownHandler + onFork []OnForkHandler } func newHooks(app *App) *hooks { @@ -30,6 +32,7 @@ func newHooks(app *App) *hooks { onName: make([]OnNameHandler, 0), onListen: make([]OnListenHandler, 0), onShutdown: make([]OnShutdownHandler, 0), + onFork: make([]OnForkHandler, 0), } } @@ -83,6 +86,13 @@ func (h *hooks) OnShutdown(handler ...OnShutdownHandler) { h.app.mutex.Unlock() } +// OnFork is a hook to execute user function after fork process. +func (h *hooks) OnFork(handler ...OnForkHandler) { + h.app.mutex.Lock() + h.onFork = append(h.onFork, handler...) + h.app.mutex.Unlock() +} + func (h *hooks) executeOnRouteHooks(route Route) error { for _, v := range h.onRoute { if err := v(route); err != nil { @@ -138,3 +148,9 @@ func (h *hooks) executeOnShutdownHooks() { _ = v() } } + +func (h *hooks) executeOnForkHooks(pid int) { + for _, v := range h.onFork { + _ = v(pid) + } +} diff --git a/hooks_test.go b/hooks_test.go index 2339e5306c..5a800920d2 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -176,3 +176,23 @@ func Test_Hook_OnListen(t *testing.T) { utils.AssertEqual(t, "ready", buf.String()) } + +func Test_Hook_OnHook(t *testing.T) { + // Reset test var + testPreforkMaster = true + testOnPrefork = true + + app := New() + + go func() { + time.Sleep(1000 * time.Millisecond) + utils.AssertEqual(t, nil, app.Shutdown()) + }() + + app.Hooks().OnFork(func(pid int) error { + utils.AssertEqual(t, 1, pid) + return nil + }) + + utils.AssertEqual(t, nil, app.prefork(NetworkTCP4, ":3000", nil)) +} diff --git a/prefork.go b/prefork.go index 459e408930..b3049abc60 100644 --- a/prefork.go +++ b/prefork.go @@ -20,6 +20,7 @@ const ( ) var testPreforkMaster = false +var testOnPrefork = false // IsChild determines if the current process is a child of Prefork func IsChild() bool { @@ -102,6 +103,15 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) childs[pid] = cmd pids = append(pids, strconv.Itoa(pid)) + // execute fork hook + if app.hooks != nil { + if testOnPrefork { + app.hooks.executeOnForkHooks(dummyPid) + } else { + app.hooks.executeOnForkHooks(pid) + } + } + // notify master if child crashes go func() { channel <- child{pid, cmd.Wait()} @@ -146,3 +156,5 @@ func dummyCmd() *exec.Cmd { } return exec.Command(dummyChildCmd, "version") } + +var dummyPid = 1 From e49880c5d6ac4abee7d5a0fbda857ebef01bc21f Mon Sep 17 00:00:00 2001 From: Maxi_Mega <52792549+Maxi-Mega@users.noreply.github.com> Date: Mon, 1 Aug 2022 12:16:49 +0200 Subject: [PATCH 019/141] :art: Fix padding around app name in startup message when containing non-ascii characters (#1987) * Fix padding around app name in startup message when it contains non-ascii characters * fix conflict, allow ending space only for odd length strings * move startup message tests to listen_test.go --- app_test.go | 56 ----------------------------------------- listen.go | 5 ++-- listen_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 58 deletions(-) diff --git a/app_test.go b/app_test.go index f64c32fbc4..c812e93a0a 100644 --- a/app_test.go +++ b/app_test.go @@ -10,16 +10,13 @@ import ( "fmt" "io" "io/ioutil" - "log" "mime/multipart" "net" "net/http" "net/http/httptest" - "os" "reflect" "regexp" "strings" - "sync" "testing" "time" @@ -1348,59 +1345,6 @@ func Test_App_SmallReadBuffer(t *testing.T) { utils.AssertEqual(t, nil, app.Listen(":4006")) } -func captureOutput(f func()) string { - reader, writer, err := os.Pipe() - if err != nil { - panic(err) - } - stdout := os.Stdout - stderr := os.Stderr - defer func() { - os.Stdout = stdout - os.Stderr = stderr - log.SetOutput(os.Stderr) - }() - os.Stdout = writer - os.Stderr = writer - log.SetOutput(writer) - out := make(chan string) - wg := new(sync.WaitGroup) - wg.Add(1) - go func() { - var buf bytes.Buffer - wg.Done() - io.Copy(&buf, reader) - out <- buf.String() - }() - wg.Wait() - f() - writer.Close() - return <-out -} - -func Test_App_Master_Process_Show_Startup_Message(t *testing.T) { - startupMessage := captureOutput(func() { - New(Config{Prefork: true}). - startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) - }) - fmt.Println(startupMessage) - utils.AssertEqual(t, true, strings.Contains(startupMessage, "https://127.0.0.1:3000")) - utils.AssertEqual(t, true, strings.Contains(startupMessage, "(bound on host 0.0.0.0 and port 3000)")) - utils.AssertEqual(t, true, strings.Contains(startupMessage, "Child PIDs")) - utils.AssertEqual(t, true, strings.Contains(startupMessage, "11111, 22222, 33333, 44444, 55555, 60000")) - utils.AssertEqual(t, true, strings.Contains(startupMessage, "Prefork ........ Enabled")) -} - -func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { - app := New(Config{Prefork: true, AppName: "Test App v1.0.1"}) - startupMessage := captureOutput(func() { - app.startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) - }) - fmt.Println(startupMessage) - utils.AssertEqual(t, "Test App v1.0.1", app.Config().AppName) - utils.AssertEqual(t, true, strings.Contains(startupMessage, app.Config().AppName)) -} - func Test_App_Server(t *testing.T) { app := New() diff --git a/listen.go b/listen.go index 8f92007b91..8bd6f0dd4d 100644 --- a/listen.go +++ b/listen.go @@ -208,11 +208,12 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { } centerValue := func(s string, width int) string { - pad := strconv.Itoa((width - len(s)) / 2) + pad := strconv.Itoa((width - len([]rune(s))) / 2) str := fmt.Sprintf("%"+pad+"s", " ") str += fmt.Sprintf("%s%s%s", colors.Cyan, s, colors.Black) str += fmt.Sprintf("%"+pad+"s", " ") - if len(str)-10 < width { + if len([]rune(str))-10 < width && len([]rune(str))%2 != 0 { + // add an ending space if the length of str is odd and str is not too long str += " " } return str diff --git a/listen_test.go b/listen_test.go index 5eb035f515..11d1908bd4 100644 --- a/listen_test.go +++ b/listen_test.go @@ -5,9 +5,14 @@ package fiber import ( + "bytes" "crypto/tls" "fmt" + "io" + "log" + "os" "strings" + "sync" "testing" "time" @@ -140,6 +145,69 @@ func Test_App_Listener_TLS_Listener(t *testing.T) { utils.AssertEqual(t, nil, app.Listener(ln)) } +func captureOutput(f func()) string { + reader, writer, err := os.Pipe() + if err != nil { + panic(err) + } + stdout := os.Stdout + stderr := os.Stderr + defer func() { + os.Stdout = stdout + os.Stderr = stderr + log.SetOutput(os.Stderr) + }() + os.Stdout = writer + os.Stderr = writer + log.SetOutput(writer) + out := make(chan string) + wg := new(sync.WaitGroup) + wg.Add(1) + go func() { + var buf bytes.Buffer + wg.Done() + io.Copy(&buf, reader) + out <- buf.String() + }() + wg.Wait() + f() + writer.Close() + return <-out +} + +func Test_App_Master_Process_Show_Startup_Message(t *testing.T) { + startupMessage := captureOutput(func() { + New(Config{Prefork: true}). + startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) + }) + fmt.Println(startupMessage) + utils.AssertEqual(t, true, strings.Contains(startupMessage, "https://127.0.0.1:3000")) + utils.AssertEqual(t, true, strings.Contains(startupMessage, "(bound on host 0.0.0.0 and port 3000)")) + utils.AssertEqual(t, true, strings.Contains(startupMessage, "Child PIDs")) + utils.AssertEqual(t, true, strings.Contains(startupMessage, "11111, 22222, 33333, 44444, 55555, 60000")) + utils.AssertEqual(t, true, strings.Contains(startupMessage, "Prefork ........ Enabled")) +} + +func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { + app := New(Config{Prefork: true, AppName: "Test App v1.0.1"}) + startupMessage := captureOutput(func() { + app.startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) + }) + fmt.Println(startupMessage) + utils.AssertEqual(t, "Test App v1.0.1", app.Config().AppName) + utils.AssertEqual(t, true, strings.Contains(startupMessage, app.Config().AppName)) +} + +func Test_App_Master_Process_Show_Startup_MessageWithAppNameNonAscii(t *testing.T) { + appName := "Serveur de vérification des données" + app := New(Config{Prefork: true, AppName: appName}) + startupMessage := captureOutput(func() { + app.startupMessage(":3000", false, "") + }) + fmt.Println(startupMessage) + utils.AssertEqual(t, true, strings.Contains(startupMessage, "│ Serveur de vérification des données │")) +} + func Test_App_print_Route(t *testing.T) { app := New(Config{EnablePrintRoutes: true}) app.Get("/", emptyHandler).Name("routeName") From c59370f47ae8dd67ad4c6bcaa352a2ecb98b63b2 Mon Sep 17 00:00:00 2001 From: RW Date: Tue, 2 Aug 2022 08:09:08 +0200 Subject: [PATCH 020/141] prepare release v2.36.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index f3b488891a..9971b64afd 100644 --- a/app.go +++ b/app.go @@ -29,7 +29,7 @@ import ( ) // Version of current fiber package -const Version = "2.35.0" +const Version = "2.36.0 // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From 7108b444e3947e4824c03725aa1035a200cdca2a Mon Sep 17 00:00:00 2001 From: RW Date: Tue, 2 Aug 2022 08:11:35 +0200 Subject: [PATCH 021/141] prepare release v2.36.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 9971b64afd..d3523c1445 100644 --- a/app.go +++ b/app.go @@ -29,7 +29,7 @@ import ( ) // Version of current fiber package -const Version = "2.36.0 +const Version = "2.36.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From 1cca495c7b0fd47ace6952d9fb90cad9f4a03565 Mon Sep 17 00:00:00 2001 From: wernerr Date: Tue, 2 Aug 2022 08:31:22 +0200 Subject: [PATCH 022/141] try to stabilize Test_Test_Timeout --- app_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app_test.go b/app_test.go index c812e93a0a..c7ce9dccde 100644 --- a/app_test.go +++ b/app_test.go @@ -1169,11 +1169,11 @@ func Test_Test_Timeout(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode, "Status code") app.Get("timeout", func(c *Ctx) error { - time.Sleep(100 * time.Millisecond) + time.Sleep(200 * time.Millisecond) return nil }) - _, err = app.Test(httptest.NewRequest(MethodGet, "/timeout", nil), 50) + _, err = app.Test(httptest.NewRequest(MethodGet, "/timeout", nil), 20) utils.AssertEqual(t, true, err != nil, "app.Test(req)") } From 4f7816774b85edf70d54e2a65c9669734a385f9f Mon Sep 17 00:00:00 2001 From: Maxi_Mega <52792549+Maxi-Mega@users.noreply.github.com> Date: Tue, 2 Aug 2022 14:23:14 +0200 Subject: [PATCH 023/141] :art: Add black colors to default overriding function (#1993) * Fix padding around app name in startup message when it contains non-ascii characters * fix conflict, allow ending space only for odd length strings * move startup message tests to listen_test.go * add black color to defaultColors overriding function, fix (again) padding in startupMessage --- color.go | 4 ++++ listen.go | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/color.go b/color.go index f982b379de..2dbf831ae9 100644 --- a/color.go +++ b/color.go @@ -67,6 +67,10 @@ var DefaultColors = Colors{ // defaultColors is a function to override default colors to config func defaultColors(colors Colors) Colors { + if colors.Black == "" { + colors.Black = DefaultColors.Black + } + if colors.Red == "" { colors.Red = DefaultColors.Red } diff --git a/listen.go b/listen.go index 8bd6f0dd4d..89a6fba6cb 100644 --- a/listen.go +++ b/listen.go @@ -212,8 +212,8 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { str := fmt.Sprintf("%"+pad+"s", " ") str += fmt.Sprintf("%s%s%s", colors.Cyan, s, colors.Black) str += fmt.Sprintf("%"+pad+"s", " ") - if len([]rune(str))-10 < width && len([]rune(str))%2 != 0 { - // add an ending space if the length of str is odd and str is not too long + if len([]rune(s))-10 < width && len([]rune(s))%2 == 0 { + // add an ending space if the length of str is even and str is not too long str += " " } return str @@ -256,7 +256,7 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { if app.config.AppName != "" { mainLogo += " │ " + centerValue(app.config.AppName, 49) + " │\n" } - mainLogo += " │ " + centerValue(" Fiber v"+Version, 49) + " │\n" + mainLogo += " │ " + centerValue("Fiber v"+Version, 49) + " │\n" if host == "0.0.0.0" { mainLogo += From fb780a01d0fa4b3daa23b97898a20b952a15fad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Wed, 3 Aug 2022 12:07:21 +0300 Subject: [PATCH 024/141] ci: add go 1.19 to tests (#1994) * ci: add go 1.19 to tests * change test runner versions skip it for 1.15, 1.17, 1.18 Co-authored-by: wernerr --- .github/workflows/benchmark.yml | 2 +- .github/workflows/test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 0adeb1782b..3a8ee2bfef 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -7,7 +7,7 @@ jobs: - name: Install Go uses: actions/setup-go@v3 with: - go-version: 1.18.x + go-version: 1.19.x - name: Fetch Repository uses: actions/checkout@v3 - name: Run Benchmark diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 68329ca723..082c3becaf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,7 +4,7 @@ jobs: Build: strategy: matrix: - go-version: [1.14.x, 1.15.x, 1.16.x, 1.17.x, 1.18.x] + go-version: [1.14.x, 1.16.x, 1.19.x] platform: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: From 96b4db17c6422453b0f720fbba923a2dffa46eb1 Mon Sep 17 00:00:00 2001 From: wernerr Date: Wed, 3 Aug 2022 13:03:06 +0200 Subject: [PATCH 025/141] reduce duplicated tests run actions --- .github/workflows/test.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 082c3becaf..a9fff3fee5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,10 @@ -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: + branches: + - master name: Test jobs: Build: From 3552136805cd143c2ce6536f5e31376baeb8d258 Mon Sep 17 00:00:00 2001 From: wernerr Date: Wed, 3 Aug 2022 13:12:47 +0200 Subject: [PATCH 026/141] reduce duplicated tests run actions --- .github/workflows/test.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a9fff3fee5..ceaf859e8e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,8 +3,6 @@ on: branches: - master pull_request: - branches: - - master name: Test jobs: Build: From 97ba1d16d59abf196e25d5349fdc1dc14e022283 Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 3 Aug 2022 14:35:47 +0200 Subject: [PATCH 027/141] Update benchmark.yml --- .github/workflows/benchmark.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 3a8ee2bfef..0adeb1782b 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.18.x - name: Fetch Repository uses: actions/checkout@v3 - name: Run Benchmark From 9761f4e79743e2c1e3832fdc7255630db81c7cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Wed, 3 Aug 2022 17:03:17 +0300 Subject: [PATCH 028/141] :bug: bug: fix gopsutil when compiling for bsd. (#1995) --- internal/gopsutil/load/load_bsd.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/gopsutil/load/load_bsd.go b/internal/gopsutil/load/load_bsd.go index 5c610a54ef..20d00a5739 100644 --- a/internal/gopsutil/load/load_bsd.go +++ b/internal/gopsutil/load/load_bsd.go @@ -9,9 +9,12 @@ import ( "strings" "unsafe" + "github.com/gofiber/fiber/v2/internal/gopsutil/common" "golang.org/x/sys/unix" ) +var invoke common.Invoker = common.Invoke{} + func Avg() (*AvgStat, error) { return AvgWithContext(context.Background()) } @@ -75,7 +78,7 @@ func MiscWithContext(ctx context.Context) (*MiscStat, error) { if err != nil { return nil, err } - ret.ProcsCreated = f.forks + ret.ProcsCreated = int64(f.forks) return &ret, nil } From 5bc187120539f58eb9901734668422accde08fe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Thu, 4 Aug 2022 20:41:03 +0300 Subject: [PATCH 029/141] :memo: ci: fix CoC links on issue templates --- .github/ISSUE_TEMPLATE/bug-report.yaml | 2 +- .github/ISSUE_TEMPLATE/feature-request.yaml | 2 +- .github/ISSUE_TEMPLATE/question.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index c4a902a86f..0f7e123505 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -77,7 +77,7 @@ body: label: "Checklist:" description: "By submitting this issue, you confirm that:" options: - - label: "I agree to follow Fiber's [Code of Conduct](../CODE_OF_CONDUCT.md)." + - label: "I agree to follow Fiber's [Code of Conduct](https://github.com/gofiber/fiber/blob/master/.github/CODE_OF_CONDUCT.md)." required: true - label: "I have checked for existing issues that describe my problem prior to opening this one." required: true diff --git a/.github/ISSUE_TEMPLATE/feature-request.yaml b/.github/ISSUE_TEMPLATE/feature-request.yaml index 0e63adc060..733dcc7f02 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yaml +++ b/.github/ISSUE_TEMPLATE/feature-request.yaml @@ -52,7 +52,7 @@ body: label: "Checklist:" description: "By submitting this issue, you confirm that:" options: - - label: "I agree to follow Fiber's [Code of Conduct](../CODE_OF_CONDUCT.md)." + - label: "I agree to follow Fiber's [Code of Conduct](https://github.com/gofiber/fiber/blob/master/.github/CODE_OF_CONDUCT.md)." required: true - label: "I have checked for existing issues that describe my suggestion prior to opening this one." required: true diff --git a/.github/ISSUE_TEMPLATE/question.yaml b/.github/ISSUE_TEMPLATE/question.yaml index b1cdfa694c..976c34a6e9 100644 --- a/.github/ISSUE_TEMPLATE/question.yaml +++ b/.github/ISSUE_TEMPLATE/question.yaml @@ -47,7 +47,7 @@ body: label: "Checklist:" description: "By submitting this issue, you confirm that:" options: - - label: "I agree to follow Fiber's [Code of Conduct](../CODE_OF_CONDUCT.md)." + - label: "I agree to follow Fiber's [Code of Conduct](https://github.com/gofiber/fiber/blob/master/.github/CODE_OF_CONDUCT.md)." required: true - label: "I have checked for existing issues that describe my questions prior to opening this one." required: true From 9c6c7da4e4178adc47888064c9bfbdefcf91064f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Thu, 4 Aug 2022 20:43:07 +0300 Subject: [PATCH 030/141] :bug: bug: add forgotten overriding for white color --- color.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/color.go b/color.go index 2dbf831ae9..0a47d81957 100644 --- a/color.go +++ b/color.go @@ -95,6 +95,10 @@ func defaultColors(colors Colors) Colors { colors.Cyan = DefaultColors.Cyan } + if colors.White == "" { + colors.White = DefaultColors.White + } + if colors.Reset == "" { colors.Reset = DefaultColors.Reset } From e5eb8d3c98b33d8047cdc27bc165330f4f8b5312 Mon Sep 17 00:00:00 2001 From: Trim21 Date: Mon, 8 Aug 2022 14:18:57 +0800 Subject: [PATCH 031/141] fix infinitely `app.Test` (#1997) * diff * better code * err message * code style * up * fix type error * use time.Timer --- app.go | 8 ++++++++ app_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/app.go b/app.go index d3523c1445..ae922aa007 100644 --- a/app.go +++ b/app.go @@ -845,7 +845,15 @@ func (app *App) Test(req *http.Request, msTimeout ...int) (resp *http.Response, // Serve conn to server channel := make(chan error) go func() { + var returned bool + defer func() { + if !returned { + channel <- fmt.Errorf("runtime.Goexit() called in handler or server panic") + } + }() + channel <- app.server.ServeConn(conn) + returned = true }() // Wait for callback diff --git a/app_test.go b/app_test.go index c7ce9dccde..716970fbf2 100644 --- a/app_test.go +++ b/app_test.go @@ -16,6 +16,7 @@ import ( "net/http/httptest" "reflect" "regexp" + "runtime" "strings" "testing" "time" @@ -1527,3 +1528,35 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") utils.AssertEqual(t, "hi, i'm a custom sub sub fiber error", string(b), "Third fiber Response body") } + +func Test_App_Test_no_timeout_infinitely(t *testing.T) { + var err error + c := make(chan int) + + go func() { + defer func() { c <- 0 }() + app := New() + app.Get("/", func(c *Ctx) error { + runtime.Goexit() + return nil + }) + + req := httptest.NewRequest(http.MethodGet, "/", http.NoBody) + _, err = app.Test(req, -1) + }() + + tk := time.NewTimer(5 * time.Second) + defer tk.Stop() + + select { + case <-tk.C: + t.Error("hanging test") + t.FailNow() + case <-c: + } + + if err == nil { + t.Error("unexpected success request") + t.FailNow() + } +} From e8a2ba363c127937b4a5728b6d5f6971c23da8bd Mon Sep 17 00:00:00 2001 From: Jinquan Wang <35188480+wangjq4214@users.noreply.github.com> Date: Tue, 9 Aug 2022 22:14:41 +0800 Subject: [PATCH 032/141] =?UTF-8?q?=F0=9F=90=9B=20bug:=20fix=20proxy=20ove?= =?UTF-8?q?rwrote=20the=20wrong=20scheme=20(#2004)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 bug: fix proxy overwrote the wrong scheme * ✅ fix: fix io not exist in go1.14 --- middleware/proxy/proxy.go | 6 ++++-- middleware/proxy/proxy_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index 421605efa5..bb96b03382 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -123,11 +123,13 @@ func Do(c *fiber.Ctx, addr string) error { res := c.Response() originalURL := utils.CopyString(c.OriginalURL()) defer req.SetRequestURI(originalURL) - req.SetRequestURI(addr) + + copiedURL := utils.CopyString(addr) + req.SetRequestURI(copiedURL) // NOTE: if req.isTLS is true, SetRequestURI keeps the scheme as https. // issue reference: // https://github.com/gofiber/fiber/issues/1762 - if scheme := getScheme(utils.UnsafeBytes(addr)); len(scheme) > 0 { + if scheme := getScheme(utils.UnsafeBytes(copiedURL)); len(scheme) > 0 { req.URI().SetSchemeBytes(scheme) } diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index 906e54669b..08160b2e86 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -4,6 +4,7 @@ import ( "crypto/tls" "io/ioutil" "net" + "net/http" "net/http/httptest" "strings" "testing" @@ -362,3 +363,30 @@ func Test_Proxy_Do_RestoreOriginalURL(t *testing.T) { utils.AssertEqual(t, nil, err1) utils.AssertEqual(t, nil, err2) } + +func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) { + t.Parallel() + + _, addr := createProxyTestServer(func(c *fiber.Ctx) error { + return c.SendString("hello world") + }, t) + + app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app.Get("/*", func(c *fiber.Ctx) error { + path := c.OriginalURL() + url := strings.TrimPrefix(path, "/") + + utils.AssertEqual(t, "http://"+addr, url) + if err := Do(c, url); err != nil { + return err + } + c.Response().Header.Del(fiber.HeaderServer) + return nil + }) + + resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/http://"+addr, nil)) + utils.AssertEqual(t, nil, err) + s, err := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "hello world", string(s)) +} From 6669ec4486b5716db49bda572045baecc239c363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Mon, 15 Aug 2022 21:25:26 +0300 Subject: [PATCH 033/141] :memo: docs: gofmt & add missing copyright texts (#2013) --- app.go | 59 ++++++++++++---------- client.go | 3 +- group.go | 23 +++++---- internal/fasttemplate/template.go | 48 +++++++++--------- internal/fwd/reader.go | 1 - internal/go-ole/guid.go | 10 ++-- internal/gopsutil/common/common.go | 7 +-- internal/gopsutil/common/common_windows.go | 7 ++- internal/gopsutil/cpu/cpu_solaris.go | 2 +- internal/gopsutil/net/net_linux.go | 3 +- internal/gopsutil/net/net_windows.go | 3 +- internal/isatty/isatty_windows.go | 3 +- internal/msgp/defs.go | 15 +++--- internal/msgp/elsize.go | 1 - internal/msgp/errors.go | 1 - internal/msgp/extension.go | 2 +- internal/msgp/file.go | 1 - internal/msgp/read_bytes.go | 2 +- internal/msgp/write.go | 12 ++--- internal/msgp/write_bytes.go | 14 ++--- internal/schema/doc.go | 16 +++--- internal/template/html/html.go | 2 +- internal/uuid/dce.go | 4 +- internal/uuid/hash.go | 4 +- internal/uuid/version4.go | 12 ++--- internal/wmi/swbemservices.go | 1 + internal/wmi/wmi.go | 2 +- listen.go | 10 ++-- middleware/cache/manager_msgp.go | 4 +- 29 files changed, 144 insertions(+), 128 deletions(-) diff --git a/app.go b/app.go index ae922aa007..3e38fab4b2 100644 --- a/app.go +++ b/app.go @@ -63,17 +63,18 @@ type Storage interface { // ErrorHandler defines a function that will process all errors // returned from any handlers in the stack -// cfg := fiber.Config{} -// cfg.ErrorHandler = func(c *Ctx, err error) error { -// code := StatusInternalServerError -// var e *fiber.Error -// if errors.As(err, &e) { -// code = e.Code -// } -// c.Set(HeaderContentType, MIMETextPlainCharsetUTF8) -// return c.Status(code).SendString(err.Error()) -// } -// app := fiber.New(cfg) +// +// cfg := fiber.Config{} +// cfg.ErrorHandler = func(c *Ctx, err error) error { +// code := StatusInternalServerError +// var e *fiber.Error +// if errors.As(err, &e) { +// code = e.Code +// } +// c.Set(HeaderContentType, MIMETextPlainCharsetUTF8) +// return c.Status(code).SendString(err.Error()) +// } +// app := fiber.New(cfg) type ErrorHandler = func(*Ctx, error) error // Error represents an error that occurred while handling a request. @@ -438,12 +439,15 @@ var DefaultErrorHandler = func(c *Ctx, err error) error { } // New creates a new Fiber named instance. -// app := fiber.New() +// +// app := fiber.New() +// // You can pass optional configuration options by passing a Config struct: -// app := fiber.New(fiber.Config{ -// Prefork: true, -// ServerHeader: "Fiber", -// }) +// +// app := fiber.New(fiber.Config{ +// Prefork: true, +// ServerHeader: "Fiber", +// }) func New(config ...Config) *App { // Create a new app app := &App{ @@ -609,15 +613,15 @@ func (app *App) GetRoute(name string) Route { // Use registers a middleware route that will match requests // with the provided prefix (which is optional and defaults to "/"). // -// app.Use(func(c *fiber.Ctx) error { -// return c.Next() -// }) -// app.Use("/api", func(c *fiber.Ctx) error { -// return c.Next() -// }) -// app.Use("/api", handler, func(c *fiber.Ctx) error { -// return c.Next() -// }) +// app.Use(func(c *fiber.Ctx) error { +// return c.Next() +// }) +// app.Use("/api", func(c *fiber.Ctx) error { +// return c.Next() +// }) +// app.Use("/api", handler, func(c *fiber.Ctx) error { +// return c.Next() +// }) // // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... func (app *App) Use(args ...interface{}) Router { @@ -710,8 +714,9 @@ func (app *App) All(path string, handlers ...Handler) Router { } // Group is used for Routes with common prefix to define a new sub-router with optional middleware. -// api := app.Group("/api") -// api.Get("/users", handler) +// +// api := app.Group("/api") +// api.Get("/users", handler) func (app *App) Group(prefix string, handlers ...Handler) Router { if len(handlers) > 0 { app.register(methodUse, prefix, handlers...) diff --git a/client.go b/client.go index 0974b80f24..97762dcdaf 100644 --- a/client.go +++ b/client.go @@ -566,7 +566,8 @@ func (a *Agent) SendFile(filename string, fieldname ...string) *Agent { // SendFiles reads files and appends them to multipart form request. // // Examples: -// SendFile("/path/to/file1", "fieldname1", "/path/to/file2") +// +// SendFile("/path/to/file1", "fieldname1", "/path/to/file2") func (a *Agent) SendFiles(filenamesAndFieldnames ...string) *Agent { pairs := len(filenamesAndFieldnames) if pairs&1 == 1 { diff --git a/group.go b/group.go index 65c14e0520..669aeab94d 100644 --- a/group.go +++ b/group.go @@ -70,15 +70,15 @@ func (grp *Group) Name(name string) Router { // Use registers a middleware route that will match requests // with the provided prefix (which is optional and defaults to "/"). // -// app.Use(func(c *fiber.Ctx) error { -// return c.Next() -// }) -// app.Use("/api", func(c *fiber.Ctx) error { -// return c.Next() -// }) -// app.Use("/api", handler, func(c *fiber.Ctx) error { -// return c.Next() -// }) +// app.Use(func(c *fiber.Ctx) error { +// return c.Next() +// }) +// app.Use("/api", func(c *fiber.Ctx) error { +// return c.Next() +// }) +// app.Use("/api", handler, func(c *fiber.Ctx) error { +// return c.Next() +// }) // // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... func (grp *Group) Use(args ...interface{}) Router { @@ -171,8 +171,9 @@ func (grp *Group) All(path string, handlers ...Handler) Router { } // Group is used for Routes with common prefix to define a new sub-router with optional middleware. -// api := app.Group("/api") -// api.Get("/users", handler) +// +// api := app.Group("/api") +// api.Get("/users", handler) func (grp *Group) Group(prefix string, handlers ...Handler) Router { prefix = getGroupPath(grp.Prefix, prefix) if len(handlers) > 0 { diff --git a/internal/fasttemplate/template.go b/internal/fasttemplate/template.go index 9cad50dc71..d4559feef8 100644 --- a/internal/fasttemplate/template.go +++ b/internal/fasttemplate/template.go @@ -65,9 +65,9 @@ func ExecuteFunc(template, startTag, endTag string, w io.Writer, f TagFunc) (int // values from the map m and writes the result to the given writer w. // // Substitution map m may contain values with the following types: -// * []byte - the fastest value type -// * string - convenient value type -// * TagFunc - flexible value type +// - []byte - the fastest value type +// - string - convenient value type +// - TagFunc - flexible value type // // Returns the number of bytes written to w. // @@ -81,9 +81,9 @@ func Execute(template, startTag, endTag string, w io.Writer, m map[string]interf // This can be used as a drop-in replacement for strings.Replacer // // Substitution map m may contain values with the following types: -// * []byte - the fastest value type -// * string - convenient value type -// * TagFunc - flexible value type +// - []byte - the fastest value type +// - string - convenient value type +// - TagFunc - flexible value type // // Returns the number of bytes written to w. // @@ -135,9 +135,9 @@ var byteBufferPool bytebufferpool.Pool // values from the map m and returns the result. // // Substitution map m may contain values with the following types: -// * []byte - the fastest value type -// * string - convenient value type -// * TagFunc - flexible value type +// - []byte - the fastest value type +// - string - convenient value type +// - TagFunc - flexible value type // // This function is optimized for constantly changing templates. // Use Template.ExecuteString for frozen templates. @@ -149,9 +149,9 @@ func ExecuteString(template, startTag, endTag string, m map[string]interface{}) // This can be used as a drop-in replacement for strings.Replacer // // Substitution map m may contain values with the following types: -// * []byte - the fastest value type -// * string - convenient value type -// * TagFunc - flexible value type +// - []byte - the fastest value type +// - string - convenient value type +// - TagFunc - flexible value type // // This function is optimized for constantly changing templates. // Use Template.ExecuteStringStd for frozen templates. @@ -305,9 +305,9 @@ func (t *Template) ExecuteFunc(w io.Writer, f TagFunc) (int64, error) { // values from the map m and writes the result to the given writer w. // // Substitution map m may contain values with the following types: -// * []byte - the fastest value type -// * string - convenient value type -// * TagFunc - flexible value type +// - []byte - the fastest value type +// - string - convenient value type +// - TagFunc - flexible value type // // Returns the number of bytes written to w. func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) { @@ -318,9 +318,9 @@ func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) // This can be used as a drop-in replacement for strings.Replacer // // Substitution map m may contain values with the following types: -// * []byte - the fastest value type -// * string - convenient value type -// * TagFunc - flexible value type +// - []byte - the fastest value type +// - string - convenient value type +// - TagFunc - flexible value type // // Returns the number of bytes written to w. func (t *Template) ExecuteStd(w io.Writer, m map[string]interface{}) (int64, error) { @@ -366,9 +366,9 @@ func (t *Template) ExecuteFuncStringWithErr(f TagFunc) (string, error) { // values from the map m and returns the result. // // Substitution map m may contain values with the following types: -// * []byte - the fastest value type -// * string - convenient value type -// * TagFunc - flexible value type +// - []byte - the fastest value type +// - string - convenient value type +// - TagFunc - flexible value type // // This function is optimized for frozen templates. // Use ExecuteString for constantly changing templates. @@ -380,9 +380,9 @@ func (t *Template) ExecuteString(m map[string]interface{}) string { // This can be used as a drop-in replacement for strings.Replacer // // Substitution map m may contain values with the following types: -// * []byte - the fastest value type -// * string - convenient value type -// * TagFunc - flexible value type +// - []byte - the fastest value type +// - string - convenient value type +// - TagFunc - flexible value type // // This function is optimized for frozen templates. // Use ExecuteStringStd for constantly changing templates. diff --git a/internal/fwd/reader.go b/internal/fwd/reader.go index 6918d3e211..c80a617bb2 100644 --- a/internal/fwd/reader.go +++ b/internal/fwd/reader.go @@ -31,7 +31,6 @@ // returns a slice pointing to the next `n` bytes of the writer, and increments // the write position by the length of the returned slice. This allows users // to write directly to the end of the buffer. -// package fwd import "io" diff --git a/internal/go-ole/guid.go b/internal/go-ole/guid.go index 8d20f68fbf..27f9557b3a 100644 --- a/internal/go-ole/guid.go +++ b/internal/go-ole/guid.go @@ -108,9 +108,9 @@ type GUID struct { // // The supplied string may be in any of these formats: // -// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX -// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX +// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} // // The conversion of the supplied string is not case-sensitive. func NewGUID(guid string) *GUID { @@ -216,11 +216,11 @@ func decodeHexChar(c byte) (byte, bool) { // String converts the GUID to string form. It will adhere to this pattern: // -// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} +// {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} // // If the GUID is nil, the string representation of an empty GUID is returned: // -// {00000000-0000-0000-0000-000000000000} +// {00000000-0000-0000-0000-000000000000} func (guid *GUID) String() string { if guid == nil { return emptyGUID diff --git a/internal/gopsutil/common/common.go b/internal/gopsutil/common/common.go index ebf70ea0bf..dc7a927b26 100644 --- a/internal/gopsutil/common/common.go +++ b/internal/gopsutil/common/common.go @@ -114,8 +114,9 @@ func ReadLines(filename string) ([]string, error) { // ReadLines reads contents from file and splits them by new line. // The offset tells at which line number to start. // The count determines the number of lines to read (starting from offset): -// n >= 0: at most n lines -// n < 0: whole file +// +// n >= 0: at most n lines +// n < 0: whole file func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) { f, err := os.Open(filename) if err != nil { @@ -308,7 +309,7 @@ func PathExists(filename string) bool { return false } -//GetEnv retrieves the environment variable key. If it does not exist it returns the default. +// GetEnv retrieves the environment variable key. If it does not exist it returns the default. func GetEnv(key string, dfault string, combineWith ...string) string { value := os.Getenv(key) if value == "" { diff --git a/internal/gopsutil/common/common_windows.go b/internal/gopsutil/common/common_windows.go index 32d4548411..5b450d3192 100644 --- a/internal/gopsutil/common/common_windows.go +++ b/internal/gopsutil/common/common_windows.go @@ -211,9 +211,12 @@ func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, con } // Convert paths using native DOS format like: -// "\Device\HarddiskVolume1\Windows\systemew\file.txt" +// +// "\Device\HarddiskVolume1\Windows\systemew\file.txt" +// // into: -// "C:\Windows\systemew\file.txt" +// +// "C:\Windows\systemew\file.txt" func ConvertDOSPath(p string) string { rawDrive := strings.Join(strings.Split(p, `\`)[:3], `\`) diff --git a/internal/gopsutil/cpu/cpu_solaris.go b/internal/gopsutil/cpu/cpu_solaris.go index 3de0984240..b7942cc0d0 100644 --- a/internal/gopsutil/cpu/cpu_solaris.go +++ b/internal/gopsutil/cpu/cpu_solaris.go @@ -29,7 +29,7 @@ func init() { } } -//sum all values in a float64 map with float64 keys +// sum all values in a float64 map with float64 keys func msum(x map[float64]float64) float64 { total := 0.0 for _, y := range x { diff --git a/internal/gopsutil/net/net_linux.go b/internal/gopsutil/net/net_linux.go index d0bf99efbc..7c7d3bd90c 100644 --- a/internal/gopsutil/net/net_linux.go +++ b/internal/gopsutil/net/net_linux.go @@ -161,7 +161,8 @@ var netProtocols = []string{ // If protocols is empty then all protocols are returned, otherwise // just the protocols in the list are returned. // Available protocols: -// ip,icmp,icmpmsg,tcp,udp,udplite +// +// ip,icmp,icmpmsg,tcp,udp,udplite func ProtoCounters(protocols []string) ([]ProtoCountersStat, error) { return ProtoCountersWithContext(context.Background(), protocols) } diff --git a/internal/gopsutil/net/net_windows.go b/internal/gopsutil/net/net_windows.go index e9d2b2ff90..947bf58232 100644 --- a/internal/gopsutil/net/net_windows.go +++ b/internal/gopsutil/net/net_windows.go @@ -208,7 +208,8 @@ func IOCountersByFileWithContext(ctx context.Context, pernic bool, filename stri // Return a list of network connections // Available kind: -// reference to netConnectionKindMap +// +// reference to netConnectionKindMap func Connections(kind string) ([]ConnectionStat, error) { return ConnectionsWithContext(context.Background(), kind) } diff --git a/internal/isatty/isatty_windows.go b/internal/isatty/isatty_windows.go index 8e3c99171b..367adab997 100644 --- a/internal/isatty/isatty_windows.go +++ b/internal/isatty/isatty_windows.go @@ -42,7 +42,8 @@ func IsTerminal(fd uintptr) bool { // Check pipe name is used for cygwin/msys2 pty. // Cygwin/MSYS2 PTY has a name like: -// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master +// +// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master func isCygwinPipeName(name string) bool { token := strings.Split(name, "-") if len(token) < 5 { diff --git a/internal/msgp/defs.go b/internal/msgp/defs.go index c634eef1df..4597ab1a28 100644 --- a/internal/msgp/defs.go +++ b/internal/msgp/defs.go @@ -5,16 +5,19 @@ // generator implement the Marshaler/Unmarshaler and Encodable/Decodable interfaces. // // This package defines four "families" of functions: -// - AppendXxxx() appends an object to a []byte in MessagePack encoding. -// - ReadXxxxBytes() reads an object from a []byte and returns the remaining bytes. -// - (*Writer).WriteXxxx() writes an object to the buffered *Writer type. -// - (*Reader).ReadXxxx() reads an object from a buffered *Reader type. +// - AppendXxxx() appends an object to a []byte in MessagePack encoding. +// - ReadXxxxBytes() reads an object from a []byte and returns the remaining bytes. +// - (*Writer).WriteXxxx() writes an object to the buffered *Writer type. +// - (*Reader).ReadXxxx() reads an object from a buffered *Reader type. // // Once a type has satisfied the `Encodable` and `Decodable` interfaces, // it can be written and read from arbitrary `io.Writer`s and `io.Reader`s using -// msgp.Encode(io.Writer, msgp.Encodable) +// +// msgp.Encode(io.Writer, msgp.Encodable) +// // and -// msgp.Decode(io.Reader, msgp.Decodable) +// +// msgp.Decode(io.Reader, msgp.Decodable) // // There are also methods for converting MessagePack to JSON without // an explicit de-serialization step. diff --git a/internal/msgp/elsize.go b/internal/msgp/elsize.go index 95762e7eeb..0bf2b9fb05 100644 --- a/internal/msgp/elsize.go +++ b/internal/msgp/elsize.go @@ -4,7 +4,6 @@ package msgp // plus type information. gives us // constant-time type information // for traversing composite objects. -// var sizes = [256]bytespec{ mnil: {size: 1, extra: constsize, typ: NilType}, mfalse: {size: 1, extra: constsize, typ: BoolType}, diff --git a/internal/msgp/errors.go b/internal/msgp/errors.go index 168c9c3371..d0757a4bcb 100644 --- a/internal/msgp/errors.go +++ b/internal/msgp/errors.go @@ -70,7 +70,6 @@ func Resumable(e error) bool { // // ErrShortBytes is not wrapped with any context due to backward compatibility // issues with the public API. -// func WrapError(err error, ctx ...interface{}) error { switch e := err.(type) { case errShort: diff --git a/internal/msgp/extension.go b/internal/msgp/extension.go index b2e110851b..310a4749be 100644 --- a/internal/msgp/extension.go +++ b/internal/msgp/extension.go @@ -30,7 +30,7 @@ var extensionReg = make(map[int8]func() Extension) // // For example, if you wanted to register a user-defined struct: // -// msgp.RegisterExtension(10, func() msgp.Extension { &MyExtension{} }) +// msgp.RegisterExtension(10, func() msgp.Extension { &MyExtension{} }) // // RegisterExtension will panic if you call it multiple times // with the same 'typ' argument, or if you use a reserved diff --git a/internal/msgp/file.go b/internal/msgp/file.go index 76f31c2ad9..b0e86a978b 100644 --- a/internal/msgp/file.go +++ b/internal/msgp/file.go @@ -21,7 +21,6 @@ import ( // is only efficient for large files; small // files are best read and written using // the ordinary streaming interfaces. -// func ReadFile(dst Unmarshaler, file *os.File) error { stat, err := file.Stat() if err != nil { diff --git a/internal/msgp/read_bytes.go b/internal/msgp/read_bytes.go index d6b300ee9e..61f7b7124f 100644 --- a/internal/msgp/read_bytes.go +++ b/internal/msgp/read_bytes.go @@ -896,7 +896,7 @@ func ReadStringBytes(b []byte) (string, []byte, error) { // into a slice of bytes. 'v' is the value of // the 'str' object, which may reside in memory // pointed to by 'scratch.' 'o' is the remaining bytes -// in 'b.'' +// in 'b.” // Possible errors: // - ErrShortBytes (b not long enough) // - TypeError{} (not 'str' type) diff --git a/internal/msgp/write.go b/internal/msgp/write.go index 407ec1f893..8b3e6cb2d0 100644 --- a/internal/msgp/write.go +++ b/internal/msgp/write.go @@ -622,12 +622,12 @@ func (mw *Writer) WriteTime(t time.Time) error { // WriteIntf writes the concrete type of 'v'. // WriteIntf will error if 'v' is not one of the following: -// - A bool, float, string, []byte, int, uint, or complex -// - A map of supported types (with string keys) -// - An array or slice of supported types -// - A pointer to a supported type -// - A type that satisfies the msgp.Encodable interface -// - A type that satisfies the msgp.Extension interface +// - A bool, float, string, []byte, int, uint, or complex +// - A map of supported types (with string keys) +// - An array or slice of supported types +// - A pointer to a supported type +// - A type that satisfies the msgp.Encodable interface +// - A type that satisfies the msgp.Extension interface func (mw *Writer) WriteIntf(v interface{}) error { if v == nil { return mw.WriteNil() diff --git a/internal/msgp/write_bytes.go b/internal/msgp/write_bytes.go index 09dd0aa028..8a55552c88 100644 --- a/internal/msgp/write_bytes.go +++ b/internal/msgp/write_bytes.go @@ -315,13 +315,13 @@ func AppendMapStrIntf(b []byte, m map[string]interface{}) ([]byte, error) { // AppendIntf appends the concrete type of 'i' to the // provided []byte. 'i' must be one of the following: -// - 'nil' -// - A bool, float, string, []byte, int, uint, or complex -// - A map[string]interface{} or map[string]string -// - A []T, where T is another supported type -// - A *T, where T is another supported type -// - A type that satisfieds the msgp.Marshaler interface -// - A type that satisfies the msgp.Extension interface +// - 'nil' +// - A bool, float, string, []byte, int, uint, or complex +// - A map[string]interface{} or map[string]string +// - A []T, where T is another supported type +// - A *T, where T is another supported type +// - A type that satisfieds the msgp.Marshaler interface +// - A type that satisfies the msgp.Extension interface func AppendIntf(b []byte, i interface{}) ([]byte, error) { if i == nil { return AppendNil(b), nil diff --git a/internal/schema/doc.go b/internal/schema/doc.go index aae9f33f9d..fff0fe7616 100644 --- a/internal/schema/doc.go +++ b/internal/schema/doc.go @@ -60,14 +60,14 @@ certain fields, use a dash for the name and it will be ignored: The supported field types in the destination struct are: - * bool - * float variants (float32, float64) - * int variants (int, int8, int16, int32, int64) - * string - * uint variants (uint, uint8, uint16, uint32, uint64) - * struct - * a pointer to one of the above types - * a slice or a pointer to a slice of one of the above types + - bool + - float variants (float32, float64) + - int variants (int, int8, int16, int32, int64) + - string + - uint variants (uint, uint8, uint16, uint32, uint64) + - struct + - a pointer to one of the above types + - a slice or a pointer to a slice of one of the above types Non-supported types are simply ignored, however custom types can be registered to be converted. diff --git a/internal/template/html/html.go b/internal/template/html/html.go index 4ed0ff2afa..da9d8c5211 100644 --- a/internal/template/html/html.go +++ b/internal/template/html/html.go @@ -56,7 +56,7 @@ func New(directory, extension string) *Engine { return engine } -//NewFileSystem ... +// NewFileSystem ... func NewFileSystem(fs http.FileSystem, extension string) *Engine { engine := &Engine{ left: "{{", diff --git a/internal/uuid/dce.go b/internal/uuid/dce.go index b98c8d68cf..d67ada3c9c 100644 --- a/internal/uuid/dce.go +++ b/internal/uuid/dce.go @@ -43,7 +43,7 @@ func NewDCESecurity(domain Domain, id uint32) (UUID, error) { // NewDCEPerson returns a DCE Security (Version 2) UUID in the person // domain with the id returned by os.Getuid. // -// NewDCESecurity(Person, uint32(os.Getuid())) +// NewDCESecurity(Person, uint32(os.Getuid())) func NewDCEPerson() (UUID, error) { return NewDCESecurity(Person, uint32(os.Getuid())) } @@ -51,7 +51,7 @@ func NewDCEPerson() (UUID, error) { // NewDCEGroup returns a DCE Security (Version 2) UUID in the group // domain with the id returned by os.Getgid. // -// NewDCESecurity(Group, uint32(os.Getgid())) +// NewDCESecurity(Group, uint32(os.Getgid())) func NewDCEGroup() (UUID, error) { return NewDCESecurity(Group, uint32(os.Getgid())) } diff --git a/internal/uuid/hash.go b/internal/uuid/hash.go index b174616315..0165f723b2 100644 --- a/internal/uuid/hash.go +++ b/internal/uuid/hash.go @@ -39,7 +39,7 @@ func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID { // NewMD5 returns a new MD5 (Version 3) UUID based on the // supplied name space and data. It is the same as calling: // -// NewHash(md5.New(), space, data, 3) +// NewHash(md5.New(), space, data, 3) func NewMD5(space UUID, data []byte) UUID { return NewHash(md5.New(), space, data, 3) } @@ -47,7 +47,7 @@ func NewMD5(space UUID, data []byte) UUID { // NewSHA1 returns a new SHA1 (Version 5) UUID based on the // supplied name space and data. It is the same as calling: // -// NewHash(sha1.New(), space, data, 5) +// NewHash(sha1.New(), space, data, 5) func NewSHA1(space UUID, data []byte) UUID { return NewHash(sha1.New(), space, data, 5) } diff --git a/internal/uuid/version4.go b/internal/uuid/version4.go index c110465db5..3d09259011 100644 --- a/internal/uuid/version4.go +++ b/internal/uuid/version4.go @@ -9,7 +9,7 @@ import "io" // New creates a new random UUID or panics. New is equivalent to // the expression // -// uuid.Must(uuid.NewRandom()) +// uuid.Must(uuid.NewRandom()) func New() UUID { return Must(NewRandom()) } @@ -21,11 +21,11 @@ func New() UUID { // // A note about uniqueness derived from the UUID Wikipedia entry: // -// Randomly generated UUIDs have 122 random bits. One's annual risk of being -// hit by a meteorite is estimated to be one chance in 17 billion, that -// means the probability is about 0.00000000006 (6 × 10−11), -// equivalent to the odds of creating a few tens of trillions of UUIDs in a -// year and having one duplicate. +// Randomly generated UUIDs have 122 random bits. One's annual risk of being +// hit by a meteorite is estimated to be one chance in 17 billion, that +// means the probability is about 0.00000000006 (6 × 10−11), +// equivalent to the odds of creating a few tens of trillions of UUIDs in a +// year and having one duplicate. func NewRandom() (UUID, error) { return NewRandomFromReader(rander) } diff --git a/internal/wmi/swbemservices.go b/internal/wmi/swbemservices.go index 88b2442b96..925b5ac71d 100644 --- a/internal/wmi/swbemservices.go +++ b/internal/wmi/swbemservices.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows package wmi diff --git a/internal/wmi/wmi.go b/internal/wmi/wmi.go index 4ae404f2a7..a324b989ad 100644 --- a/internal/wmi/wmi.go +++ b/internal/wmi/wmi.go @@ -1,3 +1,4 @@ +//go:build windows // +build windows /* @@ -20,7 +21,6 @@ Example code to print names of running processes: println(i, v.Name) } } - */ package wmi diff --git a/listen.go b/listen.go index 89a6fba6cb..d2ab361ef4 100644 --- a/listen.go +++ b/listen.go @@ -47,8 +47,8 @@ func (app *App) Listener(ln net.Listener) error { // Listen serves HTTP requests from the given addr. // -// app.Listen(":8080") -// app.Listen("127.0.0.1:8080") +// app.Listen(":8080") +// app.Listen("127.0.0.1:8080") func (app *App) Listen(addr string) error { // Start prefork if app.config.Prefork { @@ -75,7 +75,8 @@ func (app *App) Listen(addr string) error { // ListenTLS serves HTTPS requests from the given addr. // certFile and keyFile are the paths to TLS certificate and key file: -// app.ListenTLS(":8080", "./cert.pem", "./cert.key") +// +// app.ListenTLS(":8080", "./cert.pem", "./cert.key") func (app *App) ListenTLS(addr, certFile, keyFile string) error { // Check for valid cert/key path if len(certFile) == 0 || len(keyFile) == 0 { @@ -116,7 +117,8 @@ func (app *App) ListenTLS(addr, certFile, keyFile string) error { // ListenMutualTLS serves HTTPS requests from the given addr. // certFile, keyFile and clientCertFile are the paths to TLS certificate and key file: -// app.ListenMutualTLS(":8080", "./cert.pem", "./cert.key", "./client.pem") +// +// app.ListenMutualTLS(":8080", "./cert.pem", "./cert.key", "./client.pem") func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) error { // Check for valid cert/key path if len(certFile) == 0 || len(keyFile) == 0 { diff --git a/middleware/cache/manager_msgp.go b/middleware/cache/manager_msgp.go index 6d8fd8536d..c02d82546c 100644 --- a/middleware/cache/manager_msgp.go +++ b/middleware/cache/manager_msgp.go @@ -58,7 +58,7 @@ func (z *item) DecodeMsg(dc *msgp.Reader) (err error) { if z.headers == nil && zcmr > 0 { z.headers = make(map[string][]byte, zcmr) } else if len(z.headers) > 0 { - for key, _ := range z.headers { + for key := range z.headers { delete(z.headers, key) } } @@ -252,7 +252,7 @@ func (z *item) UnmarshalMsg(bts []byte) (o []byte, err error) { if z.headers == nil && zwht > 0 { z.headers = make(map[string][]byte, zwht) } else if len(z.headers) > 0 { - for key, _ := range z.headers { + for key := range z.headers { delete(z.headers, key) } } From 95abdacba071c788f72bee02673b41c65c0e3004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Tue, 16 Aug 2022 09:05:35 +0300 Subject: [PATCH 034/141] :sparkles: feature: add XML to context. (#2003) * :sparkles: feature: add XML to context. * Update app.go --- app.go | 11 +++++++++ ctx.go | 19 ++++++++++----- ctx_test.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++ utils/xml.go | 4 ++++ utils/xml_test.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 utils/xml.go create mode 100644 utils/xml_test.go diff --git a/app.go b/app.go index 3e38fab4b2..c28aeb6389 100644 --- a/app.go +++ b/app.go @@ -23,6 +23,7 @@ import ( "time" "encoding/json" + "encoding/xml" "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" @@ -324,6 +325,13 @@ type Config struct { // Default: json.Unmarshal JSONDecoder utils.JSONUnmarshal `json:"-"` + // XMLEncoder set by an external client of Fiber it will use the provided implementation of a + // XMLMarshal + // + // Allowing for flexibility in using another XML library for encoding + // Default: xml.Marshal + XMLEncoder utils.XMLMarshal `json:"-"` + // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only) // WARNING: When prefork is set to true, only "tcp4" and "tcp6" can be chose. // @@ -513,6 +521,9 @@ func New(config ...Config) *App { if app.config.JSONDecoder == nil { app.config.JSONDecoder = json.Unmarshal } + if app.config.XMLEncoder == nil { + app.config.XMLEncoder = xml.Marshal + } if app.config.Network == "" { app.config.Network = NetworkTCP4 } diff --git a/ctx.go b/ctx.go index 004cf3ec7a..30aed27682 100644 --- a/ctx.go +++ b/ctx.go @@ -518,12 +518,7 @@ func (c *Ctx) Format(body interface{}) error { case "txt": return c.SendString(b) case "xml": - raw, err := xml.Marshal(body) - if err != nil { - return fmt.Errorf("error serializing xml: %v", body) - } - c.fasthttp.Response.SetBody(raw) - return nil + return c.XML(body) } return c.SendString(b) } @@ -736,6 +731,18 @@ func (c *Ctx) JSONP(data interface{}, callback ...string) error { return c.SendString(result) } +// XML converts any interface or string to XML. +// This method also sets the content header to application/xml. +func (c *Ctx) XML(data interface{}) error { + raw, err := c.app.config.XMLEncoder(data) + if err != nil { + return err + } + c.fasthttp.Response.SetBodyRaw(raw) + c.fasthttp.Response.Header.SetContentType(MIMEApplicationXML) + return nil +} + // Links joins the links followed by the property to populate the response's Link HTTP header field. func (c *Ctx) Links(link ...string) { if len(link) == 0 { diff --git a/ctx_test.go b/ctx_test.go index 325b67d78b..3af6e806b0 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -12,6 +12,7 @@ import ( "bytes" "compress/gzip" "context" + "encoding/xml" "errors" "fmt" "io" @@ -2134,6 +2135,65 @@ func Benchmark_Ctx_JSONP(b *testing.B) { utils.AssertEqual(b, `emit({"Name":"Grame","Age":20});`, string(c.Response().Body())) } +// go test -run Test_Ctx_XML +func Test_Ctx_XML(t *testing.T) { + t.Parallel() + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + + utils.AssertEqual(t, true, c.JSON(complex(1, 1)) != nil) + + type xmlResult struct { + XMLName xml.Name `xml:"Users"` + Names []string `xml:"Names"` + Ages []int `xml:"Ages"` + } + + c.XML(xmlResult{ + Names: []string{"Grame", "John"}, + Ages: []int{1, 12, 20}, + }) + + utils.AssertEqual(t, `GrameJohn11220`, string(c.Response().Body())) + utils.AssertEqual(t, "application/xml", string(c.Response().Header.Peek("content-type"))) + + testEmpty := func(v interface{}, r string) { + err := c.XML(v) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, r, string(c.Response().Body())) + } + + testEmpty(nil, "") + testEmpty("", ``) + testEmpty(0, "0") + testEmpty([]int{}, "") +} + +// go test -run=^$ -bench=Benchmark_Ctx_XML -benchmem -count=4 +func Benchmark_Ctx_XML(b *testing.B) { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + type SomeStruct struct { + Name string `xml:"Name"` + Age uint8 `xml:"Age"` + } + data := SomeStruct{ + Name: "Grame", + Age: 20, + } + var err error + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + err = c.XML(data) + } + + utils.AssertEqual(b, nil, err) + utils.AssertEqual(b, `Grame20`, string(c.Response().Body())) +} + // go test -run Test_Ctx_Links func Test_Ctx_Links(t *testing.T) { t.Parallel() diff --git a/utils/xml.go b/utils/xml.go new file mode 100644 index 0000000000..cc6a024a3b --- /dev/null +++ b/utils/xml.go @@ -0,0 +1,4 @@ +package utils + +// XMLMarshal returns the XML encoding of v. +type XMLMarshal func(v interface{}) ([]byte, error) diff --git a/utils/xml_test.go b/utils/xml_test.go new file mode 100644 index 0000000000..bbb11708d6 --- /dev/null +++ b/utils/xml_test.go @@ -0,0 +1,59 @@ +package utils + +import ( + "encoding/xml" + "testing" +) + +type serversXMLStructure struct { + XMLName xml.Name `xml:"servers"` + Version string `xml:"version,attr"` + Servers []serverXMLStructure `xml:"server"` +} + +type serverXMLStructure struct { + XMLName xml.Name `xml:"server"` + Name string `xml:"name"` +} + +var xmlString = `fiber onefiber two` + +func Test_GolangXMLEncoder(t *testing.T) { + t.Parallel() + + var ( + ss = &serversXMLStructure{ + Version: "1", + Servers: []serverXMLStructure{ + {Name: "fiber one"}, + {Name: "fiber two"}, + }, + } + xmlEncoder XMLMarshal = xml.Marshal + ) + + raw, err := xmlEncoder(ss) + AssertEqual(t, err, nil) + + AssertEqual(t, string(raw), xmlString) +} + +func Test_DefaultXMLEncoder(t *testing.T) { + t.Parallel() + + var ( + ss = &serversXMLStructure{ + Version: "1", + Servers: []serverXMLStructure{ + {Name: "fiber one"}, + {Name: "fiber two"}, + }, + } + xmlEncoder XMLMarshal = xml.Marshal + ) + + raw, err := xmlEncoder(ss) + AssertEqual(t, err, nil) + + AssertEqual(t, string(raw), xmlString) +} From 2517944c80e7f02aeac49889c552493f88ba43c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Tue, 16 Aug 2022 09:05:50 +0300 Subject: [PATCH 035/141] :sparkles: feature: route constraints (#1998) * Segment parameters constraints and determining it's type * add parsing for constraints. * fix tests * add tests, benchs & some fixes. * fix regex & datetime tests. * clean up constraint parser, multiple constraint support. * update * regex customization. * constants, remove variadic methods. * add some benchs, refactor constraint check funtion. * more readable conditions * fix tests * precompile regex * precompile regex when parsing the route * update comments Co-authored-by: wernerr Co-authored-by: Mohab Abd El-Dayem Co-authored-by: RW --- helpers.go | 18 ++++ path.go | 297 ++++++++++++++++++++++++++++++++++++++++++++++++--- path_test.go | 260 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 563 insertions(+), 12 deletions(-) diff --git a/helpers.go b/helpers.go index e49fbfd352..f5e1ee55d8 100644 --- a/helpers.go +++ b/helpers.go @@ -695,3 +695,21 @@ const ( CookieSameSiteStrictMode = "strict" CookieSameSiteNoneMode = "none" ) + +// Route Constraints +const ( + ConstraintInt = "int" + ConstraintBool = "bool" + ConstraintFloat = "float" + ConstraintAlpha = "alpha" + ConstraintGuid = "guid" + ConstraintMinLen = "minLen" + ConstraintMaxLen = "maxLen" + ConstraintExactLen = "exactLen" + ConstraintBetweenLen = "betweenLen" + ConstraintMin = "min" + ConstraintMax = "max" + ConstraintRange = "range" + ConstraintDatetime = "datetime" + ConstraintRegex = "regex" +) diff --git a/path.go b/path.go index 21a3d6c4d0..16d1b2a37d 100644 --- a/path.go +++ b/path.go @@ -7,9 +7,13 @@ package fiber import ( + "regexp" "strconv" "strings" + "time" + "unicode" + "github.com/gofiber/fiber/v2/internal/uuid" "github.com/gofiber/fiber/v2/utils" ) @@ -33,20 +37,54 @@ type routeSegment struct { IsGreedy bool // indicates whether the parameter is greedy or not, is used with wildcard and plus IsOptional bool // indicates whether the parameter is optional or not // common information - IsLast bool // shows if the segment is the last one for the route - HasOptionalSlash bool // segment has the possibility of an optional slash - Length int // length of the parameter for segment, when its 0 then the length is undetermined + IsLast bool // shows if the segment is the last one for the route + HasOptionalSlash bool // segment has the possibility of an optional slash + Constraints []*Constraint // Constraint type if segment is a parameter, if not it will be set to noConstraint by default + Length int // length of the parameter for segment, when its 0 then the length is undetermined // future TODO: add support for optional groups "/abc(/def)?" } // different special routing signs const ( - wildcardParam byte = '*' // indicates a optional greedy parameter - plusParam byte = '+' // indicates a required greedy parameter - optionalParam byte = '?' // concludes a parameter by name and makes it optional - paramStarterChar byte = ':' // start character for a parameter with name - slashDelimiter byte = '/' // separator for the route, unlike the other delimiters this character at the end can be optional - escapeChar byte = '\\' // escape character + wildcardParam byte = '*' // indicates a optional greedy parameter + plusParam byte = '+' // indicates a required greedy parameter + optionalParam byte = '?' // concludes a parameter by name and makes it optional + paramStarterChar byte = ':' // start character for a parameter with name + slashDelimiter byte = '/' // separator for the route, unlike the other delimiters this character at the end can be optional + escapeChar byte = '\\' // escape character + paramConstraintStart byte = '<' // start of type constraint for a parameter + paramConstraintEnd byte = '>' // end of type constraint for a parameter + paramConstraintSeparator byte = ';' // separator of type constraints for a parameter + paramConstraintDataStart byte = '(' // start of data of type constraint for a parameter + paramConstraintDataEnd byte = ')' // end of data of type constraint for a parameter + paramConstraintDataSeparator byte = ',' // separator of datas of type constraint for a parameter +) + +// parameter constraint types +type TypeConstraint int16 + +type Constraint struct { + ID TypeConstraint + RegexCompiler *regexp.Regexp + Data []string +} + +const ( + noConstraint TypeConstraint = iota + 1 + intConstraint + boolConstraint + floatConstraint + alphaConstraint + datetimeConstraint + guidConstraint + minLenConstraint + maxLenConstraint + exactLenConstraint + betweenLenConstraint + minConstraint + maxConstraint + rangeConstraint + regexConstraint ) // list of possible parameter and segment delimiter @@ -61,6 +99,18 @@ var ( parameterDelimiterChars = append([]byte{paramStarterChar, escapeChar}, routeDelimiter...) // list of chars to find the end of a parameter parameterEndChars = append([]byte{optionalParam}, parameterDelimiterChars...) + // list of parameter constraint start + parameterConstraintStartChars = []byte{paramConstraintStart} + // list of parameter constraint end + parameterConstraintEndChars = []byte{paramConstraintEnd} + // list of parameter separator + parameterConstraintSeparatorChars = []byte{paramConstraintSeparator} + // list of parameter constraint data start + parameterConstraintDataStartChars = []byte{paramConstraintDataStart} + // list of parameter constraint data end + parameterConstraintDataEndChars = []byte{paramConstraintDataEnd} + // list of parameter constraint data separator + parameterConstraintDataSeparatorChars = []byte{paramConstraintDataSeparator} ) // parseRoute analyzes the route and divides it into segments for constant areas and parameters, @@ -177,8 +227,16 @@ func (routeParser *routeParser) analyseConstantPart(pattern string, nextParamPos func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *routeSegment) { isWildCard := pattern[0] == wildcardParam isPlusParam := pattern[0] == plusParam - parameterEndPosition := findNextNonEscapedCharsetPosition(pattern[1:], parameterEndChars) + var parameterEndPosition int + if strings.ContainsRune(pattern, rune(paramConstraintStart)) && strings.ContainsRune(pattern, rune(paramConstraintEnd)) { + parameterEndPosition = findNextCharsetPositionConstraint(pattern[1:], parameterEndChars) + } else { + parameterEndPosition = findNextNonEscapedCharsetPosition(pattern[1:], parameterEndChars) + } + + parameterConstraintStart := -1 + parameterConstraintEnd := -1 // handle wildcard end if isWildCard || isPlusParam { parameterEndPosition = 0 @@ -187,10 +245,53 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r } else if !isInCharset(pattern[parameterEndPosition+1], parameterDelimiterChars) { parameterEndPosition++ } + + // find constraint part if exists in the parameter part and remove it + if parameterEndPosition > 0 { + parameterConstraintStart = findNextNonEscapedCharsetPosition(pattern[0:parameterEndPosition], parameterConstraintStartChars) + parameterConstraintEnd = findNextNonEscapedCharsetPosition(pattern[0:parameterEndPosition+1], parameterConstraintEndChars) + } + // cut params part processedPart := pattern[0 : parameterEndPosition+1] - paramName := RemoveEscapeChar(GetTrimmedParam(processedPart)) + + // Check has constraint + var constraints []*Constraint + + if hasConstraint := (parameterConstraintStart != -1 && parameterConstraintEnd != -1); hasConstraint { + constraintString := pattern[parameterConstraintStart+1 : parameterConstraintEnd] + userconstraints := strings.Split(constraintString, string(parameterConstraintSeparatorChars)) + constraints = make([]*Constraint, 0, len(userconstraints)) + + for _, c := range userconstraints { + start := findNextNonEscapedCharsetPosition(c, parameterConstraintDataStartChars) + end := findNextNonEscapedCharsetPosition(c, parameterConstraintDataEndChars) + + // Assign constraint + if start != -1 && end != -1 { + constraint := &Constraint{ + ID: getParamConstraintType(c[:start]), + Data: strings.Split(RemoveEscapeChar(c[start+1:end]), string(parameterConstraintDataSeparatorChars)), + } + + // Precompile regex if has regex constraint + if constraint.ID == regexConstraint { + constraint.RegexCompiler = regexp.MustCompile(constraint.Data[0]) + } + + constraints = append(constraints, constraint) + } else { + constraints = append(constraints, &Constraint{ + ID: getParamConstraintType(c), + Data: []string{}, + }) + } + } + + paramName = RemoveEscapeChar(GetTrimmedParam(pattern[0:parameterConstraintStart])) + } + // add access iterator to wildcard and plus if isWildCard { routeParser.wildCardCount++ @@ -200,12 +301,18 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r paramName += strconv.Itoa(routeParser.plusCount) } - return processedPart, &routeSegment{ + segment := &routeSegment{ ParamName: paramName, IsParam: true, IsOptional: isWildCard || pattern[parameterEndPosition] == optionalParam, IsGreedy: isWildCard || isPlusParam, } + + if len(constraints) > 0 { + segment.Constraints = constraints + } + + return processedPart, segment } // isInCharset check is the given character in the charset list @@ -230,6 +337,34 @@ func findNextCharsetPosition(search string, charset []byte) int { return nextPosition } +// findNextCharsetPositionConstraint search the next char position from the charset +// unlike findNextCharsetPosition, it takes care of constraint start-end chars to parse route pattern +func findNextCharsetPositionConstraint(search string, charset []byte) int { + nextPosition := -1 + constraintStart := -1 + constraintEnd := -1 + + for _, char := range charset { + pos := strings.IndexByte(search, char) + + if char == paramConstraintStart { + constraintStart = pos + } + + if char == paramConstraintEnd { + constraintEnd = pos + } + //fmt.Println(string(char)) + if pos != -1 && (pos < nextPosition || nextPosition == -1) { + if pos > constraintStart && pos < constraintEnd { + nextPosition = pos + } + } + } + + return nextPosition +} + // findNextNonEscapedCharsetPosition search the next char position from the charset and skip the escaped characters func findNextNonEscapedCharsetPosition(search string, charset []byte) int { pos := findNextCharsetPosition(search, charset) @@ -272,6 +407,14 @@ func (routeParser *routeParser) getMatch(detectionPath, path string, params *[ma } // take over the params positions params[paramsIterator] = path[:i] + + // check constraint + for _, c := range segment.Constraints { + if matched := c.CheckConstraint(params[paramsIterator]); !matched { + return false + } + } + paramsIterator++ } @@ -370,3 +513,133 @@ func RemoveEscapeChar(word string) string { } return word } + +func getParamConstraintType(constraintPart string) TypeConstraint { + switch constraintPart { + case ConstraintInt: + return intConstraint + case ConstraintBool: + return boolConstraint + case ConstraintFloat: + return floatConstraint + case ConstraintAlpha: + return alphaConstraint + case ConstraintGuid: + return guidConstraint + case ConstraintMinLen: + return minLenConstraint + case ConstraintMaxLen: + return maxLenConstraint + case ConstraintExactLen: + return exactLenConstraint + case ConstraintBetweenLen: + return betweenLenConstraint + case ConstraintMin: + return minConstraint + case ConstraintMax: + return maxConstraint + case ConstraintRange: + return rangeConstraint + case ConstraintDatetime: + return datetimeConstraint + case ConstraintRegex: + return regexConstraint + default: + return noConstraint + } + +} + +func (c *Constraint) CheckConstraint(param string) bool { + var err error + var num int + + // check data exists + needOneData := []TypeConstraint{minLenConstraint, maxLenConstraint, exactLenConstraint, minConstraint, maxConstraint, datetimeConstraint, regexConstraint} + needTwoData := []TypeConstraint{betweenLenConstraint, rangeConstraint} + + for _, data := range needOneData { + if c.ID == data && len(c.Data) == 0 { + return false + } + } + + for _, data := range needTwoData { + if c.ID == data && len(c.Data) < 2 { + return false + } + } + + // check constraints + switch c.ID { + case intConstraint: + _, err = strconv.Atoi(param) + case boolConstraint: + _, err = strconv.ParseBool(param) + case floatConstraint: + _, err = strconv.ParseFloat(param, 32) + case alphaConstraint: + for _, r := range param { + if !unicode.IsLetter(r) { + return false + } + } + case guidConstraint: + _, err = uuid.Parse(param) + case minLenConstraint: + data, _ := strconv.Atoi(c.Data[0]) + + if len(param) < data { + return false + } + case maxLenConstraint: + data, _ := strconv.Atoi(c.Data[0]) + + if len(param) > data { + return false + } + case exactLenConstraint: + data, _ := strconv.Atoi(c.Data[0]) + + if len(param) != data { + return false + } + case betweenLenConstraint: + data, _ := strconv.Atoi(c.Data[0]) + data2, _ := strconv.Atoi(c.Data[1]) + length := len(param) + if length < data || length > data2 { + return false + } + case minConstraint: + data, _ := strconv.Atoi(c.Data[0]) + num, err = strconv.Atoi(param) + + if num < data { + return false + } + case maxConstraint: + data, _ := strconv.Atoi(c.Data[0]) + num, err = strconv.Atoi(param) + + if num > data { + return false + } + case rangeConstraint: + data, _ := strconv.Atoi(c.Data[0]) + data2, _ := strconv.Atoi(c.Data[1]) + num, err = strconv.Atoi(param) + + if num < data || num > data2 { + return false + } + case datetimeConstraint: + _, err = time.Parse(c.Data[0], param) + case regexConstraint: + if match := c.RegexCompiler.MatchString(param); !match { + return false + } + } + + return err == nil +} diff --git a/path_test.go b/path_test.go index 9a237520dd..7ad514098f 100644 --- a/path_test.go +++ b/path_test.go @@ -431,6 +431,124 @@ func Test_Path_matchParams(t *testing.T) { {url: "/api", params: nil, match: false}, {url: "/api/:test", params: nil, match: false}, }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/true", params: []string{"true"}, match: true}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/8728382.5", params: []string{"8728382.5"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/#!?", params: []string{"#!?"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/f0fa66cc-d22e-445b-866d-1d76e776371d", params: []string{"f0fa66cc-d22e-445b-866d-1d76e776371d"}, match: true}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/123", params: []string{"123"}, match: false}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/ent", params: []string{"ent"}, match: true}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: false}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/e", params: []string{"e"}, match: false}, + {url: "/api/v1/en", params: []string{"en"}, match: true}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/e", params: []string{"e"}, match: false}, + {url: "/api/v1/en", params: []string{"en"}, match: true}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/1", params: []string{"1"}, match: false}, + {url: "/api/v1/5", params: []string{"5"}, match: true}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/1", params: []string{"1"}, match: true}, + {url: "/api/v1/5", params: []string{"5"}, match: true}, + {url: "/api/v1/15", params: []string{"15"}, match: false}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/9", params: []string{"9"}, match: true}, + {url: "/api/v1/5", params: []string{"5"}, match: true}, + {url: "/api/v1/15", params: []string{"15"}, match: false}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/15", params: []string{"15"}, match: false}, + {url: "/api/v1/peach", params: []string{"peach"}, match: true}, + {url: "/api/v1/p34ch", params: []string{"p34ch"}, match: false}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) } func Test_Utils_GetTrimmedParam(t *testing.T) { @@ -519,4 +637,146 @@ func Benchmark_Path_matchParams(t *testing.B) { {url: "/api/v2", params: nil, match: false}, {url: "/api/v1/", params: nil, match: false}, }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/true", params: []string{"true"}, match: true}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/8728382.5", params: []string{"8728382.5"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/#!?", params: []string{"#!?"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/f0fa66cc-d22e-445b-866d-1d76e776371d", params: []string{"f0fa66cc-d22e-445b-866d-1d76e776371d"}, match: true}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/123", params: []string{"123"}, match: false}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/ent", params: []string{"ent"}, match: true}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: false}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/e", params: []string{"e"}, match: false}, + {url: "/api/v1/en", params: []string{"en"}, match: true}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/e", params: []string{"e"}, match: false}, + {url: "/api/v1/en", params: []string{"en"}, match: true}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/12345", params: []string{"12345"}, match: true}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/1", params: []string{"1"}, match: false}, + {url: "/api/v1/5", params: []string{"5"}, match: true}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/1", params: []string{"1"}, match: true}, + {url: "/api/v1/5", params: []string{"5"}, match: true}, + {url: "/api/v1/15", params: []string{"15"}, match: false}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/9", params: []string{"9"}, match: true}, + {url: "/api/v1/5", params: []string{"5"}, match: true}, + {url: "/api/v1/15", params: []string{"15"}, match: false}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/15", params: []string{"15"}, match: false}, + {url: "/api/v1/peach", params: []string{"peach"}, match: true}, + {url: "/api/v1/p34ch", params: []string{"p34ch"}, match: false}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/123", params: []string{"123"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) +} + +func Test_Path_matchParams0(t *testing.T) { + t.Parallel() + type testparams struct { + url string + params []string + match bool + partialCheck bool + } + var ctxParams [maxParams]string + testCase := func(r string, cases []testparams) { + parser := parseRoute(r) + for _, c := range cases { + match := parser.getMatch(c.url, c.url, &ctxParams, c.partialCheck) + utils.AssertEqual(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) + if match && len(c.params) > 0 { + utils.AssertEqual(t, c.params[0:len(c.params)], ctxParams[0:len(c.params)], fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) + } + } + } + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true}, + }) } From aa4d2c3f655692b488d66298c5496b502c4dc096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Tue, 16 Aug 2022 09:13:38 +0300 Subject: [PATCH 036/141] :memo: docs: make Hooks public (#2015) --- app.go | 4 ++-- hooks.go | 35 ++++++++++++++++++----------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/app.go b/app.go index c28aeb6389..ebc5ac1261 100644 --- a/app.go +++ b/app.go @@ -110,7 +110,7 @@ type App struct { // Mounted and main apps appList map[string]*App // Hooks - hooks *hooks + hooks *Hooks // Latest route & group latestRoute *Route latestGroup *Group @@ -819,7 +819,7 @@ func (app *App) Server() *fasthttp.Server { } // Hooks returns the hook struct to register hooks. -func (app *App) Hooks() *hooks { +func (app *App) Hooks() *Hooks { return app.hooks } diff --git a/hooks.go b/hooks.go index 571ac25210..0cae4a53a0 100644 --- a/hooks.go +++ b/hooks.go @@ -9,7 +9,8 @@ type OnListenHandler = func() error type OnShutdownHandler = OnListenHandler type OnForkHandler = func(int) error -type hooks struct { +// Hooks is a struct to use it with App. +type Hooks struct { // Embed app app *App @@ -23,8 +24,8 @@ type hooks struct { onFork []OnForkHandler } -func newHooks(app *App) *hooks { - return &hooks{ +func newHooks(app *App) *Hooks { + return &Hooks{ app: app, onRoute: make([]OnRouteHandler, 0), onGroup: make([]OnGroupHandler, 0), @@ -38,7 +39,7 @@ func newHooks(app *App) *hooks { // OnRoute is a hook to execute user functions on each route registeration. // Also you can get route properties by route parameter. -func (h *hooks) OnRoute(handler ...OnRouteHandler) { +func (h *Hooks) OnRoute(handler ...OnRouteHandler) { h.app.mutex.Lock() h.onRoute = append(h.onRoute, handler...) h.app.mutex.Unlock() @@ -48,7 +49,7 @@ func (h *hooks) OnRoute(handler ...OnRouteHandler) { // Also you can get route properties by route parameter. // // WARN: OnName only works with naming routes, not groups. -func (h *hooks) OnName(handler ...OnNameHandler) { +func (h *Hooks) OnName(handler ...OnNameHandler) { h.app.mutex.Lock() h.onName = append(h.onName, handler...) h.app.mutex.Unlock() @@ -56,7 +57,7 @@ func (h *hooks) OnName(handler ...OnNameHandler) { // OnGroup is a hook to execute user functions on each group registeration. // Also you can get group properties by group parameter. -func (h *hooks) OnGroup(handler ...OnGroupHandler) { +func (h *Hooks) OnGroup(handler ...OnGroupHandler) { h.app.mutex.Lock() h.onGroup = append(h.onGroup, handler...) h.app.mutex.Unlock() @@ -66,34 +67,34 @@ func (h *hooks) OnGroup(handler ...OnGroupHandler) { // Also you can get group properties by group parameter. // // WARN: OnGroupName only works with naming groups, not routes. -func (h *hooks) OnGroupName(handler ...OnGroupNameHandler) { +func (h *Hooks) OnGroupName(handler ...OnGroupNameHandler) { h.app.mutex.Lock() h.onGroupName = append(h.onGroupName, handler...) h.app.mutex.Unlock() } // OnListen is a hook to execute user functions on Listen, ListenTLS, Listener. -func (h *hooks) OnListen(handler ...OnListenHandler) { +func (h *Hooks) OnListen(handler ...OnListenHandler) { h.app.mutex.Lock() h.onListen = append(h.onListen, handler...) h.app.mutex.Unlock() } // OnShutdown is a hook to execute user functions after Shutdown. -func (h *hooks) OnShutdown(handler ...OnShutdownHandler) { +func (h *Hooks) OnShutdown(handler ...OnShutdownHandler) { h.app.mutex.Lock() h.onShutdown = append(h.onShutdown, handler...) h.app.mutex.Unlock() } // OnFork is a hook to execute user function after fork process. -func (h *hooks) OnFork(handler ...OnForkHandler) { +func (h *Hooks) OnFork(handler ...OnForkHandler) { h.app.mutex.Lock() h.onFork = append(h.onFork, handler...) h.app.mutex.Unlock() } -func (h *hooks) executeOnRouteHooks(route Route) error { +func (h *Hooks) executeOnRouteHooks(route Route) error { for _, v := range h.onRoute { if err := v(route); err != nil { return err @@ -103,7 +104,7 @@ func (h *hooks) executeOnRouteHooks(route Route) error { return nil } -func (h *hooks) executeOnNameHooks(route Route) error { +func (h *Hooks) executeOnNameHooks(route Route) error { for _, v := range h.onName { if err := v(route); err != nil { return err @@ -113,7 +114,7 @@ func (h *hooks) executeOnNameHooks(route Route) error { return nil } -func (h *hooks) executeOnGroupHooks(group Group) error { +func (h *Hooks) executeOnGroupHooks(group Group) error { for _, v := range h.onGroup { if err := v(group); err != nil { return err @@ -123,7 +124,7 @@ func (h *hooks) executeOnGroupHooks(group Group) error { return nil } -func (h *hooks) executeOnGroupNameHooks(group Group) error { +func (h *Hooks) executeOnGroupNameHooks(group Group) error { for _, v := range h.onGroupName { if err := v(group); err != nil { return err @@ -133,7 +134,7 @@ func (h *hooks) executeOnGroupNameHooks(group Group) error { return nil } -func (h *hooks) executeOnListenHooks() error { +func (h *Hooks) executeOnListenHooks() error { for _, v := range h.onListen { if err := v(); err != nil { return err @@ -143,13 +144,13 @@ func (h *hooks) executeOnListenHooks() error { return nil } -func (h *hooks) executeOnShutdownHooks() { +func (h *Hooks) executeOnShutdownHooks() { for _, v := range h.onShutdown { _ = v() } } -func (h *hooks) executeOnForkHooks(pid int) { +func (h *Hooks) executeOnForkHooks(pid int) { for _, v := range h.onFork { _ = v(pid) } From 648e662cc5aecdf03e9788cd715bc2f53f864871 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Aug 2022 13:26:39 +0200 Subject: [PATCH 037/141] Bump github.com/valyala/fasthttp from 1.38.0 to 1.39.0 (#2017) Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.38.0 to 1.39.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.38.0...v1.39.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3a3ca5d952..f998b13c97 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module github.com/gofiber/fiber/v2 go 1.16 require ( - github.com/valyala/fasthttp v1.38.0 + github.com/valyala/fasthttp v1.39.0 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 ) diff --git a/go.sum b/go.sum index 2f5953c3dd..4b837f064b 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwc github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 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.38.0 h1:yTjSSNjuDi2PPvXY2836bIwLmiTS2T4T9p1coQshpco= -github.com/valyala/fasthttp v1.38.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= +github.com/valyala/fasthttp v1.39.0 h1:lW8mGeM7yydOqZKmwyMTaz/PH/A+CLgtmmcjv+OORfU= +github.com/valyala/fasthttp v1.39.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= From f031e08d1872106d78bc375c38c752223b427fa8 Mon Sep 17 00:00:00 2001 From: RW Date: Thu, 18 Aug 2022 16:29:52 +0200 Subject: [PATCH 038/141] prepare release 2.37.0-rc.1 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index ebc5ac1261..fc2b68e0bd 100644 --- a/app.go +++ b/app.go @@ -30,7 +30,7 @@ import ( ) // Version of current fiber package -const Version = "2.36.0" +const Version = "2.37.0-rc.1" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From 2edcf95f57104c5c43bb44531b0563c476c7e1e7 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 19 Aug 2022 08:19:22 +0200 Subject: [PATCH 039/141] =?UTF-8?q?=E2=9C=A8=20update:=20tls.ClientHelloIn?= =?UTF-8?q?fo=20in=20Ctx=20(#2011)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update go.mod * wip * wip * wip * wip * wip * Move tlsHandler from Config to App * Use NewError instead of panic * Add a test with ServerName * Add some tests on ClientHelloInfo * fix missing import * remove unnecessary ctx field. Co-authored-by: RW Co-authored-by: Muhammed Efe Çetin --- app.go | 2 ++ ctx.go | 21 ++++++++++++++++++ ctx_test.go | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ listen.go | 37 ++++++++++++++++++++++---------- 4 files changed, 111 insertions(+), 11 deletions(-) diff --git a/app.go b/app.go index fc2b68e0bd..f311617e3a 100644 --- a/app.go +++ b/app.go @@ -114,6 +114,8 @@ type App struct { // Latest route & group latestRoute *Route latestGroup *Group + // TLS handler + tlsHandler *tlsHandler } // Config is a struct holding the server settings. diff --git a/ctx.go b/ctx.go index 30aed27682..d8818ff855 100644 --- a/ctx.go +++ b/ctx.go @@ -7,6 +7,7 @@ package fiber import ( "bytes" "context" + "crypto/tls" "encoding/json" "encoding/xml" "errors" @@ -67,6 +68,17 @@ type Ctx struct { viewBindMap *dictpool.Dict // Default view map to bind template engine } +// tlsHandle object +type tlsHandler struct { + clientHelloInfo *tls.ClientHelloInfo +} + +// GetClientInfo Callback function to set CHI +func (t *tlsHandler) GetClientInfo(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + t.clientHelloInfo = info + return nil, nil +} + // Range data for c.Range type Range struct { Type string @@ -797,6 +809,15 @@ func (c *Ctx) MultipartForm() (*multipart.Form, error) { return c.fasthttp.MultipartForm() } +// ClientHelloInfo return CHI from context +func (c *Ctx) ClientHelloInfo() *tls.ClientHelloInfo { + if c.app.tlsHandler != nil { + return c.app.tlsHandler.clientHelloInfo + } + + return nil +} + // Next executes the next method in the stack that matches the current route. func (c *Ctx) Next() (err error) { // Increment handler index diff --git a/ctx_test.go b/ctx_test.go index 3af6e806b0..825cbb8966 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -12,6 +12,7 @@ import ( "bytes" "compress/gzip" "context" + "crypto/tls" "encoding/xml" "errors" "fmt" @@ -1248,6 +1249,67 @@ func Test_Ctx_Method(t *testing.T) { utils.AssertEqual(t, MethodPost, c.Method()) } +// go test -run Test_Ctx_ClientHelloInfo +func Test_Ctx_ClientHelloInfo(t *testing.T) { + t.Parallel() + app := New() + app.Get("/ServerName", func(c *Ctx) error { + result := c.ClientHelloInfo() + if result != nil { + return c.SendString(result.ServerName) + } + + return c.SendString("ClientHelloInfo is nil") + }) + app.Get("/SignatureSchemes", func(c *Ctx) error { + result := c.ClientHelloInfo() + if result != nil { + return c.JSON(result.SignatureSchemes) + } + + return c.SendString("ClientHelloInfo is nil") + }) + app.Get("/SupportedVersions", func(c *Ctx) error { + result := c.ClientHelloInfo() + if result != nil { + return c.JSON(result.SupportedVersions) + } + + return c.SendString("ClientHelloInfo is nil") + }) + + // Test without TLS handler + resp, _ := app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) + body, _ := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, []byte("ClientHelloInfo is nil"), body) + + // Test with TLS Handler + const ( + PSSWithSHA256 = 0x0804 + VersionTLS13 = 0x0304 + ) + app.tlsHandler = &tlsHandler{clientHelloInfo: &tls.ClientHelloInfo{ + ServerName: "example.golang", + SignatureSchemes: []tls.SignatureScheme{PSSWithSHA256}, + SupportedVersions: []uint16{VersionTLS13}, + }} + + // Test ServerName + resp, _ = app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) + body, _ = ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, []byte("example.golang"), body) + + // Test SignatureSchemes + resp, _ = app.Test(httptest.NewRequest(MethodGet, "/SignatureSchemes", nil)) + body, _ = ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, "["+strconv.Itoa(PSSWithSHA256)+"]", string(body)) + + // Test SupportedVersions + resp, _ = app.Test(httptest.NewRequest(MethodGet, "/SupportedVersions", nil)) + body, _ = ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, "["+strconv.Itoa(VersionTLS13)+"]", string(body)) +} + // go test -run Test_Ctx_InvalidMethod func Test_Ctx_InvalidMethod(t *testing.T) { t.Parallel() diff --git a/listen.go b/listen.go index d2ab361ef4..4d8d25b72d 100644 --- a/listen.go +++ b/listen.go @@ -82,22 +82,28 @@ func (app *App) ListenTLS(addr, certFile, keyFile string) error { if len(certFile) == 0 || len(keyFile) == 0 { return errors.New("tls: provide a valid cert or key path") } + // Set TLS config with handler + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) + } + tlsHandler := &tlsHandler{} + config := &tls.Config{ + MinVersion: tls.VersionTLS12, + Certificates: []tls.Certificate{ + cert, + }, + GetCertificate: tlsHandler.GetClientInfo, + } // Prefork is supported if app.config.Prefork { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) - } - config := &tls.Config{ - MinVersion: tls.VersionTLS12, - Certificates: []tls.Certificate{ - cert, - }, - } return app.prefork(app.config.Network, addr, config) } + // Setup listener ln, err := net.Listen(app.config.Network, addr) + ln = tls.NewListener(ln, config) + if err != nil { return err } @@ -111,8 +117,12 @@ func (app *App) ListenTLS(addr, certFile, keyFile string) error { if app.config.EnablePrintRoutes { app.printRoutesMessage() } + + // Attach the tlsHandler to the config + app.tlsHandler = tlsHandler + // Start listening - return app.server.ServeTLS(ln, certFile, keyFile) + return app.server.Serve(ln) } // ListenMutualTLS serves HTTPS requests from the given addr. @@ -137,6 +147,7 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) clientCertPool := x509.NewCertPool() clientCertPool.AppendCertsFromPEM(clientCACert) + tlsHandler := &tlsHandler{} config := &tls.Config{ MinVersion: tls.VersionTLS12, ClientAuth: tls.RequireAndVerifyClientCert, @@ -144,6 +155,7 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) Certificates: []tls.Certificate{ cert, }, + GetCertificate: tlsHandler.GetClientInfo, } // Prefork is supported @@ -170,6 +182,9 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) app.printRoutesMessage() } + // Attach the tlsHandler to the config + app.tlsHandler = tlsHandler + // Start listening return app.server.Serve(ln) } From 4adda508b0a51717c17a2226bf48ca79caff98bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6khan=20=C3=96zelo=C4=9Flu?= <33967642+gozeloglu@users.noreply.github.com> Date: Fri, 19 Aug 2022 09:20:14 +0300 Subject: [PATCH 040/141] =?UTF-8?q?=E2=9C=A8=20v3=20(feature):=20add=20ret?= =?UTF-8?q?ry=20mechanism=20(#1972)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * v3-retry-mechanism: Add retry mechanism * General logic is implemented. * Unit tests are added. Signed-off-by: Gökhan Özeloğlu * Refactor test assertion * Replaced testify/assert with fiber's assert. Signed-off-by: Gökhan Özeloğlu * Add test for next method * currentInterval bug is fixed in Retry. * If condition is fixed in next. * struct definition refactored and if condtion is removed in TestExponentialBackoff_Retry. Signed-off-by: Gökhan Özeloğlu * Add config for retry. * Constant variables are removed. * Helper function is added for default. * Helper function is used in New function. Signed-off-by: Gökhan Özeloğlu * Replace math/rand with crypto/rand * Random number generation package has been replaced with more secure one, crypto/rand. Signed-off-by: Gökhan Özeloğlu * Add a README for retry middleware * Explanation and examples are added. Signed-off-by: Gökhan Özeloğlu * Add comment line for documentation * Comment lines are added for ExponentialBackoff variables. Signed-off-by: Gökhan Özeloğlu * Run go mod tidy * Unused package(s) removed. Signed-off-by: Gökhan Özeloğlu * move middleware -> addon Signed-off-by: Gökhan Özeloğlu Co-authored-by: Muhammed Efe Çetin --- addon/retry/README.md | 97 ++++++++++++++++++ addon/retry/config.go | 66 +++++++++++++ addon/retry/exponential_backoff.go | 73 ++++++++++++++ addon/retry/exponential_backoff_test.go | 124 ++++++++++++++++++++++++ 4 files changed, 360 insertions(+) create mode 100644 addon/retry/README.md create mode 100644 addon/retry/config.go create mode 100644 addon/retry/exponential_backoff.go create mode 100644 addon/retry/exponential_backoff_test.go diff --git a/addon/retry/README.md b/addon/retry/README.md new file mode 100644 index 0000000000..a07c7637e5 --- /dev/null +++ b/addon/retry/README.md @@ -0,0 +1,97 @@ +# Retry Addon + +Retry addon for [Fiber](https://github.com/gofiber/fiber) designed to apply retry mechanism for unsuccessful network +operations. This addon uses exponential backoff algorithm with jitter. It calls the function multiple times and tries +to make it successful. If all calls are failed, then, it returns error. It adds a jitter at each retry step because adding +a jitter is a way to break synchronization across the client and avoid collision. + +## Table of Contents + +- [Retry Addon](#retry-addon) + - [Table of Contents](#table-of-contents) + - [Signatures](#signatures) + - [Examples](#examples) + - [Default Config](#default-config) + - [Custom Config](#custom-config) + - [Config](#config) + - [Default Config Example](#default-config-example) + +## Signatures + +```go +func NewExponentialBackoff(config ...Config) *ExponentialBackoff +``` + +## Examples + +Firstly, import the addon from Fiber, + +```go +import ( + "github.com/gofiber/fiber/v3/addon/retry" +) +``` + +## Default Config + +```go +retry.NewExponentialBackoff() +``` + +## Custom Config + +```go +retry.NewExponentialBackoff(retry.Config{ + InitialInterval: 2 * time.Second, + MaxBackoffTime: 64 * time.Second, + Multiplier: 2.0, + MaxRetryCount: 15, +}) +``` + +## Config + +```go +// Config defines the config for addon. +type Config struct { + // InitialInterval defines the initial time interval for backoff algorithm. + // + // Optional. Default: 1 * time.Second + InitialInterval time.Duration + + // MaxBackoffTime defines maximum time duration for backoff algorithm. When + // the algorithm is reached this time, rest of the retries will be maximum + // 32 seconds. + // + // Optional. Default: 32 * time.Second + MaxBackoffTime time.Duration + + // Multiplier defines multiplier number of the backoff algorithm. + // + // Optional. Default: 2.0 + Multiplier float64 + + // MaxRetryCount defines maximum retry count for the backoff algorithm. + // + // Optional. Default: 10 + MaxRetryCount int + + // currentInterval tracks the current waiting time. + // + // Optional. Default: 1 * time.Second + currentInterval time.Duration +} +``` + +## Default Config Example + +```go +// DefaultConfig is the default config for retry. +var DefaultConfig = Config{ + InitialInterval: 1 * time.Second, + MaxBackoffTime: 32 * time.Second, + Multiplier: 2.0, + MaxRetryCount: 10, + currentInterval: 1 * time.Second, +} +``` \ No newline at end of file diff --git a/addon/retry/config.go b/addon/retry/config.go new file mode 100644 index 0000000000..a2dcd27122 --- /dev/null +++ b/addon/retry/config.go @@ -0,0 +1,66 @@ +package retry + +import "time" + +// Config defines the config for addon. +type Config struct { + // InitialInterval defines the initial time interval for backoff algorithm. + // + // Optional. Default: 1 * time.Second + InitialInterval time.Duration + + // MaxBackoffTime defines maximum time duration for backoff algorithm. When + // the algorithm is reached this time, rest of the retries will be maximum + // 32 seconds. + // + // Optional. Default: 32 * time.Second + MaxBackoffTime time.Duration + + // Multiplier defines multiplier number of the backoff algorithm. + // + // Optional. Default: 2.0 + Multiplier float64 + + // MaxRetryCount defines maximum retry count for the backoff algorithm. + // + // Optional. Default: 10 + MaxRetryCount int + + // currentInterval tracks the current waiting time. + // + // Optional. Default: 1 * time.Second + currentInterval time.Duration +} + +// DefaultConfig is the default config for retry. +var DefaultConfig = Config{ + InitialInterval: 1 * time.Second, + MaxBackoffTime: 32 * time.Second, + Multiplier: 2.0, + MaxRetryCount: 10, + currentInterval: 1 * time.Second, +} + +// configDefault sets the config values if they are not set. +func configDefault(config ...Config) Config { + if len(config) == 0 { + return DefaultConfig + } + cfg := config[0] + if cfg.InitialInterval == 0 { + cfg.InitialInterval = DefaultConfig.InitialInterval + } + if cfg.MaxBackoffTime == 0 { + cfg.MaxBackoffTime = DefaultConfig.MaxBackoffTime + } + if cfg.Multiplier <= 0 { + cfg.Multiplier = DefaultConfig.Multiplier + } + if cfg.MaxRetryCount <= 0 { + cfg.MaxRetryCount = DefaultConfig.MaxRetryCount + } + if cfg.currentInterval != cfg.InitialInterval { + cfg.currentInterval = DefaultConfig.currentInterval + } + return cfg +} diff --git a/addon/retry/exponential_backoff.go b/addon/retry/exponential_backoff.go new file mode 100644 index 0000000000..6740c35971 --- /dev/null +++ b/addon/retry/exponential_backoff.go @@ -0,0 +1,73 @@ +package retry + +import ( + "crypto/rand" + "math/big" + "time" +) + +// ExponentialBackoff is a retry mechanism for retrying some calls. +type ExponentialBackoff struct { + // InitialInterval is the initial time interval for backoff algorithm. + InitialInterval time.Duration + + // MaxBackoffTime is the maximum time duration for backoff algorithm. It limits + // the maximum sleep time. + MaxBackoffTime time.Duration + + // Multiplier is a multiplier number of the backoff algorithm. + Multiplier float64 + + // MaxRetryCount is the maximum number of retry count. + MaxRetryCount int + + // currentInterval tracks the current sleep time. + currentInterval time.Duration +} + +// NewExponentialBackoff creates a ExponentialBackoff with default values. +func NewExponentialBackoff(config ...Config) *ExponentialBackoff { + cfg := configDefault(config...) + return &ExponentialBackoff{ + InitialInterval: cfg.InitialInterval, + MaxBackoffTime: cfg.MaxBackoffTime, + Multiplier: cfg.Multiplier, + MaxRetryCount: cfg.MaxRetryCount, + currentInterval: cfg.currentInterval, + } +} + +// Retry is the core logic of the retry mechanism. If the calling function returns +// nil as an error, then the Retry method is terminated with returning nil. Otherwise, +// if all function calls are returned error, then the method returns this error. +func (e *ExponentialBackoff) Retry(f func() error) error { + if e.currentInterval <= 0 { + e.currentInterval = e.InitialInterval + } + var err error + for i := 0; i < e.MaxRetryCount; i++ { + err = f() + if err == nil { + return nil + } + next := e.next() + time.Sleep(next) + } + return err +} + +// next calculates the next sleeping time interval. +func (e *ExponentialBackoff) next() time.Duration { + // generate a random value between [0, 1000) + n, err := rand.Int(rand.Reader, big.NewInt(1000)) + if err != nil { + return e.MaxBackoffTime + } + t := e.currentInterval + (time.Duration(n.Int64()) * time.Millisecond) + e.currentInterval = time.Duration(float64(e.currentInterval) * e.Multiplier) + if t >= e.MaxBackoffTime { + e.currentInterval = e.MaxBackoffTime + return e.MaxBackoffTime + } + return t +} diff --git a/addon/retry/exponential_backoff_test.go b/addon/retry/exponential_backoff_test.go new file mode 100644 index 0000000000..aa8fd9df27 --- /dev/null +++ b/addon/retry/exponential_backoff_test.go @@ -0,0 +1,124 @@ +package retry + +import ( + "fmt" + "github.com/gofiber/fiber/v3/utils" + "testing" + "time" +) + +func TestExponentialBackoff_Retry(t *testing.T) { + tests := []struct { + name string + expBackoff *ExponentialBackoff + f func() error + expErr error + }{ + { + name: "With default values - successful", + expBackoff: NewExponentialBackoff(), + f: func() error { + return nil + }, + }, + { + name: "With default values - unsuccessful", + expBackoff: NewExponentialBackoff(), + f: func() error { + return fmt.Errorf("failed function") + }, + expErr: fmt.Errorf("failed function"), + }, + { + name: "Successful function", + expBackoff: &ExponentialBackoff{ + InitialInterval: 1 * time.Millisecond, + MaxBackoffTime: 100 * time.Millisecond, + Multiplier: 2.0, + MaxRetryCount: 5, + }, + f: func() error { + return nil + }, + }, + { + name: "Unsuccessful function", + expBackoff: &ExponentialBackoff{ + InitialInterval: 2 * time.Millisecond, + MaxBackoffTime: 100 * time.Millisecond, + Multiplier: 2.0, + MaxRetryCount: 5, + }, + f: func() error { + return fmt.Errorf("failed function") + }, + expErr: fmt.Errorf("failed function"), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.expBackoff.Retry(tt.f) + utils.AssertEqual(t, tt.expErr, err) + }) + } +} + +func TestExponentialBackoff_Next(t *testing.T) { + tests := []struct { + name string + expBackoff *ExponentialBackoff + expNextTimeIntervals []time.Duration + }{ + { + name: "With default values", + expBackoff: NewExponentialBackoff(), + expNextTimeIntervals: []time.Duration{ + 1 * time.Second, + 2 * time.Second, + 4 * time.Second, + 8 * time.Second, + 16 * time.Second, + 32 * time.Second, + 32 * time.Second, + 32 * time.Second, + 32 * time.Second, + 32 * time.Second, + }, + }, + { + name: "Custom values", + expBackoff: &ExponentialBackoff{ + InitialInterval: 2.0 * time.Second, + MaxBackoffTime: 64 * time.Second, + Multiplier: 3.0, + MaxRetryCount: 8, + currentInterval: 2.0 * time.Second, + }, + expNextTimeIntervals: []time.Duration{ + 2 * time.Second, + 6 * time.Second, + 18 * time.Second, + 54 * time.Second, + 64 * time.Second, + 64 * time.Second, + 64 * time.Second, + 64 * time.Second, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for i := 0; i < tt.expBackoff.MaxRetryCount; i++ { + next := tt.expBackoff.next() + if next < tt.expNextTimeIntervals[i] || next > tt.expNextTimeIntervals[i]+1*time.Second { + t.Errorf("wrong next time:\n"+ + "actual:%v\n"+ + "expected range:%v-%v\n", + next, tt.expNextTimeIntervals[i], tt.expNextTimeIntervals[i]+1*time.Second) + } + } + }) + } +} From 1fec8757c4115a5c1405733fb5cd61e66a364223 Mon Sep 17 00:00:00 2001 From: Tushar Dahiya Date: Fri, 19 Aug 2022 11:50:33 +0530 Subject: [PATCH 041/141] =?UTF-8?q?Update=20README.md=20=F0=9F=93=9D=20(#2?= =?UTF-8?q?023)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update README.md Grammatical Error --- .github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index 3f88da06c0..a3c045acba 100644 --- a/.github/README.md +++ b/.github/README.md @@ -106,7 +106,7 @@ func main() { ## 🤖 Benchmarks -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 results, please visit our [Wiki](https://docs.gofiber.io/extra/benchmarks). +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).

From 32d311cd599b94b44c6e68c14c0095ed1f1d413d Mon Sep 17 00:00:00 2001 From: Jinquan Wang <35188480+wangjq4214@users.noreply.github.com> Date: Fri, 19 Aug 2022 21:09:16 +0800 Subject: [PATCH 042/141] =?UTF-8?q?=F0=9F=90=9B=20monitor=20middleware=20-?= =?UTF-8?q?=20fix=20ignore=20custom=20settings=20(#2024)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 fix: ignore custom settings * 🐛 fix: ignore custom settings * ✅ chore: add tests for config default --- middleware/monitor/config.go | 20 ++-- middleware/monitor/config_test.go | 154 ++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+), 9 deletions(-) create mode 100644 middleware/monitor/config_test.go diff --git a/middleware/monitor/config.go b/middleware/monitor/config.go index a5d08b65ca..559dd405ef 100644 --- a/middleware/monitor/config.go +++ b/middleware/monitor/config.go @@ -96,15 +96,8 @@ func configDefault(config ...Config) Config { if cfg.ChartJsURL == "" { cfg.ChartJsURL = defaultChartJsURL } - if cfg.Title == ConfigDefault.Title && cfg.Refresh == ConfigDefault.Refresh { - cfg.index = ConfigDefault.index - } else { - if cfg.Refresh < minRefresh { - cfg.Refresh = minRefresh - } - // update cfg.index with custom title/refresh - cfg.index = newIndex(viewBag{cfg.Title, - cfg.Refresh, cfg.FontURL, cfg.ChartJsURL, cfg.CustomHead}) + if cfg.Refresh < minRefresh { + cfg.Refresh = minRefresh } if cfg.Next == nil { @@ -115,5 +108,14 @@ func configDefault(config ...Config) Config { cfg.APIOnly = ConfigDefault.APIOnly } + // update cfg.index with custom title/refresh + cfg.index = newIndex(viewBag{ + title: cfg.Title, + refresh: cfg.Refresh, + fontUrl: cfg.FontURL, + chartJsUrl: cfg.ChartJsURL, + customHead: cfg.CustomHead, + }) + return cfg } diff --git a/middleware/monitor/config_test.go b/middleware/monitor/config_test.go new file mode 100644 index 0000000000..0759624c62 --- /dev/null +++ b/middleware/monitor/config_test.go @@ -0,0 +1,154 @@ +package monitor + +import ( + "testing" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" +) + +func Test_Config_Default(t *testing.T) { + t.Parallel() + + t.Run("use default", func(t *testing.T) { + cfg := configDefault() + + utils.AssertEqual(t, defaultTitle, cfg.Title) + utils.AssertEqual(t, defaultRefresh, cfg.Refresh) + utils.AssertEqual(t, defaultFontURL, cfg.FontURL) + utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) + utils.AssertEqual(t, false, cfg.APIOnly) + utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set title", func(t *testing.T) { + title := "title" + cfg := configDefault(Config{ + Title: title, + }) + + utils.AssertEqual(t, title, cfg.Title) + utils.AssertEqual(t, defaultRefresh, cfg.Refresh) + utils.AssertEqual(t, defaultFontURL, cfg.FontURL) + utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) + utils.AssertEqual(t, false, cfg.APIOnly) + utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + utils.AssertEqual(t, newIndex(viewBag{title, defaultRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set refresh less than default", func(t *testing.T) { + cfg := configDefault(Config{ + Refresh: 100 * time.Millisecond, + }) + + utils.AssertEqual(t, defaultTitle, cfg.Title) + utils.AssertEqual(t, minRefresh, cfg.Refresh) + utils.AssertEqual(t, defaultFontURL, cfg.FontURL) + utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) + utils.AssertEqual(t, false, cfg.APIOnly) + utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, minRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set refresh", func(t *testing.T) { + refresh := time.Second + cfg := configDefault(Config{ + Refresh: refresh, + }) + + utils.AssertEqual(t, defaultTitle, cfg.Title) + utils.AssertEqual(t, refresh, cfg.Refresh) + utils.AssertEqual(t, defaultFontURL, cfg.FontURL) + utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) + utils.AssertEqual(t, false, cfg.APIOnly) + utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, refresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set font url", func(t *testing.T) { + fontUrl := "https://example.com" + cfg := configDefault(Config{ + FontURL: fontUrl, + }) + + utils.AssertEqual(t, defaultTitle, cfg.Title) + utils.AssertEqual(t, defaultRefresh, cfg.Refresh) + utils.AssertEqual(t, fontUrl, cfg.FontURL) + utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) + utils.AssertEqual(t, false, cfg.APIOnly) + utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, fontUrl, defaultChartJsURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set chart js url", func(t *testing.T) { + chartUrl := "http://example.com" + cfg := configDefault(Config{ + ChartJsURL: chartUrl, + }) + + utils.AssertEqual(t, defaultTitle, cfg.Title) + utils.AssertEqual(t, defaultRefresh, cfg.Refresh) + utils.AssertEqual(t, defaultFontURL, cfg.FontURL) + utils.AssertEqual(t, chartUrl, cfg.ChartJsURL) + utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) + utils.AssertEqual(t, false, cfg.APIOnly) + utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, chartUrl, defaultCustomHead}), cfg.index) + }) + + t.Run("set custom head", func(t *testing.T) { + head := "head" + cfg := configDefault(Config{ + CustomHead: head, + }) + + utils.AssertEqual(t, defaultTitle, cfg.Title) + utils.AssertEqual(t, defaultRefresh, cfg.Refresh) + utils.AssertEqual(t, defaultFontURL, cfg.FontURL) + utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, head, cfg.CustomHead) + utils.AssertEqual(t, false, cfg.APIOnly) + utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, head}), cfg.index) + }) + + t.Run("set api only", func(t *testing.T) { + cfg := configDefault(Config{ + APIOnly: true, + }) + + utils.AssertEqual(t, defaultTitle, cfg.Title) + utils.AssertEqual(t, defaultRefresh, cfg.Refresh) + utils.AssertEqual(t, defaultFontURL, cfg.FontURL) + utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) + utils.AssertEqual(t, true, cfg.APIOnly) + utils.AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set next", func(t *testing.T) { + f := func(c *fiber.Ctx) bool { + return true + } + cfg := configDefault(Config{ + Next: f, + }) + + utils.AssertEqual(t, defaultTitle, cfg.Title) + utils.AssertEqual(t, defaultRefresh, cfg.Refresh) + utils.AssertEqual(t, defaultFontURL, cfg.FontURL) + utils.AssertEqual(t, defaultChartJsURL, cfg.ChartJsURL) + utils.AssertEqual(t, defaultCustomHead, cfg.CustomHead) + utils.AssertEqual(t, false, cfg.APIOnly) + utils.AssertEqual(t, f(nil), cfg.Next(nil)) + utils.AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJsURL, defaultCustomHead}), cfg.index) + }) +} From 73d0b712c8f6d998458653fc0ab3f8bb09cbbef7 Mon Sep 17 00:00:00 2001 From: Trim21 Date: Fri, 19 Aug 2022 22:39:52 +0800 Subject: [PATCH 043/141] =?UTF-8?q?=E2=9C=A8=20v3=20(enhancement):=20add?= =?UTF-8?q?=20`[]byte`=20support=20to=20`utils.EqualFold`=20(#2029)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * EqualFold * add comment --- utils/strings.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/utils/strings.go b/utils/strings.go index 109d132f1e..2310cdd606 100644 --- a/utils/strings.go +++ b/utils/strings.go @@ -61,8 +61,12 @@ func TrimRight(s string, cutset byte) string { return s[:lenStr] } -// EqualFold tests ascii strings for equality case-insensitively -func EqualFold(b, s string) bool { +type byteSeq interface { + ~string | ~[]byte +} + +// EqualFold tests ascii strings or bytes for equality case-insensitively +func EqualFold[S byteSeq](b, s S) bool { if len(b) != len(s) { return false } From b161f805c261b3be5fee3a665edca0d2fdcaf8d5 Mon Sep 17 00:00:00 2001 From: Trim21 Date: Sat, 20 Aug 2022 13:52:09 +0800 Subject: [PATCH 044/141] v3(enhancement): remove `utils.Trim*` (#2030) stdlib functions have same performance in go1.19 --- ctx.go | 16 +-- helpers.go | 5 +- middleware/filesystem/filesystem.go | 7 +- middleware/filesystem/utils.go | 3 +- path.go | 6 +- router.go | 4 +- utils/README.md | 141 +++++++++------------ utils/assertions_test.go | 1 + utils/bytes.go | 52 +------- utils/bytes_test.go | 182 +++------------------------- utils/byteseq.go | 18 +++ utils/byteseq_test.go | 86 +++++++++++++ utils/strings.go | 52 -------- utils/strings_test.go | 167 +------------------------ 14 files changed, 202 insertions(+), 538 deletions(-) create mode 100644 utils/byteseq.go create mode 100644 utils/byteseq_test.go diff --git a/ctx.go b/ctx.go index 50181bf2de..06fc2e67a1 100644 --- a/ctx.go +++ b/ctx.go @@ -108,9 +108,9 @@ func (c *DefaultCtx) Accepts(offers ...string) string { for len(header) > 0 && commaPos != -1 { commaPos = strings.IndexByte(header, ',') if commaPos != -1 { - spec = utils.Trim(header[:commaPos], ' ') + spec = strings.TrimLeft(header[:commaPos], " ") } else { - spec = utils.TrimLeft(header, ' ') + spec = strings.TrimLeft(header, " ") } if factorSign := strings.IndexByte(spec, ';'); factorSign != -1 { spec = spec[:factorSign] @@ -508,10 +508,10 @@ func (c *DefaultCtx) IPs() (ips []string) { for { commaPos = bytes.IndexByte(header, ',') if commaPos != -1 { - ips[i] = utils.Trim(c.app.getString(header[:commaPos]), ' ') + ips[i] = strings.Trim(c.app.getString(header[:commaPos]), " ") header, i = header[commaPos+1:], i+1 } else { - ips[i] = utils.Trim(c.app.getString(header), ' ') + ips[i] = strings.Trim(c.app.getString(header), " ") return } } @@ -526,7 +526,7 @@ func (c *DefaultCtx) Is(extension string) bool { } return strings.HasPrefix( - utils.TrimLeft(utils.UnsafeString(c.fasthttp.Request.Header.ContentType()), ' '), + strings.TrimLeft(utils.UnsafeString(c.fasthttp.Request.Header.ContentType()), " "), extensionHeader, ) } @@ -597,7 +597,7 @@ func (c *DefaultCtx) Links(link ...string) { _, _ = bb.WriteString(`; rel="` + link[i] + `",`) } } - c.setCanonical(HeaderLink, utils.TrimRight(c.app.getString(bb.Bytes()), ',')) + c.setCanonical(HeaderLink, strings.TrimRight(c.app.getString(bb.Bytes()), ",")) bytebufferpool.Put(bb) } @@ -1245,7 +1245,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.EqualFoldBytes(utils.UnsafeBytes(c.Get(HeaderXRequestedWith)), []byte("xmlhttprequest")) + return utils.EqualFold(c.Get(HeaderXRequestedWith), "xmlhttprequest") } // configDependentPaths set paths for route recognition and prepared paths for the user, @@ -1267,7 +1267,7 @@ func (c *DefaultCtx) configDependentPaths() { } // If StrictRouting is disabled, we strip all trailing slashes if !c.app.config.StrictRouting && len(c.detectionPathBuffer) > 1 && c.detectionPathBuffer[len(c.detectionPathBuffer)-1] == '/' { - c.detectionPathBuffer = utils.TrimRightBytes(c.detectionPathBuffer, '/') + c.detectionPathBuffer = bytes.TrimRight(c.detectionPathBuffer, "/") } c.detectionPath = c.app.getString(c.detectionPathBuffer) diff --git a/helpers.go b/helpers.go index 3da1e3473c..a88ca0a33a 100644 --- a/helpers.go +++ b/helpers.go @@ -17,7 +17,6 @@ import ( "time" "unsafe" - "github.com/gofiber/fiber/v3/utils" "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) @@ -223,7 +222,7 @@ func getGroupPath(prefix, path string) string { path = "/" + path } - return utils.TrimRight(prefix, '/') + path + return strings.TrimRight(prefix, "/") + path } // return valid offer for header negotiation @@ -238,7 +237,7 @@ func getOffer(header string, offers ...string) string { for len(header) > 0 && commaPos != -1 { commaPos = strings.IndexByte(header, ',') if commaPos != -1 { - spec = utils.Trim(header[:commaPos], ' ') + spec = strings.TrimSpace(header[:commaPos]) } else { spec = header } diff --git a/middleware/filesystem/filesystem.go b/middleware/filesystem/filesystem.go index f769577f49..ded63874ce 100644 --- a/middleware/filesystem/filesystem.go +++ b/middleware/filesystem/filesystem.go @@ -8,7 +8,6 @@ import ( "sync" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" ) // Config defines the config for middleware. @@ -133,7 +132,7 @@ func New(config ...Config) fiber.Handler { ) if len(path) > 1 { - path = utils.TrimRight(path, '/') + path = strings.TrimRight(path, "/") } file, err = cfg.Root.Open(path) if err != nil && os.IsNotExist(err) && cfg.NotFoundFile != "" { @@ -153,7 +152,7 @@ func New(config ...Config) fiber.Handler { // Serve index if path is directory if stat.IsDir() { - indexPath := utils.TrimRight(path, '/') + cfg.Index + indexPath := strings.TrimRight(path, "/") + cfg.Index index, err := cfg.Root.Open(indexPath) if err == nil { indexStat, err := index.Stat() @@ -226,7 +225,7 @@ func SendFile(c fiber.Ctx, fs http.FileSystem, path string) (err error) { // Serve index if path is directory if stat.IsDir() { - indexPath := utils.TrimRight(path, '/') + ConfigDefault.Index + indexPath := strings.TrimRight(path, "/") + ConfigDefault.Index index, err := fs.Open(indexPath) if err == nil { indexStat, err := index.Stat() diff --git a/middleware/filesystem/utils.go b/middleware/filesystem/utils.go index 9988d4264a..7ec68cfde9 100644 --- a/middleware/filesystem/utils.go +++ b/middleware/filesystem/utils.go @@ -10,7 +10,6 @@ import ( "strings" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" ) func getFileExtension(path string) string { @@ -41,7 +40,7 @@ func dirList(c fiber.Ctx, f http.File) error { fmt.Fprint(c, "

    ") if len(basePathEscaped) > 1 { - parentPathEscaped := html.EscapeString(utils.TrimRight(c.Path(), '/') + "/..") + parentPathEscaped := html.EscapeString(strings.TrimRight(c.Path(), "/") + "/..") fmt.Fprintf(c, `
  • ..
  • `, parentPathEscaped) } diff --git a/path.go b/path.go index 3ddfcfcb96..9874cf7056 100644 --- a/path.go +++ b/path.go @@ -13,7 +13,6 @@ import ( "time" "unicode" - "github.com/gofiber/fiber/v3/utils" "github.com/google/uuid" ) @@ -51,6 +50,7 @@ const ( optionalParam byte = '?' // concludes a parameter by name and makes it optional paramStarterChar byte = ':' // start character for a parameter with name slashDelimiter byte = '/' // separator for the route, unlike the other delimiters this character at the end can be optional + slashDelimiterStr = "/" // separator for the route, unlike the other delimiters this character at the end can be optional escapeChar byte = '\\' // escape character paramConstraintStart byte = '<' // start of type constraint for a parameter paramConstraintEnd byte = '>' // end of type constraint for a parameter @@ -159,7 +159,7 @@ func addParameterMetaInfo(segs []*routeSegment) []*routeSegment { } else { comparePart = segs[i].Const if len(comparePart) > 1 { - comparePart = utils.TrimRight(comparePart, slashDelimiter) + comparePart = strings.TrimRight(comparePart, slashDelimiterStr) } } } @@ -354,7 +354,7 @@ func findNextCharsetPositionConstraint(search string, charset []byte) int { if char == paramConstraintEnd { constraintEnd = pos } - //fmt.Println(string(char)) + // fmt.Println(string(char)) if pos != -1 && (pos < nextPosition || nextPosition == -1) { if pos > constraintStart && pos < constraintEnd { nextPosition = pos diff --git a/router.go b/router.go index bf533d56f0..559635b399 100644 --- a/router.go +++ b/router.go @@ -188,7 +188,7 @@ func (app *App) addPrefixToRoute(prefix string, route *Route) *Route { } // Strict routing, remove trailing slashes if !app.config.StrictRouting && len(prettyPath) > 1 { - prettyPath = utils.TrimRight(prettyPath, '/') + prettyPath = strings.TrimRight(prettyPath, "/") } route.Path = prefixedPath @@ -246,7 +246,7 @@ func (app *App) register(method, pathRaw string, handlers ...Handler) Router { } // Strict routing, remove trailing slashes if !app.config.StrictRouting && len(pathPretty) > 1 { - pathPretty = utils.TrimRight(pathPretty, '/') + pathPretty = strings.TrimRight(pathPretty, "/") } // Is layer a middleware? isUse := method == methodUse diff --git a/utils/README.md b/utils/README.md index 0276ff380e..0030be7da6 100644 --- a/utils/README.md +++ b/utils/README.md @@ -2,89 +2,60 @@ A collection of common functions but with better performance, less allocations a ```go // go test -benchmem -run=^$ -bench=Benchmark_ -count=2 - -Benchmark_ToLowerBytes/fiber-16 42847654 25.7 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/fiber-16 46143196 25.7 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/default-16 17387322 67.4 ns/op 48 B/op 1 allocs/op -Benchmark_ToLowerBytes/default-16 17906491 67.4 ns/op 48 B/op 1 allocs/op - -Benchmark_ToUpperBytes/fiber-16 46143729 25.7 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/fiber-16 47989250 25.6 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/default-16 15580854 76.7 ns/op 48 B/op 1 allocs/op -Benchmark_ToUpperBytes/default-16 15381202 76.9 ns/op 48 B/op 1 allocs/op - -Benchmark_TrimRightBytes/fiber-16 70572459 16.3 ns/op 8 B/op 1 allocs/op -Benchmark_TrimRightBytes/fiber-16 74983597 16.3 ns/op 8 B/op 1 allocs/op -Benchmark_TrimRightBytes/default-16 16212578 74.1 ns/op 40 B/op 2 allocs/op -Benchmark_TrimRightBytes/default-16 16434686 74.1 ns/op 40 B/op 2 allocs/op - -Benchmark_TrimLeftBytes/fiber-16 74983128 16.3 ns/op 8 B/op 1 allocs/op -Benchmark_TrimLeftBytes/fiber-16 74985002 16.3 ns/op 8 B/op 1 allocs/op -Benchmark_TrimLeftBytes/default-16 21047868 56.5 ns/op 40 B/op 2 allocs/op -Benchmark_TrimLeftBytes/default-16 21048015 56.5 ns/op 40 B/op 2 allocs/op - -Benchmark_TrimBytes/fiber-16 54533307 21.9 ns/op 16 B/op 1 allocs/op -Benchmark_TrimBytes/fiber-16 54532812 21.9 ns/op 16 B/op 1 allocs/op -Benchmark_TrimBytes/default-16 14282517 84.6 ns/op 48 B/op 2 allocs/op -Benchmark_TrimBytes/default-16 14114508 84.7 ns/op 48 B/op 2 allocs/op - -Benchmark_EqualFolds/fiber-16 36355153 32.6 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFolds/fiber-16 36355593 32.6 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFolds/default-16 15186220 78.1 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFolds/default-16 15186412 78.3 ns/op 0 B/op 0 allocs/op - -Benchmark_UUID/fiber-16 23994625 49.8 ns/op 48 B/op 1 allocs/op -Benchmark_UUID/fiber-16 23994768 50.1 ns/op 48 B/op 1 allocs/op -Benchmark_UUID/default-16 3233772 371 ns/op 208 B/op 6 allocs/op -Benchmark_UUID/default-16 3251295 370 ns/op 208 B/op 6 allocs/op - -Benchmark_GetString/unsafe-16 1000000000 0.709 ns/op 0 B/op 0 allocs/op -Benchmark_GetString/unsafe-16 1000000000 0.713 ns/op 0 B/op 0 allocs/op -Benchmark_GetString/default-16 59986202 19.0 ns/op 16 B/op 1 allocs/op -Benchmark_GetString/default-16 63142939 19.0 ns/op 16 B/op 1 allocs/op - -Benchmark_GetBytes/unsafe-16 508360195 2.36 ns/op 0 B/op 0 allocs/op -Benchmark_GetBytes/unsafe-16 508359979 2.35 ns/op 0 B/op 0 allocs/op -Benchmark_GetBytes/default-16 46143019 25.7 ns/op 16 B/op 1 allocs/op -Benchmark_GetBytes/default-16 44434734 25.6 ns/op 16 B/op 1 allocs/op - -Benchmark_GetMIME/fiber-16 21423750 56.3 ns/op 0 B/op 0 allocs/op -Benchmark_GetMIME/fiber-16 21423559 55.4 ns/op 0 B/op 0 allocs/op -Benchmark_GetMIME/default-16 6735282 173 ns/op 0 B/op 0 allocs/op -Benchmark_GetMIME/default-16 6895002 172 ns/op 0 B/op 0 allocs/op - -Benchmark_StatusMessage/fiber-16 1000000000 0.766 ns/op 0 B/op 0 allocs/op -Benchmark_StatusMessage/fiber-16 1000000000 0.767 ns/op 0 B/op 0 allocs/op -Benchmark_StatusMessage/default-16 159538528 7.50 ns/op 0 B/op 0 allocs/op -Benchmark_StatusMessage/default-16 159750830 7.51 ns/op 0 B/op 0 allocs/op - -Benchmark_ToUpper/fiber-16 22217408 53.3 ns/op 48 B/op 1 allocs/op -Benchmark_ToUpper/fiber-16 22636554 53.2 ns/op 48 B/op 1 allocs/op -Benchmark_ToUpper/default-16 11108600 108 ns/op 48 B/op 1 allocs/op -Benchmark_ToUpper/default-16 11108580 108 ns/op 48 B/op 1 allocs/op - -Benchmark_ToLower/fiber-16 23994720 49.8 ns/op 48 B/op 1 allocs/op -Benchmark_ToLower/fiber-16 23994768 50.1 ns/op 48 B/op 1 allocs/op -Benchmark_ToLower/default-16 10808376 110 ns/op 48 B/op 1 allocs/op -Benchmark_ToLower/default-16 10617034 110 ns/op 48 B/op 1 allocs/op - -Benchmark_TrimRight/fiber-16 413699521 2.94 ns/op 0 B/op 0 allocs/op -Benchmark_TrimRight/fiber-16 415131687 2.91 ns/op 0 B/op 0 allocs/op -Benchmark_TrimRight/default-16 23994577 49.1 ns/op 32 B/op 1 allocs/op -Benchmark_TrimRight/default-16 24484249 49.4 ns/op 32 B/op 1 allocs/op - -Benchmark_TrimLeft/fiber-16 379661170 3.13 ns/op 0 B/op 0 allocs/op -Benchmark_TrimLeft/fiber-16 382079941 3.16 ns/op 0 B/op 0 allocs/op -Benchmark_TrimLeft/default-16 27900877 41.9 ns/op 32 B/op 1 allocs/op -Benchmark_TrimLeft/default-16 28564898 42.0 ns/op 32 B/op 1 allocs/op - -Benchmark_Trim/fiber-16 236632856 4.96 ns/op 0 B/op 0 allocs/op -Benchmark_Trim/fiber-16 237570085 4.93 ns/op 0 B/op 0 allocs/op -Benchmark_Trim/default-16 18457221 66.0 ns/op 32 B/op 1 allocs/op -Benchmark_Trim/default-16 18177328 65.9 ns/op 32 B/op 1 allocs/op -Benchmark_Trim/default.trimspace-16 188933770 6.33 ns/op 0 B/op 0 allocs/op -Benchmark_Trim/default.trimspace-16 184007649 6.42 ns/op 0 B/op 0 allocs/op - -Benchmark_ConvertToBytes/fiber-8 43773547 24.43 ns/op 0 B/op 0 allocs/op -Benchmark_ConvertToBytes/fiber-8 45849477 25.33 ns/op 0 B/op 0 allocs/op +goos: windows +goarch: amd64 +pkg: github.com/gofiber/fiber/v3/utils +cpu: AMD Ryzen 7 5800X 8-Core Processor +Benchmark_ToLowerBytes/fiber-16 51138252 22.61 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/fiber-16 52126545 22.63 ns/op 0 B/op 0 allocs/op +Benchmark_ToLowerBytes/default-16 16114736 72.76 ns/op 80 B/op 1 allocs/op +Benchmark_ToLowerBytes/default-16 16651540 73.85 ns/op 80 B/op 1 allocs/op +Benchmark_ToUpperBytes/fiber-16 52127224 22.62 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/fiber-16 54283167 22.86 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpperBytes/default-16 14060098 84.12 ns/op 80 B/op 1 allocs/op +Benchmark_ToUpperBytes/default-16 14183122 84.51 ns/op 80 B/op 1 allocs/op +Benchmark_EqualFoldBytes/fiber-16 29240264 41.22 ns/op 0 B/op 0 allocs/op +Benchmark_EqualFoldBytes/fiber-16 28535826 40.84 ns/op 0 B/op 0 allocs/op +Benchmark_EqualFoldBytes/default-16 7929867 150.2 ns/op 0 B/op 0 allocs/op +Benchmark_EqualFoldBytes/default-16 7935478 149.7 ns/op 0 B/op 0 allocs/op +Benchmark_EqualFold/fiber-16 35442768 34.25 ns/op 0 B/op 0 allocs/op +Benchmark_EqualFold/fiber-16 35946870 34.96 ns/op 0 B/op 0 allocs/op +Benchmark_EqualFold/default-16 8942130 133.5 ns/op 0 B/op 0 allocs/op +Benchmark_EqualFold/default-16 8977231 134.3 ns/op 0 B/op 0 allocs/op +Benchmark_UUID/fiber-16 30726213 40.57 ns/op 48 B/op 1 allocs/op +Benchmark_UUID/fiber-16 26539394 40.25 ns/op 48 B/op 1 allocs/op +Benchmark_UUID/default-16 4737199 247.5 ns/op 168 B/op 6 allocs/op +Benchmark_UUID/default-16 4603738 250.8 ns/op 168 B/op 6 allocs/op +Benchmark_ConvertToBytes/fiber-16 62450884 19.41 ns/op 0 B/op 0 allocs/op +Benchmark_ConvertToBytes/fiber-16 52123602 19.53 ns/op 0 B/op 0 allocs/op +Benchmark_UnsafeString/unsafe-16 1000000000 0.4496 ns/op 0 B/op 0 allocs/op +Benchmark_UnsafeString/unsafe-16 1000000000 0.4488 ns/op 0 B/op 0 allocs/op +Benchmark_UnsafeString/default-16 79925935 13.99 ns/op 16 B/op 1 allocs/op +Benchmark_UnsafeString/default-16 85637211 14.35 ns/op 16 B/op 1 allocs/op +Benchmark_UnsafeBytes/unsafe-16 540970148 2.214 ns/op 0 B/op 0 allocs/op +Benchmark_UnsafeBytes/unsafe-16 543356940 2.212 ns/op 0 B/op 0 allocs/op +Benchmark_UnsafeBytes/default-16 68896224 17.19 ns/op 16 B/op 1 allocs/op +Benchmark_UnsafeBytes/default-16 70560426 17.05 ns/op 16 B/op 1 allocs/op +Benchmark_ToString-16 29504036 39.57 ns/op 40 B/op 2 allocs/op +Benchmark_ToString-16 30738334 38.89 ns/op 40 B/op 2 allocs/op +Benchmark_GetMIME/fiber-16 28207086 41.84 ns/op 0 B/op 0 allocs/op +Benchmark_GetMIME/fiber-16 28165773 41.83 ns/op 0 B/op 0 allocs/op +Benchmark_GetMIME/default-16 12583132 94.04 ns/op 0 B/op 0 allocs/op +Benchmark_GetMIME/default-16 12829614 93.50 ns/op 0 B/op 0 allocs/op +Benchmark_ParseVendorSpecificContentType/vendorContentType-16 30267411 38.72 ns/op 16 B/op 1 allocs/op +Benchmark_ParseVendorSpecificContentType/vendorContentType-16 28543563 38.60 ns/op 16 B/op 1 allocs/op +Benchmark_ParseVendorSpecificContentType/defaultContentType-16 249869286 4.830 ns/op 0 B/op 0 allocs/op +Benchmark_ParseVendorSpecificContentType/defaultContentType-16 248999592 4.805 ns/op 0 B/op 0 allocs/op +Benchmark_StatusMessage/fiber-16 1000000000 0.6744 ns/op 0 B/op 0 allocs/op +Benchmark_StatusMessage/fiber-16 1000000000 0.6788 ns/op 0 B/op 0 allocs/op +Benchmark_StatusMessage/default-16 446818872 2.664 ns/op 0 B/op 0 allocs/op +Benchmark_StatusMessage/default-16 447009616 2.661 ns/op 0 B/op 0 allocs/op +Benchmark_ToUpper/fiber-16 20480331 56.50 ns/op 80 B/op 1 allocs/op +Benchmark_ToUpper/fiber-16 21541200 56.65 ns/op 80 B/op 1 allocs/op +Benchmark_ToUpper/default-16 8433409 141.2 ns/op 80 B/op 1 allocs/op +Benchmark_ToUpper/default-16 8473737 141.1 ns/op 80 B/op 1 allocs/op +Benchmark_ToLower/fiber-16 27248326 44.68 ns/op 80 B/op 1 allocs/op +Benchmark_ToLower/fiber-16 26918443 44.70 ns/op 80 B/op 1 allocs/op +Benchmark_ToLower/default-16 8447336 141.9 ns/op 80 B/op 1 allocs/op +Benchmark_ToLower/default-16 8423156 140.6 ns/op 80 B/op 1 allocs/op ``` diff --git a/utils/assertions_test.go b/utils/assertions_test.go index d2d32dc0cf..9b886aae65 100644 --- a/utils/assertions_test.go +++ b/utils/assertions_test.go @@ -10,4 +10,5 @@ func Test_AssertEqual(t *testing.T) { t.Parallel() AssertEqual(nil, []string{}, []string{}) AssertEqual(t, []string{}, []string{}) + AssertEqual(t, []byte("aq"), []byte("aq")) } diff --git a/utils/bytes.go b/utils/bytes.go index 49f55b2de7..e5b548858c 100644 --- a/utils/bytes.go +++ b/utils/bytes.go @@ -4,7 +4,7 @@ package utils -// ToLowerBytes converts ascii slice to lower-case +// ToLowerBytes converts ascii slice to lower-case in-place. func ToLowerBytes(b []byte) []byte { for i := 0; i < len(b); i++ { b[i] = toLowerTable[b[i]] @@ -12,58 +12,10 @@ func ToLowerBytes(b []byte) []byte { return b } -// ToUpperBytes converts ascii slice to upper-case +// ToUpperBytes converts ascii slice to upper-case in-place. func ToUpperBytes(b []byte) []byte { for i := 0; i < len(b); i++ { b[i] = toUpperTable[b[i]] } return b } - -// TrimRightBytes is the equivalent of bytes.TrimRight -func TrimRightBytes(b []byte, cutset byte) []byte { - lenStr := len(b) - for lenStr > 0 && b[lenStr-1] == cutset { - lenStr-- - } - return b[:lenStr] -} - -// TrimLeftBytes is the equivalent of bytes.TrimLeft -func TrimLeftBytes(b []byte, cutset byte) []byte { - lenStr, start := len(b), 0 - for start < lenStr && b[start] == cutset { - start++ - } - return b[start:] -} - -// TrimBytes is the equivalent of bytes.Trim -func TrimBytes(b []byte, cutset byte) []byte { - i, j := 0, len(b)-1 - for ; i <= j; i++ { - if b[i] != cutset { - break - } - } - for ; i < j; j-- { - if b[j] != cutset { - break - } - } - - return b[i : j+1] -} - -// EqualFoldBytes tests ascii slices for equality case-insensitively -func EqualFoldBytes(b, s []byte) bool { - if len(b) != len(s) { - return false - } - for i := len(b) - 1; i >= 0; i-- { - if toUpperTable[b[i]] != toUpperTable[s[i]] { - return false - } - } - return true -} diff --git a/utils/bytes_test.go b/utils/bytes_test.go index a449856db1..06ab1c4a8e 100644 --- a/utils/bytes_test.go +++ b/utils/bytes_test.go @@ -11,16 +11,11 @@ import ( func Test_ToLowerBytes(t *testing.T) { t.Parallel() - res := ToLowerBytes([]byte("/MY/NAME/IS/:PARAM/*")) - AssertEqual(t, true, bytes.Equal([]byte("/my/name/is/:param/*"), res)) - res = ToLowerBytes([]byte("/MY1/NAME/IS/:PARAM/*")) - AssertEqual(t, true, bytes.Equal([]byte("/my1/name/is/:param/*"), res)) - res = ToLowerBytes([]byte("/MY2/NAME/IS/:PARAM/*")) - AssertEqual(t, true, bytes.Equal([]byte("/my2/name/is/:param/*"), res)) - res = ToLowerBytes([]byte("/MY3/NAME/IS/:PARAM/*")) - AssertEqual(t, true, bytes.Equal([]byte("/my3/name/is/:param/*"), res)) - res = ToLowerBytes([]byte("/MY4/NAME/IS/:PARAM/*")) - AssertEqual(t, true, bytes.Equal([]byte("/my4/name/is/:param/*"), res)) + AssertEqual(t, []byte("/my/name/is/:param/*"), ToLowerBytes([]byte("/MY/NAME/IS/:PARAM/*"))) + AssertEqual(t, []byte("/my1/name/is/:param/*"), ToLowerBytes([]byte("/MY1/NAME/IS/:PARAM/*"))) + AssertEqual(t, []byte("/my2/name/is/:param/*"), ToLowerBytes([]byte("/MY2/NAME/IS/:PARAM/*"))) + AssertEqual(t, []byte("/my3/name/is/:param/*"), ToLowerBytes([]byte("/MY3/NAME/IS/:PARAM/*"))) + AssertEqual(t, []byte("/my4/name/is/:param/*"), ToLowerBytes([]byte("/MY4/NAME/IS/:PARAM/*"))) } func Benchmark_ToLowerBytes(b *testing.B) { @@ -31,28 +26,23 @@ func Benchmark_ToLowerBytes(b *testing.B) { for n := 0; n < b.N; n++ { res = ToLowerBytes(path) } - AssertEqual(b, bytes.Equal(want, res), true) + AssertEqual(b, want, res) }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = bytes.ToLower(path) } - AssertEqual(b, bytes.Equal(want, res), true) + AssertEqual(b, want, res) }) } func Test_ToUpperBytes(t *testing.T) { t.Parallel() - res := ToUpperBytes([]byte("/my/name/is/:param/*")) - AssertEqual(t, true, bytes.Equal([]byte("/MY/NAME/IS/:PARAM/*"), res)) - res = ToUpperBytes([]byte("/my1/name/is/:param/*")) - AssertEqual(t, true, bytes.Equal([]byte("/MY1/NAME/IS/:PARAM/*"), res)) - res = ToUpperBytes([]byte("/my2/name/is/:param/*")) - AssertEqual(t, true, bytes.Equal([]byte("/MY2/NAME/IS/:PARAM/*"), res)) - res = ToUpperBytes([]byte("/my3/name/is/:param/*")) - AssertEqual(t, true, bytes.Equal([]byte("/MY3/NAME/IS/:PARAM/*"), res)) - res = ToUpperBytes([]byte("/my4/name/is/:param/*")) - AssertEqual(t, true, bytes.Equal([]byte("/MY4/NAME/IS/:PARAM/*"), res)) + AssertEqual(t, []byte("/MY/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my/name/is/:param/*"))) + AssertEqual(t, []byte("/MY1/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my1/name/is/:param/*"))) + AssertEqual(t, []byte("/MY2/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my2/name/is/:param/*"))) + AssertEqual(t, []byte("/MY3/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my3/name/is/:param/*"))) + AssertEqual(t, []byte("/MY4/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my4/name/is/:param/*"))) } func Benchmark_ToUpperBytes(b *testing.B) { @@ -63,156 +53,12 @@ func Benchmark_ToUpperBytes(b *testing.B) { for n := 0; n < b.N; n++ { res = ToUpperBytes(path) } - AssertEqual(b, bytes.Equal(want, res), true) + AssertEqual(b, want, res) }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = bytes.ToUpper(path) } - AssertEqual(b, bytes.Equal(want, res), true) + AssertEqual(b, want, res) }) } - -func Test_TrimRightBytes(t *testing.T) { - t.Parallel() - res := TrimRightBytes([]byte("/test//////"), '/') - AssertEqual(t, []byte("/test"), res) - - res = TrimRightBytes([]byte("/test"), '/') - AssertEqual(t, []byte("/test"), res) - - res = TrimRightBytes([]byte(" "), ' ') - AssertEqual(t, 0, len(res)) - - res = TrimRightBytes([]byte(" "), ' ') - AssertEqual(t, 0, len(res)) - - res = TrimRightBytes([]byte(""), ' ') - AssertEqual(t, 0, len(res)) -} - -func Benchmark_TrimRightBytes(b *testing.B) { - var res []byte - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = TrimRightBytes([]byte("foobar "), ' ') - } - AssertEqual(b, []byte("foobar"), res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.TrimRight([]byte("foobar "), " ") - } - AssertEqual(b, []byte("foobar"), res) - }) -} - -func Test_TrimLeftBytes(t *testing.T) { - t.Parallel() - res := TrimLeftBytes([]byte("////test/"), '/') - AssertEqual(t, []byte("test/"), res) - - res = TrimLeftBytes([]byte("test/"), '/') - AssertEqual(t, []byte("test/"), res) - - res = TrimLeftBytes([]byte(" "), ' ') - AssertEqual(t, 0, len(res)) - - res = TrimLeftBytes([]byte(" "), ' ') - AssertEqual(t, 0, len(res)) - - res = TrimLeftBytes([]byte(""), ' ') - AssertEqual(t, 0, len(res)) -} - -func Benchmark_TrimLeftBytes(b *testing.B) { - var res []byte - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = TrimLeftBytes([]byte(" foobar"), ' ') - } - AssertEqual(b, []byte("foobar"), res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.TrimLeft([]byte(" foobar"), " ") - } - AssertEqual(b, []byte("foobar"), res) - }) -} - -func Test_TrimBytes(t *testing.T) { - t.Parallel() - res := TrimBytes([]byte(" test "), ' ') - AssertEqual(t, []byte("test"), res) - - res = TrimBytes([]byte("test"), ' ') - AssertEqual(t, []byte("test"), res) - - res = TrimBytes([]byte(".test"), '.') - AssertEqual(t, []byte("test"), res) - - res = TrimBytes([]byte(" "), ' ') - AssertEqual(t, 0, len(res)) - - res = TrimBytes([]byte(" "), ' ') - AssertEqual(t, 0, len(res)) - - res = TrimBytes([]byte(""), ' ') - AssertEqual(t, 0, len(res)) -} - -func Benchmark_TrimBytes(b *testing.B) { - var res []byte - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = TrimBytes([]byte(" foobar "), ' ') - } - AssertEqual(b, []byte("foobar"), res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.Trim([]byte(" foobar "), " ") - } - AssertEqual(b, []byte("foobar"), res) - }) -} - -func Benchmark_EqualFoldBytes(b *testing.B) { - left := []byte(upperStr) - right := []byte(lowerStr) - var res bool - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = EqualFoldBytes(left, right) - } - AssertEqual(b, true, res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.EqualFold(left, right) - } - AssertEqual(b, true, res) - }) -} - -func Test_EqualFoldBytes(t *testing.T) { - t.Parallel() - res := EqualFoldBytes([]byte("/MY/NAME/IS/:PARAM/*"), []byte("/my/name/is/:param/*")) - AssertEqual(t, true, res) - res = EqualFoldBytes([]byte("/MY1/NAME/IS/:PARAM/*"), []byte("/MY1/NAME/IS/:PARAM/*")) - AssertEqual(t, true, res) - res = EqualFoldBytes([]byte("/my2/name/is/:param/*"), []byte("/my2/name")) - AssertEqual(t, false, res) - res = EqualFoldBytes([]byte("/dddddd"), []byte("eeeeee")) - AssertEqual(t, false, res) - res = EqualFoldBytes([]byte("\na"), []byte("*A")) - AssertEqual(t, false, res) - res = EqualFoldBytes([]byte("/MY3/NAME/IS/:PARAM/*"), []byte("/my3/name/is/:param/*")) - AssertEqual(t, true, res) - res = EqualFoldBytes([]byte("/MY4/NAME/IS/:PARAM/*"), []byte("/my4/nAME/IS/:param/*")) - AssertEqual(t, true, res) -} diff --git a/utils/byteseq.go b/utils/byteseq.go new file mode 100644 index 0000000000..82932ca368 --- /dev/null +++ b/utils/byteseq.go @@ -0,0 +1,18 @@ +package utils + +type byteSeq interface { + ~string | ~[]byte +} + +// EqualFold tests ascii strings or bytes for equality case-insensitively +func EqualFold[S byteSeq](b, s S) bool { + if len(b) != len(s) { + return false + } + for i := len(b) - 1; i >= 0; i-- { + if toUpperTable[b[i]] != toUpperTable[s[i]] { + return false + } + } + return true +} diff --git a/utils/byteseq_test.go b/utils/byteseq_test.go new file mode 100644 index 0000000000..dfd0757703 --- /dev/null +++ b/utils/byteseq_test.go @@ -0,0 +1,86 @@ +package utils + +import ( + "bytes" + "strings" + "testing" +) + +func Benchmark_EqualFoldBytes(b *testing.B) { + left := []byte(upperStr) + right := []byte(lowerStr) + var res bool + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = EqualFold(left, right) + } + AssertEqual(b, true, res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = bytes.EqualFold(left, right) + } + AssertEqual(b, true, res) + }) +} + +func Test_EqualFoldBytes(t *testing.T) { + t.Parallel() + res := EqualFold([]byte("/MY/NAME/IS/:PARAM/*"), []byte("/my/name/is/:param/*")) + AssertEqual(t, true, res) + res = EqualFold([]byte("/MY1/NAME/IS/:PARAM/*"), []byte("/MY1/NAME/IS/:PARAM/*")) + AssertEqual(t, true, res) + res = EqualFold([]byte("/my2/name/is/:param/*"), []byte("/my2/name")) + AssertEqual(t, false, res) + res = EqualFold([]byte("/dddddd"), []byte("eeeeee")) + AssertEqual(t, false, res) + res = EqualFold([]byte("\na"), []byte("*A")) + AssertEqual(t, false, res) + res = EqualFold([]byte("/MY3/NAME/IS/:PARAM/*"), []byte("/my3/name/is/:param/*")) + AssertEqual(t, true, res) + res = EqualFold([]byte("/MY4/NAME/IS/:PARAM/*"), []byte("/my4/nAME/IS/:param/*")) + AssertEqual(t, true, res) +} + +// go test -v -run=^$ -bench=Benchmark_EqualFold -benchmem -count=4 ./utils/ +func Benchmark_EqualFold(b *testing.B) { + var res bool + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = EqualFold(upperStr, lowerStr) + } + AssertEqual(b, true, res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = strings.EqualFold(upperStr, lowerStr) + } + AssertEqual(b, true, res) + }) +} + +func Test_EqualFold(t *testing.T) { + t.Parallel() + testCases := []struct { + Expected bool + S1 string + S2 string + }{ + {Expected: true, S1: "/MY/NAME/IS/:PARAM/*", S2: "/my/name/is/:param/*"}, + {Expected: true, S1: "/MY/NAME/IS/:PARAM/*", S2: "/my/name/is/:param/*"}, + {Expected: true, S1: "/MY1/NAME/IS/:PARAM/*", S2: "/MY1/NAME/IS/:PARAM/*"}, + {Expected: false, S1: "/my2/name/is/:param/*", S2: "/my2/name"}, + {Expected: false, S1: "/dddddd", S2: "eeeeee"}, + {Expected: false, S1: "\na", S2: "*A"}, + {Expected: true, S1: "/MY3/NAME/IS/:PARAM/*", S2: "/my3/name/is/:param/*"}, + {Expected: true, S1: "/MY4/NAME/IS/:PARAM/*", S2: "/my4/nAME/IS/:param/*"}, + } + + for _, tc := range testCases { + res := EqualFold[string](tc.S1, tc.S2) + AssertEqual(t, tc.Expected, res, "string") + + res = EqualFold[[]byte]([]byte(tc.S1), []byte(tc.S2)) + AssertEqual(t, tc.Expected, res, "bytes") + } +} diff --git a/utils/strings.go b/utils/strings.go index 2310cdd606..364c25f5c9 100644 --- a/utils/strings.go +++ b/utils/strings.go @@ -25,55 +25,3 @@ func ToUpper(b string) string { return UnsafeString(res) } - -// TrimLeft is the equivalent of strings.TrimLeft -func TrimLeft(s string, cutset byte) string { - lenStr, start := len(s), 0 - for start < lenStr && s[start] == cutset { - start++ - } - return s[start:] -} - -// Trim is the equivalent of strings.Trim -func Trim(s string, cutset byte) string { - i, j := 0, len(s)-1 - for ; i <= j; i++ { - if s[i] != cutset { - break - } - } - for ; i < j; j-- { - if s[j] != cutset { - break - } - } - - return s[i : j+1] -} - -// TrimRight is the equivalent of strings.TrimRight -func TrimRight(s string, cutset byte) string { - lenStr := len(s) - for lenStr > 0 && s[lenStr-1] == cutset { - lenStr-- - } - return s[:lenStr] -} - -type byteSeq interface { - ~string | ~[]byte -} - -// EqualFold tests ascii strings or bytes for equality case-insensitively -func EqualFold[S byteSeq](b, s S) bool { - if len(b) != len(s) { - return false - } - for i := len(b) - 1; i >= 0; i-- { - if toUpperTable[b[i]] != toUpperTable[s[i]] { - return false - } - } - return true -} diff --git a/utils/strings_test.go b/utils/strings_test.go index c0de5875c6..b0d5176753 100644 --- a/utils/strings_test.go +++ b/utils/strings_test.go @@ -11,8 +11,7 @@ import ( func Test_ToUpper(t *testing.T) { t.Parallel() - res := ToUpper("/my/name/is/:param/*") - AssertEqual(t, "/MY/NAME/IS/:PARAM/*", res) + AssertEqual(t, "/MY/NAME/IS/:PARAM/*", ToUpper("/my/name/is/:param/*")) } const ( @@ -39,16 +38,11 @@ func Benchmark_ToUpper(b *testing.B) { func Test_ToLower(t *testing.T) { t.Parallel() - res := ToLower("/MY/NAME/IS/:PARAM/*") - AssertEqual(t, "/my/name/is/:param/*", res) - res = ToLower("/MY1/NAME/IS/:PARAM/*") - AssertEqual(t, "/my1/name/is/:param/*", res) - res = ToLower("/MY2/NAME/IS/:PARAM/*") - AssertEqual(t, "/my2/name/is/:param/*", res) - res = ToLower("/MY3/NAME/IS/:PARAM/*") - AssertEqual(t, "/my3/name/is/:param/*", res) - res = ToLower("/MY4/NAME/IS/:PARAM/*") - AssertEqual(t, "/my4/name/is/:param/*", res) + AssertEqual(t, "/my/name/is/:param/*", ToLower("/MY/NAME/IS/:PARAM/*")) + AssertEqual(t, "/my1/name/is/:param/*", ToLower("/MY1/NAME/IS/:PARAM/*")) + AssertEqual(t, "/my2/name/is/:param/*", ToLower("/MY2/NAME/IS/:PARAM/*")) + AssertEqual(t, "/my3/name/is/:param/*", ToLower("/MY3/NAME/IS/:PARAM/*")) + AssertEqual(t, "/my4/name/is/:param/*", ToLower("/MY4/NAME/IS/:PARAM/*")) } func Benchmark_ToLower(b *testing.B) { @@ -66,152 +60,3 @@ func Benchmark_ToLower(b *testing.B) { AssertEqual(b, lowerStr, res) }) } - -func Test_TrimRight(t *testing.T) { - t.Parallel() - res := TrimRight("/test//////", '/') - AssertEqual(t, "/test", res) - - res = TrimRight("/test", '/') - AssertEqual(t, "/test", res) - - res = TrimRight(" ", ' ') - AssertEqual(t, "", res) - - res = TrimRight(" ", ' ') - AssertEqual(t, "", res) - - res = TrimRight("", ' ') - AssertEqual(t, "", res) -} - -func Benchmark_TrimRight(b *testing.B) { - var res string - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = TrimRight("foobar ", ' ') - } - AssertEqual(b, "foobar", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.TrimRight("foobar ", " ") - } - AssertEqual(b, "foobar", res) - }) -} - -func Test_TrimLeft(t *testing.T) { - t.Parallel() - res := TrimLeft("////test/", '/') - AssertEqual(t, "test/", res) - - res = TrimLeft("test/", '/') - AssertEqual(t, "test/", res) - - res = TrimLeft(" ", ' ') - AssertEqual(t, "", res) - - res = TrimLeft(" ", ' ') - AssertEqual(t, "", res) - - res = TrimLeft("", ' ') - AssertEqual(t, "", res) -} - -func Benchmark_TrimLeft(b *testing.B) { - var res string - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = TrimLeft(" foobar", ' ') - } - AssertEqual(b, "foobar", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.TrimLeft(" foobar", " ") - } - AssertEqual(b, "foobar", res) - }) -} - -func Test_Trim(t *testing.T) { - t.Parallel() - res := Trim(" test ", ' ') - AssertEqual(t, "test", res) - - res = Trim("test", ' ') - AssertEqual(t, "test", res) - - res = Trim(".test", '.') - AssertEqual(t, "test", res) - - res = Trim(" ", ' ') - AssertEqual(t, "", res) - - res = Trim(" ", ' ') - AssertEqual(t, "", res) - - res = Trim("", ' ') - AssertEqual(t, "", res) -} - -func Benchmark_Trim(b *testing.B) { - var res string - - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = Trim(" foobar ", ' ') - } - AssertEqual(b, "foobar", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.Trim(" foobar ", " ") - } - AssertEqual(b, "foobar", res) - }) - b.Run("default.trimspace", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.TrimSpace(" foobar ") - } - AssertEqual(b, "foobar", res) - }) -} - -// go test -v -run=^$ -bench=Benchmark_EqualFold -benchmem -count=4 -func Benchmark_EqualFold(b *testing.B) { - var res bool - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = EqualFold(upperStr, lowerStr) - } - AssertEqual(b, true, res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.EqualFold(upperStr, lowerStr) - } - AssertEqual(b, true, res) - }) -} - -func Test_EqualFold(t *testing.T) { - t.Parallel() - res := EqualFold("/MY/NAME/IS/:PARAM/*", "/my/name/is/:param/*") - AssertEqual(t, true, res) - res = EqualFold("/MY1/NAME/IS/:PARAM/*", "/MY1/NAME/IS/:PARAM/*") - AssertEqual(t, true, res) - res = EqualFold("/my2/name/is/:param/*", "/my2/name") - AssertEqual(t, false, res) - res = EqualFold("/dddddd", "eeeeee") - AssertEqual(t, false, res) - res = EqualFold("\na", "*A") - AssertEqual(t, false, res) - res = EqualFold("/MY3/NAME/IS/:PARAM/*", "/my3/name/is/:param/*") - AssertEqual(t, true, res) - res = EqualFold("/MY4/NAME/IS/:PARAM/*", "/my4/nAME/IS/:param/*") - AssertEqual(t, true, res) -} From c964fda009401ff37948a39da1ec7f427653c741 Mon Sep 17 00:00:00 2001 From: Trim21 Date: Mon, 22 Aug 2022 13:57:10 +0800 Subject: [PATCH 045/141] v3(tests): use testify for assertion (#2036) * replace * write * rewrite more * rewrite * rewrite * fix * rewrite --- addon/retry/exponential_backoff_test.go | 5 +- app_test.go | 617 ++++++------ bind_test.go | 448 ++++----- binder/mapping_test.go | 20 +- client_test.go | 315 +++---- ctx_test.go | 889 +++++++++--------- error_test.go | 22 +- go.mod | 4 + go.sum | 15 + helpers_test.go | 74 +- hooks_test.go | 30 +- internal/schema/doc.go | 16 +- listen_test.go | 88 +- middleware/basicauth/basicauth_test.go | 16 +- middleware/cache/cache_test.go | 209 ++-- middleware/compress/compress_test.go | 66 +- middleware/cors/cors_test.go | 38 +- middleware/csrf/csrf_test.go | 47 +- .../encryptcookie/encryptcookie_test.go | 48 +- middleware/etag/etag_test.go | 62 +- middleware/expvar/expvar_test.go | 42 +- middleware/favicon/favicon_test.go | 48 +- middleware/filesystem/filesystem_test.go | 30 +- middleware/helmet/helmet_test.go | 38 +- middleware/limiter/limiter_test.go | 92 +- middleware/logger/logger_test.go | 94 +- middleware/pprof/pprof_test.go | 32 +- middleware/proxy/proxy_test.go | 117 +-- middleware/recover/recover_test.go | 16 +- middleware/requestid/requestid_test.go | 24 +- middleware/session/session.go | 2 +- middleware/session/session_test.go | 154 +-- middleware/session/store_test.go | 10 +- middleware/skip/skip_test.go | 14 +- path_test.go | 51 +- prefork_test.go | 32 +- router_test.go | 269 +++--- utils/assertions.go | 67 -- utils/assertions_test.go | 14 - utils/bytes_test.go | 32 +- utils/byteseq_test.go | 32 +- utils/common_test.go | 66 +- utils/convert_test.go | 26 +- utils/http_test.go | 54 +- utils/json_test.go | 14 +- utils/strings_test.go | 22 +- utils/xml_test.go | 10 +- 47 files changed, 2188 insertions(+), 2243 deletions(-) delete mode 100644 utils/assertions.go delete mode 100644 utils/assertions_test.go diff --git a/addon/retry/exponential_backoff_test.go b/addon/retry/exponential_backoff_test.go index aa8fd9df27..3f4edd62cd 100644 --- a/addon/retry/exponential_backoff_test.go +++ b/addon/retry/exponential_backoff_test.go @@ -2,9 +2,10 @@ package retry import ( "fmt" - "github.com/gofiber/fiber/v3/utils" "testing" "time" + + "github.com/stretchr/testify/require" ) func TestExponentialBackoff_Retry(t *testing.T) { @@ -59,7 +60,7 @@ func TestExponentialBackoff_Retry(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.expBackoff.Retry(tt.f) - utils.AssertEqual(t, tt.expErr, err) + require.Equal(t, tt.expErr, err) }) } } diff --git a/app_test.go b/app_test.go index 28faf5dfc4..5a3dc3148c 100644 --- a/app_test.go +++ b/app_test.go @@ -20,7 +20,7 @@ import ( "testing" "time" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -34,17 +34,17 @@ func testStatus200(t *testing.T, app *App, url string, method string) { req := httptest.NewRequest(method, url, nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") } func testErrorResponse(t *testing.T, err error, resp *http.Response, expectedBodyError string) { - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 500, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 500, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, expectedBodyError, string(body), "Response body") + require.NoError(t, err) + require.Equal(t, expectedBodyError, string(body), "Response body") } func Test_App_MethodNotAllowed(t *testing.T) { @@ -59,41 +59,41 @@ func Test_App_MethodNotAllowed(t *testing.T) { app.Options("/", testEmptyHandler) resp, err := app.Test(httptest.NewRequest(MethodPost, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) - utils.AssertEqual(t, "", resp.Header.Get(HeaderAllow)) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) + require.Equal(t, "", resp.Header.Get(HeaderAllow)) resp, err = app.Test(httptest.NewRequest(MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 405, resp.StatusCode) - utils.AssertEqual(t, "POST, OPTIONS", resp.Header.Get(HeaderAllow)) + require.NoError(t, err) + require.Equal(t, 405, resp.StatusCode) + require.Equal(t, "POST, OPTIONS", resp.Header.Get(HeaderAllow)) resp, err = app.Test(httptest.NewRequest(MethodPatch, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 405, resp.StatusCode) - utils.AssertEqual(t, "POST, OPTIONS", resp.Header.Get(HeaderAllow)) + require.NoError(t, err) + require.Equal(t, 405, resp.StatusCode) + require.Equal(t, "POST, OPTIONS", resp.Header.Get(HeaderAllow)) resp, err = app.Test(httptest.NewRequest(MethodPut, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 405, resp.StatusCode) - utils.AssertEqual(t, "POST, OPTIONS", resp.Header.Get(HeaderAllow)) + require.NoError(t, err) + require.Equal(t, 405, resp.StatusCode) + require.Equal(t, "POST, OPTIONS", resp.Header.Get(HeaderAllow)) app.Get("/", testEmptyHandler) resp, err = app.Test(httptest.NewRequest(MethodTrace, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 405, resp.StatusCode) - utils.AssertEqual(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow)) + require.NoError(t, err) + require.Equal(t, 405, resp.StatusCode) + require.Equal(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow)) resp, err = app.Test(httptest.NewRequest(MethodPatch, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 405, resp.StatusCode) - utils.AssertEqual(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow)) + require.NoError(t, err) + require.Equal(t, 405, resp.StatusCode) + require.Equal(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow)) resp, err = app.Test(httptest.NewRequest(MethodPut, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 405, resp.StatusCode) - utils.AssertEqual(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow)) + require.NoError(t, err) + require.Equal(t, 405, resp.StatusCode) + require.Equal(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow)) } func Test_App_Custom_Middleware_404_Should_Not_SetMethodNotAllowed(t *testing.T) { @@ -106,8 +106,8 @@ func Test_App_Custom_Middleware_404_Should_Not_SetMethodNotAllowed(t *testing.T) app.Post("/", testEmptyHandler) resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 404, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 404, resp.StatusCode) g := app.Group("/with-next", func(c Ctx) error { return c.Status(404).Next() @@ -116,8 +116,8 @@ func Test_App_Custom_Middleware_404_Should_Not_SetMethodNotAllowed(t *testing.T) g.Post("/", testEmptyHandler) resp, err = app.Test(httptest.NewRequest(MethodGet, "/with-next", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 404, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 404, resp.StatusCode) } func Test_App_ServerErrorHandler_SmallReadBuffer(t *testing.T) { @@ -139,12 +139,7 @@ func Test_App_ServerErrorHandler_SmallReadBuffer(t *testing.T) { t.Error("Expect an error at app.Test(request)") } - utils.AssertEqual( - t, - true, - expectedError.MatchString(err.Error()), - fmt.Sprintf("Has: %s, expected pattern: %s", err.Error(), expectedError.String()), - ) + require.Regexp(t, expectedError, err.Error()) } func Test_App_Errors(t *testing.T) { @@ -157,16 +152,16 @@ func Test_App_Errors(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 500, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 500, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "hi, i'm an error", string(body)) + require.NoError(t, err) + require.Equal(t, "hi, i'm an error", string(body)) _, err = app.Test(httptest.NewRequest(MethodGet, "/", strings.NewReader("big body"))) if err != nil { - utils.AssertEqual(t, "body size exceeds the given limit", err.Error(), "app.Test(req)") + require.Equal(t, "body size exceeds the given limit", err.Error(), "app.Test(req)") } } @@ -182,28 +177,28 @@ func Test_App_ErrorHandler_Custom(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "hi, i'm an custom error", string(body)) + require.NoError(t, err) + require.Equal(t, "hi, i'm an custom error", string(body)) } func Test_App_ErrorHandler_HandlerStack(t *testing.T) { app := New(Config{ ErrorHandler: func(c Ctx, err error) error { - utils.AssertEqual(t, "1: USE error", err.Error()) + require.Equal(t, "1: USE error", err.Error()) return DefaultErrorHandler(c, err) }, }) app.Use("/", func(c Ctx) error { err := c.Next() // call next USE - utils.AssertEqual(t, "2: USE error", err.Error()) + require.Equal(t, "2: USE error", err.Error()) return errors.New("1: USE error") }, func(c Ctx) error { err := c.Next() // call [0] GET - utils.AssertEqual(t, "0: GET error", err.Error()) + require.Equal(t, "0: GET error", err.Error()) return errors.New("2: USE error") }) app.Get("/", func(c Ctx) error { @@ -211,24 +206,24 @@ func Test_App_ErrorHandler_HandlerStack(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 500, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 500, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "1: USE error", string(body)) + require.NoError(t, err) + require.Equal(t, "1: USE error", string(body)) } func Test_App_ErrorHandler_RouteStack(t *testing.T) { app := New(Config{ ErrorHandler: func(c Ctx, err error) error { - utils.AssertEqual(t, "1: USE error", err.Error()) + require.Equal(t, "1: USE error", err.Error()) return DefaultErrorHandler(c, err) }, }) app.Use("/", func(c Ctx) error { err := c.Next() - utils.AssertEqual(t, "0: GET error", err.Error()) + require.Equal(t, "0: GET error", err.Error()) return errors.New("1: USE error") // [2] call ErrorHandler }) app.Get("/test", func(c Ctx) error { @@ -236,18 +231,18 @@ func Test_App_ErrorHandler_RouteStack(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 500, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 500, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "1: USE error", string(body)) + require.NoError(t, err) + require.Equal(t, "1: USE error", string(body)) } func Test_App_ErrorHandler_GroupMount(t *testing.T) { micro := New(Config{ ErrorHandler: func(c Ctx, err error) error { - utils.AssertEqual(t, "0: GET error", err.Error()) + require.Equal(t, "0: GET error", err.Error()) return c.Status(500).SendString("1: custom error") }, }) @@ -266,7 +261,7 @@ func Test_App_ErrorHandler_GroupMount(t *testing.T) { func Test_App_ErrorHandler_GroupMountRootLevel(t *testing.T) { micro := New(Config{ ErrorHandler: func(c Ctx, err error) error { - utils.AssertEqual(t, "0: GET error", err.Error()) + require.Equal(t, "0: GET error", err.Error()) return c.Status(500).SendString("1: custom error") }, }) @@ -301,8 +296,8 @@ func Test_App_Nested_Params(t *testing.T) { req := httptest.NewRequest(MethodGet, "/test/john/test/doe", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") } // go test -run Test_App_Mount @@ -316,45 +311,45 @@ func Test_App_Mount(t *testing.T) { app.Mount("/john", micro) resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, uint32(2), app.handlersCount) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.Equal(t, uint32(2), app.handlersCount) } func Test_App_Use_Params(t *testing.T) { app := New() app.Use("/prefix/:param", func(c Ctx) error { - utils.AssertEqual(t, "john", c.Params("param")) + require.Equal(t, "john", c.Params("param")) return nil }) app.Use("/foo/:bar?", func(c Ctx) error { - utils.AssertEqual(t, "foobar", c.Params("bar", "foobar")) + require.Equal(t, "foobar", c.Params("bar", "foobar")) return nil }) app.Use("/:param/*", func(c Ctx) error { - utils.AssertEqual(t, "john", c.Params("param")) - utils.AssertEqual(t, "doe", c.Params("*")) + require.Equal(t, "john", c.Params("param")) + require.Equal(t, "doe", c.Params("*")) return nil }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/prefix/john", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") defer func() { if err := recover(); err != nil { - utils.AssertEqual(t, "use: invalid handler func()\n", fmt.Sprintf("%v", err)) + require.Equal(t, "use: invalid handler func()\n", fmt.Sprintf("%v", err)) } }() @@ -367,28 +362,28 @@ func Test_App_Use_UnescapedPath(t *testing.T) { app := New(Config{UnescapePath: true, CaseSensitive: true}) app.Use("/cRéeR/:param", func(c Ctx) error { - utils.AssertEqual(t, "/cRéeR/اختبار", c.Path()) + require.Equal(t, "/cRéeR/اختبار", c.Path()) return c.SendString(c.Params("param")) }) app.Use("/abc", func(c Ctx) error { - utils.AssertEqual(t, "/AbC", c.Path()) + require.Equal(t, "/AbC", c.Path()) return nil }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/cR%C3%A9eR/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") + require.NoError(t, err, "app.Test(req)") // check the param result - utils.AssertEqual(t, "اختبار", app.getString(body)) + require.Equal(t, "اختبار", app.getString(body)) // with lowercase letters resp, err = app.Test(httptest.NewRequest(MethodGet, "/cr%C3%A9er/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusNotFound, resp.StatusCode, "Status code") } func Test_App_Use_CaseSensitive(t *testing.T) { @@ -400,32 +395,32 @@ func Test_App_Use_CaseSensitive(t *testing.T) { // wrong letters in the requested route -> 404 resp, err := app.Test(httptest.NewRequest(MethodGet, "/AbC", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusNotFound, resp.StatusCode, "Status code") // right letters in the requrested route -> 200 resp, err = app.Test(httptest.NewRequest(MethodGet, "/abc", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") // check the detected path when the case insensitive recognition is activated app.config.CaseSensitive = false // check the case sensitive feature resp, err = app.Test(httptest.NewRequest(MethodGet, "/AbC", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") + require.NoError(t, err, "app.Test(req)") // check the detected path result - utils.AssertEqual(t, "/AbC", app.getString(body)) + require.Equal(t, "/AbC", app.getString(body)) } func Test_App_Add_Method_Test(t *testing.T) { app := New() defer func() { if err := recover(); err != nil { - utils.AssertEqual(t, "add: invalid http method JOHN\n", fmt.Sprintf("%v", err)) + require.Equal(t, "add: invalid http method JOHN\n", fmt.Sprintf("%v", err)) } }() app.Add("JOHN", "/doe", testEmptyHandler) @@ -443,8 +438,8 @@ func Test_App_GETOnly(t *testing.T) { req := httptest.NewRequest(MethodPost, "/", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusMethodNotAllowed, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusMethodNotAllowed, resp.StatusCode, "Status code") } func Test_App_Use_Params_Group(t *testing.T) { @@ -455,14 +450,14 @@ func Test_App_Use_Params_Group(t *testing.T) { return c.Next() }) group.Get("/test", func(c Ctx) error { - utils.AssertEqual(t, "john", c.Params("param")) - utils.AssertEqual(t, "doe", c.Params("*")) + require.Equal(t, "john", c.Params("param")) + require.Equal(t, "doe", c.Params("*")) return nil }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/prefix/john/doe/test", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") } func Test_App_Chaining(t *testing.T) { @@ -474,13 +469,13 @@ func Test_App_Chaining(t *testing.T) { return c.SendStatus(202) }) // check handler count for registered HEAD route - utils.AssertEqual(t, 5, len(app.stack[methodInt(MethodHead)][0].Handlers), "app.Test(req)") + require.Equal(t, 5, len(app.stack[methodInt(MethodHead)][0].Handlers), "app.Test(req)") req := httptest.NewRequest(MethodPost, "/john", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 202, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 202, resp.StatusCode, "Status code") app.Get("/test", n, n, n, n, func(c Ctx) error { return c.SendStatus(203) @@ -489,8 +484,8 @@ func Test_App_Chaining(t *testing.T) { req = httptest.NewRequest(MethodGet, "/test", nil) resp, err = app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 203, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 203, resp.StatusCode, "Status code") } func Test_App_Order(t *testing.T) { @@ -514,12 +509,12 @@ func Test_App_Order(t *testing.T) { req := httptest.NewRequest(MethodGet, "/test", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "123", string(body)) + require.NoError(t, err) + require.Equal(t, "123", string(body)) } func Test_App_Methods(t *testing.T) { @@ -582,12 +577,12 @@ func Test_App_Route_Naming(t *testing.T) { subGroup := jane.Group("/sub-group").Name("sub.") subGroup.Get("/done", handler).Name("done") - utils.AssertEqual(t, "post", app.GetRoute("post").Name) - utils.AssertEqual(t, "john", app.GetRoute("john").Name) - utils.AssertEqual(t, "jane.test", app.GetRoute("jane.test").Name) - utils.AssertEqual(t, "jane.trace", app.GetRoute("jane.trace").Name) - utils.AssertEqual(t, "jane.sub.done", app.GetRoute("jane.sub.done").Name) - utils.AssertEqual(t, "test", app.GetRoute("test").Name) + require.Equal(t, "post", app.GetRoute("post").Name) + require.Equal(t, "john", app.GetRoute("john").Name) + require.Equal(t, "jane.test", app.GetRoute("jane.test").Name) + require.Equal(t, "jane.trace", app.GetRoute("jane.trace").Name) + require.Equal(t, "jane.sub.done", app.GetRoute("jane.sub.done").Name) + require.Equal(t, "test", app.GetRoute("test").Name) } func Test_App_New(t *testing.T) { @@ -604,7 +599,7 @@ func Test_App_Config(t *testing.T) { app := New(Config{ DisableStartupMessage: true, }) - utils.AssertEqual(t, true, app.Config().DisableStartupMessage) + require.True(t, app.Config().DisableStartupMessage) } func Test_App_Shutdown(t *testing.T) { @@ -612,7 +607,7 @@ func Test_App_Shutdown(t *testing.T) { app := New(Config{ DisableStartupMessage: true, }) - utils.AssertEqual(t, true, app.Shutdown() == nil) + require.True(t, app.Shutdown() == nil) }) t.Run("no server", func(t *testing.T) { @@ -634,24 +629,24 @@ func Test_App_Static_Index_Default(t *testing.T) { app.Static("test", "", Static{Index: "index.html"}) resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!")) + require.NoError(t, err) + require.True(t, strings.Contains(string(body), "Hello, World!")) resp, err = app.Test(httptest.NewRequest(MethodGet, "/not-found", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 404, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 404, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "Cannot GET /not-found", string(body)) + require.NoError(t, err) + require.Equal(t, "Cannot GET /not-found", string(body)) } // go test -run Test_App_Static_Index @@ -661,25 +656,25 @@ func Test_App_Static_Direct(t *testing.T) { app.Static("/", "./.github") resp, err := app.Test(httptest.NewRequest(MethodGet, "/index.html", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!")) + require.NoError(t, err) + require.True(t, strings.Contains(string(body), "Hello, World!")) resp, err = app.Test(httptest.NewRequest(MethodGet, "/testdata/testRoutes.json", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMEApplicationJSON, resp.Header.Get("Content-Type")) - utils.AssertEqual(t, "", resp.Header.Get(HeaderCacheControl), "CacheControl Control") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMEApplicationJSON, resp.Header.Get("Content-Type")) + require.Equal(t, "", resp.Header.Get(HeaderCacheControl), "CacheControl Control") body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, strings.Contains(string(body), "testRoutes")) + require.NoError(t, err) + require.True(t, strings.Contains(string(body), "testRoutes")) } // go test -run Test_App_Static_MaxAge @@ -689,11 +684,11 @@ func Test_App_Static_MaxAge(t *testing.T) { app.Static("/", "./.github", Static{MaxAge: 100}) resp, err := app.Test(httptest.NewRequest("GET", "/index.html", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, "text/html; charset=utf-8", resp.Header.Get(HeaderContentType)) - utils.AssertEqual(t, "public, max-age=100", resp.Header.Get(HeaderCacheControl), "CacheControl Control") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, "text/html; charset=utf-8", resp.Header.Get(HeaderContentType)) + require.Equal(t, "public, max-age=100", resp.Header.Get(HeaderCacheControl), "CacheControl Control") } // go test -run Test_App_Static_Download @@ -703,11 +698,11 @@ 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)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, "image/png", resp.Header.Get(HeaderContentType)) - utils.AssertEqual(t, `attachment`, resp.Header.Get(HeaderContentDisposition)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, "image/png", resp.Header.Get(HeaderContentType)) + require.Equal(t, `attachment`, resp.Header.Get(HeaderContentDisposition)) } // go test -run Test_App_Static_Group @@ -723,21 +718,21 @@ func Test_App_Static_Group(t *testing.T) { req := httptest.NewRequest(MethodGet, "/v1/v2", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) - utils.AssertEqual(t, "123", resp.Header.Get("Test-Header")) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.Equal(t, "123", resp.Header.Get("Test-Header")) grp = app.Group("/v2") grp.Static("/v3*", "./.github/index.html") req = httptest.NewRequest(MethodGet, "/v2/v3/john/doe", nil) resp, err = app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) } func Test_App_Static_Wildcard(t *testing.T) { @@ -747,14 +742,14 @@ func Test_App_Static_Wildcard(t *testing.T) { req := httptest.NewRequest(MethodGet, "/yesyes/john/doe", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, strings.Contains(string(body), "Test file")) + require.NoError(t, err) + require.True(t, strings.Contains(string(body), "Test file")) } func Test_App_Static_Prefix_Wildcard(t *testing.T) { @@ -764,22 +759,22 @@ func Test_App_Static_Prefix_Wildcard(t *testing.T) { req := httptest.NewRequest(MethodGet, "/test/john/doe", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) app.Static("/my/nameisjohn*", "./.github/index.html") resp, err = app.Test(httptest.NewRequest(MethodGet, "/my/nameisjohn/no/its/not", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, strings.Contains(string(body), "Test file")) + require.NoError(t, err) + require.True(t, strings.Contains(string(body), "Test file")) } func Test_App_Static_Prefix(t *testing.T) { @@ -788,28 +783,28 @@ func Test_App_Static_Prefix(t *testing.T) { req := httptest.NewRequest(MethodGet, "/john/index.html", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) app.Static("/prefix", "./.github/testdata") req = httptest.NewRequest(MethodGet, "/prefix/index.html", nil) resp, err = app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) app.Static("/single", "./.github/testdata/testRoutes.json") req = httptest.NewRequest(MethodGet, "/single", nil) resp, err = app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMEApplicationJSON, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMEApplicationJSON, resp.Header.Get(HeaderContentType)) } func Test_App_Static_Trailing_Slash(t *testing.T) { @@ -818,44 +813,44 @@ func Test_App_Static_Trailing_Slash(t *testing.T) { req := httptest.NewRequest(MethodGet, "/john/", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) app.Static("/john_without_index", "./.github/testdata/fs/css") req = httptest.NewRequest(MethodGet, "/john_without_index/", nil) resp, err = app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 404, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 404, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) app.Static("/john/", "./.github") req = httptest.NewRequest(MethodGet, "/john/", nil) resp, err = app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) req = httptest.NewRequest(MethodGet, "/john", nil) resp, err = app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) app.Static("/john_without_index/", "./.github/testdata/fs/css") req = httptest.NewRequest(MethodGet, "/john_without_index/", nil) resp, err = app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 404, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 404, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) } func Test_App_Static_Next(t *testing.T) { @@ -875,28 +870,28 @@ func Test_App_Static_Next(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) req.Header.Set("X-Custom-Header", "skip") resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, strings.Contains(string(body), "You've skipped app.Static")) + require.NoError(t, err) + require.True(t, strings.Contains(string(body), "You've skipped app.Static")) }) t.Run("app.Static is not skipped: serving index.html", func(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) req.Header.Set("X-Custom-Header", "don't skip") resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!")) + require.NoError(t, err) + require.True(t, strings.Contains(string(body), "Hello, World!")) }) } @@ -919,35 +914,35 @@ func Test_App_Mixed_Routes_WithSameLen(t *testing.T) { // match get route req := httptest.NewRequest(MethodGet, "/foobar", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, "TestValue", resp.Header.Get("TestHeader")) - utils.AssertEqual(t, "text/html", resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, "TestValue", resp.Header.Get("TestHeader")) + require.Equal(t, "text/html", resp.Header.Get(HeaderContentType)) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "FOO_BAR", string(body)) + require.NoError(t, err) + require.Equal(t, "FOO_BAR", string(body)) // match static route req = httptest.NewRequest(MethodGet, "/tesbar", nil) resp, err = app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") - utils.AssertEqual(t, "TestValue", resp.Header.Get("TestHeader")) - utils.AssertEqual(t, "text/html; charset=utf-8", resp.Header.Get(HeaderContentType)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(HeaderContentLength) == "") + require.Equal(t, "TestValue", resp.Header.Get("TestHeader")) + require.Equal(t, "text/html; charset=utf-8", resp.Header.Get(HeaderContentType)) body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!"), "Response: "+string(body)) - utils.AssertEqual(t, true, strings.HasPrefix(string(body), ""), "Response: "+string(body)) + require.NoError(t, err) + require.True(t, strings.Contains(string(body), "Hello, World!"), "Response: "+string(body)) + require.True(t, strings.HasPrefix(string(body), ""), "Response: "+string(body)) } func Test_App_Group_Invalid(t *testing.T) { defer func() { if err := recover(); err != nil { - utils.AssertEqual(t, "use: invalid handler int\n", fmt.Sprintf("%v", err)) + require.Equal(t, "use: invalid handler int\n", fmt.Sprintf("%v", err)) } }() New().Group("/").Use(1) @@ -965,9 +960,9 @@ func Test_App_Group_Mount(t *testing.T) { v1.Mount("/john", micro) resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, uint32(2), app.handlersCount) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.Equal(t, uint32(2), app.handlersCount) } func Test_App_Group(t *testing.T) { @@ -1019,14 +1014,14 @@ func Test_App_Group(t *testing.T) { api.Post("/", dummyHandler) resp, err := app.Test(httptest.NewRequest(MethodPost, "/test/v1/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") // utils.AssertEqual(t, "/test/v1", resp.Header.Get("Location"), "Location") api.Get("/users", dummyHandler) resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1/UsErS", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") // utils.AssertEqual(t, "/test/v1/users", resp.Header.Get("Location"), "Location") } @@ -1071,12 +1066,12 @@ func Test_App_Route(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodPost, "/test/v1/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1/UsErS", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") } func Test_App_Deep_Group(t *testing.T) { @@ -1095,7 +1090,7 @@ func Test_App_Deep_Group(t *testing.T) { return c.SendStatus(200) }) testStatus200(t, app, "/api/v1/user/authenticate", MethodGet) - utils.AssertEqual(t, 4, runThroughCount, "Loop count") + require.Equal(t, 4, runThroughCount, "Loop count") } // go test -run Test_App_Next_Method @@ -1104,15 +1099,15 @@ func Test_App_Next_Method(t *testing.T) { app.config.DisableStartupMessage = true app.Use(func(c Ctx) error { - utils.AssertEqual(t, MethodGet, c.Method()) + require.Equal(t, MethodGet, c.Method()) err := c.Next() - utils.AssertEqual(t, MethodGet, c.Method()) + require.Equal(t, MethodGet, c.Method()) return err }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 404, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 404, resp.StatusCode, "Status code") } // go test -v -run=^$ -bench=Benchmark_AcquireCtx -benchmem -count=4 @@ -1136,8 +1131,8 @@ func Benchmark_NewError(b *testing.B) { // go test -run Test_NewError func Test_NewError(t *testing.T) { e := NewError(StatusForbidden, "permission denied") - utils.AssertEqual(t, StatusForbidden, e.Code) - utils.AssertEqual(t, "permission denied", e.Message) + require.Equal(t, StatusForbidden, e.Code) + require.Equal(t, "permission denied", e.Message) } // go test -run Test_Test_Timeout @@ -1148,8 +1143,8 @@ func Test_Test_Timeout(t *testing.T) { app.Get("/", testEmptyHandler) resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil), -1) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") app.Get("timeout", func(c Ctx) error { time.Sleep(200 * time.Millisecond) @@ -1157,7 +1152,7 @@ func Test_Test_Timeout(t *testing.T) { }) _, err = app.Test(httptest.NewRequest(MethodGet, "/timeout", nil), 20) - utils.AssertEqual(t, true, err != nil, "app.Test(req)") + require.True(t, err != nil, "app.Test(req)") } type errorReader int @@ -1174,14 +1169,14 @@ func Test_Test_DumpError(t *testing.T) { app.Get("/", testEmptyHandler) resp, err := app.Test(httptest.NewRequest(MethodGet, "/", errorReader(0))) - utils.AssertEqual(t, true, resp == nil) - utils.AssertEqual(t, "errorReader", err.Error()) + require.True(t, resp == nil) + require.Equal(t, "errorReader", err.Error()) } // go test -run Test_App_Handler func Test_App_Handler(t *testing.T) { h := New().Handler() - utils.AssertEqual(t, "fasthttp.RequestHandler", reflect.TypeOf(h).String()) + require.Equal(t, "fasthttp.RequestHandler", reflect.TypeOf(h).String()) } type invalidView struct{} @@ -1196,7 +1191,7 @@ func Test_App_Init_Error_View(t *testing.T) { defer func() { if err := recover(); err != nil { - utils.AssertEqual(t, "implement me", fmt.Sprintf("%v", err)) + require.Equal(t, "implement me", fmt.Sprintf("%v", err)) } }() _ = app.config.Views.Render(nil, "", nil) @@ -1212,16 +1207,16 @@ func Test_App_Stack(t *testing.T) { app.Post("/path3", testEmptyHandler) stack := app.Stack() - utils.AssertEqual(t, 9, len(stack)) - utils.AssertEqual(t, 3, len(stack[methodInt(MethodGet)])) - utils.AssertEqual(t, 3, len(stack[methodInt(MethodHead)])) - utils.AssertEqual(t, 2, len(stack[methodInt(MethodPost)])) - utils.AssertEqual(t, 1, len(stack[methodInt(MethodPut)])) - utils.AssertEqual(t, 1, len(stack[methodInt(MethodPatch)])) - utils.AssertEqual(t, 1, len(stack[methodInt(MethodDelete)])) - utils.AssertEqual(t, 1, len(stack[methodInt(MethodConnect)])) - utils.AssertEqual(t, 1, len(stack[methodInt(MethodOptions)])) - utils.AssertEqual(t, 1, len(stack[methodInt(MethodTrace)])) + require.Equal(t, 9, len(stack)) + require.Equal(t, 3, len(stack[methodInt(MethodGet)])) + require.Equal(t, 3, len(stack[methodInt(MethodHead)])) + require.Equal(t, 2, len(stack[methodInt(MethodPost)])) + require.Equal(t, 1, len(stack[methodInt(MethodPut)])) + require.Equal(t, 1, len(stack[methodInt(MethodPatch)])) + require.Equal(t, 1, len(stack[methodInt(MethodDelete)])) + require.Equal(t, 1, len(stack[methodInt(MethodConnect)])) + require.Equal(t, 1, len(stack[methodInt(MethodOptions)])) + require.Equal(t, 1, len(stack[methodInt(MethodTrace)])) } // go test -run Test_App_HandlersCount @@ -1233,7 +1228,7 @@ func Test_App_HandlersCount(t *testing.T) { app.Post("/path3", testEmptyHandler) count := app.HandlersCount() - utils.AssertEqual(t, uint32(4), count) + require.Equal(t, uint32(4), count) } // go test -run Test_App_ReadTimeout @@ -1253,23 +1248,23 @@ func Test_App_ReadTimeout(t *testing.T) { time.Sleep(500 * time.Millisecond) conn, err := net.Dial(NetworkTCP4, "127.0.0.1:4004") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) defer conn.Close() _, err = conn.Write([]byte("HEAD /read-timeout HTTP/1.1\r\n")) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) buf := make([]byte, 1024) var n int n, err = conn.Read(buf) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, bytes.Contains(buf[:n], []byte("408 Request Timeout"))) + require.NoError(t, err) + require.True(t, bytes.Contains(buf[:n], []byte("408 Request Timeout"))) - utils.AssertEqual(t, nil, app.Shutdown()) + require.Nil(t, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.Listen(":4004")) + require.Nil(t, app.Listen(":4004")) } // go test -run Test_App_BadRequest @@ -1285,23 +1280,23 @@ func Test_App_BadRequest(t *testing.T) { go func() { time.Sleep(500 * time.Millisecond) conn, err := net.Dial(NetworkTCP4, "127.0.0.1:4005") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) defer conn.Close() _, err = conn.Write([]byte("BadRequest\r\n")) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) buf := make([]byte, 1024) var n int n, err = conn.Read(buf) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) - utils.AssertEqual(t, true, bytes.Contains(buf[:n], []byte("400 Bad Request"))) + require.True(t, bytes.Contains(buf[:n], []byte("400 Bad Request"))) - utils.AssertEqual(t, nil, app.Shutdown()) + require.Nil(t, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.Listen(":4005")) + require.Nil(t, app.Listen(":4005")) } // go test -run Test_App_SmallReadBuffer @@ -1319,19 +1314,19 @@ func Test_App_SmallReadBuffer(t *testing.T) { time.Sleep(500 * time.Millisecond) resp, err := http.Get("http://127.0.0.1:4006/small-read-buffer") if resp != nil { - utils.AssertEqual(t, 431, resp.StatusCode) + require.Equal(t, 431, resp.StatusCode) } - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, nil, app.Shutdown()) + require.NoError(t, err) + require.Nil(t, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.Listen(":4006")) + require.Nil(t, app.Listen(":4006")) } func Test_App_Server(t *testing.T) { app := New() - utils.AssertEqual(t, false, app.Server() == nil) + require.False(t, app.Server() == nil) } func Test_App_Error_In_Fasthttp_Server(t *testing.T) { @@ -1342,8 +1337,8 @@ func Test_App_Error_In_Fasthttp_Server(t *testing.T) { app.server.GetOnly = true resp, err := app.Test(httptest.NewRequest(MethodPost, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 500, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 500, resp.StatusCode) } // go test -race -run Test_App_New_Test_Parallel @@ -1368,10 +1363,10 @@ func Test_App_ReadBodyStream(t *testing.T) { }) testString := "this is a test" resp, err := app.Test(httptest.NewRequest("POST", "/", bytes.NewBufferString(testString))) - utils.AssertEqual(t, nil, err, "app.Test(req)") + require.NoError(t, err, "app.Test(req)") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "io.ReadAll(resp.Body)") - utils.AssertEqual(t, fmt.Sprintf("true %s", testString), string(body)) + require.NoError(t, err, "io.ReadAll(resp.Body)") + require.Equal(t, fmt.Sprintf("true %s", testString), string(body)) } func Test_App_DisablePreParseMultipartForm(t *testing.T) { @@ -1405,20 +1400,20 @@ func Test_App_DisablePreParseMultipartForm(t *testing.T) { b := &bytes.Buffer{} w := multipart.NewWriter(b) writer, err := w.CreateFormFile("test", "test") - utils.AssertEqual(t, nil, err, "w.CreateFormFile") + require.NoError(t, err, "w.CreateFormFile") n, err := writer.Write([]byte(testString)) - utils.AssertEqual(t, nil, err, "writer.Write") - utils.AssertEqual(t, len(testString), n, "writer n") - utils.AssertEqual(t, nil, w.Close(), "w.Close()") + require.NoError(t, err, "writer.Write") + require.Equal(t, len(testString), n, "writer n") + require.Nil(t, w.Close(), "w.Close()") req := httptest.NewRequest("POST", "/", b) req.Header.Set("Content-Type", w.FormDataContentType()) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") + require.NoError(t, err, "app.Test(req)") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "io.ReadAll(resp.Body)") + require.NoError(t, err, "io.ReadAll(resp.Body)") - utils.AssertEqual(t, testString, string(body)) + require.Equal(t, testString, string(body)) } func Test_App_UseMountedErrorHandler(t *testing.T) { @@ -1495,20 +1490,20 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { app.Mount("/api", fiber) resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub", nil)) - utils.AssertEqual(t, nil, err, "/api/sub req") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "/api/sub req") + require.Equal(t, 200, resp.StatusCode, "Status code") b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") - utils.AssertEqual(t, "hi, i'm a custom sub fiber error", string(b), "Response body") + require.NoError(t, err, "iotuil.ReadAll()") + require.Equal(t, "hi, i'm a custom sub fiber error", string(b), "Response body") resp2, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub/third", nil)) - utils.AssertEqual(t, nil, err, "/api/sub/third req") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "/api/sub/third req") + require.Equal(t, 200, resp.StatusCode, "Status code") b, err = io.ReadAll(resp2.Body) - utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") - utils.AssertEqual(t, "hi, i'm a custom sub sub fiber error", string(b), "Third fiber Response body") + require.NoError(t, err, "iotuil.ReadAll()") + require.Equal(t, "hi, i'm a custom sub sub fiber error", string(b), "Third fiber Response body") } func Test_App_Test_no_timeout_infinitely(t *testing.T) { diff --git a/bind_test.go b/bind_test.go index 6d64f8e059..090f76db04 100644 --- a/bind_test.go +++ b/bind_test.go @@ -12,7 +12,7 @@ import ( "time" "github.com/gofiber/fiber/v3/binder" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -31,23 +31,23 @@ func Test_Bind_Query(t *testing.T) { c.Request().Header.SetContentType("") c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball&hobby=football") q := new(Query) - utils.AssertEqual(t, nil, c.Bind().Query(q)) - utils.AssertEqual(t, 2, len(q.Hobby)) + require.Nil(t, c.Bind().Query(q)) + require.Equal(t, 2, len(q.Hobby)) c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football") q = new(Query) - utils.AssertEqual(t, nil, c.Bind().Query(q)) - utils.AssertEqual(t, 2, len(q.Hobby)) + require.Nil(t, c.Bind().Query(q)) + require.Equal(t, 2, len(q.Hobby)) c.Request().URI().SetQueryString("id=1&name=tom&hobby=scoccer&hobby=basketball,football") q = new(Query) - utils.AssertEqual(t, nil, c.Bind().Query(q)) - utils.AssertEqual(t, 3, len(q.Hobby)) + require.Nil(t, c.Bind().Query(q)) + require.Equal(t, 3, len(q.Hobby)) empty := new(Query) c.Request().URI().SetQueryString("") - utils.AssertEqual(t, nil, c.Bind().Query(empty)) - utils.AssertEqual(t, 0, len(empty.Hobby)) + require.Nil(t, c.Bind().Query(empty)) + require.Equal(t, 0, len(empty.Hobby)) type Query2 struct { Bool bool @@ -64,30 +64,30 @@ func Test_Bind_Query(t *testing.T) { q2 := new(Query2) q2.Bool = true q2.Name = "hello world" - utils.AssertEqual(t, nil, c.Bind().Query(q2)) - utils.AssertEqual(t, "basketball,football", q2.Hobby) - utils.AssertEqual(t, true, q2.Bool) - utils.AssertEqual(t, "tom", q2.Name) // check value get overwritten - utils.AssertEqual(t, []string{"milo", "coke", "pepsi"}, q2.FavouriteDrinks) + require.Nil(t, c.Bind().Query(q2)) + require.Equal(t, "basketball,football", q2.Hobby) + require.True(t, q2.Bool) + require.Equal(t, "tom", q2.Name) // check value get overwritten + require.Equal(t, []string{"milo", "coke", "pepsi"}, q2.FavouriteDrinks) var nilSlice []string - utils.AssertEqual(t, nilSlice, q2.Empty) - utils.AssertEqual(t, []string{""}, q2.Alloc) - utils.AssertEqual(t, []int64{1}, q2.No) + require.Equal(t, nilSlice, q2.Empty) + require.Equal(t, []string{""}, q2.Alloc) + require.Equal(t, []int64{1}, q2.No) type RequiredQuery struct { Name string `query:"name,required"` } rq := new(RequiredQuery) c.Request().URI().SetQueryString("") - utils.AssertEqual(t, "name is empty", c.Bind().Query(rq).Error()) + require.Equal(t, "name is empty", c.Bind().Query(rq).Error()) type ArrayQuery struct { Data []string } aq := new(ArrayQuery) c.Request().URI().SetQueryString("data[]=john&data[]=doe") - utils.AssertEqual(t, nil, c.Bind().Query(aq)) - utils.AssertEqual(t, 2, len(aq.Data)) + require.Nil(t, c.Bind().Query(aq)) + require.Equal(t, 2, len(aq.Data)) } // go test -run Test_Bind_Query_Map -v @@ -101,32 +101,32 @@ func Test_Bind_Query_Map(t *testing.T) { c.Request().Header.SetContentType("") c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball&hobby=football") q := make(map[string][]string) - utils.AssertEqual(t, nil, c.Bind().Query(&q)) - utils.AssertEqual(t, 2, len(q["hobby"])) + require.Nil(t, c.Bind().Query(&q)) + require.Equal(t, 2, len(q["hobby"])) c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football") q = make(map[string][]string) - utils.AssertEqual(t, nil, c.Bind().Query(&q)) - utils.AssertEqual(t, 2, len(q["hobby"])) + require.Nil(t, c.Bind().Query(&q)) + require.Equal(t, 2, len(q["hobby"])) c.Request().URI().SetQueryString("id=1&name=tom&hobby=scoccer&hobby=basketball,football") q = make(map[string][]string) - utils.AssertEqual(t, nil, c.Bind().Query(&q)) - utils.AssertEqual(t, 3, len(q["hobby"])) + require.Nil(t, c.Bind().Query(&q)) + require.Equal(t, 3, len(q["hobby"])) c.Request().URI().SetQueryString("id=1&name=tom&hobby=scoccer") qq := make(map[string]string) - utils.AssertEqual(t, nil, c.Bind().Query(&qq)) - utils.AssertEqual(t, "1", qq["id"]) + require.Nil(t, c.Bind().Query(&qq)) + require.Equal(t, "1", qq["id"]) empty := make(map[string][]string) c.Request().URI().SetQueryString("") - utils.AssertEqual(t, nil, c.Bind().Query(&empty)) - utils.AssertEqual(t, 0, len(empty["hobby"])) + require.Nil(t, c.Bind().Query(&empty)) + require.Equal(t, 0, len(empty["hobby"])) em := make(map[string][]int) c.Request().URI().SetQueryString("") - utils.AssertEqual(t, binder.ErrMapNotConvertable, c.Bind().Query(&em)) + require.Equal(t, binder.ErrMapNotConvertable, c.Bind().Query(&em)) } // go test -run Test_Bind_Query_WithSetParserDecoder -v @@ -166,20 +166,20 @@ func Test_Bind_Query_WithSetParserDecoder(t *testing.T) { q := new(NonRFCTimeInput) c.Request().URI().SetQueryString("date=2021-04-10&title=CustomDateTest&Body=October") - utils.AssertEqual(t, nil, c.Bind().Query(q)) + require.Nil(t, c.Bind().Query(q)) fmt.Println(q.Date, "q.Date") - utils.AssertEqual(t, "CustomDateTest", q.Title) + require.Equal(t, "CustomDateTest", q.Title) date := fmt.Sprintf("%v", q.Date) - utils.AssertEqual(t, "{0 63753609600 }", date) - utils.AssertEqual(t, "October", q.Body) + require.Equal(t, "{0 63753609600 }", date) + require.Equal(t, "October", q.Body) c.Request().URI().SetQueryString("date=2021-04-10&title&Body=October") q = &NonRFCTimeInput{ Title: "Existing title", Body: "Existing Body", } - utils.AssertEqual(t, nil, c.Bind().Query(q)) - utils.AssertEqual(t, "", q.Title) + require.Nil(t, c.Bind().Query(q)) + require.Equal(t, "", q.Title) } // go test -run Test_Bind_Query_Schema -v @@ -198,19 +198,19 @@ func Test_Bind_Query_Schema(t *testing.T) { c.Request().Header.SetContentType("") c.Request().URI().SetQueryString("name=tom&nested.age=10") q := new(Query1) - utils.AssertEqual(t, nil, c.Bind().Query(q)) + require.Nil(t, c.Bind().Query(q)) c.Request().URI().SetQueryString("namex=tom&nested.age=10") q = new(Query1) - utils.AssertEqual(t, "name is empty", c.Bind().Query(q).Error()) + require.Equal(t, "name is empty", c.Bind().Query(q).Error()) c.Request().URI().SetQueryString("name=tom&nested.agex=10") q = new(Query1) - utils.AssertEqual(t, nil, c.Bind().Query(q)) + require.Nil(t, c.Bind().Query(q)) c.Request().URI().SetQueryString("name=tom&test.age=10") q = new(Query1) - utils.AssertEqual(t, "nested is empty", c.Bind().Query(q).Error()) + require.Equal(t, "nested is empty", c.Bind().Query(q).Error()) type Query2 struct { Name string `query:"name"` @@ -220,19 +220,19 @@ func Test_Bind_Query_Schema(t *testing.T) { } c.Request().URI().SetQueryString("name=tom&nested.age=10") q2 := new(Query2) - utils.AssertEqual(t, nil, c.Bind().Query(q2)) + require.Nil(t, c.Bind().Query(q2)) c.Request().URI().SetQueryString("nested.age=10") q2 = new(Query2) - utils.AssertEqual(t, nil, c.Bind().Query(q2)) + require.Nil(t, c.Bind().Query(q2)) c.Request().URI().SetQueryString("nested.agex=10") q2 = new(Query2) - utils.AssertEqual(t, "nested.age is empty", c.Bind().Query(q2).Error()) + require.Equal(t, "nested.age is empty", c.Bind().Query(q2).Error()) c.Request().URI().SetQueryString("nested.agex=10") q2 = new(Query2) - utils.AssertEqual(t, "nested.age is empty", c.Bind().Query(q2).Error()) + require.Equal(t, "nested.age is empty", c.Bind().Query(q2).Error()) type Node struct { Value int `query:"val,required"` @@ -240,20 +240,20 @@ func Test_Bind_Query_Schema(t *testing.T) { } c.Request().URI().SetQueryString("val=1&next.val=3") n := new(Node) - utils.AssertEqual(t, nil, c.Bind().Query(n)) - utils.AssertEqual(t, 1, n.Value) - utils.AssertEqual(t, 3, n.Next.Value) + require.Nil(t, c.Bind().Query(n)) + require.Equal(t, 1, n.Value) + require.Equal(t, 3, n.Next.Value) c.Request().URI().SetQueryString("next.val=2") n = new(Node) - utils.AssertEqual(t, "val is empty", c.Bind().Query(n).Error()) + require.Equal(t, "val is empty", c.Bind().Query(n).Error()) c.Request().URI().SetQueryString("val=3&next.value=2") n = new(Node) n.Next = new(Node) - utils.AssertEqual(t, nil, c.Bind().Query(n)) - utils.AssertEqual(t, 3, n.Value) - utils.AssertEqual(t, 0, n.Next.Value) + require.Nil(t, c.Bind().Query(n)) + require.Equal(t, 3, n.Value) + require.Equal(t, 0, n.Next.Value) type Person struct { Name string `query:"name"` @@ -266,21 +266,21 @@ func Test_Bind_Query_Schema(t *testing.T) { c.Request().URI().SetQueryString("data[0][name]=john&data[0][age]=10&data[1][name]=doe&data[1][age]=12") cq := new(CollectionQuery) - utils.AssertEqual(t, nil, c.Bind().Query(cq)) - utils.AssertEqual(t, 2, len(cq.Data)) - utils.AssertEqual(t, "john", cq.Data[0].Name) - utils.AssertEqual(t, 10, cq.Data[0].Age) - utils.AssertEqual(t, "doe", cq.Data[1].Name) - utils.AssertEqual(t, 12, cq.Data[1].Age) + require.Nil(t, c.Bind().Query(cq)) + require.Equal(t, 2, len(cq.Data)) + require.Equal(t, "john", cq.Data[0].Name) + require.Equal(t, 10, cq.Data[0].Age) + require.Equal(t, "doe", cq.Data[1].Name) + require.Equal(t, 12, cq.Data[1].Age) c.Request().URI().SetQueryString("data.0.name=john&data.0.age=10&data.1.name=doe&data.1.age=12") cq = new(CollectionQuery) - utils.AssertEqual(t, nil, c.Bind().Query(cq)) - utils.AssertEqual(t, 2, len(cq.Data)) - utils.AssertEqual(t, "john", cq.Data[0].Name) - utils.AssertEqual(t, 10, cq.Data[0].Age) - utils.AssertEqual(t, "doe", cq.Data[1].Name) - utils.AssertEqual(t, 12, cq.Data[1].Age) + require.Nil(t, c.Bind().Query(cq)) + require.Equal(t, 2, len(cq.Data)) + require.Equal(t, "john", cq.Data[0].Name) + require.Equal(t, 10, cq.Data[0].Age) + require.Equal(t, "doe", cq.Data[1].Name) + require.Equal(t, 12, cq.Data[1].Age) } // go test -run Test_Bind_Header -v @@ -301,19 +301,19 @@ func Test_Bind_Header(t *testing.T) { c.Request().Header.Add("Name", "John Doe") c.Request().Header.Add("Hobby", "golang,fiber") q := new(Header) - utils.AssertEqual(t, nil, c.Bind().Header(q)) - utils.AssertEqual(t, 2, len(q.Hobby)) + require.Nil(t, c.Bind().Header(q)) + require.Equal(t, 2, len(q.Hobby)) c.Request().Header.Del("hobby") c.Request().Header.Add("Hobby", "golang,fiber,go") q = new(Header) - utils.AssertEqual(t, nil, c.Bind().Header(q)) - utils.AssertEqual(t, 3, len(q.Hobby)) + require.Nil(t, c.Bind().Header(q)) + require.Equal(t, 3, len(q.Hobby)) empty := new(Header) c.Request().Header.Del("hobby") - utils.AssertEqual(t, nil, c.Bind().Query(empty)) - utils.AssertEqual(t, 0, len(empty.Hobby)) + require.Nil(t, c.Bind().Query(empty)) + require.Equal(t, 0, len(empty.Hobby)) type Header2 struct { Bool bool @@ -337,22 +337,22 @@ func Test_Bind_Header(t *testing.T) { h2 := new(Header2) h2.Bool = true h2.Name = "hello world" - utils.AssertEqual(t, nil, c.Bind().Header(h2)) - utils.AssertEqual(t, "go,fiber", h2.Hobby) - utils.AssertEqual(t, true, h2.Bool) - utils.AssertEqual(t, "Jane Doe", h2.Name) // check value get overwritten - utils.AssertEqual(t, []string{"milo", "coke", "pepsi"}, h2.FavouriteDrinks) + require.Nil(t, c.Bind().Header(h2)) + require.Equal(t, "go,fiber", h2.Hobby) + require.True(t, h2.Bool) + require.Equal(t, "Jane Doe", h2.Name) // check value get overwritten + require.Equal(t, []string{"milo", "coke", "pepsi"}, h2.FavouriteDrinks) var nilSlice []string - utils.AssertEqual(t, nilSlice, h2.Empty) - utils.AssertEqual(t, []string{""}, h2.Alloc) - utils.AssertEqual(t, []int64{1}, h2.No) + require.Equal(t, nilSlice, h2.Empty) + require.Equal(t, []string{""}, h2.Alloc) + require.Equal(t, []int64{1}, h2.No) type RequiredHeader struct { Name string `header:"name,required"` } rh := new(RequiredHeader) c.Request().Header.Del("name") - utils.AssertEqual(t, "name is empty", c.Bind().Header(rh).Error()) + require.Equal(t, "name is empty", c.Bind().Header(rh).Error()) } // go test -run Test_Bind_Header_Map -v @@ -369,19 +369,19 @@ func Test_Bind_Header_Map(t *testing.T) { c.Request().Header.Add("Name", "John Doe") c.Request().Header.Add("Hobby", "golang,fiber") q := make(map[string][]string, 0) - utils.AssertEqual(t, nil, c.Bind().Header(&q)) - utils.AssertEqual(t, 2, len(q["Hobby"])) + require.Nil(t, c.Bind().Header(&q)) + require.Equal(t, 2, len(q["Hobby"])) c.Request().Header.Del("hobby") c.Request().Header.Add("Hobby", "golang,fiber,go") q = make(map[string][]string, 0) - utils.AssertEqual(t, nil, c.Bind().Header(&q)) - utils.AssertEqual(t, 3, len(q["Hobby"])) + require.Nil(t, c.Bind().Header(&q)) + require.Equal(t, 3, len(q["Hobby"])) empty := make(map[string][]string, 0) c.Request().Header.Del("hobby") - utils.AssertEqual(t, nil, c.Bind().Query(&empty)) - utils.AssertEqual(t, 0, len(empty["Hobby"])) + require.Nil(t, c.Bind().Query(&empty)) + require.Equal(t, 0, len(empty["Hobby"])) } // go test -run Test_Bind_Header_WithSetParserDecoder -v @@ -424,20 +424,20 @@ func Test_Bind_Header_WithSetParserDecoder(t *testing.T) { c.Request().Header.Add("Title", "CustomDateTest") c.Request().Header.Add("Body", "October") - utils.AssertEqual(t, nil, c.Bind().Header(r)) + require.Nil(t, c.Bind().Header(r)) fmt.Println(r.Date, "q.Date") - utils.AssertEqual(t, "CustomDateTest", r.Title) + require.Equal(t, "CustomDateTest", r.Title) date := fmt.Sprintf("%v", r.Date) - utils.AssertEqual(t, "{0 63753609600 }", date) - utils.AssertEqual(t, "October", r.Body) + require.Equal(t, "{0 63753609600 }", date) + require.Equal(t, "October", r.Body) c.Request().Header.Add("Title", "") r = &NonRFCTimeInput{ Title: "Existing title", Body: "Existing Body", } - utils.AssertEqual(t, nil, c.Bind().Header(r)) - utils.AssertEqual(t, "", r.Title) + require.Nil(t, c.Bind().Header(r)) + require.Equal(t, "", r.Title) } // go test -run Test_Bind_Header_Schema -v @@ -458,21 +458,21 @@ func Test_Bind_Header_Schema(t *testing.T) { c.Request().Header.Add("Name", "tom") c.Request().Header.Add("Nested.Age", "10") q := new(Header1) - utils.AssertEqual(t, nil, c.Bind().Header(q)) + require.Nil(t, c.Bind().Header(q)) c.Request().Header.Del("Name") q = new(Header1) - utils.AssertEqual(t, "Name is empty", c.Bind().Header(q).Error()) + require.Equal(t, "Name is empty", c.Bind().Header(q).Error()) c.Request().Header.Add("Name", "tom") c.Request().Header.Del("Nested.Age") c.Request().Header.Add("Nested.Agex", "10") q = new(Header1) - utils.AssertEqual(t, nil, c.Bind().Header(q)) + require.Nil(t, c.Bind().Header(q)) c.Request().Header.Del("Nested.Agex") q = new(Header1) - utils.AssertEqual(t, "Nested is empty", c.Bind().Header(q).Error()) + require.Equal(t, "Nested is empty", c.Bind().Header(q).Error()) c.Request().Header.Del("Nested.Agex") c.Request().Header.Del("Name") @@ -488,17 +488,17 @@ func Test_Bind_Header_Schema(t *testing.T) { c.Request().Header.Add("Nested.Age", "10") h2 := new(Header2) - utils.AssertEqual(t, nil, c.Bind().Header(h2)) + require.Nil(t, c.Bind().Header(h2)) c.Request().Header.Del("Name") h2 = new(Header2) - utils.AssertEqual(t, nil, c.Bind().Header(h2)) + require.Nil(t, c.Bind().Header(h2)) c.Request().Header.Del("Name") c.Request().Header.Del("Nested.Age") c.Request().Header.Add("Nested.Agex", "10") h2 = new(Header2) - utils.AssertEqual(t, "Nested.age is empty", c.Bind().Header(h2).Error()) + require.Equal(t, "Nested.age is empty", c.Bind().Header(h2).Error()) type Node struct { Value int `header:"Val,required"` @@ -507,22 +507,22 @@ func Test_Bind_Header_Schema(t *testing.T) { c.Request().Header.Add("Val", "1") c.Request().Header.Add("Next.Val", "3") n := new(Node) - utils.AssertEqual(t, nil, c.Bind().Header(n)) - utils.AssertEqual(t, 1, n.Value) - utils.AssertEqual(t, 3, n.Next.Value) + require.Nil(t, c.Bind().Header(n)) + require.Equal(t, 1, n.Value) + require.Equal(t, 3, n.Next.Value) c.Request().Header.Del("Val") n = new(Node) - utils.AssertEqual(t, "Val is empty", c.Bind().Header(n).Error()) + require.Equal(t, "Val is empty", c.Bind().Header(n).Error()) c.Request().Header.Add("Val", "3") c.Request().Header.Del("Next.Val") c.Request().Header.Add("Next.Value", "2") n = new(Node) n.Next = new(Node) - utils.AssertEqual(t, nil, c.Bind().Header(n)) - utils.AssertEqual(t, 3, n.Value) - utils.AssertEqual(t, 0, n.Next.Value) + require.Nil(t, c.Bind().Header(n)) + require.Equal(t, 3, n.Value) + require.Equal(t, 0, n.Next.Value) } // go test -run Test_Bind_Resp_Header -v @@ -543,19 +543,19 @@ func Test_Bind_RespHeader(t *testing.T) { c.Response().Header.Add("Name", "John Doe") c.Response().Header.Add("Hobby", "golang,fiber") q := new(Header) - utils.AssertEqual(t, nil, c.Bind().RespHeader(q)) - utils.AssertEqual(t, 2, len(q.Hobby)) + require.Nil(t, c.Bind().RespHeader(q)) + require.Equal(t, 2, len(q.Hobby)) c.Response().Header.Del("hobby") c.Response().Header.Add("Hobby", "golang,fiber,go") q = new(Header) - utils.AssertEqual(t, nil, c.Bind().RespHeader(q)) - utils.AssertEqual(t, 3, len(q.Hobby)) + require.Nil(t, c.Bind().RespHeader(q)) + require.Equal(t, 3, len(q.Hobby)) empty := new(Header) c.Response().Header.Del("hobby") - utils.AssertEqual(t, nil, c.Bind().Query(empty)) - utils.AssertEqual(t, 0, len(empty.Hobby)) + require.Nil(t, c.Bind().Query(empty)) + require.Equal(t, 0, len(empty.Hobby)) type Header2 struct { Bool bool @@ -579,22 +579,22 @@ func Test_Bind_RespHeader(t *testing.T) { h2 := new(Header2) h2.Bool = true h2.Name = "hello world" - utils.AssertEqual(t, nil, c.Bind().RespHeader(h2)) - utils.AssertEqual(t, "go,fiber", h2.Hobby) - utils.AssertEqual(t, true, h2.Bool) - utils.AssertEqual(t, "Jane Doe", h2.Name) // check value get overwritten - utils.AssertEqual(t, []string{"milo", "coke", "pepsi"}, h2.FavouriteDrinks) + require.Nil(t, c.Bind().RespHeader(h2)) + require.Equal(t, "go,fiber", h2.Hobby) + require.True(t, h2.Bool) + require.Equal(t, "Jane Doe", h2.Name) // check value get overwritten + require.Equal(t, []string{"milo", "coke", "pepsi"}, h2.FavouriteDrinks) var nilSlice []string - utils.AssertEqual(t, nilSlice, h2.Empty) - utils.AssertEqual(t, []string{""}, h2.Alloc) - utils.AssertEqual(t, []int64{1}, h2.No) + require.Equal(t, nilSlice, h2.Empty) + require.Equal(t, []string{""}, h2.Alloc) + require.Equal(t, []int64{1}, h2.No) type RequiredHeader struct { Name string `respHeader:"name,required"` } rh := new(RequiredHeader) c.Response().Header.Del("name") - utils.AssertEqual(t, "name is empty", c.Bind().RespHeader(rh).Error()) + require.Equal(t, "name is empty", c.Bind().RespHeader(rh).Error()) } // go test -run Test_Bind_RespHeader_Map -v @@ -611,19 +611,19 @@ func Test_Bind_RespHeader_Map(t *testing.T) { c.Response().Header.Add("Name", "John Doe") c.Response().Header.Add("Hobby", "golang,fiber") q := make(map[string][]string, 0) - utils.AssertEqual(t, nil, c.Bind().RespHeader(&q)) - utils.AssertEqual(t, 2, len(q["Hobby"])) + require.Nil(t, c.Bind().RespHeader(&q)) + require.Equal(t, 2, len(q["Hobby"])) c.Response().Header.Del("hobby") c.Response().Header.Add("Hobby", "golang,fiber,go") q = make(map[string][]string, 0) - utils.AssertEqual(t, nil, c.Bind().RespHeader(&q)) - utils.AssertEqual(t, 3, len(q["Hobby"])) + require.Nil(t, c.Bind().RespHeader(&q)) + require.Equal(t, 3, len(q["Hobby"])) empty := make(map[string][]string, 0) c.Response().Header.Del("hobby") - utils.AssertEqual(t, nil, c.Bind().Query(&empty)) - utils.AssertEqual(t, 0, len(empty["Hobby"])) + require.Nil(t, c.Bind().Query(&empty)) + require.Equal(t, 0, len(empty["Hobby"])) } // go test -v -run=^$ -bench=Benchmark_Bind_Query -benchmem -count=4 @@ -645,7 +645,7 @@ func Benchmark_Bind_Query(b *testing.B) { for n := 0; n < b.N; n++ { c.Bind().Query(q) } - utils.AssertEqual(b, nil, c.Bind().Query(q)) + require.Nil(b, c.Bind().Query(q)) } // go test -v -run=^$ -bench=Benchmark_Bind_Query_Map -benchmem -count=4 @@ -662,7 +662,7 @@ func Benchmark_Bind_Query_Map(b *testing.B) { for n := 0; n < b.N; n++ { c.Bind().Query(&q) } - utils.AssertEqual(b, nil, c.Bind().Query(&q)) + require.Nil(b, c.Bind().Query(&q)) } // go test -v -run=^$ -bench=Benchmark_Bind_Query_WithParseParam -benchmem -count=4 @@ -690,7 +690,7 @@ func Benchmark_Bind_Query_WithParseParam(b *testing.B) { c.Bind().Query(cq) } - utils.AssertEqual(b, nil, c.Bind().Query(cq)) + require.Nil(b, c.Bind().Query(cq)) } // go test -v -run=^$ -bench=Benchmark_Bind_Query_Comma -benchmem -count=4 @@ -713,7 +713,7 @@ func Benchmark_Bind_Query_Comma(b *testing.B) { for n := 0; n < b.N; n++ { c.Bind().Query(q) } - utils.AssertEqual(b, nil, c.Bind().Query(q)) + require.Nil(b, c.Bind().Query(q)) } // go test -v -run=^$ -bench=Benchmark_Bind_Header -benchmem -count=4 @@ -739,7 +739,7 @@ func Benchmark_Bind_Header(b *testing.B) { for n := 0; n < b.N; n++ { c.Bind().Header(q) } - utils.AssertEqual(b, nil, c.Bind().Header(q)) + require.Nil(b, c.Bind().Header(q)) } // go test -v -run=^$ -bench=Benchmark_Bind_Header_Map -benchmem -count=4 @@ -760,7 +760,7 @@ func Benchmark_Bind_Header_Map(b *testing.B) { for n := 0; n < b.N; n++ { c.Bind().Header(&q) } - utils.AssertEqual(b, nil, c.Bind().Header(&q)) + require.Nil(b, c.Bind().Header(&q)) } // go test -v -run=^$ -bench=Benchmark_Bind_RespHeader -benchmem -count=4 @@ -786,7 +786,7 @@ func Benchmark_Bind_RespHeader(b *testing.B) { for n := 0; n < b.N; n++ { c.Bind().RespHeader(q) } - utils.AssertEqual(b, nil, c.Bind().RespHeader(q)) + require.Nil(b, c.Bind().RespHeader(q)) } // go test -v -run=^$ -bench=Benchmark_Bind_RespHeader_Map -benchmem -count=4 @@ -807,7 +807,7 @@ func Benchmark_Bind_RespHeader_Map(b *testing.B) { for n := 0; n < b.N; n++ { c.Bind().RespHeader(&q) } - utils.AssertEqual(b, nil, c.Bind().RespHeader(&q)) + require.Nil(b, c.Bind().RespHeader(&q)) } // go test -run Test_Bind_Body @@ -831,8 +831,8 @@ func Test_Bind_Body(t *testing.T) { c.Request().SetBody(gzipJSON.Bytes()) c.Request().Header.SetContentLength(len(gzipJSON.Bytes())) d := new(Demo) - utils.AssertEqual(t, nil, c.Bind().Body(d)) - utils.AssertEqual(t, "john", d.Name) + require.Nil(t, c.Bind().Body(d)) + require.Equal(t, "john", d.Name) c.Request().Header.Del(HeaderContentEncoding) } @@ -841,8 +841,8 @@ func Test_Bind_Body(t *testing.T) { c.Request().SetBody([]byte(body)) c.Request().Header.SetContentLength(len(body)) d := new(Demo) - utils.AssertEqual(t, nil, c.Bind().Body(d)) - utils.AssertEqual(t, "john", d.Name) + require.Nil(t, c.Bind().Body(d)) + require.Equal(t, "john", d.Name) } testDecodeParser(MIMEApplicationJSON, `{"name":"john"}`) @@ -854,7 +854,7 @@ func Test_Bind_Body(t *testing.T) { c.Request().Header.SetContentType(contentType) c.Request().SetBody([]byte(body)) c.Request().Header.SetContentLength(len(body)) - utils.AssertEqual(t, false, c.Bind().Body(nil) == nil) + require.False(t, c.Bind().Body(nil) == nil) } testDecodeParserError("invalid-content-type", "") @@ -869,20 +869,20 @@ func Test_Bind_Body(t *testing.T) { c.Request().SetBody([]byte("data[0][name]=john&data[1][name]=doe")) c.Request().Header.SetContentLength(len(c.Body())) cq := new(CollectionQuery) - utils.AssertEqual(t, nil, c.Bind().Body(cq)) - utils.AssertEqual(t, 2, len(cq.Data)) - utils.AssertEqual(t, "john", cq.Data[0].Name) - utils.AssertEqual(t, "doe", cq.Data[1].Name) + require.Nil(t, c.Bind().Body(cq)) + require.Equal(t, 2, len(cq.Data)) + require.Equal(t, "john", cq.Data[0].Name) + require.Equal(t, "doe", cq.Data[1].Name) c.Request().Reset() c.Request().Header.SetContentType(MIMEApplicationForm) c.Request().SetBody([]byte("data.0.name=john&data.1.name=doe")) c.Request().Header.SetContentLength(len(c.Body())) cq = new(CollectionQuery) - utils.AssertEqual(t, nil, c.Bind().Body(cq)) - utils.AssertEqual(t, 2, len(cq.Data)) - utils.AssertEqual(t, "john", cq.Data[0].Name) - utils.AssertEqual(t, "doe", cq.Data[1].Name) + require.Nil(t, c.Bind().Body(cq)) + require.Equal(t, 2, len(cq.Data)) + require.Equal(t, "john", cq.Data[0].Name) + require.Equal(t, "doe", cq.Data[1].Name) } // go test -run Test_Bind_Body_WithSetParserDecoder @@ -925,11 +925,11 @@ func Test_Bind_Body_WithSetParserDecoder(t *testing.T) { Title: "Existing title", Body: "Existing Body", } - utils.AssertEqual(t, nil, c.Bind().Body(&d)) + require.Nil(t, c.Bind().Body(&d)) date := fmt.Sprintf("%v", d.Date) - utils.AssertEqual(t, "{0 63743587200 }", date) - utils.AssertEqual(t, "", d.Title) - utils.AssertEqual(t, "New Body", d.Body) + require.Equal(t, "{0 63743587200 }", date) + require.Equal(t, "", d.Title) + require.Equal(t, "New Body", d.Body) } testDecodeParser(MIMEApplicationForm, "date=2020-12-15&title=&body=New Body") @@ -956,8 +956,8 @@ func Benchmark_Bind_Body_JSON(b *testing.B) { for n := 0; n < b.N; n++ { _ = c.Bind().Body(d) } - utils.AssertEqual(b, nil, c.Bind().Body(d)) - utils.AssertEqual(b, "john", d.Name) + require.Nil(b, c.Bind().Body(d)) + require.Equal(b, "john", d.Name) } // go test -v -run=^$ -bench=Benchmark_Bind_Body_XML -benchmem -count=4 @@ -980,8 +980,8 @@ func Benchmark_Bind_Body_XML(b *testing.B) { for n := 0; n < b.N; n++ { _ = c.Bind().Body(d) } - utils.AssertEqual(b, nil, c.Bind().Body(d)) - utils.AssertEqual(b, "john", d.Name) + require.Nil(b, c.Bind().Body(d)) + require.Equal(b, "john", d.Name) } // go test -v -run=^$ -bench=Benchmark_Bind_Body_Form -benchmem -count=4 @@ -1004,8 +1004,8 @@ func Benchmark_Bind_Body_Form(b *testing.B) { for n := 0; n < b.N; n++ { _ = c.Bind().Body(d) } - utils.AssertEqual(b, nil, c.Bind().Body(d)) - utils.AssertEqual(b, "john", d.Name) + require.Nil(b, c.Bind().Body(d)) + require.Equal(b, "john", d.Name) } // go test -v -run=^$ -bench=Benchmark_Bind_Body_MultipartForm -benchmem -count=4 @@ -1029,8 +1029,8 @@ func Benchmark_Bind_Body_MultipartForm(b *testing.B) { for n := 0; n < b.N; n++ { _ = c.Bind().Body(d) } - utils.AssertEqual(b, nil, c.Bind().Body(d)) - utils.AssertEqual(b, "john", d.Name) + require.Nil(b, c.Bind().Body(d)) + require.Equal(b, "john", d.Name) } // go test -v -run=^$ -bench=Benchmark_Bind_Body_Form_Map -benchmem -count=4 @@ -1050,8 +1050,8 @@ func Benchmark_Bind_Body_Form_Map(b *testing.B) { for n := 0; n < b.N; n++ { _ = c.Bind().Body(&d) } - utils.AssertEqual(b, nil, c.Bind().Body(&d)) - utils.AssertEqual(b, "john", d["name"]) + require.Nil(b, c.Bind().Body(&d)) + require.Equal(b, "john", d["name"]) } // go test -run Test_Bind_URI @@ -1070,8 +1070,8 @@ func Test_Bind_URI(t *testing.T) { if err := c.Bind().URI(d); err != nil { t.Fatal(err) } - utils.AssertEqual(t, uint(111), d.UserID) - utils.AssertEqual(t, uint(222), d.RoleID) + require.Equal(t, uint(111), d.UserID) + require.Equal(t, uint(222), d.RoleID) return nil }) app.Test(httptest.NewRequest(MethodGet, "/test1/111/role/222", nil)) @@ -1089,8 +1089,8 @@ func Test_Bind_URI_Map(t *testing.T) { if err := c.Bind().URI(&d); err != nil { t.Fatal(err) } - utils.AssertEqual(t, uint(111), d["userId"]) - utils.AssertEqual(t, uint(222), d["roleId"]) + require.Equal(t, uint(111), d["userId"]) + require.Equal(t, uint(222), d["roleId"]) return nil }) app.Test(httptest.NewRequest(MethodGet, "/test1/111/role/222", nil)) @@ -1125,10 +1125,10 @@ func Benchmark_Bind_URI(b *testing.B) { c.Bind().URI(&res) } - utils.AssertEqual(b, "john", res.Param1) - utils.AssertEqual(b, "doe", res.Param2) - utils.AssertEqual(b, "is", res.Param3) - utils.AssertEqual(b, "awesome", res.Param4) + require.Equal(b, "john", res.Param1) + require.Equal(b, "doe", res.Param2) + require.Equal(b, "is", res.Param3) + require.Equal(b, "awesome", res.Param4) } // go test -v -run=^$ -bench=Benchmark_Bind_URI_Map -benchmem -count=4 @@ -1154,10 +1154,10 @@ func Benchmark_Bind_URI_Map(b *testing.B) { c.Bind().URI(&res) } - utils.AssertEqual(b, "john", res["param1"]) - utils.AssertEqual(b, "doe", res["param2"]) - utils.AssertEqual(b, "is", res["param3"]) - utils.AssertEqual(b, "awesome", res["param4"]) + require.Equal(b, "john", res["param1"]) + require.Equal(b, "doe", res["param2"]) + require.Equal(b, "is", res["param3"]) + require.Equal(b, "awesome", res["param4"]) } // go test -run Test_Bind_Cookie -v @@ -1179,19 +1179,19 @@ func Test_Bind_Cookie(t *testing.T) { c.Request().Header.SetCookie("Name", "John Doe") c.Request().Header.SetCookie("Hobby", "golang,fiber") q := new(Cookie) - utils.AssertEqual(t, nil, c.Bind().Cookie(q)) - utils.AssertEqual(t, 2, len(q.Hobby)) + require.Nil(t, c.Bind().Cookie(q)) + require.Equal(t, 2, len(q.Hobby)) c.Request().Header.DelCookie("hobby") c.Request().Header.SetCookie("Hobby", "golang,fiber,go") q = new(Cookie) - utils.AssertEqual(t, nil, c.Bind().Cookie(q)) - utils.AssertEqual(t, 3, len(q.Hobby)) + require.Nil(t, c.Bind().Cookie(q)) + require.Equal(t, 3, len(q.Hobby)) empty := new(Cookie) c.Request().Header.DelCookie("hobby") - utils.AssertEqual(t, nil, c.Bind().Query(empty)) - utils.AssertEqual(t, 0, len(empty.Hobby)) + require.Nil(t, c.Bind().Query(empty)) + require.Equal(t, 0, len(empty.Hobby)) type Cookie2 struct { Bool bool @@ -1215,22 +1215,22 @@ func Test_Bind_Cookie(t *testing.T) { h2 := new(Cookie2) h2.Bool = true h2.Name = "hello world" - utils.AssertEqual(t, nil, c.Bind().Cookie(h2)) - utils.AssertEqual(t, "go,fiber", h2.Hobby) - utils.AssertEqual(t, true, h2.Bool) - utils.AssertEqual(t, "Jane Doe", h2.Name) // check value get overwritten - utils.AssertEqual(t, []string{"milo", "coke", "pepsi"}, h2.FavouriteDrinks) + require.Nil(t, c.Bind().Cookie(h2)) + require.Equal(t, "go,fiber", h2.Hobby) + require.True(t, h2.Bool) + require.Equal(t, "Jane Doe", h2.Name) // check value get overwritten + require.Equal(t, []string{"milo", "coke", "pepsi"}, h2.FavouriteDrinks) var nilSlice []string - utils.AssertEqual(t, nilSlice, h2.Empty) - utils.AssertEqual(t, []string{""}, h2.Alloc) - utils.AssertEqual(t, []int64{1}, h2.No) + require.Equal(t, nilSlice, h2.Empty) + require.Equal(t, []string{""}, h2.Alloc) + require.Equal(t, []int64{1}, h2.No) type RequiredCookie struct { Name string `cookie:"name,required"` } rh := new(RequiredCookie) c.Request().Header.DelCookie("name") - utils.AssertEqual(t, "name is empty", c.Bind().Cookie(rh).Error()) + require.Equal(t, "name is empty", c.Bind().Cookie(rh).Error()) } // go test -run Test_Bind_Cookie_Map -v @@ -1247,19 +1247,19 @@ func Test_Bind_Cookie_Map(t *testing.T) { c.Request().Header.SetCookie("Name", "John Doe") c.Request().Header.SetCookie("Hobby", "golang,fiber") q := make(map[string][]string) - utils.AssertEqual(t, nil, c.Bind().Cookie(&q)) - utils.AssertEqual(t, 2, len(q["Hobby"])) + require.Nil(t, c.Bind().Cookie(&q)) + require.Equal(t, 2, len(q["Hobby"])) c.Request().Header.DelCookie("hobby") c.Request().Header.SetCookie("Hobby", "golang,fiber,go") q = make(map[string][]string) - utils.AssertEqual(t, nil, c.Bind().Cookie(&q)) - utils.AssertEqual(t, 3, len(q["Hobby"])) + require.Nil(t, c.Bind().Cookie(&q)) + require.Equal(t, 3, len(q["Hobby"])) empty := make(map[string][]string) c.Request().Header.DelCookie("hobby") - utils.AssertEqual(t, nil, c.Bind().Query(&empty)) - utils.AssertEqual(t, 0, len(empty["Hobby"])) + require.Nil(t, c.Bind().Query(&empty)) + require.Equal(t, 0, len(empty["Hobby"])) } // go test -run Test_Bind_Cookie_WithSetParserDecoder -v @@ -1302,20 +1302,20 @@ func Test_Bind_Cookie_WithSetParserDecoder(t *testing.T) { c.Request().Header.SetCookie("Title", "CustomDateTest") c.Request().Header.SetCookie("Body", "October") - utils.AssertEqual(t, nil, c.Bind().Cookie(r)) + require.Nil(t, c.Bind().Cookie(r)) fmt.Println(r.Date, "q.Date") - utils.AssertEqual(t, "CustomDateTest", r.Title) + require.Equal(t, "CustomDateTest", r.Title) date := fmt.Sprintf("%v", r.Date) - utils.AssertEqual(t, "{0 63753609600 }", date) - utils.AssertEqual(t, "October", r.Body) + require.Equal(t, "{0 63753609600 }", date) + require.Equal(t, "October", r.Body) c.Request().Header.SetCookie("Title", "") r = &NonRFCTimeInput{ Title: "Existing title", Body: "Existing Body", } - utils.AssertEqual(t, nil, c.Bind().Cookie(r)) - utils.AssertEqual(t, "", r.Title) + require.Nil(t, c.Bind().Cookie(r)) + require.Equal(t, "", r.Title) } // go test -run Test_Bind_Cookie_Schema -v @@ -1337,21 +1337,21 @@ func Test_Bind_Cookie_Schema(t *testing.T) { c.Request().Header.SetCookie("Name", "tom") c.Request().Header.SetCookie("Nested.Age", "10") q := new(Cookie1) - utils.AssertEqual(t, nil, c.Bind().Cookie(q)) + require.Nil(t, c.Bind().Cookie(q)) c.Request().Header.DelCookie("Name") q = new(Cookie1) - utils.AssertEqual(t, "Name is empty", c.Bind().Cookie(q).Error()) + require.Equal(t, "Name is empty", c.Bind().Cookie(q).Error()) c.Request().Header.SetCookie("Name", "tom") c.Request().Header.DelCookie("Nested.Age") c.Request().Header.SetCookie("Nested.Agex", "10") q = new(Cookie1) - utils.AssertEqual(t, nil, c.Bind().Cookie(q)) + require.Nil(t, c.Bind().Cookie(q)) c.Request().Header.DelCookie("Nested.Agex") q = new(Cookie1) - utils.AssertEqual(t, "Nested is empty", c.Bind().Cookie(q).Error()) + require.Equal(t, "Nested is empty", c.Bind().Cookie(q).Error()) c.Request().Header.DelCookie("Nested.Agex") c.Request().Header.DelCookie("Name") @@ -1367,17 +1367,17 @@ func Test_Bind_Cookie_Schema(t *testing.T) { c.Request().Header.SetCookie("Nested.Age", "10") h2 := new(Cookie2) - utils.AssertEqual(t, nil, c.Bind().Cookie(h2)) + require.Nil(t, c.Bind().Cookie(h2)) c.Request().Header.DelCookie("Name") h2 = new(Cookie2) - utils.AssertEqual(t, nil, c.Bind().Cookie(h2)) + require.Nil(t, c.Bind().Cookie(h2)) c.Request().Header.DelCookie("Name") c.Request().Header.DelCookie("Nested.Age") c.Request().Header.SetCookie("Nested.Agex", "10") h2 = new(Cookie2) - utils.AssertEqual(t, "Nested.Age is empty", c.Bind().Cookie(h2).Error()) + require.Equal(t, "Nested.Age is empty", c.Bind().Cookie(h2).Error()) type Node struct { Value int `cookie:"Val,required"` @@ -1386,22 +1386,22 @@ func Test_Bind_Cookie_Schema(t *testing.T) { c.Request().Header.SetCookie("Val", "1") c.Request().Header.SetCookie("Next.Val", "3") n := new(Node) - utils.AssertEqual(t, nil, c.Bind().Cookie(n)) - utils.AssertEqual(t, 1, n.Value) - utils.AssertEqual(t, 3, n.Next.Value) + require.Nil(t, c.Bind().Cookie(n)) + require.Equal(t, 1, n.Value) + require.Equal(t, 3, n.Next.Value) c.Request().Header.DelCookie("Val") n = new(Node) - utils.AssertEqual(t, "Val is empty", c.Bind().Cookie(n).Error()) + require.Equal(t, "Val is empty", c.Bind().Cookie(n).Error()) c.Request().Header.SetCookie("Val", "3") c.Request().Header.DelCookie("Next.Val") c.Request().Header.SetCookie("Next.Value", "2") n = new(Node) n.Next = new(Node) - utils.AssertEqual(t, nil, c.Bind().Cookie(n)) - utils.AssertEqual(t, 3, n.Value) - utils.AssertEqual(t, 0, n.Next.Value) + require.Nil(t, c.Bind().Cookie(n)) + require.Equal(t, 3, n.Value) + require.Equal(t, 0, n.Next.Value) } // go test -v -run=^$ -bench=Benchmark_Bind_Cookie -benchmem -count=4 @@ -1428,7 +1428,7 @@ func Benchmark_Bind_Cookie(b *testing.B) { for n := 0; n < b.N; n++ { c.Bind().Cookie(q) } - utils.AssertEqual(b, nil, c.Bind().Cookie(q)) + require.Nil(b, c.Bind().Cookie(q)) } // go test -v -run=^$ -bench=Benchmark_Bind_Cookie_Map -benchmem -count=4 @@ -1450,7 +1450,7 @@ func Benchmark_Bind_Cookie_Map(b *testing.B) { for n := 0; n < b.N; n++ { c.Bind().Cookie(&q) } - utils.AssertEqual(b, nil, c.Bind().Cookie(&q)) + require.Nil(b, c.Bind().Cookie(&q)) } // custom binder for testing @@ -1486,10 +1486,10 @@ func Test_Bind_CustomBinder(t *testing.T) { c.Request().Header.SetContentLength(len(body)) d := new(Demo) - utils.AssertEqual(t, nil, c.Bind().Body(d)) - utils.AssertEqual(t, nil, c.Bind().Custom("custom", d)) - utils.AssertEqual(t, ErrCustomBinderNotFound, c.Bind().Custom("not_custom", d)) - utils.AssertEqual(t, "john", d.Name) + require.Nil(t, c.Bind().Body(d)) + require.Nil(t, c.Bind().Custom("custom", d)) + require.Equal(t, ErrCustomBinderNotFound, c.Bind().Custom("not_custom", d)) + require.Equal(t, "john", d.Name) } // go test -run Test_Bind_Must @@ -1503,8 +1503,8 @@ func Test_Bind_Must(t *testing.T) { rq := new(RequiredQuery) c.Request().URI().SetQueryString("") err := c.Bind().Must().Query(rq) - utils.AssertEqual(t, StatusBadRequest, c.Response().StatusCode()) - utils.AssertEqual(t, "Bad request: name is empty", err.Error()) + require.Equal(t, StatusBadRequest, c.Response().StatusCode()) + require.Equal(t, "Bad request: name is empty", err.Error()) } // simple struct validator for testing @@ -1536,9 +1536,9 @@ func Test_Bind_StructValidator(t *testing.T) { rq := new(simpleQuery) c.Request().URI().SetQueryString("name=efe") - utils.AssertEqual(t, "you should have entered right name!", c.Bind().Query(rq).Error()) + require.Equal(t, "you should have entered right name!", c.Bind().Query(rq).Error()) rq = new(simpleQuery) c.Request().URI().SetQueryString("name=john") - utils.AssertEqual(t, nil, c.Bind().Query(rq)) + require.Nil(t, c.Bind().Query(rq)) } diff --git a/binder/mapping_test.go b/binder/mapping_test.go index 2c5d275b8b..aec91ff2be 100644 --- a/binder/mapping_test.go +++ b/binder/mapping_test.go @@ -4,28 +4,28 @@ import ( "reflect" "testing" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) func Test_EqualFieldType(t *testing.T) { var out int - utils.AssertEqual(t, false, equalFieldType(&out, reflect.Int, "key")) + require.False(t, equalFieldType(&out, reflect.Int, "key")) var dummy struct{ f string } - utils.AssertEqual(t, false, equalFieldType(&dummy, reflect.String, "key")) + require.False(t, equalFieldType(&dummy, reflect.String, "key")) var dummy2 struct{ f string } - utils.AssertEqual(t, false, equalFieldType(&dummy2, reflect.String, "f")) + require.False(t, equalFieldType(&dummy2, reflect.String, "f")) var user struct { Name string Address string `query:"address"` Age int `query:"AGE"` } - utils.AssertEqual(t, true, equalFieldType(&user, reflect.String, "name")) - utils.AssertEqual(t, true, equalFieldType(&user, reflect.String, "Name")) - utils.AssertEqual(t, true, equalFieldType(&user, reflect.String, "address")) - utils.AssertEqual(t, true, equalFieldType(&user, reflect.String, "Address")) - utils.AssertEqual(t, true, equalFieldType(&user, reflect.Int, "AGE")) - utils.AssertEqual(t, true, equalFieldType(&user, reflect.Int, "age")) + require.True(t, equalFieldType(&user, reflect.String, "name")) + require.True(t, equalFieldType(&user, reflect.String, "Name")) + require.True(t, equalFieldType(&user, reflect.String, "address")) + require.True(t, equalFieldType(&user, reflect.String, "Address")) + require.True(t, equalFieldType(&user, reflect.Int, "AGE")) + require.True(t, equalFieldType(&user, reflect.Int, "age")) } diff --git a/client_test.go b/client_test.go index e0c682f8c6..987b0c3cbb 100644 --- a/client_test.go +++ b/client_test.go @@ -19,7 +19,7 @@ import ( "encoding/json" "github.com/gofiber/fiber/v3/internal/tlstest" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp/fasthttputil" ) @@ -34,7 +34,7 @@ func Test_Client_Invalid_URL(t *testing.T) { return c.SendString(c.Hostname()) }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() a := Get("http://example.com\r\n\r\nGET /\r\n\r\n") @@ -42,9 +42,9 @@ func Test_Client_Invalid_URL(t *testing.T) { _, body, errs := a.String() - utils.AssertEqual(t, "", body) - utils.AssertEqual(t, 1, len(errs)) - utils.AssertEqual(t, "missing required Host header in request", errs[0].Error()) + require.Equal(t, "", body) + require.Equal(t, 1, len(errs)) + require.Equal(t, "missing required Host header in request", errs[0].Error()) } func Test_Client_Unsupported_Protocol(t *testing.T) { @@ -54,10 +54,11 @@ func Test_Client_Unsupported_Protocol(t *testing.T) { _, body, errs := a.String() - utils.AssertEqual(t, "", body) - utils.AssertEqual(t, 1, len(errs)) - utils.AssertEqual(t, `unsupported protocol "ftp". http and https are supported`, + require.Equal(t, "", body) + require.Equal(t, 1, len(errs)) + require.Equal(t, `unsupported protocol "ftp". http and https are supported`, errs[0].Error()) + } func Test_Client_Get(t *testing.T) { @@ -71,7 +72,7 @@ func Test_Client_Get(t *testing.T) { return c.SendString(c.Hostname()) }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() for i := 0; i < 5; i++ { a := Get("http://example.com") @@ -80,9 +81,9 @@ func Test_Client_Get(t *testing.T) { code, body, errs := a.String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "example.com", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "example.com", body) + require.Equal(t, 0, len(errs)) } } @@ -97,7 +98,7 @@ func Test_Client_Head(t *testing.T) { return c.SendString(c.Hostname()) }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() for i := 0; i < 5; i++ { a := Head("http://example.com") @@ -106,9 +107,9 @@ func Test_Client_Head(t *testing.T) { code, body, errs := a.String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "", body) + require.Equal(t, 0, len(errs)) } } @@ -124,7 +125,7 @@ func Test_Client_Post(t *testing.T) { SendString(c.FormValue("foo")) }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() for i := 0; i < 5; i++ { args := AcquireArgs() @@ -138,9 +139,9 @@ func Test_Client_Post(t *testing.T) { code, body, errs := a.String() - utils.AssertEqual(t, StatusCreated, code) - utils.AssertEqual(t, "bar", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusCreated, code) + require.Equal(t, "bar", body) + require.Equal(t, 0, len(errs)) ReleaseArgs(args) } @@ -157,7 +158,7 @@ func Test_Client_Put(t *testing.T) { return c.SendString(c.FormValue("foo")) }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() for i := 0; i < 5; i++ { args := AcquireArgs() @@ -171,9 +172,9 @@ func Test_Client_Put(t *testing.T) { code, body, errs := a.String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "bar", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "bar", body) + require.Equal(t, 0, len(errs)) ReleaseArgs(args) } @@ -190,7 +191,7 @@ func Test_Client_Patch(t *testing.T) { return c.SendString(c.FormValue("foo")) }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() for i := 0; i < 5; i++ { args := AcquireArgs() @@ -204,9 +205,9 @@ func Test_Client_Patch(t *testing.T) { code, body, errs := a.String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "bar", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "bar", body) + require.Equal(t, 0, len(errs)) ReleaseArgs(args) } @@ -224,7 +225,7 @@ func Test_Client_Delete(t *testing.T) { SendString("deleted") }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() for i := 0; i < 5; i++ { args := AcquireArgs() @@ -235,9 +236,9 @@ func Test_Client_Delete(t *testing.T) { code, body, errs := a.String() - utils.AssertEqual(t, StatusNoContent, code) - utils.AssertEqual(t, "", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusNoContent, code) + require.Equal(t, "", body) + require.Equal(t, 0, len(errs)) ReleaseArgs(args) } @@ -254,7 +255,7 @@ func Test_Client_UserAgent(t *testing.T) { return c.Send(c.Request().Header.UserAgent()) }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() t.Run("default", func(t *testing.T) { for i := 0; i < 5; i++ { @@ -264,9 +265,9 @@ func Test_Client_UserAgent(t *testing.T) { code, body, errs := a.String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, defaultUserAgent, body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, defaultUserAgent, body) + require.Equal(t, 0, len(errs)) } }) @@ -281,9 +282,9 @@ func Test_Client_UserAgent(t *testing.T) { code, body, errs := a.String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "ua", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "ua", body) + require.Equal(t, 0, len(errs)) ReleaseClient(c) } }) @@ -396,21 +397,21 @@ func Test_Client_Agent_Host(t *testing.T) { return c.SendString(c.Hostname()) }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() a := Get("http://1.1.1.1:8080"). Host("example.com"). HostBytes([]byte("example.com")) - utils.AssertEqual(t, "1.1.1.1:8080", a.HostClient.Addr) + require.Equal(t, "1.1.1.1:8080", a.HostClient.Addr) a.HostClient.Dial = func(addr string) (net.Conn, error) { return ln.Dial() } code, body, errs := a.String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "example.com", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "example.com", body) + require.Equal(t, 0, len(errs)) } func Test_Client_Agent_QueryString(t *testing.T) { @@ -432,7 +433,7 @@ func Test_Client_Agent_BasicAuth(t *testing.T) { auth := c.Get(HeaderAuthorization) // Decode the header contents raw, err := base64.StdEncoding.DecodeString(auth[6:]) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) return c.Send(raw) } @@ -492,7 +493,7 @@ func Test_Client_Agent_Custom_Response(t *testing.T) { return c.SendString("custom") }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() for i := 0; i < 5; i++ { a := AcquireAgent() @@ -502,17 +503,17 @@ func Test_Client_Agent_Custom_Response(t *testing.T) { req.Header.SetMethod(MethodGet) req.SetRequestURI("http://example.com") - utils.AssertEqual(t, nil, a.Parse()) + require.Nil(t, a.Parse()) a.HostClient.Dial = func(addr string) (net.Conn, error) { return ln.Dial() } code, body, errs := a.SetResponse(resp). String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "custom", body) - utils.AssertEqual(t, "custom", string(resp.Body())) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "custom", body) + require.Equal(t, "custom", string(resp.Body())) + require.Equal(t, 0, len(errs)) ReleaseResponse(resp) } @@ -529,7 +530,7 @@ func Test_Client_Agent_Dest(t *testing.T) { return c.SendString("dest") }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() t.Run("small dest", func(t *testing.T) { dest := []byte("de") @@ -540,10 +541,10 @@ func Test_Client_Agent_Dest(t *testing.T) { code, body, errs := a.Dest(dest[:0]).String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "dest", body) - utils.AssertEqual(t, "de", string(dest)) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "dest", body) + require.Equal(t, "de", string(dest)) + require.Equal(t, 0, len(errs)) }) t.Run("enough dest", func(t *testing.T) { @@ -555,10 +556,10 @@ func Test_Client_Agent_Dest(t *testing.T) { code, body, errs := a.Dest(dest[:0]).String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "dest", body) - utils.AssertEqual(t, "destar", string(dest)) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "dest", body) + require.Equal(t, "destar", string(dest)) + require.Equal(t, 0, len(errs)) }) } @@ -593,7 +594,7 @@ func Test_Client_Agent_RetryIf(t *testing.T) { app := New(Config{DisableStartupMessage: true}) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() a := Post("http://example.com"). RetryIf(func(req *Request) bool { @@ -618,13 +619,13 @@ func Test_Client_Agent_RetryIf(t *testing.T) { } _, _, errs := a.String() - utils.AssertEqual(t, dialsCount, 4) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, dialsCount, 4) + require.Equal(t, 0, len(errs)) } func Test_Client_Agent_Json(t *testing.T) { handler := func(c Ctx) error { - utils.AssertEqual(t, MIMEApplicationJSON, string(c.Request().Header.ContentType())) + require.Equal(t, MIMEApplicationJSON, string(c.Request().Header.ContentType())) return c.Send(c.Request().Body()) } @@ -643,14 +644,14 @@ func Test_Client_Agent_Json_Error(t *testing.T) { _, body, errs := a.String() - utils.AssertEqual(t, "", body) - utils.AssertEqual(t, 1, len(errs)) - utils.AssertEqual(t, "json: unsupported type: complex128", errs[0].Error()) + require.Equal(t, "", body) + require.Equal(t, 1, len(errs)) + require.Equal(t, "json: unsupported type: complex128", errs[0].Error()) } func Test_Client_Agent_XML(t *testing.T) { handler := func(c Ctx) error { - utils.AssertEqual(t, MIMEApplicationXML, string(c.Request().Header.ContentType())) + require.Equal(t, MIMEApplicationXML, string(c.Request().Header.ContentType())) return c.Send(c.Request().Body()) } @@ -668,14 +669,14 @@ func Test_Client_Agent_XML_Error(t *testing.T) { _, body, errs := a.String() - utils.AssertEqual(t, "", body) - utils.AssertEqual(t, 1, len(errs)) - utils.AssertEqual(t, "xml: unsupported type: complex128", errs[0].Error()) + require.Equal(t, "", body) + require.Equal(t, 1, len(errs)) + require.Equal(t, "xml: unsupported type: complex128", errs[0].Error()) } func Test_Client_Agent_Form(t *testing.T) { handler := func(c Ctx) error { - utils.AssertEqual(t, MIMEApplicationForm, string(c.Request().Header.ContentType())) + require.Equal(t, MIMEApplicationForm, string(c.Request().Header.ContentType())) return c.Send(c.Request().Body()) } @@ -701,16 +702,16 @@ func Test_Client_Agent_MultipartForm(t *testing.T) { app := New(Config{DisableStartupMessage: true}) app.Post("/", func(c Ctx) error { - utils.AssertEqual(t, "multipart/form-data; boundary=myBoundary", c.Get(HeaderContentType)) + require.Equal(t, "multipart/form-data; boundary=myBoundary", c.Get(HeaderContentType)) mf, err := c.MultipartForm() - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "bar", mf.Value["foo"][0]) + require.NoError(t, err) + require.Equal(t, "bar", mf.Value["foo"][0]) return c.Send(c.Request().Body()) }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() args := AcquireArgs() @@ -724,9 +725,9 @@ func Test_Client_Agent_MultipartForm(t *testing.T) { code, body, errs := a.String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "--myBoundary\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n--myBoundary--\r\n", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "--myBoundary\r\nContent-Disposition: form-data; name=\"foo\"\r\n\r\nbar\r\n--myBoundary--\r\n", body) + require.Equal(t, 0, len(errs)) ReleaseArgs(args) } @@ -744,7 +745,7 @@ func Test_Client_Agent_MultipartForm_Errors(t *testing.T) { a.FileData(ff1, ff2). MultipartForm(args) - utils.AssertEqual(t, 4, len(a.errs)) + require.Equal(t, 4, len(a.errs)) ReleaseArgs(args) } @@ -756,31 +757,31 @@ func Test_Client_Agent_MultipartForm_SendFiles(t *testing.T) { app := New(Config{DisableStartupMessage: true}) app.Post("/", func(c Ctx) error { - utils.AssertEqual(t, "multipart/form-data; boundary=myBoundary", c.Get(HeaderContentType)) + require.Equal(t, "multipart/form-data; boundary=myBoundary", c.Get(HeaderContentType)) fh1, err := c.FormFile("field1") - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fh1.Filename, "name") + require.NoError(t, err) + require.Equal(t, fh1.Filename, "name") buf := make([]byte, fh1.Size) f, err := fh1.Open() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) defer func() { _ = f.Close() }() _, err = f.Read(buf) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "form file", string(buf)) + require.NoError(t, err) + require.Equal(t, "form file", string(buf)) fh2, err := c.FormFile("index") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) checkFormFile(t, fh2, ".github/testdata/index.html") fh3, err := c.FormFile("file3") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) checkFormFile(t, fh3, ".github/testdata/index.tmpl") return c.SendString("multipart form files") }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() for i := 0; i < 5; i++ { ff := AcquireFormFile() @@ -798,9 +799,9 @@ func Test_Client_Agent_MultipartForm_SendFiles(t *testing.T) { code, body, errs := a.String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "multipart form files", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "multipart form files", body) + require.Equal(t, 0, len(errs)) ReleaseFormFile(ff) } @@ -810,18 +811,18 @@ func checkFormFile(t *testing.T, fh *multipart.FileHeader, filename string) { t.Helper() basename := filepath.Base(filename) - utils.AssertEqual(t, fh.Filename, basename) + require.Equal(t, fh.Filename, basename) b1, err := os.ReadFile(filename) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) b2 := make([]byte, fh.Size) f, err := fh.Open() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) defer func() { _ = f.Close() }() _, err = f.Read(b2) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, b1, b2) + require.NoError(t, err) + require.Equal(t, b1, b2) } func Test_Client_Agent_Multipart_Random_Boundary(t *testing.T) { @@ -832,7 +833,7 @@ func Test_Client_Agent_Multipart_Random_Boundary(t *testing.T) { reg := regexp.MustCompile(`multipart/form-data; boundary=\w{30}`) - utils.AssertEqual(t, true, reg.Match(a.req.Header.Peek(HeaderContentType))) + require.True(t, reg.Match(a.req.Header.Peek(HeaderContentType))) } func Test_Client_Agent_Multipart_Invalid_Boundary(t *testing.T) { @@ -842,8 +843,8 @@ func Test_Client_Agent_Multipart_Invalid_Boundary(t *testing.T) { Boundary("*"). MultipartForm(nil) - utils.AssertEqual(t, 1, len(a.errs)) - utils.AssertEqual(t, "mime: invalid boundary character", a.errs[0].Error()) + require.Equal(t, 1, len(a.errs)) + require.Equal(t, "mime: invalid boundary character", a.errs[0].Error()) } func Test_Client_Agent_SendFile_Error(t *testing.T) { @@ -852,8 +853,8 @@ func Test_Client_Agent_SendFile_Error(t *testing.T) { a := Post("http://example.com"). SendFile("non-exist-file!", "") - utils.AssertEqual(t, 1, len(a.errs)) - utils.AssertEqual(t, true, strings.Contains(a.errs[0].Error(), "open non-exist-file!")) + require.Equal(t, 1, len(a.errs)) + require.True(t, strings.Contains(a.errs[0].Error(), "open non-exist-file!")) } func Test_Client_Debug(t *testing.T) { @@ -871,12 +872,12 @@ func Test_Client_Debug(t *testing.T) { str := output.String() - utils.AssertEqual(t, true, strings.Contains(str, "Connected to example.com(pipe)")) - utils.AssertEqual(t, true, strings.Contains(str, "GET / HTTP/1.1")) - utils.AssertEqual(t, true, strings.Contains(str, "User-Agent: fiber")) - utils.AssertEqual(t, true, strings.Contains(str, "Host: example.com\r\n\r\n")) - utils.AssertEqual(t, true, strings.Contains(str, "HTTP/1.1 200 OK")) - utils.AssertEqual(t, true, strings.Contains(str, "Content-Type: text/plain; charset=utf-8\r\nContent-Length: 5\r\n\r\ndebug")) + require.True(t, strings.Contains(str, "Connected to example.com(pipe)")) + 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")) + require.True(t, strings.Contains(str, "HTTP/1.1 200 OK")) + require.True(t, strings.Contains(str, "Content-Type: text/plain; charset=utf-8\r\nContent-Length: 5\r\n\r\ndebug")) } func Test_Client_Agent_Timeout(t *testing.T) { @@ -891,7 +892,7 @@ func Test_Client_Agent_Timeout(t *testing.T) { return c.SendString("timeout") }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() a := Get("http://example.com"). Timeout(time.Millisecond * 50) @@ -900,9 +901,9 @@ func Test_Client_Agent_Timeout(t *testing.T) { _, body, errs := a.String() - utils.AssertEqual(t, "", body) - utils.AssertEqual(t, 1, len(errs)) - utils.AssertEqual(t, "timeout", errs[0].Error()) + require.Equal(t, "", body) + require.Equal(t, 1, len(errs)) + require.Equal(t, "timeout", errs[0].Error()) } func Test_Client_Agent_Reuse(t *testing.T) { @@ -916,7 +917,7 @@ func Test_Client_Agent_Reuse(t *testing.T) { return c.SendString("reuse") }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() a := Get("http://example.com"). Reuse() @@ -925,29 +926,29 @@ func Test_Client_Agent_Reuse(t *testing.T) { code, body, errs := a.String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "reuse", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "reuse", body) + require.Equal(t, 0, len(errs)) code, body, errs = a.String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "reuse", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "reuse", body) + require.Equal(t, 0, len(errs)) } func Test_Client_Agent_InsecureSkipVerify(t *testing.T) { t.Parallel() cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) serverTLSConf := &tls.Config{ Certificates: []tls.Certificate{cer}, } ln, err := net.Listen(NetworkTCP4, "127.0.0.1:0") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) ln = tls.NewListener(ln, serverTLSConf) @@ -957,26 +958,26 @@ func Test_Client_Agent_InsecureSkipVerify(t *testing.T) { return c.SendString("ignore tls") }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() code, body, errs := Get("https://" + ln.Addr().String()). InsecureSkipVerify(). InsecureSkipVerify(). String() - utils.AssertEqual(t, 0, len(errs)) - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "ignore tls", body) + require.Equal(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "ignore tls", body) } func Test_Client_Agent_TLS(t *testing.T) { t.Parallel() serverTLSConf, clientTLSConf, err := tlstest.GetTLSConfigs() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) ln, err := net.Listen(NetworkTCP4, "127.0.0.1:0") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) ln = tls.NewListener(ln, serverTLSConf) @@ -986,15 +987,15 @@ func Test_Client_Agent_TLS(t *testing.T) { return c.SendString("tls") }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() code, body, errs := Get("https://" + ln.Addr().String()). TLSConfig(clientTLSConf). String() - utils.AssertEqual(t, 0, len(errs)) - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, "tls", body) + require.Equal(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, "tls", body) } func Test_Client_Agent_MaxRedirectsCount(t *testing.T) { @@ -1014,7 +1015,7 @@ func Test_Client_Agent_MaxRedirectsCount(t *testing.T) { return c.SendString("redirect") }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() t.Run("success", func(t *testing.T) { a := Get("http://example.com?foo"). @@ -1024,9 +1025,9 @@ func Test_Client_Agent_MaxRedirectsCount(t *testing.T) { code, body, errs := a.String() - utils.AssertEqual(t, 200, code) - utils.AssertEqual(t, "redirect", body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, 200, code) + require.Equal(t, "redirect", body) + require.Equal(t, 0, len(errs)) }) t.Run("error", func(t *testing.T) { @@ -1037,9 +1038,9 @@ func Test_Client_Agent_MaxRedirectsCount(t *testing.T) { _, body, errs := a.String() - utils.AssertEqual(t, "", body) - utils.AssertEqual(t, 1, len(errs)) - utils.AssertEqual(t, "too many redirects detected when doing the request", errs[0].Error()) + require.Equal(t, "", body) + require.Equal(t, 1, len(errs)) + require.Equal(t, "too many redirects detected when doing the request", errs[0].Error()) }) } @@ -1058,7 +1059,7 @@ func Test_Client_Agent_Struct(t *testing.T) { return c.SendString(`{"success"`) }) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() t.Run("success", func(t *testing.T) { t.Parallel() @@ -1071,10 +1072,10 @@ func Test_Client_Agent_Struct(t *testing.T) { code, body, errs := a.Struct(&d) - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, `{"success":true}`, string(body)) - utils.AssertEqual(t, 0, len(errs)) - utils.AssertEqual(t, true, d.Success) + require.Equal(t, StatusOK, code) + require.Equal(t, `{"success":true}`, string(body)) + require.Equal(t, 0, len(errs)) + require.True(t, d.Success) }) t.Run("pre error", func(t *testing.T) { @@ -1087,10 +1088,10 @@ func Test_Client_Agent_Struct(t *testing.T) { var d data _, body, errs := a.Struct(&d) - utils.AssertEqual(t, "", string(body)) - utils.AssertEqual(t, 1, len(errs)) - utils.AssertEqual(t, "pre errors", errs[0].Error()) - utils.AssertEqual(t, false, d.Success) + require.Equal(t, "", string(body)) + require.Equal(t, 1, len(errs)) + require.Equal(t, "pre errors", errs[0].Error()) + require.False(t, d.Success) }) t.Run("error", func(t *testing.T) { @@ -1102,10 +1103,10 @@ func Test_Client_Agent_Struct(t *testing.T) { code, body, errs := a.JSONDecoder(json.Unmarshal).Struct(&d) - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, `{"success"`, string(body)) - utils.AssertEqual(t, 1, len(errs)) - utils.AssertEqual(t, "unexpected end of JSON input", errs[0].Error()) + require.Equal(t, StatusOK, code) + require.Equal(t, `{"success"`, string(body)) + require.Equal(t, 1, len(errs)) + require.Equal(t, "unexpected end of JSON input", errs[0].Error()) }) } @@ -1114,12 +1115,12 @@ func Test_Client_Agent_Parse(t *testing.T) { a := Get("https://example.com:10443") - utils.AssertEqual(t, nil, a.Parse()) + require.Nil(t, a.Parse()) } func Test_AddMissingPort_TLS(t *testing.T) { addr := addMissingPort("example.com", true) - utils.AssertEqual(t, "example.com:443", addr) + require.Equal(t, "example.com:443", addr) } func testAgent(t *testing.T, handler Handler, wrapAgent func(agent *Agent), excepted string, count ...int) { @@ -1131,7 +1132,7 @@ func testAgent(t *testing.T, handler Handler, wrapAgent func(agent *Agent), exce app.Get("/", handler) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() c := 1 if len(count) > 0 { @@ -1147,9 +1148,9 @@ func testAgent(t *testing.T, handler Handler, wrapAgent func(agent *Agent), exce code, body, errs := a.String() - utils.AssertEqual(t, StatusOK, code) - utils.AssertEqual(t, excepted, body) - utils.AssertEqual(t, 0, len(errs)) + require.Equal(t, StatusOK, code) + require.Equal(t, excepted, body) + require.Equal(t, 0, len(errs)) } } diff --git a/ctx_test.go b/ctx_test.go index 2b41f30d51..9998b474e8 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -27,6 +27,7 @@ import ( "github.com/gofiber/fiber/v3/internal/storage/memory" "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) @@ -38,24 +39,24 @@ func Test_Ctx_Accepts(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAccept, "text/html,application/xhtml+xml,application/xml;q=0.9") - utils.AssertEqual(t, "", c.Accepts("")) - utils.AssertEqual(t, "", c.Accepts()) - utils.AssertEqual(t, ".xml", c.Accepts(".xml")) - utils.AssertEqual(t, "", c.Accepts(".john")) + require.Equal(t, "", c.Accepts("")) + require.Equal(t, "", c.Accepts()) + require.Equal(t, ".xml", c.Accepts(".xml")) + require.Equal(t, "", c.Accepts(".john")) c.Request().Header.Set(HeaderAccept, "text/*, application/json") - utils.AssertEqual(t, "html", c.Accepts("html")) - utils.AssertEqual(t, "text/html", c.Accepts("text/html")) - utils.AssertEqual(t, "json", c.Accepts("json", "text")) - utils.AssertEqual(t, "application/json", c.Accepts("application/json")) - utils.AssertEqual(t, "", c.Accepts("image/png")) - utils.AssertEqual(t, "", c.Accepts("png")) + require.Equal(t, "html", c.Accepts("html")) + require.Equal(t, "text/html", c.Accepts("text/html")) + require.Equal(t, "json", c.Accepts("json", "text")) + require.Equal(t, "application/json", c.Accepts("application/json")) + require.Equal(t, "", c.Accepts("image/png")) + require.Equal(t, "", c.Accepts("png")) c.Request().Header.Set(HeaderAccept, "text/html, application/json") - utils.AssertEqual(t, "text/*", c.Accepts("text/*")) + require.Equal(t, "text/*", c.Accepts("text/*")) c.Request().Header.Set(HeaderAccept, "*/*") - utils.AssertEqual(t, "html", c.Accepts("html")) + require.Equal(t, "html", c.Accepts("html")) } // go test -v -run=^$ -bench=Benchmark_Ctx_Accepts -benchmem -count=4 @@ -70,7 +71,7 @@ func Benchmark_Ctx_Accepts(b *testing.B) { for n := 0; n < b.N; n++ { res = c.Accepts(".xml") } - utils.AssertEqual(b, ".xml", res) + require.Equal(b, ".xml", res) } type customCtx struct { @@ -97,10 +98,10 @@ func Test_Ctx_CustomCtx(t *testing.T) { return c.SendString(c.Params("id")) }) resp, err := app.Test(httptest.NewRequest("GET", "/v3", &bytes.Buffer{})) - utils.AssertEqual(t, nil, err, "app.Test(req)") + require.NoError(t, err, "app.Test(req)") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "io.ReadAll(resp.Body)") - utils.AssertEqual(t, "prefix_v3", string(body)) + require.NoError(t, err, "io.ReadAll(resp.Body)") + require.Equal(t, "prefix_v3", string(body)) } // go test -run Test_Ctx_Accepts_EmptyAccept @@ -109,7 +110,7 @@ func Test_Ctx_Accepts_EmptyAccept(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, ".forwarded", c.Accepts(".forwarded")) + require.Equal(t, ".forwarded", c.Accepts(".forwarded")) } // go test -run Test_Ctx_Accepts_Wildcard @@ -119,11 +120,11 @@ func Test_Ctx_Accepts_Wildcard(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAccept, "*/*;q=0.9") - utils.AssertEqual(t, "html", c.Accepts("html")) - utils.AssertEqual(t, "foo", c.Accepts("foo")) - utils.AssertEqual(t, ".bar", c.Accepts(".bar")) + require.Equal(t, "html", c.Accepts("html")) + require.Equal(t, "foo", c.Accepts("foo")) + require.Equal(t, ".bar", c.Accepts(".bar")) c.Request().Header.Set(HeaderAccept, "text/html,application/*;q=0.9") - utils.AssertEqual(t, "xml", c.Accepts("xml")) + require.Equal(t, "xml", c.Accepts("xml")) } // go test -run Test_Ctx_AcceptsCharsets @@ -133,7 +134,7 @@ func Test_Ctx_AcceptsCharsets(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAcceptCharset, "utf-8, iso-8859-1;q=0.5") - utils.AssertEqual(t, "utf-8", c.AcceptsCharsets("utf-8")) + require.Equal(t, "utf-8", c.AcceptsCharsets("utf-8")) } // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsCharsets -benchmem -count=4 @@ -148,7 +149,7 @@ func Benchmark_Ctx_AcceptsCharsets(b *testing.B) { for n := 0; n < b.N; n++ { res = c.AcceptsCharsets("utf-8") } - utils.AssertEqual(b, "utf-8", res) + require.Equal(b, "utf-8", res) } // go test -run Test_Ctx_AcceptsEncodings @@ -158,8 +159,8 @@ func Test_Ctx_AcceptsEncodings(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAcceptEncoding, "deflate, gzip;q=1.0, *;q=0.5") - utils.AssertEqual(t, "gzip", c.AcceptsEncodings("gzip")) - utils.AssertEqual(t, "abc", c.AcceptsEncodings("abc")) + require.Equal(t, "gzip", c.AcceptsEncodings("gzip")) + require.Equal(t, "abc", c.AcceptsEncodings("abc")) } // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsEncodings -benchmem -count=4 @@ -174,7 +175,7 @@ func Benchmark_Ctx_AcceptsEncodings(b *testing.B) { for n := 0; n < b.N; n++ { res = c.AcceptsEncodings("gzip") } - utils.AssertEqual(b, "gzip", res) + require.Equal(b, "gzip", res) } // go test -run Test_Ctx_AcceptsLanguages @@ -184,7 +185,7 @@ func Test_Ctx_AcceptsLanguages(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderAcceptLanguage, "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5") - utils.AssertEqual(t, "fr", c.AcceptsLanguages("fr")) + require.Equal(t, "fr", c.AcceptsLanguages("fr")) } // go test -v -run=^$ -bench=Benchmark_Ctx_AcceptsLanguages -benchmem -count=4 @@ -199,7 +200,7 @@ func Benchmark_Ctx_AcceptsLanguages(b *testing.B) { for n := 0; n < b.N; n++ { res = c.AcceptsLanguages("fr") } - utils.AssertEqual(b, "fr", res) + require.Equal(b, "fr", res) } // go test -run Test_Ctx_App @@ -209,7 +210,7 @@ func Test_Ctx_App(t *testing.T) { app.config.BodyLimit = 1000 c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, 1000, c.App().config.BodyLimit) + require.Equal(t, 1000, c.App().config.BodyLimit) } // go test -run Test_Ctx_Append @@ -241,11 +242,11 @@ func Test_Ctx_Append(t *testing.T) { // without append value c.Append("X-Custom-Header") - utils.AssertEqual(t, "Hello, World", string(c.Response().Header.Peek("X-Test"))) - utils.AssertEqual(t, "World, XHello, Hello", string(c.Response().Header.Peek("X2-Test"))) - utils.AssertEqual(t, "XHello, World, Hello", string(c.Response().Header.Peek("X3-Test"))) - utils.AssertEqual(t, "XHello, Hello, HelloZ, YHello", string(c.Response().Header.Peek("X4-Test"))) - utils.AssertEqual(t, "", string(c.Response().Header.Peek("x-custom-header"))) + require.Equal(t, "Hello, World", string(c.Response().Header.Peek("X-Test"))) + require.Equal(t, "World, XHello, Hello", string(c.Response().Header.Peek("X2-Test"))) + require.Equal(t, "XHello, World, Hello", string(c.Response().Header.Peek("X3-Test"))) + require.Equal(t, "XHello, Hello, HelloZ, YHello", string(c.Response().Header.Peek("X4-Test"))) + require.Equal(t, "", string(c.Response().Header.Peek("x-custom-header"))) } // go test -v -run=^$ -bench=Benchmark_Ctx_Append -benchmem -count=4 @@ -260,7 +261,7 @@ func Benchmark_Ctx_Append(b *testing.B) { c.Append("X-Custom-Header", "World") c.Append("X-Custom-Header", "Hello") } - utils.AssertEqual(b, "Hello, World", app.getString(c.Response().Header.Peek("X-Custom-Header"))) + require.Equal(b, "Hello, World", app.getString(c.Response().Header.Peek("X-Custom-Header"))) } // go test -run Test_Ctx_Attachment @@ -271,14 +272,14 @@ func Test_Ctx_Attachment(t *testing.T) { // empty c.Attachment() - utils.AssertEqual(t, `attachment`, string(c.Response().Header.Peek(HeaderContentDisposition))) + require.Equal(t, `attachment`, string(c.Response().Header.Peek(HeaderContentDisposition))) // real filename c.Attachment("./static/img/logo.png") - utils.AssertEqual(t, `attachment; filename="logo.png"`, string(c.Response().Header.Peek(HeaderContentDisposition))) - utils.AssertEqual(t, "image/png", string(c.Response().Header.Peek(HeaderContentType))) + require.Equal(t, `attachment; filename="logo.png"`, string(c.Response().Header.Peek(HeaderContentDisposition))) + require.Equal(t, "image/png", string(c.Response().Header.Peek(HeaderContentType))) // check quoting c.Attachment("another document.pdf\"\r\nBla: \"fasel") - utils.AssertEqual(t, `attachment; filename="another+document.pdf%22%0D%0ABla%3A+%22fasel"`, string(c.Response().Header.Peek(HeaderContentDisposition))) + require.Equal(t, `attachment; filename="another+document.pdf%22%0D%0ABla%3A+%22fasel"`, string(c.Response().Header.Peek(HeaderContentDisposition))) } // go test -v -run=^$ -bench=Benchmark_Ctx_Attachment -benchmem -count=4 @@ -292,7 +293,7 @@ func Benchmark_Ctx_Attachment(b *testing.B) { // example with quote params c.Attachment("another document.pdf\"\r\nBla: \"fasel") } - utils.AssertEqual(b, `attachment; filename="another+document.pdf%22%0D%0ABla%3A+%22fasel"`, string(c.Response().Header.Peek(HeaderContentDisposition))) + require.Equal(b, `attachment; filename="another+document.pdf%22%0D%0ABla%3A+%22fasel"`, string(c.Response().Header.Peek(HeaderContentDisposition))) } // go test -run Test_Ctx_BaseURL @@ -302,9 +303,9 @@ func Test_Ctx_BaseURL(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") - utils.AssertEqual(t, "http://google.com", c.BaseURL()) + require.Equal(t, "http://google.com", c.BaseURL()) // Check cache - utils.AssertEqual(t, "http://google.com", c.BaseURL()) + require.Equal(t, "http://google.com", c.BaseURL()) } // go test -v -run=^$ -bench=Benchmark_Ctx_BaseURL -benchmem @@ -320,7 +321,7 @@ func Benchmark_Ctx_BaseURL(b *testing.B) { for n := 0; n < b.N; n++ { res = c.BaseURL() } - utils.AssertEqual(b, "http://google.com:1337", res) + require.Equal(b, "http://google.com:1337", res) } // go test -run Test_Ctx_Body @@ -330,7 +331,7 @@ func Test_Ctx_Body(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetBody([]byte("john=doe")) - utils.AssertEqual(t, []byte("john=doe"), c.Body()) + require.Equal(t, []byte("john=doe"), c.Body()) } // go test -run Test_Ctx_Body_With_Compression @@ -343,13 +344,13 @@ func Test_Ctx_Body_With_Compression(t *testing.T) { var b bytes.Buffer gz := gzip.NewWriter(&b) _, err := gz.Write([]byte("john=doe")) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) err = gz.Flush() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) err = gz.Close() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) c.Request().SetBody(b.Bytes()) - utils.AssertEqual(t, []byte("john=doe"), c.Body()) + require.Equal(t, []byte("john=doe"), c.Body()) } // go test -v -run=^$ -bench=Benchmark_Ctx_Body_With_Compression -benchmem -count=4 @@ -361,11 +362,11 @@ func Benchmark_Ctx_Body_With_Compression(b *testing.B) { var buf bytes.Buffer gz := gzip.NewWriter(&buf) _, err := gz.Write([]byte("john=doe")) - utils.AssertEqual(b, nil, err) + require.NoError(b, err) err = gz.Flush() - utils.AssertEqual(b, nil, err) + require.NoError(b, err) err = gz.Close() - utils.AssertEqual(b, nil, err) + require.NoError(b, err) c.Request().SetBody(buf.Bytes()) @@ -373,7 +374,7 @@ func Benchmark_Ctx_Body_With_Compression(b *testing.B) { _ = c.Body() } - utils.AssertEqual(b, []byte("john=doe"), c.Body()) + require.Equal(b, []byte("john=doe"), c.Body()) } // go test -run Test_Ctx_Context @@ -382,7 +383,7 @@ func Test_Ctx_Context(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, "*fasthttp.RequestCtx", fmt.Sprintf("%T", c.Context())) + require.Equal(t, "*fasthttp.RequestCtx", fmt.Sprintf("%T", c.Context())) } // go test -run Test_Ctx_UserContext @@ -392,13 +393,13 @@ func Test_Ctx_UserContext(t *testing.T) { t.Run("Nil_Context", func(t *testing.T) { ctx := c.UserContext() - utils.AssertEqual(t, ctx, context.Background()) + require.Equal(t, ctx, context.Background()) }) t.Run("ValueContext", func(t *testing.T) { testKey := struct{}{} testValue := "Test Value" ctx := context.WithValue(context.Background(), testKey, testValue) - utils.AssertEqual(t, testValue, ctx.Value(testKey)) + require.Equal(t, testValue, ctx.Value(testKey)) }) } @@ -411,7 +412,7 @@ func Test_Ctx_SetUserContext(t *testing.T) { testValue := "Test Value" ctx := context.WithValue(context.Background(), testKey, testValue) c.SetUserContext(ctx) - utils.AssertEqual(t, testValue, c.UserContext().Value(testKey)) + require.Equal(t, testValue, c.UserContext().Value(testKey)) } // go test -run Test_Ctx_UserContext_Multiple_Requests @@ -439,12 +440,12 @@ func Test_Ctx_UserContext_Multiple_Requests(t *testing.T) { t.Run(fmt.Sprintf("request_%d", i), func(t *testing.T) { resp, err := app.Test(httptest.NewRequest(MethodGet, fmt.Sprintf("/?input=%d", i), nil)) - utils.AssertEqual(t, nil, err, "Unexpected error from response") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "context.Context returned from c.UserContext() is reused") + require.NoError(t, err, "Unexpected error from response") + require.Equal(t, StatusOK, resp.StatusCode, "context.Context returned from c.UserContext() is reused") b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "Unexpected error from reading response body") - utils.AssertEqual(t, fmt.Sprintf("resp_%d_returned", i), string(b), "response text incorrect") + require.NoError(t, err, "Unexpected error from reading response body") + require.Equal(t, fmt.Sprintf("resp_%d_returned", i), string(b), "response text incorrect") }) } } @@ -467,23 +468,23 @@ func Test_Ctx_Cookie(t *testing.T) { } c.Cookie(cookie) expect := "username=john; expires=" + httpdate + "; path=/; SameSite=Lax" - utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) + require.Equal(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) expect = "username=john; expires=" + httpdate + "; path=/" cookie.SameSite = CookieSameSiteDisabled c.Cookie(cookie) - utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) + require.Equal(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) expect = "username=john; expires=" + httpdate + "; path=/; SameSite=Strict" cookie.SameSite = CookieSameSiteStrictMode c.Cookie(cookie) - utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) + require.Equal(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) expect = "username=john; expires=" + httpdate + "; path=/; secure; SameSite=None" cookie.Secure = true cookie.SameSite = CookieSameSiteNoneMode c.Cookie(cookie) - utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) + require.Equal(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) expect = "username=john; path=/; secure; SameSite=None" // should remove expires and max-age headers @@ -491,7 +492,7 @@ func Test_Ctx_Cookie(t *testing.T) { cookie.Expires = expire cookie.MaxAge = 10000 c.Cookie(cookie) - utils.AssertEqual(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) + require.Equal(t, expect, string(c.Response().Header.Peek(HeaderSetCookie))) } // go test -v -run=^$ -bench=Benchmark_Ctx_Cookie -benchmem -count=4 @@ -507,7 +508,7 @@ func Benchmark_Ctx_Cookie(b *testing.B) { Value: "Doe", }) } - utils.AssertEqual(b, "John=Doe; path=/; SameSite=Lax", app.getString(c.Response().Header.Peek("Set-Cookie"))) + require.Equal(b, "John=Doe; path=/; SameSite=Lax", app.getString(c.Response().Header.Peek("Set-Cookie"))) } // go test -run Test_Ctx_Cookies @@ -517,8 +518,8 @@ func Test_Ctx_Cookies(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set("Cookie", "john=doe") - utils.AssertEqual(t, "doe", c.Cookies("john")) - utils.AssertEqual(t, "default", c.Cookies("unknown", "default")) + require.Equal(t, "doe", c.Cookies("john")) + require.Equal(t, "default", c.Cookies("unknown", "default")) } // go test -run Test_Ctx_Format @@ -529,35 +530,35 @@ func Test_Ctx_Format(t *testing.T) { c.Request().Header.Set(HeaderAccept, MIMETextPlain) c.Format([]byte("Hello, World!")) - utils.AssertEqual(t, "Hello, World!", string(c.Response().Body())) + require.Equal(t, "Hello, World!", string(c.Response().Body())) c.Request().Header.Set(HeaderAccept, MIMETextHTML) c.Format("Hello, World!") - utils.AssertEqual(t, "

    Hello, World!

    ", string(c.Response().Body())) + require.Equal(t, "

    Hello, World!

    ", string(c.Response().Body())) c.Request().Header.Set(HeaderAccept, MIMEApplicationJSON) c.Format("Hello, World!") - utils.AssertEqual(t, `"Hello, World!"`, string(c.Response().Body())) + require.Equal(t, `"Hello, World!"`, string(c.Response().Body())) c.Request().Header.Set(HeaderAccept, MIMETextPlain) c.Format(complex(1, 1)) - utils.AssertEqual(t, "(1+1i)", string(c.Response().Body())) + require.Equal(t, "(1+1i)", string(c.Response().Body())) c.Request().Header.Set(HeaderAccept, MIMEApplicationXML) c.Format("Hello, World!") - utils.AssertEqual(t, `Hello, World!`, string(c.Response().Body())) + require.Equal(t, `Hello, World!`, string(c.Response().Body())) err := c.Format(complex(1, 1)) - utils.AssertEqual(t, true, err != nil) + require.True(t, err != nil) c.Request().Header.Set(HeaderAccept, MIMETextPlain) c.Format(Map{}) - utils.AssertEqual(t, "map[]", string(c.Response().Body())) + require.Equal(t, "map[]", string(c.Response().Body())) type broken string c.Request().Header.Set(HeaderAccept, "broken/accept") c.Format(broken("Hello, World!")) - utils.AssertEqual(t, `Hello, World!`, string(c.Response().Body())) + require.Equal(t, `Hello, World!`, string(c.Response().Body())) } // go test -v -run=^$ -bench=Benchmark_Ctx_Format -benchmem -count=4 @@ -571,7 +572,7 @@ func Benchmark_Ctx_Format(b *testing.B) { for n := 0; n < b.N; n++ { c.Format("Hello, World!") } - utils.AssertEqual(b, `Hello, World!`, string(c.Response().Body())) + require.Equal(b, `Hello, World!`, string(c.Response().Body())) } // go test -v -run=^$ -bench=Benchmark_Ctx_Format_HTML -benchmem -count=4 @@ -585,7 +586,7 @@ func Benchmark_Ctx_Format_HTML(b *testing.B) { for n := 0; n < b.N; n++ { c.Format("Hello, World!") } - utils.AssertEqual(b, "

    Hello, World!

    ", string(c.Response().Body())) + require.Equal(b, "

    Hello, World!

    ", string(c.Response().Body())) } // go test -v -run=^$ -bench=Benchmark_Ctx_Format_JSON -benchmem -count=4 @@ -599,7 +600,7 @@ func Benchmark_Ctx_Format_JSON(b *testing.B) { for n := 0; n < b.N; n++ { c.Format("Hello, World!") } - utils.AssertEqual(b, `"Hello, World!"`, string(c.Response().Body())) + require.Equal(b, `"Hello, World!"`, string(c.Response().Body())) } // go test -v -run=^$ -bench=Benchmark_Ctx_Format_XML -benchmem -count=4 @@ -613,7 +614,7 @@ func Benchmark_Ctx_Format_XML(b *testing.B) { for n := 0; n < b.N; n++ { c.Format("Hello, World!") } - utils.AssertEqual(b, `Hello, World!`, string(c.Response().Body())) + require.Equal(b, `Hello, World!`, string(c.Response().Body())) } // go test -run Test_Ctx_FormFile @@ -624,18 +625,18 @@ func Test_Ctx_FormFile(t *testing.T) { app.Post("/test", func(c Ctx) error { fh, err := c.FormFile("file") - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "test", fh.Filename) + require.NoError(t, err) + require.Equal(t, "test", fh.Filename) f, err := fh.Open() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) b := new(bytes.Buffer) _, err = io.Copy(b, f) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) f.Close() - utils.AssertEqual(t, "hello world", b.String()) + require.Equal(t, "hello world", b.String()) return nil }) @@ -643,10 +644,10 @@ func Test_Ctx_FormFile(t *testing.T) { writer := multipart.NewWriter(body) ioWriter, err := writer.CreateFormFile("file", "test") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) _, err = ioWriter.Write([]byte("hello world")) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) writer.Close() @@ -655,8 +656,8 @@ func Test_Ctx_FormFile(t *testing.T) { req.Header.Set(HeaderContentLength, strconv.Itoa(len(body.Bytes()))) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") } // go test -run Test_Ctx_FormValue @@ -665,14 +666,14 @@ func Test_Ctx_FormValue(t *testing.T) { app := New() app.Post("/test", func(c Ctx) error { - utils.AssertEqual(t, "john", c.FormValue("name")) + require.Equal(t, "john", c.FormValue("name")) return nil }) body := &bytes.Buffer{} writer := multipart.NewWriter(body) - utils.AssertEqual(t, nil, writer.WriteField("name", "john")) + require.Nil(t, writer.WriteField("name", "john")) writer.Close() req := httptest.NewRequest(MethodPost, "/test", body) @@ -680,8 +681,8 @@ func Test_Ctx_FormValue(t *testing.T) { req.Header.Set("Content-Length", strconv.Itoa(len(body.Bytes()))) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") } // go test -v -run=^$ -bench=Benchmark_Ctx_Fresh_StaleEtag -benchmem -count=4 @@ -706,44 +707,44 @@ func Test_Ctx_Fresh(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, false, c.Fresh()) + require.False(t, c.Fresh()) c.Request().Header.Set(HeaderIfNoneMatch, "*") c.Request().Header.Set(HeaderCacheControl, "no-cache") - utils.AssertEqual(t, false, c.Fresh()) + require.False(t, c.Fresh()) c.Request().Header.Set(HeaderIfNoneMatch, "*") c.Request().Header.Set(HeaderCacheControl, ",no-cache,") - utils.AssertEqual(t, false, c.Fresh()) + require.False(t, c.Fresh()) c.Request().Header.Set(HeaderIfNoneMatch, "*") c.Request().Header.Set(HeaderCacheControl, "aa,no-cache,") - utils.AssertEqual(t, false, c.Fresh()) + require.False(t, c.Fresh()) c.Request().Header.Set(HeaderIfNoneMatch, "*") c.Request().Header.Set(HeaderCacheControl, ",no-cache,bb") - utils.AssertEqual(t, false, c.Fresh()) + require.False(t, c.Fresh()) c.Request().Header.Set(HeaderIfNoneMatch, "675af34563dc-tr34") c.Request().Header.Set(HeaderCacheControl, "public") - utils.AssertEqual(t, false, c.Fresh()) + require.False(t, c.Fresh()) c.Request().Header.Set(HeaderIfNoneMatch, "a, b") c.Response().Header.Set(HeaderETag, "c") - utils.AssertEqual(t, false, c.Fresh()) + require.False(t, c.Fresh()) c.Response().Header.Set(HeaderETag, "a") - utils.AssertEqual(t, true, c.Fresh()) + require.True(t, c.Fresh()) c.Request().Header.Set(HeaderIfModifiedSince, "xxWed, 21 Oct 2015 07:28:00 GMT") c.Response().Header.Set(HeaderLastModified, "xxWed, 21 Oct 2015 07:28:00 GMT") - utils.AssertEqual(t, false, c.Fresh()) + require.False(t, c.Fresh()) c.Response().Header.Set(HeaderLastModified, "Wed, 21 Oct 2015 07:28:00 GMT") - utils.AssertEqual(t, false, c.Fresh()) + require.False(t, c.Fresh()) c.Request().Header.Set(HeaderIfModifiedSince, "Wed, 21 Oct 2015 07:28:00 GMT") - utils.AssertEqual(t, false, c.Fresh()) + require.False(t, c.Fresh()) } // go test -v -run=^$ -bench=Benchmark_Ctx_Fresh_WithNoCache -benchmem -count=4 @@ -766,9 +767,9 @@ func Test_Ctx_Get(t *testing.T) { c.Request().Header.Set(HeaderAcceptCharset, "utf-8, iso-8859-1;q=0.5") c.Request().Header.Set(HeaderReferer, "Monster") - utils.AssertEqual(t, "utf-8, iso-8859-1;q=0.5", c.Get(HeaderAcceptCharset)) - utils.AssertEqual(t, "Monster", c.Get(HeaderReferer)) - utils.AssertEqual(t, "default", c.Get("unknown", "default")) + require.Equal(t, "utf-8, iso-8859-1;q=0.5", c.Get(HeaderAcceptCharset)) + require.Equal(t, "Monster", c.Get(HeaderReferer)) + require.Equal(t, "default", c.Get("unknown", "default")) } // go test -run Test_Ctx_Hostname @@ -778,7 +779,7 @@ func Test_Ctx_Hostname(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") - utils.AssertEqual(t, "google.com", c.Hostname()) + require.Equal(t, "google.com", c.Hostname()) } // go test -run Test_Ctx_Hostname_Untrusted @@ -790,7 +791,7 @@ func Test_Ctx_Hostname_UntrustedProxy(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") - utils.AssertEqual(t, "google.com", c.Hostname()) + require.Equal(t, "google.com", c.Hostname()) app.ReleaseCtx(c) } // Trust to specific proxy list @@ -799,7 +800,7 @@ func Test_Ctx_Hostname_UntrustedProxy(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") - utils.AssertEqual(t, "google.com", c.Hostname()) + require.Equal(t, "google.com", c.Hostname()) app.ReleaseCtx(c) } } @@ -812,7 +813,7 @@ func Test_Ctx_Hostname_TrustedProxy(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") - utils.AssertEqual(t, "google1.com", c.Hostname()) + require.Equal(t, "google1.com", c.Hostname()) app.ReleaseCtx(c) } } @@ -825,7 +826,7 @@ func Test_Ctx_Hostname_TrustedProxyRange(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") - utils.AssertEqual(t, "google1.com", c.Hostname()) + require.Equal(t, "google1.com", c.Hostname()) app.ReleaseCtx(c) } @@ -837,7 +838,7 @@ func Test_Ctx_Hostname_UntrustedProxyRange(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") - utils.AssertEqual(t, "google.com", c.Hostname()) + require.Equal(t, "google.com", c.Hostname()) app.ReleaseCtx(c) } @@ -847,7 +848,7 @@ func Test_Ctx_Port(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, "0", c.Port()) + require.Equal(t, "0", c.Port()) } // go test -run Test_Ctx_PortInHandler @@ -860,12 +861,12 @@ func Test_Ctx_PortInHandler(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/port", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "0", string(body)) + require.NoError(t, err) + require.Equal(t, "0", string(body)) } // go test -run Test_Ctx_IP @@ -874,7 +875,7 @@ func Test_Ctx_IP(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, "0.0.0.0", c.IP()) + require.Equal(t, "0.0.0.0", c.IP()) } // go test -run Test_Ctx_IP_ProxyHeader @@ -883,7 +884,7 @@ func Test_Ctx_IP_ProxyHeader(t *testing.T) { app := New(Config{ProxyHeader: "Real-Ip"}) c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, "", c.IP()) + require.Equal(t, "", c.IP()) } // go test -run Test_Ctx_IP_UntrustedProxy @@ -893,7 +894,7 @@ func Test_Ctx_IP_UntrustedProxy(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1") - utils.AssertEqual(t, "0.0.0.0", c.IP()) + require.Equal(t, "0.0.0.0", c.IP()) } // go test -run Test_Ctx_IP_TrustedProxy @@ -903,7 +904,7 @@ func Test_Ctx_IP_TrustedProxy(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1") - utils.AssertEqual(t, "0.0.0.1", c.IP()) + require.Equal(t, "0.0.0.1", c.IP()) } // go test -run Test_Ctx_IPs -parallel @@ -913,13 +914,13 @@ func Test_Ctx_IPs(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, 127.0.0.2, 127.0.0.3") - utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs()) + require.Equal(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs()) c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1,127.0.0.2 ,127.0.0.3") - utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs()) + require.Equal(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs()) c.Request().Header.Set(HeaderXForwardedFor, "") - utils.AssertEqual(t, 0, len(c.IPs())) + require.Equal(t, 0, len(c.IPs())) } // go test -v -run=^$ -bench=Benchmark_Ctx_IPs -benchmem -count=4 @@ -934,7 +935,7 @@ func Benchmark_Ctx_IPs(b *testing.B) { for n := 0; n < b.N; n++ { res = c.IPs() } - utils.AssertEqual(b, []string{"127.0.0.1", "127.0.0.1", "127.0.0.1"}, res) + require.Equal(b, []string{"127.0.0.1", "127.0.0.1", "127.0.0.1"}, res) } // go test -run Test_Ctx_Is @@ -944,32 +945,32 @@ func Test_Ctx_Is(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderContentType, MIMETextHTML+"; boundary=something") - utils.AssertEqual(t, true, c.Is(".html")) - utils.AssertEqual(t, true, c.Is("html")) - utils.AssertEqual(t, false, c.Is("json")) - utils.AssertEqual(t, false, c.Is(".json")) - utils.AssertEqual(t, false, c.Is("")) - utils.AssertEqual(t, false, c.Is(".foooo")) + require.True(t, c.Is(".html")) + require.True(t, c.Is("html")) + require.False(t, c.Is("json")) + require.False(t, c.Is(".json")) + require.False(t, c.Is("")) + require.False(t, c.Is(".foooo")) c.Request().Header.Set(HeaderContentType, MIMEApplicationJSONCharsetUTF8) - utils.AssertEqual(t, false, c.Is("html")) - utils.AssertEqual(t, true, c.Is("json")) - utils.AssertEqual(t, true, c.Is(".json")) + require.False(t, c.Is("html")) + require.True(t, c.Is("json")) + require.True(t, c.Is(".json")) c.Request().Header.Set(HeaderContentType, " application/json;charset=UTF-8") - utils.AssertEqual(t, false, c.Is("html")) - utils.AssertEqual(t, true, c.Is("json")) - utils.AssertEqual(t, true, c.Is(".json")) + require.False(t, c.Is("html")) + require.True(t, c.Is("json")) + require.True(t, c.Is(".json")) c.Request().Header.Set(HeaderContentType, MIMEApplicationXMLCharsetUTF8) - utils.AssertEqual(t, false, c.Is("html")) - utils.AssertEqual(t, true, c.Is("xml")) - utils.AssertEqual(t, true, c.Is(".xml")) + require.False(t, c.Is("html")) + require.True(t, c.Is("xml")) + require.True(t, c.Is(".xml")) c.Request().Header.Set(HeaderContentType, MIMETextPlain) - utils.AssertEqual(t, false, c.Is("html")) - utils.AssertEqual(t, true, c.Is("txt")) - utils.AssertEqual(t, true, c.Is(".txt")) + require.False(t, c.Is("html")) + require.True(t, c.Is("txt")) + require.True(t, c.Is(".txt")) } // go test -v -run=^$ -bench=Benchmark_Ctx_Is -benchmem -count=4 @@ -985,7 +986,7 @@ func Benchmark_Ctx_Is(b *testing.B) { _ = c.Is(".json") res = c.Is("json") } - utils.AssertEqual(b, true, res) + require.True(b, res) } // go test -run Test_Ctx_Locals @@ -996,12 +997,12 @@ func Test_Ctx_Locals(t *testing.T) { return c.Next() }) app.Get("/test", func(c Ctx) error { - utils.AssertEqual(t, "doe", c.Locals("john")) + require.Equal(t, "doe", c.Locals("john")) return nil }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") } // go test -run Test_Ctx_Method @@ -1012,12 +1013,12 @@ func Test_Ctx_Method(t *testing.T) { app := New() c := app.NewCtx(fctx) - utils.AssertEqual(t, MethodGet, c.Method()) + require.Equal(t, MethodGet, c.Method()) c.Method(MethodPost) - utils.AssertEqual(t, MethodPost, c.Method()) + require.Equal(t, MethodPost, c.Method()) c.Method("MethodInvalid") - utils.AssertEqual(t, MethodPost, c.Method()) + require.Equal(t, MethodPost, c.Method()) } // go test -run Test_Ctx_ClientHelloInfo @@ -1052,7 +1053,7 @@ func Test_Ctx_ClientHelloInfo(t *testing.T) { // Test without TLS handler resp, _ := app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) body, _ := io.ReadAll(resp.Body) - utils.AssertEqual(t, []byte("ClientHelloInfo is nil"), body) + require.Equal(t, []byte("ClientHelloInfo is nil"), body) // Test with TLS Handler const ( @@ -1068,17 +1069,17 @@ func Test_Ctx_ClientHelloInfo(t *testing.T) { // Test ServerName resp, _ = app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) body, _ = io.ReadAll(resp.Body) - utils.AssertEqual(t, []byte("example.golang"), body) + require.Equal(t, []byte("example.golang"), body) // Test SignatureSchemes resp, _ = app.Test(httptest.NewRequest(MethodGet, "/SignatureSchemes", nil)) body, _ = io.ReadAll(resp.Body) - utils.AssertEqual(t, "["+strconv.Itoa(PSSWithSHA256)+"]", string(body)) + require.Equal(t, "["+strconv.Itoa(PSSWithSHA256)+"]", string(body)) // Test SupportedVersions resp, _ = app.Test(httptest.NewRequest(MethodGet, "/SupportedVersions", nil)) body, _ = io.ReadAll(resp.Body) - utils.AssertEqual(t, "["+strconv.Itoa(VersionTLS13)+"]", string(body)) + require.Equal(t, "["+strconv.Itoa(VersionTLS13)+"]", string(body)) } // go test -run Test_Ctx_InvalidMethod @@ -1095,8 +1096,8 @@ func Test_Ctx_InvalidMethod(t *testing.T) { app.Handler()(fctx) - utils.AssertEqual(t, 400, fctx.Response.StatusCode()) - utils.AssertEqual(t, []byte("Invalid http method"), fctx.Response.Body()) + require.Equal(t, 400, fctx.Response.StatusCode()) + require.Equal(t, []byte("Invalid http method"), fctx.Response.Body()) } // go test -run Test_Ctx_MultipartForm @@ -1106,15 +1107,15 @@ func Test_Ctx_MultipartForm(t *testing.T) { app.Post("/test", func(c Ctx) error { result, err := c.MultipartForm() - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "john", result.Value["name"][0]) + require.NoError(t, err) + require.Equal(t, "john", result.Value["name"][0]) return nil }) body := &bytes.Buffer{} writer := multipart.NewWriter(body) - utils.AssertEqual(t, nil, writer.WriteField("name", "john")) + require.Nil(t, writer.WriteField("name", "john")) writer.Close() req := httptest.NewRequest(MethodPost, "/test", body) @@ -1122,8 +1123,8 @@ func Test_Ctx_MultipartForm(t *testing.T) { req.Header.Set(HeaderContentLength, strconv.Itoa(len(body.Bytes()))) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") } // go test -v -run=^$ -bench=Benchmark_Ctx_MultipartForm -benchmem -count=4 @@ -1159,7 +1160,7 @@ func Test_Ctx_OriginalURL(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.SetRequestURI("http://google.com/test?search=demo") - utils.AssertEqual(t, "http://google.com/test?search=demo", c.OriginalURL()) + require.Equal(t, "http://google.com/test?search=demo", c.OriginalURL()) } // go test -race -run Test_Ctx_Params @@ -1167,38 +1168,38 @@ func Test_Ctx_Params(t *testing.T) { t.Parallel() app := New() app.Get("/test/:user", func(c Ctx) error { - utils.AssertEqual(t, "john", c.Params("user")) + require.Equal(t, "john", c.Params("user")) return nil }) app.Get("/test2/*", func(c Ctx) error { - utils.AssertEqual(t, "im/a/cookie", c.Params("*")) + require.Equal(t, "im/a/cookie", c.Params("*")) return nil }) app.Get("/test3/*/blafasel/*", func(c Ctx) error { - utils.AssertEqual(t, "1111", c.Params("*1")) - utils.AssertEqual(t, "2222", c.Params("*2")) - utils.AssertEqual(t, "1111", c.Params("*")) + require.Equal(t, "1111", c.Params("*1")) + require.Equal(t, "2222", c.Params("*2")) + require.Equal(t, "1111", c.Params("*")) return nil }) app.Get("/test4/:optional?", func(c Ctx) error { - utils.AssertEqual(t, "", c.Params("optional")) + require.Equal(t, "", c.Params("optional")) return nil }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/test/john", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/test2/im/a/cookie", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/test3/1111/blafasel/2222", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/test4", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") } // go test -v -run=^$ -bench=Benchmark_Ctx_Params -benchmem -count=4 @@ -1223,7 +1224,7 @@ func Benchmark_Ctx_Params(b *testing.B) { _ = c.Params("param3") res = c.Params("param4") } - utils.AssertEqual(b, "awesome", res) + require.Equal(b, "awesome", res) } // go test -run Test_Ctx_Path @@ -1231,23 +1232,23 @@ func Test_Ctx_Path(t *testing.T) { t.Parallel() app := New(Config{UnescapePath: true}) app.Get("/test/:user", func(c Ctx) error { - utils.AssertEqual(t, "/Test/John", c.Path()) + require.Equal(t, "/Test/John", c.Path()) // not strict && case insensitive - utils.AssertEqual(t, "/ABC/", c.Path("/ABC/")) - utils.AssertEqual(t, "/test/john/", c.Path("/test/john/")) + require.Equal(t, "/ABC/", c.Path("/ABC/")) + require.Equal(t, "/test/john/", c.Path("/test/john/")) return nil }) // test with special chars app.Get("/specialChars/:name", func(c Ctx) error { - utils.AssertEqual(t, "/specialChars/créer", c.Path()) + require.Equal(t, "/specialChars/créer", c.Path()) // unescape is also working if you set the path afterwards - utils.AssertEqual(t, "/اختبار/", c.Path("/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1/")) + require.Equal(t, "/اختبار/", c.Path("/%D8%A7%D8%AE%D8%AA%D8%A8%D8%A7%D8%B1/")) return nil }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/specialChars/cr%C3%A9er", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") } // go test -run Test_Ctx_Protocol @@ -1256,10 +1257,10 @@ func Test_Ctx_Protocol(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, "HTTP/1.1", c.Protocol()) + require.Equal(t, "HTTP/1.1", c.Protocol()) c.Request().Header.SetProtocol("HTTP/2") - utils.AssertEqual(t, "HTTP/2", c.Protocol()) + require.Equal(t, "HTTP/2", c.Protocol()) } // go test -v -run=^$ -bench=Benchmark_Ctx_Protocol -benchmem -count=4 @@ -1275,7 +1276,7 @@ func Benchmark_Ctx_Protocol(b *testing.B) { res = c.Protocol() } - utils.AssertEqual(b, "HTTP/1.1", res) + require.Equal(b, "HTTP/1.1", res) } // go test -run Test_Ctx_Scheme @@ -1288,22 +1289,22 @@ func Test_Ctx_Scheme(t *testing.T) { c := app.NewCtx(freq) c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "https", c.Scheme()) + require.Equal(t, "https", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "https", c.Scheme()) + require.Equal(t, "https", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "https", c.Scheme()) + require.Equal(t, "https", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "https", c.Scheme()) + require.Equal(t, "https", c.Scheme()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) } // go test -v -run=^$ -bench=Benchmark_Ctx_Scheme -benchmem -count=4 @@ -1317,7 +1318,7 @@ func Benchmark_Ctx_Scheme(b *testing.B) { for n := 0; n < b.N; n++ { res = c.Scheme() } - utils.AssertEqual(b, "http", res) + require.Equal(b, "http", res) } // go test -run Test_Ctx_Scheme_TrustedProxy @@ -1327,22 +1328,22 @@ func Test_Ctx_Scheme_TrustedProxy(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "https", c.Scheme()) + require.Equal(t, "https", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "https", c.Scheme()) + require.Equal(t, "https", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "https", c.Scheme()) + require.Equal(t, "https", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "https", c.Scheme()) + require.Equal(t, "https", c.Scheme()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) } // go test -run Test_Ctx_Scheme_TrustedProxyRange @@ -1352,22 +1353,22 @@ func Test_Ctx_Scheme_TrustedProxyRange(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "https", c.Scheme()) + require.Equal(t, "https", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "https", c.Scheme()) + require.Equal(t, "https", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "https", c.Scheme()) + require.Equal(t, "https", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "https", c.Scheme()) + require.Equal(t, "https", c.Scheme()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) } // go test -run Test_Ctx_Scheme_UntrustedProxyRange @@ -1377,22 +1378,22 @@ func Test_Ctx_Scheme_UntrustedProxyRange(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) } // go test -run Test_Ctx_Scheme_UnTrustedProxy @@ -1402,22 +1403,22 @@ func Test_Ctx_Scheme_UnTrustedProxy(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedProto, "https") - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedProtocol, "https") - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXForwardedSsl, "on") - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) c.Request().Header.Reset() c.Request().Header.Set(HeaderXUrlScheme, "https") - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) c.Request().Header.Reset() - utils.AssertEqual(t, "http", c.Scheme()) + require.Equal(t, "http", c.Scheme()) } // go test -run Test_Ctx_Query @@ -1427,9 +1428,9 @@ func Test_Ctx_Query(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetQueryString("search=john&age=20") - utils.AssertEqual(t, "john", c.Query("search")) - utils.AssertEqual(t, "20", c.Query("age")) - utils.AssertEqual(t, "default", c.Query("unknown", "default")) + require.Equal(t, "john", c.Query("search")) + require.Equal(t, "20", c.Query("age")) + require.Equal(t, "default", c.Query("unknown", "default")) } // go test -run Test_Ctx_Range @@ -1444,27 +1445,27 @@ func Test_Ctx_Range(t *testing.T) { ) _, err = c.Range(1000) - utils.AssertEqual(t, true, err != nil) + require.True(t, err != nil) c.Request().Header.Set(HeaderRange, "bytes=500") _, err = c.Range(1000) - utils.AssertEqual(t, true, err != nil) + require.True(t, err != nil) c.Request().Header.Set(HeaderRange, "bytes=500=") _, err = c.Range(1000) - utils.AssertEqual(t, true, err != nil) + require.True(t, err != nil) c.Request().Header.Set(HeaderRange, "bytes=500-300") _, err = c.Range(1000) - utils.AssertEqual(t, true, err != nil) + require.True(t, err != nil) testRange := func(header string, start, end int) { c.Request().Header.Set(HeaderRange, header) result, err = c.Range(1000) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "bytes", result.Type) - utils.AssertEqual(t, start, result.Ranges[0].Start) - utils.AssertEqual(t, end, result.Ranges[0].End) + require.NoError(t, err) + require.Equal(t, "bytes", result.Type) + require.Equal(t, start, result.Ranges[0].Start) + require.Equal(t, end, result.Ranges[0].End) } testRange("bytes=a-700", 300, 999) @@ -1478,18 +1479,18 @@ func Test_Ctx_Route(t *testing.T) { t.Parallel() app := New() app.Get("/test", func(c Ctx) error { - utils.AssertEqual(t, "/test", c.Route().Path) + require.Equal(t, "/test", c.Route().Path) return nil }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/test", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, "/", c.Route().Path) - utils.AssertEqual(t, MethodGet, c.Route().Method) - utils.AssertEqual(t, 0, len(c.Route().Handlers)) + require.Equal(t, "/", c.Route().Path) + require.Equal(t, MethodGet, c.Route().Method) + require.Equal(t, 0, len(c.Route().Handlers)) } // go test -run Test_Ctx_RouteNormalized @@ -1497,12 +1498,12 @@ func Test_Ctx_RouteNormalized(t *testing.T) { t.Parallel() app := New() app.Get("/test", func(c Ctx) error { - utils.AssertEqual(t, "/test", c.Route().Path) + require.Equal(t, "/test", c.Route().Path) return nil }) resp, err := app.Test(httptest.NewRequest(MethodGet, "//test", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusNotFound, resp.StatusCode, "Status code") } // go test -run Test_Ctx_SaveFile @@ -1513,18 +1514,18 @@ func Test_Ctx_SaveFile(t *testing.T) { app.Post("/test", func(c Ctx) error { fh, err := c.FormFile("file") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) tempFile, err := os.CreateTemp(os.TempDir(), "test-") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) defer os.Remove(tempFile.Name()) err = c.SaveFile(fh, tempFile.Name()) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) bs, err := os.ReadFile(tempFile.Name()) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "hello world", string(bs)) + require.NoError(t, err) + require.Equal(t, "hello world", string(bs)) return nil }) @@ -1532,10 +1533,10 @@ func Test_Ctx_SaveFile(t *testing.T) { writer := multipart.NewWriter(body) ioWriter, err := writer.CreateFormFile("file", "test") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) _, err = ioWriter.Write([]byte("hello world")) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) writer.Close() req := httptest.NewRequest(MethodPost, "/test", body) @@ -1543,8 +1544,8 @@ func Test_Ctx_SaveFile(t *testing.T) { req.Header.Set("Content-Length", strconv.Itoa(len(body.Bytes()))) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") } // go test -run Test_Ctx_SaveFileToStorage @@ -1555,17 +1556,17 @@ func Test_Ctx_SaveFileToStorage(t *testing.T) { app.Post("/test", func(c Ctx) error { fh, err := c.FormFile("file") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) err = c.SaveFileToStorage(fh, "test", storage) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) file, err := storage.Get("test") - utils.AssertEqual(t, []byte("hello world"), file) - utils.AssertEqual(t, nil, err) + require.Equal(t, []byte("hello world"), file) + require.NoError(t, err) err = storage.Delete("test") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) return nil }) @@ -1574,10 +1575,10 @@ func Test_Ctx_SaveFileToStorage(t *testing.T) { writer := multipart.NewWriter(body) ioWriter, err := writer.CreateFormFile("file", "test") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) _, err = ioWriter.Write([]byte("hello world")) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) writer.Close() req := httptest.NewRequest(MethodPost, "/test", body) @@ -1585,8 +1586,8 @@ func Test_Ctx_SaveFileToStorage(t *testing.T) { req.Header.Set("Content-Length", strconv.Itoa(len(body.Bytes()))) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") } // go test -run Test_Ctx_Secure @@ -1596,7 +1597,7 @@ func Test_Ctx_Secure(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) // TODO Add TLS conn - utils.AssertEqual(t, false, c.Secure()) + require.False(t, c.Secure()) } // go test -run Test_Ctx_Stale @@ -1605,7 +1606,7 @@ func Test_Ctx_Stale(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, true, c.Stale()) + require.True(t, c.Stale()) } // go test -run Test_Ctx_Subdomains @@ -1615,10 +1616,10 @@ func Test_Ctx_Subdomains(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().URI().SetHost("john.doe.is.awesome.google.com") - utils.AssertEqual(t, []string{"john", "doe"}, c.Subdomains(4)) + require.Equal(t, []string{"john", "doe"}, c.Subdomains(4)) c.Request().URI().SetHost("localhost:3000") - utils.AssertEqual(t, []string{"localhost:3000"}, c.Subdomains()) + require.Equal(t, []string{"localhost:3000"}, c.Subdomains()) } // go test -v -run=^$ -bench=Benchmark_Ctx_Subdomains -benchmem -count=4 @@ -1633,7 +1634,7 @@ func Benchmark_Ctx_Subdomains(b *testing.B) { for n := 0; n < b.N; n++ { res = c.Subdomains() } - utils.AssertEqual(b, []string{"john", "doe"}, res) + require.Equal(b, []string{"john", "doe"}, res) } // go test -run Test_Ctx_ClearCookie @@ -1644,13 +1645,13 @@ func Test_Ctx_ClearCookie(t *testing.T) { c.Request().Header.Set(HeaderCookie, "john=doe") c.ClearCookie("john") - utils.AssertEqual(t, true, strings.HasPrefix(string(c.Response().Header.Peek(HeaderSetCookie)), "john=; expires=")) + require.True(t, strings.HasPrefix(string(c.Response().Header.Peek(HeaderSetCookie)), "john=; expires=")) c.Request().Header.Set(HeaderCookie, "test1=dummy") c.Request().Header.Set(HeaderCookie, "test2=dummy") c.ClearCookie() - utils.AssertEqual(t, true, strings.Contains(string(c.Response().Header.Peek(HeaderSetCookie)), "test1=; expires=")) - utils.AssertEqual(t, true, strings.Contains(string(c.Response().Header.Peek(HeaderSetCookie)), "test2=; expires=")) + require.True(t, strings.Contains(string(c.Response().Header.Peek(HeaderSetCookie)), "test1=; expires=")) + require.True(t, strings.Contains(string(c.Response().Header.Peek(HeaderSetCookie)), "test2=; expires=")) } // go test -race -run Test_Ctx_Download @@ -1662,16 +1663,16 @@ func Test_Ctx_Download(t *testing.T) { c.Download("ctx.go", "Awesome File!") f, err := os.Open("./ctx.go") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) defer f.Close() expect, err := io.ReadAll(f) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, expect, c.Response().Body()) - utils.AssertEqual(t, `attachment; filename="Awesome+File%21"`, string(c.Response().Header.Peek(HeaderContentDisposition))) + require.NoError(t, err) + require.Equal(t, expect, c.Response().Body()) + require.Equal(t, `attachment; filename="Awesome+File%21"`, string(c.Response().Header.Peek(HeaderContentDisposition))) c.Download("ctx.go") - utils.AssertEqual(t, `attachment; filename="ctx.go"`, string(c.Response().Header.Peek(HeaderContentDisposition))) + require.Equal(t, `attachment; filename="ctx.go"`, string(c.Response().Header.Peek(HeaderContentDisposition))) } // go test -race -run Test_Ctx_SendFile @@ -1681,30 +1682,30 @@ func Test_Ctx_SendFile(t *testing.T) { // fetch file content f, err := os.Open("./ctx.go") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) defer f.Close() expectFileContent, err := io.ReadAll(f) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) // fetch file info for the not modified test case fI, err := os.Stat("./ctx.go") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) // simple test case c := app.NewCtx(&fasthttp.RequestCtx{}) err = c.SendFile("ctx.go") // check expectation - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, expectFileContent, c.Response().Body()) - utils.AssertEqual(t, StatusOK, c.Response().StatusCode()) + require.NoError(t, err) + require.Equal(t, expectFileContent, c.Response().Body()) + require.Equal(t, StatusOK, c.Response().StatusCode()) app.ReleaseCtx(c) // test with custom error code c = app.NewCtx(&fasthttp.RequestCtx{}) err = c.Status(StatusInternalServerError).SendFile("ctx.go") // check expectation - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, expectFileContent, c.Response().Body()) - utils.AssertEqual(t, StatusInternalServerError, c.Response().StatusCode()) + require.NoError(t, err) + require.Equal(t, expectFileContent, c.Response().Body()) + require.Equal(t, StatusInternalServerError, c.Response().StatusCode()) app.ReleaseCtx(c) // test not modified @@ -1712,9 +1713,9 @@ func Test_Ctx_SendFile(t *testing.T) { c.Request().Header.Set(HeaderIfModifiedSince, fI.ModTime().Format(time.RFC1123)) err = c.SendFile("ctx.go") // check expectation - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, StatusNotModified, c.Response().StatusCode()) - utils.AssertEqual(t, []byte(nil), c.Response().Body()) + require.NoError(t, err) + require.Equal(t, StatusNotModified, c.Response().StatusCode()) + require.Equal(t, []byte(nil), c.Response().Body()) app.ReleaseCtx(c) } @@ -1724,13 +1725,13 @@ func Test_Ctx_SendFile_404(t *testing.T) { app := New() app.Get("/", func(c Ctx) error { err := c.SendFile(filepath.FromSlash("john_dow.go/")) - utils.AssertEqual(t, false, err == nil) + require.False(t, err == nil) return err }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, StatusNotFound, resp.StatusCode) } // go test -race -run Test_Ctx_SendFile_Immutable @@ -1742,7 +1743,7 @@ func Test_Ctx_SendFile_Immutable(t *testing.T) { endpointsForTest = append(endpointsForTest, endpoint) app.Get(endpoint, func(c Ctx) error { if err := c.SendFile(file); err != nil { - utils.AssertEqual(t, nil, err) + require.NoError(t, err) return err } return c.SendStatus(200) @@ -1757,7 +1758,7 @@ func Test_Ctx_SendFile_Immutable(t *testing.T) { // absolute paths if path, err := filepath.Abs(".github/index.html"); err != nil { - utils.AssertEqual(t, nil, err) + require.NoError(t, err) } else { addEndpoint(path, "/absolute") addEndpoint(filepath.FromSlash(path), "/absoluteOS") // os related @@ -1767,12 +1768,12 @@ func Test_Ctx_SendFile_Immutable(t *testing.T) { t.Run(endpoint, func(t *testing.T) { // 1st try resp, err := app.Test(httptest.NewRequest("GET", endpoint, nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, StatusOK, resp.StatusCode) // 2nd try resp, err = app.Test(httptest.NewRequest("GET", endpoint, nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, StatusOK, resp.StatusCode) }) } } @@ -1784,7 +1785,7 @@ func Test_Ctx_SendFile_RestoreOriginalURL(t *testing.T) { app.Get("/", func(c Ctx) error { originalURL := utils.CopyString(c.OriginalURL()) err := c.SendFile("ctx.go") - utils.AssertEqual(t, originalURL, c.OriginalURL()) + require.Equal(t, originalURL, c.OriginalURL()) return err }) @@ -1792,8 +1793,8 @@ func Test_Ctx_SendFile_RestoreOriginalURL(t *testing.T) { // second request required to confirm with zero allocation _, err2 := app.Test(httptest.NewRequest("GET", "/?test=true", nil)) - utils.AssertEqual(t, nil, err1) - utils.AssertEqual(t, nil, err2) + require.Nil(t, err1) + require.Nil(t, err2) } // go test -run Test_Ctx_JSON @@ -1802,19 +1803,19 @@ func Test_Ctx_JSON(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, true, c.JSON(complex(1, 1)) != nil) + require.True(t, c.JSON(complex(1, 1)) != nil) c.JSON(Map{ // map has no order "Name": "Grame", "Age": 20, }) - utils.AssertEqual(t, `{"Age":20,"Name":"Grame"}`, string(c.Response().Body())) - utils.AssertEqual(t, "application/json", string(c.Response().Header.Peek("content-type"))) + require.Equal(t, `{"Age":20,"Name":"Grame"}`, string(c.Response().Body())) + require.Equal(t, "application/json", string(c.Response().Header.Peek("content-type"))) testEmpty := func(v any, r string) { err := c.JSON(v) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, r, string(c.Response().Body())) + require.NoError(t, err) + require.Equal(t, r, string(c.Response().Body())) } testEmpty(nil, "null") @@ -1842,8 +1843,8 @@ func Benchmark_Ctx_JSON(b *testing.B) { for n := 0; n < b.N; n++ { err = c.JSON(data) } - utils.AssertEqual(b, nil, err) - utils.AssertEqual(b, `{"Name":"Grame","Age":20}`, string(c.Response().Body())) + require.NoError(b, err) + require.Equal(b, `{"Name":"Grame","Age":20}`, string(c.Response().Body())) } // go test -run Test_Ctx_JSONP @@ -1852,21 +1853,21 @@ func Test_Ctx_JSONP(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, true, c.JSONP(complex(1, 1)) != nil) + require.True(t, c.JSONP(complex(1, 1)) != nil) c.JSONP(Map{ "Name": "Grame", "Age": 20, }) - utils.AssertEqual(t, `callback({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) - utils.AssertEqual(t, "application/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) + require.Equal(t, `callback({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) + require.Equal(t, "application/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) c.JSONP(Map{ "Name": "Grame", "Age": 20, }, "john") - utils.AssertEqual(t, `john({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) - utils.AssertEqual(t, "application/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) + require.Equal(t, `john({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) + require.Equal(t, "application/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) } // go test -v -run=^$ -bench=Benchmark_Ctx_JSONP -benchmem -count=4 @@ -1889,8 +1890,8 @@ func Benchmark_Ctx_JSONP(b *testing.B) { for n := 0; n < b.N; n++ { err = c.JSONP(data, callback) } - utils.AssertEqual(b, nil, err) - utils.AssertEqual(b, `emit({"Name":"Grame","Age":20});`, string(c.Response().Body())) + require.NoError(b, err) + require.Equal(b, `emit({"Name":"Grame","Age":20});`, string(c.Response().Body())) } // go test -run Test_Ctx_XML @@ -1899,7 +1900,7 @@ func Test_Ctx_XML(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) - utils.AssertEqual(t, true, c.JSON(complex(1, 1)) != nil) + require.True(t, c.JSON(complex(1, 1)) != nil) type xmlResult struct { XMLName xml.Name `xml:"Users"` @@ -1912,13 +1913,13 @@ func Test_Ctx_XML(t *testing.T) { Ages: []int{1, 12, 20}, }) - utils.AssertEqual(t, `GrameJohn11220`, string(c.Response().Body())) - utils.AssertEqual(t, "application/xml", string(c.Response().Header.Peek("content-type"))) + require.Equal(t, `GrameJohn11220`, string(c.Response().Body())) + require.Equal(t, "application/xml", string(c.Response().Header.Peek("content-type"))) testEmpty := func(v any, r string) { err := c.XML(v) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, r, string(c.Response().Body())) + require.NoError(t, err) + require.Equal(t, r, string(c.Response().Body())) } testEmpty(nil, "") @@ -1946,8 +1947,8 @@ func Benchmark_Ctx_XML(b *testing.B) { err = c.XML(data) } - utils.AssertEqual(b, nil, err) - utils.AssertEqual(b, `Grame20`, string(c.Response().Body())) + require.NoError(b, err) + require.Equal(b, `Grame20`, string(c.Response().Body())) } // go test -run Test_Ctx_Links @@ -1957,13 +1958,13 @@ func Test_Ctx_Links(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Links() - utils.AssertEqual(t, "", string(c.Response().Header.Peek(HeaderLink))) + require.Equal(t, "", string(c.Response().Header.Peek(HeaderLink))) c.Links( "http://api.example.com/users?page=2", "next", "http://api.example.com/users?page=5", "last", ) - utils.AssertEqual(t, `; rel="next",; rel="last"`, string(c.Response().Header.Peek(HeaderLink))) + require.Equal(t, `; rel="next",; rel="last"`, string(c.Response().Header.Peek(HeaderLink))) } // go test -v -run=^$ -bench=Benchmark_Ctx_Links -benchmem -count=4 @@ -1988,7 +1989,7 @@ func Test_Ctx_Location(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Location("http://example.com") - utils.AssertEqual(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) + require.Equal(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) } // go test -run Test_Ctx_Next @@ -2002,9 +2003,9 @@ func Test_Ctx_Next(t *testing.T) { return nil }) resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/test", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") - utils.AssertEqual(t, "Works", resp.Header.Get("X-Next-Result")) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") + require.Equal(t, "Works", resp.Header.Get("X-Next-Result")) } // go test -run Test_Ctx_Next_Error @@ -2016,9 +2017,9 @@ func Test_Ctx_Next_Error(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/test", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") - utils.AssertEqual(t, "Works", resp.Header.Get("X-Next-Result")) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusNotFound, resp.StatusCode, "Status code") + require.Equal(t, "Works", resp.Header.Get("X-Next-Result")) } // go test -run Test_Ctx_Redirect @@ -2028,12 +2029,12 @@ func Test_Ctx_Redirect(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Redirect("http://default.com") - utils.AssertEqual(t, 302, c.Response().StatusCode()) - utils.AssertEqual(t, "http://default.com", string(c.Response().Header.Peek(HeaderLocation))) + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "http://default.com", string(c.Response().Header.Peek(HeaderLocation))) c.Redirect("http://example.com", 301) - utils.AssertEqual(t, 301, c.Response().StatusCode()) - utils.AssertEqual(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) + require.Equal(t, 301, c.Response().StatusCode()) + require.Equal(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) } // go test -run Test_Ctx_RedirectToRouteWithParams @@ -2048,8 +2049,8 @@ func Test_Ctx_RedirectToRouteWithParams(t *testing.T) { c.RedirectToRoute("user", Map{ "name": "fiber", }) - utils.AssertEqual(t, 302, c.Response().StatusCode()) - utils.AssertEqual(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } // go test -run Test_Ctx_RedirectToRouteWithParams @@ -2065,12 +2066,12 @@ func Test_Ctx_RedirectToRouteWithQueries(t *testing.T) { "name": "fiber", "queries": map[string]string{"data[0][name]": "john", "data[0][age]": "10", "test": "doe"}, }) - utils.AssertEqual(t, 302, c.Response().StatusCode()) + require.Equal(t, 302, c.Response().StatusCode()) // analysis of query parameters with url parsing, since a map pass is always randomly ordered location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) - utils.AssertEqual(t, nil, err, "url.Parse(location)") - utils.AssertEqual(t, "/user/fiber", location.Path) - utils.AssertEqual(t, url.Values{"data[0][name]": []string{"john"}, "data[0][age]": []string{"10"}, "test": []string{"doe"}}, location.Query()) + require.NoError(t, err, "url.Parse(location)") + require.Equal(t, "/user/fiber", location.Path) + require.Equal(t, url.Values{"data[0][name]": []string{"john"}, "data[0][age]": []string{"10"}, "test": []string{"doe"}}, location.Query()) } // go test -run Test_Ctx_RedirectToRouteWithOptionalParams @@ -2085,8 +2086,8 @@ func Test_Ctx_RedirectToRouteWithOptionalParams(t *testing.T) { c.RedirectToRoute("user", Map{ "name": "fiber", }) - utils.AssertEqual(t, 302, c.Response().StatusCode()) - utils.AssertEqual(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } // go test -run Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue @@ -2099,8 +2100,8 @@ func Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.RedirectToRoute("user", Map{}) - utils.AssertEqual(t, 302, c.Response().StatusCode()) - utils.AssertEqual(t, "/user/", string(c.Response().Header.Peek(HeaderLocation))) + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/user/", string(c.Response().Header.Peek(HeaderLocation))) } // go test -run Test_Ctx_RedirectToRouteWithGreedyParameters @@ -2115,8 +2116,8 @@ func Test_Ctx_RedirectToRouteWithGreedyParameters(t *testing.T) { c.RedirectToRoute("user", Map{ "+": "test/routes", }) - utils.AssertEqual(t, 302, c.Response().StatusCode()) - utils.AssertEqual(t, "/user/test/routes", string(c.Response().Header.Peek(HeaderLocation))) + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/user/test/routes", string(c.Response().Header.Peek(HeaderLocation))) } // go test -run Test_Ctx_RedirectBack @@ -2129,8 +2130,8 @@ func Test_Ctx_RedirectBack(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.RedirectBack("/") - utils.AssertEqual(t, 302, c.Response().StatusCode()) - utils.AssertEqual(t, "/", string(c.Response().Header.Peek(HeaderLocation))) + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/", string(c.Response().Header.Peek(HeaderLocation))) } // go test -run Test_Ctx_RedirectBackWithReferer @@ -2147,9 +2148,9 @@ func Test_Ctx_RedirectBackWithReferer(t *testing.T) { c.Request().Header.Set(HeaderReferer, "/back") c.RedirectBack("/") - utils.AssertEqual(t, 302, c.Response().StatusCode()) - utils.AssertEqual(t, "/back", c.Get(HeaderReferer)) - utils.AssertEqual(t, "/back", string(c.Response().Header.Peek(HeaderLocation))) + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/back", c.Get(HeaderReferer)) + require.Equal(t, "/back", string(c.Response().Header.Peek(HeaderLocation))) } // go test -run Test_Ctx_Render @@ -2166,14 +2167,14 @@ func Test_Ctx_Render(t *testing.T) { _, _ = buf.WriteString("overwrite") defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

    Hello, World!

    ", string(c.Response().Body())) + require.NoError(t, err) + require.Equal(t, "

    Hello, World!

    ", string(c.Response().Body())) err = c.Render("./.github/testdata/template-non-exists.html", nil) - utils.AssertEqual(t, false, err == nil) + require.False(t, err == nil) err = c.Render("./.github/testdata/template-invalid.html", nil) - utils.AssertEqual(t, false, err == nil) + require.False(t, err == nil) } // go test -run Test_Ctx_Render_Mount @@ -2197,12 +2198,12 @@ func Test_Ctx_Render_Mount(t *testing.T) { app.Mount("/hello", sub) resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/a", nil)) - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") - utils.AssertEqual(t, nil, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

    Hello a!

    ", string(body)) + require.NoError(t, err) + require.Equal(t, "

    Hello a!

    ", string(body)) } func Test_Ctx_Render_MountGroup(t *testing.T) { @@ -2226,12 +2227,12 @@ func Test_Ctx_Render_MountGroup(t *testing.T) { v1.Mount("/john", micro) resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

    Hello doe!

    ", string(body)) + require.NoError(t, err) + require.Equal(t, "

    Hello doe!

    ", string(body)) } func Test_Ctx_RenderWithoutLocals(t *testing.T) { @@ -2249,8 +2250,8 @@ func Test_Ctx_RenderWithoutLocals(t *testing.T) { _, _ = buf.WriteString("overwrite") defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

    ", string(c.Response().Body())) + require.NoError(t, err) + require.Equal(t, "

    ", string(c.Response().Body())) } func Test_Ctx_RenderWithLocals(t *testing.T) { @@ -2268,8 +2269,8 @@ func Test_Ctx_RenderWithLocals(t *testing.T) { _, _ = buf.WriteString("overwrite") defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

    Hello, World!

    ", string(c.Response().Body())) + require.NoError(t, err) + require.Equal(t, "

    Hello, World!

    ", string(c.Response().Body())) } @@ -2289,8 +2290,8 @@ func Test_Ctx_RenderWithBindVars(t *testing.T) { _, _ = buf.WriteString("overwrite") defer bytebufferpool.Put(buf) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

    Hello, World!

    ", string(c.Response().Body())) + require.NoError(t, err) + require.Equal(t, "

    Hello, World!

    ", string(c.Response().Body())) } @@ -2311,8 +2312,8 @@ func Test_Ctx_RenderWithBindVarsLocals(t *testing.T) { err := c.Render("./.github/testdata/template.tmpl", Map{}) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

    Hello, World! Test

    ", string(c.Response().Body())) + require.NoError(t, err) + require.Equal(t, "

    Hello, World! Test

    ", string(c.Response().Body())) } @@ -2320,7 +2321,7 @@ func Test_Ctx_RenderWithLocalsAndBinding(t *testing.T) { t.Parallel() engine := &testTemplateEngine{} err := engine.Load() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) app := New(Config{ PassLocalsToViews: true, @@ -2334,14 +2335,14 @@ func Test_Ctx_RenderWithLocalsAndBinding(t *testing.T) { "Title": "Hello, World!", }) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

    Hello, World!

    ", string(c.Response().Body())) + require.NoError(t, err) + require.Equal(t, "

    Hello, World!

    ", string(c.Response().Body())) } func Benchmark_Ctx_RenderWithLocalsAndBindVars(b *testing.B) { engine := &testTemplateEngine{} err := engine.Load() - utils.AssertEqual(b, nil, err) + require.NoError(b, err) app := New(Config{ PassLocalsToViews: true, Views: engine, @@ -2360,8 +2361,8 @@ func Benchmark_Ctx_RenderWithLocalsAndBindVars(b *testing.B) { err = c.Render("template.tmpl", Map{}) } - utils.AssertEqual(b, nil, err) - utils.AssertEqual(b, "

    Hello, World! Test

    ", string(c.Response().Body())) + require.NoError(b, err) + require.Equal(b, "

    Hello, World! Test

    ", string(c.Response().Body())) } func Benchmark_Ctx_RedirectToRoute(b *testing.B) { @@ -2381,8 +2382,8 @@ func Benchmark_Ctx_RedirectToRoute(b *testing.B) { }) } - utils.AssertEqual(b, 302, c.Response().StatusCode()) - utils.AssertEqual(b, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) + require.Equal(b, 302, c.Response().StatusCode()) + require.Equal(b, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } func Benchmark_Ctx_RedirectToRouteWithQueries(b *testing.B) { @@ -2403,18 +2404,18 @@ func Benchmark_Ctx_RedirectToRouteWithQueries(b *testing.B) { }) } - utils.AssertEqual(b, 302, c.Response().StatusCode()) + require.Equal(b, 302, c.Response().StatusCode()) // analysis of query parameters with url parsing, since a map pass is always randomly ordered location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) - utils.AssertEqual(b, nil, err, "url.Parse(location)") - utils.AssertEqual(b, "/user/fiber", location.Path) - utils.AssertEqual(b, url.Values{"a": []string{"a"}, "b": []string{"b"}}, location.Query()) + require.NoError(b, err, "url.Parse(location)") + require.Equal(b, "/user/fiber", location.Path) + require.Equal(b, url.Values{"a": []string{"a"}, "b": []string{"b"}}, location.Query()) } func Benchmark_Ctx_RenderLocals(b *testing.B) { engine := &testTemplateEngine{} err := engine.Load() - utils.AssertEqual(b, nil, err) + require.NoError(b, err) app := New(Config{ PassLocalsToViews: true, }) @@ -2430,14 +2431,14 @@ func Benchmark_Ctx_RenderLocals(b *testing.B) { err = c.Render("index.tmpl", Map{}) } - utils.AssertEqual(b, nil, err) - utils.AssertEqual(b, "

    Hello, World!

    ", string(c.Response().Body())) + require.NoError(b, err) + require.Equal(b, "

    Hello, World!

    ", string(c.Response().Body())) } func Benchmark_Ctx_RenderBindVars(b *testing.B) { engine := &testTemplateEngine{} err := engine.Load() - utils.AssertEqual(b, nil, err) + require.NoError(b, err) app := New() app.config.Views = engine c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -2453,8 +2454,8 @@ func Benchmark_Ctx_RenderBindVars(b *testing.B) { err = c.Render("index.tmpl", Map{}) } - utils.AssertEqual(b, nil, err) - utils.AssertEqual(b, "

    Hello, World!

    ", string(c.Response().Body())) + require.NoError(b, err) + require.Equal(b, "

    Hello, World!

    ", string(c.Response().Body())) } // go test -run Test_Ctx_RestartRouting @@ -2469,9 +2470,9 @@ func Test_Ctx_RestartRouting(t *testing.T) { return nil }) resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") - utils.AssertEqual(t, 3, calls, "Number of calls") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") + require.Equal(t, 3, calls, "Number of calls") } // go test -run Test_Ctx_RestartRoutingWithChangedPath @@ -2494,10 +2495,10 @@ func Test_Ctx_RestartRoutingWithChangedPath(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/old", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, executedOldHandler, "Executed old handler") - utils.AssertEqual(t, true, executedNewHandler, "Executed new handler") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") + require.False(t, executedOldHandler, "Executed old handler") + require.True(t, executedNewHandler, "Executed new handler") } // go test -run Test_Ctx_RestartRoutingWithChangedPathAnd404 @@ -2516,8 +2517,8 @@ func Test_Ctx_RestartRoutingWithChangedPathAndCatchAll(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "http://example.com/old", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") } type testTemplateEngine struct { @@ -2548,8 +2549,8 @@ func Test_Ctx_Render_Engine(t *testing.T) { err := c.Render("index.tmpl", Map{ "Title": "Hello, World!", }) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

    Hello, World!

    ", string(c.Response().Body())) + require.NoError(t, err) + require.Equal(t, "

    Hello, World!

    ", string(c.Response().Body())) } // go test -run Test_Ctx_Render_Engine_With_View_Layout @@ -2563,15 +2564,15 @@ func Test_Ctx_Render_Engine_With_View_Layout(t *testing.T) { err := c.Render("index.tmpl", Map{ "Title": "Hello, World!", }) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

    Hello, World!

    I'm main

    ", string(c.Response().Body())) + require.NoError(t, err) + require.Equal(t, "

    Hello, World!

    I'm main

    ", string(c.Response().Body())) } // go test -v -run=^$ -bench=Benchmark_Ctx_Render_Engine -benchmem -count=4 func Benchmark_Ctx_Render_Engine(b *testing.B) { engine := &testTemplateEngine{} err := engine.Load() - utils.AssertEqual(b, nil, err) + require.NoError(b, err) app := New() app.config.Views = engine c := app.NewCtx(&fasthttp.RequestCtx{}) @@ -2583,8 +2584,8 @@ func Benchmark_Ctx_Render_Engine(b *testing.B) { "Title": "Hello, World!", }) } - utils.AssertEqual(b, nil, err) - utils.AssertEqual(b, "

    Hello, World!

    ", string(c.Response().Body())) + require.NoError(b, err) + require.Equal(b, "

    Hello, World!

    ", string(c.Response().Body())) } // go test -v -run=^$ -bench=Benchmark_Ctx_Get_Location_From_Route -benchmem -count=4 @@ -2610,8 +2611,8 @@ func Test_Ctx_Get_Location_From_Route_name(t *testing.T) { }).Name("User") location, err := c.GetRouteURL("User", Map{"name": "fiber"}) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "/user/fiber", location) + require.NoError(t, err) + require.Equal(t, "/user/fiber", location) } // go test -run Test_Ctx_Get_Location_From_Route_name_Optional_greedy @@ -2628,8 +2629,8 @@ func Test_Ctx_Get_Location_From_Route_name_Optional_greedy(t *testing.T) { "*1": "sms", "*2": "test-msg", }) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "/23456789/sms/send/test-msg", location) + require.NoError(t, err) + require.Equal(t, "/23456789/sms/send/test-msg", location) } // go test -run Test_Ctx_Get_Location_From_Route_name_Optional_greedy_one_param @@ -2645,8 +2646,8 @@ func Test_Ctx_Get_Location_From_Route_name_Optional_greedy_one_param(t *testing. "phone": "23456789", "*": "sms", }) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "/23456789/sms/send", location) + require.NoError(t, err) + require.Equal(t, "/23456789/sms/send", location) } type errorTemplateEngine struct{} @@ -2664,7 +2665,7 @@ func Test_Ctx_Render_Engine_Error(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) err := c.Render("index.tmpl", nil) - utils.AssertEqual(t, false, err == nil) + require.False(t, err == nil) } // go test -run Test_Ctx_Render_Go_Template @@ -2672,22 +2673,22 @@ func Test_Ctx_Render_Go_Template(t *testing.T) { t.Parallel() file, err := os.CreateTemp(os.TempDir(), "fiber") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) defer os.Remove(file.Name()) _, err = file.Write([]byte("template")) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) err = file.Close() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) err = c.Render(file.Name(), nil) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "template", string(c.Response().Body())) + require.NoError(t, err) + require.Equal(t, "template", string(c.Response().Body())) } // go test -run Test_Ctx_Send @@ -2699,7 +2700,7 @@ func Test_Ctx_Send(t *testing.T) { c.Send([]byte("Hello, World")) c.Send([]byte("Don't crash please")) c.Send([]byte("1337")) - utils.AssertEqual(t, "1337", string(c.Response().Body())) + require.Equal(t, "1337", string(c.Response().Body())) } // go test -v -run=^$ -bench=Benchmark_Ctx_Send -benchmem -count=4 @@ -2713,7 +2714,7 @@ func Benchmark_Ctx_Send(b *testing.B) { for n := 0; n < b.N; n++ { c.Send(byt) } - utils.AssertEqual(b, "Hello, World!", string(c.Response().Body())) + require.Equal(b, "Hello, World!", string(c.Response().Body())) } // go test -run Test_Ctx_SendStatus @@ -2723,8 +2724,8 @@ func Test_Ctx_SendStatus(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.SendStatus(415) - utils.AssertEqual(t, 415, c.Response().StatusCode()) - utils.AssertEqual(t, "Unsupported Media Type", string(c.Response().Body())) + require.Equal(t, 415, c.Response().StatusCode()) + require.Equal(t, "Unsupported Media Type", string(c.Response().Body())) } // go test -run Test_Ctx_SendString @@ -2734,7 +2735,7 @@ func Test_Ctx_SendString(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.SendString("Don't crash please") - utils.AssertEqual(t, "Don't crash please", string(c.Response().Body())) + require.Equal(t, "Don't crash please", string(c.Response().Body())) } // go test -run Test_Ctx_SendStream @@ -2744,18 +2745,18 @@ func Test_Ctx_SendStream(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.SendStream(bytes.NewReader([]byte("Don't crash please"))) - utils.AssertEqual(t, "Don't crash please", string(c.Response().Body())) + require.Equal(t, "Don't crash please", string(c.Response().Body())) c.SendStream(bytes.NewReader([]byte("Don't crash please")), len([]byte("Don't crash please"))) - utils.AssertEqual(t, "Don't crash please", string(c.Response().Body())) + require.Equal(t, "Don't crash please", string(c.Response().Body())) c.SendStream(bufio.NewReader(bytes.NewReader([]byte("Hello bufio")))) - utils.AssertEqual(t, "Hello bufio", string(c.Response().Body())) + require.Equal(t, "Hello bufio", string(c.Response().Body())) file, err := os.Open("./.github/index.html") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) c.SendStream(bufio.NewReader(file)) - utils.AssertEqual(t, true, c.Response().Header.ContentLength() > 200) + require.True(t, c.Response().Header.ContentLength() > 200) } // go test -run Test_Ctx_Set @@ -2768,9 +2769,9 @@ func Test_Ctx_Set(t *testing.T) { c.Set("X-2", "2") c.Set("X-3", "3") c.Set("X-3", "1337") - utils.AssertEqual(t, "1", string(c.Response().Header.Peek("x-1"))) - utils.AssertEqual(t, "2", string(c.Response().Header.Peek("x-2"))) - utils.AssertEqual(t, "1337", string(c.Response().Header.Peek("x-3"))) + require.Equal(t, "1", string(c.Response().Header.Peek("x-1"))) + require.Equal(t, "2", string(c.Response().Header.Peek("x-2"))) + require.Equal(t, "1337", string(c.Response().Header.Peek("x-3"))) } // go test -run Test_Ctx_Set_Splitter @@ -2781,11 +2782,11 @@ func Test_Ctx_Set_Splitter(t *testing.T) { c.Set("Location", "foo\r\nSet-Cookie:%20SESSIONID=MaliciousValue\r\n") h := string(c.Response().Header.Peek("Location")) - utils.AssertEqual(t, false, strings.Contains(h, "\r\n"), h) + require.False(t, strings.Contains(h, "\r\n"), h) c.Set("Location", "foo\nSet-Cookie:%20SESSIONID=MaliciousValue\n") h = string(c.Response().Header.Peek("Location")) - utils.AssertEqual(t, false, strings.Contains(h, "\n"), h) + require.False(t, strings.Contains(h, "\n"), h) } // go test -v -run=^$ -bench=Benchmark_Ctx_Set -benchmem -count=4 @@ -2808,10 +2809,10 @@ func Test_Ctx_Status(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Status(400) - utils.AssertEqual(t, 400, c.Response().StatusCode()) + require.Equal(t, 400, c.Response().StatusCode()) c.Status(415).Send([]byte("Hello, World")) - utils.AssertEqual(t, 415, c.Response().StatusCode()) - utils.AssertEqual(t, "Hello, World", string(c.Response().Body())) + require.Equal(t, 415, c.Response().StatusCode()) + require.Equal(t, "Hello, World", string(c.Response().Body())) } // go test -run Test_Ctx_Type @@ -2821,16 +2822,16 @@ func Test_Ctx_Type(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Type(".json") - utils.AssertEqual(t, "application/json", string(c.Response().Header.Peek("Content-Type"))) + require.Equal(t, "application/json", string(c.Response().Header.Peek("Content-Type"))) c.Type("json", "utf-8") - utils.AssertEqual(t, "application/json; charset=utf-8", string(c.Response().Header.Peek("Content-Type"))) + require.Equal(t, "application/json; charset=utf-8", string(c.Response().Header.Peek("Content-Type"))) c.Type(".html") - utils.AssertEqual(t, "text/html", string(c.Response().Header.Peek("Content-Type"))) + require.Equal(t, "text/html", string(c.Response().Header.Peek("Content-Type"))) c.Type("html", "utf-8") - utils.AssertEqual(t, "text/html; charset=utf-8", string(c.Response().Header.Peek("Content-Type"))) + require.Equal(t, "text/html; charset=utf-8", string(c.Response().Header.Peek("Content-Type"))) } // go test -v -run=^$ -bench=Benchmark_Ctx_Type -benchmem -count=4 @@ -2868,7 +2869,7 @@ func Test_Ctx_Vary(t *testing.T) { c.Vary("Origin") c.Vary("User-Agent") c.Vary("Accept-Encoding", "Accept") - utils.AssertEqual(t, "Origin, User-Agent, Accept-Encoding, Accept", string(c.Response().Header.Peek("Vary"))) + require.Equal(t, "Origin, User-Agent, Accept-Encoding, Accept", string(c.Response().Header.Peek("Vary"))) } // go test -v -run=^$ -bench=Benchmark_Ctx_Vary -benchmem -count=4 @@ -2891,7 +2892,7 @@ func Test_Ctx_Write(t *testing.T) { c.Write([]byte("Hello, ")) c.Write([]byte("World!")) - utils.AssertEqual(t, "Hello, World!", string(c.Response().Body())) + require.Equal(t, "Hello, World!", string(c.Response().Body())) } // go test -v -run=^$ -bench=Benchmark_Ctx_Write -benchmem -count=4 @@ -2915,7 +2916,7 @@ func Test_Ctx_Writef(t *testing.T) { world := "World!" c.Writef("Hello, %s", world) - utils.AssertEqual(t, "Hello, World!", string(c.Response().Body())) + require.Equal(t, "Hello, World!", string(c.Response().Body())) } // go test -v -run=^$ -bench=Benchmark_Ctx_Writef -benchmem -count=4 @@ -2939,7 +2940,7 @@ func Test_Ctx_WriteString(t *testing.T) { c.WriteString("Hello, ") c.WriteString("World!") - utils.AssertEqual(t, "Hello, World!", string(c.Response().Body())) + require.Equal(t, "Hello, World!", string(c.Response().Body())) } // go test -run Test_Ctx_XHR @@ -2949,7 +2950,7 @@ func Test_Ctx_XHR(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXRequestedWith, "XMLHttpRequest") - utils.AssertEqual(t, true, c.XHR()) + require.True(t, c.XHR()) } // go test -run=^$ -bench=Benchmark_Ctx_XHR -benchmem -count=4 @@ -2964,7 +2965,7 @@ func Benchmark_Ctx_XHR(b *testing.B) { for n := 0; n < b.N; n++ { equal = c.XHR() } - utils.AssertEqual(b, true, equal) + require.True(b, equal) } // go test -v -run=^$ -bench=Benchmark_Ctx_SendString_B -benchmem -count=4 @@ -2978,7 +2979,7 @@ func Benchmark_Ctx_SendString_B(b *testing.B) { for n := 0; n < b.N; n++ { c.SendString(body) } - utils.AssertEqual(b, []byte("Hello, world!"), c.Response().Body()) + require.Equal(b, []byte("Hello, world!"), c.Response().Body()) } // go test -run Test_Ctx_BodyStreamWriter @@ -3036,7 +3037,7 @@ func Test_Ctx_String(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, "#0000000000000000 - 0.0.0.0:0 <-> 0.0.0.0:0 - GET http:///", c.String()) + require.Equal(t, "#0000000000000000 - 0.0.0.0:0 <-> 0.0.0.0:0 - GET http:///", c.String()) } func TestCtx_ParamsInt(t *testing.T) { @@ -3140,8 +3141,8 @@ func Test_Ctx_GetRespHeader(t *testing.T) { c.Set("test", "Hello, World 👋!") c.Response().Header.Set(HeaderContentType, "application/json") - utils.AssertEqual(t, c.GetRespHeader("test"), "Hello, World 👋!") - utils.AssertEqual(t, c.GetRespHeader(HeaderContentType), "application/json") + require.Equal(t, c.GetRespHeader("test"), "Hello, World 👋!") + require.Equal(t, c.GetRespHeader(HeaderContentType), "application/json") } // go test -run Test_Ctx_IsFromLocal @@ -3152,7 +3153,7 @@ func Test_Ctx_IsFromLocal(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, true, c.IsFromLocal()) + require.True(t, c.IsFromLocal()) } // This is a test for "0.0.0.0" { @@ -3160,7 +3161,7 @@ func Test_Ctx_IsFromLocal(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.0") - utils.AssertEqual(t, true, c.IsFromLocal()) + require.True(t, c.IsFromLocal()) } // This is a test for "127.0.0.1" @@ -3169,7 +3170,7 @@ func Test_Ctx_IsFromLocal(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1") - utils.AssertEqual(t, true, c.IsFromLocal()) + require.True(t, c.IsFromLocal()) } // This is a test for "localhost" @@ -3177,7 +3178,7 @@ func Test_Ctx_IsFromLocal(t *testing.T) { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - utils.AssertEqual(t, true, c.IsFromLocal()) + require.True(t, c.IsFromLocal()) } // This is testing "::1", it is the compressed format IPV6 loopback address 0:0:0:0:0:0:0:1. @@ -3187,7 +3188,7 @@ func Test_Ctx_IsFromLocal(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "::1") - utils.AssertEqual(t, true, c.IsFromLocal()) + require.True(t, c.IsFromLocal()) } { @@ -3195,6 +3196,6 @@ func Test_Ctx_IsFromLocal(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().Header.Set(HeaderXForwardedFor, "93.46.8.90") - utils.AssertEqual(t, false, c.IsFromLocal()) + require.False(t, c.IsFromLocal()) } } diff --git a/error_test.go b/error_test.go index ca78044faf..7fce3c12aa 100644 --- a/error_test.go +++ b/error_test.go @@ -7,61 +7,61 @@ import ( jerrors "encoding/json" "github.com/gofiber/fiber/v3/internal/schema" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) func TestConversionError(t *testing.T) { ok := errors.As(ConversionError{}, &schema.ConversionError{}) - utils.AssertEqual(t, true, ok) + require.True(t, ok) } func TestUnknownKeyError(t *testing.T) { ok := errors.As(UnknownKeyError{}, &schema.UnknownKeyError{}) - utils.AssertEqual(t, true, ok) + require.True(t, ok) } func TestEmptyFieldError(t *testing.T) { ok := errors.As(EmptyFieldError{}, &schema.EmptyFieldError{}) - utils.AssertEqual(t, true, ok) + require.True(t, ok) } func TestMultiError(t *testing.T) { ok := errors.As(MultiError{}, &schema.MultiError{}) - utils.AssertEqual(t, true, ok) + require.True(t, ok) } func TestInvalidUnmarshalError(t *testing.T) { var e *jerrors.InvalidUnmarshalError ok := errors.As(&InvalidUnmarshalError{}, &e) - utils.AssertEqual(t, true, ok) + require.True(t, ok) } func TestMarshalerError(t *testing.T) { var e *jerrors.MarshalerError ok := errors.As(&MarshalerError{}, &e) - utils.AssertEqual(t, true, ok) + require.True(t, ok) } func TestSyntaxError(t *testing.T) { var e *jerrors.SyntaxError ok := errors.As(&SyntaxError{}, &e) - utils.AssertEqual(t, true, ok) + require.True(t, ok) } func TestUnmarshalTypeError(t *testing.T) { var e *jerrors.UnmarshalTypeError ok := errors.As(&UnmarshalTypeError{}, &e) - utils.AssertEqual(t, true, ok) + require.True(t, ok) } func TestUnsupportedTypeError(t *testing.T) { var e *jerrors.UnsupportedTypeError ok := errors.As(&UnsupportedTypeError{}, &e) - utils.AssertEqual(t, true, ok) + require.True(t, ok) } func TestUnsupportedValeError(t *testing.T) { var e *jerrors.UnsupportedValueError ok := errors.As(&UnsupportedValueError{}, &e) - utils.AssertEqual(t, true, ok) + require.True(t, ok) } diff --git a/go.mod b/go.mod index b6575c4b55..7bdc4a2efb 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/savsgio/dictpool v0.0.0-20220406081701-03de5edb2e6d + github.com/stretchr/testify v1.8.0 github.com/tinylib/msgp v1.1.6 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.39.0 @@ -15,9 +16,12 @@ require ( require ( github.com/andybalholm/brotli v1.0.4 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/klauspost/compress v1.15.9 // indirect github.com/philhofer/fwd v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d // indirect github.com/valyala/tcplisten v1.0.0 // indirect golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4a18053ba1..c72df004de 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,8 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/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.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= @@ -11,11 +14,18 @@ github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peK github.com/mattn/go-isatty v0.0.16/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= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/savsgio/dictpool v0.0.0-20220406081701-03de5edb2e6d h1:ICMDEgNgR5xFW6ZDeMKTtmh07YiLr7GkDw897I2DwKg= github.com/savsgio/dictpool v0.0.0-20220406081701-03de5edb2e6d/go.mod h1:jrsy/bTK2n5uybo7bAvtLGzmuzAbxp+nKS8bzgrZURE= github.com/savsgio/gotils v0.0.0-20220401102855-e56b59f40436/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d h1:Q+gqLBOPkFGHyCJxXMRqtUgUbTjI8/Ze8vu8GGyNFwo= github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 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= @@ -62,3 +72,8 @@ golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4f golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/helpers_test.go b/helpers_test.go index 0718f6bb3b..e299cd4417 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -13,6 +13,7 @@ import ( "time" "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -20,7 +21,7 @@ func Test_Utils_UniqueRouteStack(t *testing.T) { route1 := &Route{} route2 := &Route{} route3 := &Route{} - utils.AssertEqual( + require.Equal( t, []*Route{ route1, @@ -40,32 +41,32 @@ func Test_Utils_UniqueRouteStack(t *testing.T) { route1, route2, route3, - }), - ) + })) + } func Test_Utils_getGroupPath(t *testing.T) { t.Parallel() res := getGroupPath("/v1", "/") - utils.AssertEqual(t, "/v1", res) + require.Equal(t, "/v1", res) res = getGroupPath("/v1/", "/") - utils.AssertEqual(t, "/v1/", res) + require.Equal(t, "/v1/", res) res = getGroupPath("/v1", "/") - utils.AssertEqual(t, "/v1", res) + require.Equal(t, "/v1", res) res = getGroupPath("/", "/") - utils.AssertEqual(t, "/", res) + require.Equal(t, "/", res) res = getGroupPath("/v1/api/", "/") - utils.AssertEqual(t, "/v1/api/", res) + require.Equal(t, "/v1/api/", res) res = getGroupPath("/v1/api", "group") - utils.AssertEqual(t, "/v1/api/group", res) + require.Equal(t, "/v1/api/group", res) res = getGroupPath("/v1/api", "") - utils.AssertEqual(t, "/v1/api", res) + require.Equal(t, "/v1/api", res) } // go test -v -run=^$ -bench=Benchmark_Utils_ -benchmem -count=3 @@ -78,7 +79,7 @@ func Benchmark_Utils_getGroupPath(b *testing.B) { _ = getGroupPath("/v1", "/api") res = getGroupPath("/v1", "/api/register/:project") } - utils.AssertEqual(b, "/v1/api/register/:project", res) + require.Equal(b, "/v1/api/register/:project", res) } func Benchmark_Utils_Unescape(b *testing.B) { @@ -92,7 +93,7 @@ func Benchmark_Utils_Unescape(b *testing.B) { unescaped = utils.UnsafeString(pathBytes) } - utils.AssertEqual(b, "/créer", unescaped) + require.Equal(b, "/créer", unescaped) } func Test_Utils_Parse_Address(t *testing.T) { @@ -106,22 +107,22 @@ func Test_Utils_Parse_Address(t *testing.T) { for _, c := range testCases { host, port := parseAddr(c.addr) - utils.AssertEqual(t, c.host, host, "addr host") - utils.AssertEqual(t, c.port, port, "addr port") + require.Equal(t, c.host, host, "addr host") + require.Equal(t, c.port, port, "addr port") } } func Test_Utils_GetOffset(t *testing.T) { - utils.AssertEqual(t, "", getOffer("hello")) - utils.AssertEqual(t, "1", getOffer("", "1")) - utils.AssertEqual(t, "", getOffer("2", "1")) + require.Equal(t, "", getOffer("hello")) + require.Equal(t, "1", getOffer("", "1")) + require.Equal(t, "", getOffer("2", "1")) } func Test_Utils_TestConn_Deadline(t *testing.T) { conn := &testConn{} - utils.AssertEqual(t, nil, conn.SetDeadline(time.Time{})) - utils.AssertEqual(t, nil, conn.SetReadDeadline(time.Time{})) - utils.AssertEqual(t, nil, conn.SetWriteDeadline(time.Time{})) + require.Nil(t, conn.SetDeadline(time.Time{})) + require.Nil(t, conn.SetReadDeadline(time.Time{})) + require.Nil(t, conn.SetWriteDeadline(time.Time{})) } func Test_Utils_IsNoCache(t *testing.T) { @@ -141,8 +142,9 @@ func Test_Utils_IsNoCache(t *testing.T) { for _, c := range testCases { ok := isNoCache(c.string) - utils.AssertEqual(t, c.bool, ok, + require.Equal(t, c.bool, ok, fmt.Sprintf("want %t, got isNoCache(%s)=%t", c.bool, c.string, ok)) + } } @@ -157,48 +159,48 @@ func Benchmark_Utils_IsNoCache(b *testing.B) { _ = isNoCache("no-cache, public") ok = isNoCache("max-age=30, no-cache,public") } - utils.AssertEqual(b, true, ok) + require.True(b, ok) } func Test_Utils_lnMetadata(t *testing.T) { t.Run("closed listen", func(t *testing.T) { ln, err := net.Listen(NetworkTCP, ":0") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) - utils.AssertEqual(t, nil, ln.Close()) + require.Nil(t, ln.Close()) addr, config := lnMetadata(NetworkTCP, ln) - utils.AssertEqual(t, ln.Addr().String(), addr) - utils.AssertEqual(t, true, config == nil) + require.Equal(t, ln.Addr().String(), addr) + require.True(t, config == nil) }) t.Run("non tls", func(t *testing.T) { ln, err := net.Listen(NetworkTCP, ":0") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) addr, config := lnMetadata(NetworkTCP4, ln) - utils.AssertEqual(t, ln.Addr().String(), addr) - utils.AssertEqual(t, true, config == nil) + require.Equal(t, ln.Addr().String(), addr) + require.True(t, config == nil) }) t.Run("tls", func(t *testing.T) { cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) config := &tls.Config{Certificates: []tls.Certificate{cer}} ln, err := net.Listen(NetworkTCP4, ":0") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) ln = tls.NewListener(ln, config) addr, config := lnMetadata(NetworkTCP4, ln) - utils.AssertEqual(t, ln.Addr().String(), addr) - utils.AssertEqual(t, true, config != nil) + require.Equal(t, ln.Addr().String(), addr) + require.True(t, config != nil) }) } @@ -213,7 +215,7 @@ func Benchmark_SlashRecognition(b *testing.B) { result = true } } - utils.AssertEqual(b, true, result) + require.True(b, result) }) b.Run("forEach", func(b *testing.B) { result = false @@ -226,7 +228,7 @@ func Benchmark_SlashRecognition(b *testing.B) { } } } - utils.AssertEqual(b, true, result) + require.True(b, result) }) b.Run("IndexRune", func(b *testing.B) { result = false @@ -234,7 +236,7 @@ func Benchmark_SlashRecognition(b *testing.B) { for i := 0; i < b.N; i++ { result = IndexRune(search, c) } - utils.AssertEqual(b, true, result) + require.True(b, result) }) } diff --git a/hooks_test.go b/hooks_test.go index b657b037d9..52ff08a6d3 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/bytebufferpool" ) @@ -20,7 +20,7 @@ func Test_Hook_OnRoute(t *testing.T) { app := New() app.Hooks().OnRoute(func(r Route) error { - utils.AssertEqual(t, "", r.Name) + require.Equal(t, "", r.Name) return nil }) @@ -55,7 +55,7 @@ func Test_Hook_OnName(t *testing.T) { app.Mount("/sub", subApp) - utils.AssertEqual(t, "index", buf.String()) + require.Equal(t, "index", buf.String()) } func Test_Hook_OnName_Error(t *testing.T) { @@ -64,7 +64,7 @@ func Test_Hook_OnName_Error(t *testing.T) { app := New() defer func() { if err := recover(); err != nil { - utils.AssertEqual(t, "unknown error", fmt.Sprintf("%v", err)) + require.Equal(t, "unknown error", fmt.Sprintf("%v", err)) } }() @@ -92,7 +92,7 @@ func Test_Hook_OnGroup(t *testing.T) { grp := app.Group("/x").Name("x.") grp.Group("/a") - utils.AssertEqual(t, "/x/x/a", buf.String()) + require.Equal(t, "/x/x/a", buf.String()) } func Test_Hook_OnGroupName(t *testing.T) { @@ -113,7 +113,7 @@ func Test_Hook_OnGroupName(t *testing.T) { grp.Get("/test", testSimpleHandler) grp.Get("/test2", testSimpleHandler) - utils.AssertEqual(t, "x.", buf.String()) + require.Equal(t, "x.", buf.String()) } func Test_Hook_OnGroupName_Error(t *testing.T) { @@ -122,7 +122,7 @@ func Test_Hook_OnGroupName_Error(t *testing.T) { app := New() defer func() { if err := recover(); err != nil { - utils.AssertEqual(t, "unknown error", fmt.Sprintf("%v", err)) + require.Equal(t, "unknown error", fmt.Sprintf("%v", err)) } }() @@ -148,8 +148,8 @@ func Test_Hook_OnShutdown(t *testing.T) { return nil }) - utils.AssertEqual(t, nil, app.Shutdown()) - utils.AssertEqual(t, "shutdowning", buf.String()) + require.Nil(t, app.Shutdown()) + require.Equal(t, "shutdowning", buf.String()) } func Test_Hook_OnListen(t *testing.T) { @@ -170,11 +170,11 @@ func Test_Hook_OnListen(t *testing.T) { go func() { time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) + require.Nil(t, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.Listen(":9000")) + require.Nil(t, app.Listen(":9000")) - utils.AssertEqual(t, "ready", buf.String()) + require.Equal(t, "ready", buf.String()) } func Test_Hook_OnHook(t *testing.T) { @@ -186,13 +186,13 @@ func Test_Hook_OnHook(t *testing.T) { go func() { time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) + require.Nil(t, app.Shutdown()) }() app.Hooks().OnFork(func(pid int) error { - utils.AssertEqual(t, 1, pid) + require.Equal(t, 1, pid) return nil }) - utils.AssertEqual(t, nil, app.prefork(NetworkTCP4, ":3000", nil)) + require.Nil(t, app.prefork(NetworkTCP4, ":3000", nil)) } diff --git a/internal/schema/doc.go b/internal/schema/doc.go index aae9f33f9d..fff0fe7616 100644 --- a/internal/schema/doc.go +++ b/internal/schema/doc.go @@ -60,14 +60,14 @@ certain fields, use a dash for the name and it will be ignored: The supported field types in the destination struct are: - * bool - * float variants (float32, float64) - * int variants (int, int8, int16, int32, int64) - * string - * uint variants (uint, uint8, uint16, uint32, uint64) - * struct - * a pointer to one of the above types - * a slice or a pointer to a slice of one of the above types + - bool + - float variants (float32, float64) + - int variants (int, int8, int16, int32, int64) + - string + - uint variants (uint, uint8, uint16, uint32, uint64) + - struct + - a pointer to one of the above types + - a slice or a pointer to a slice of one of the above types Non-supported types are simply ignored, however custom types can be registered to be converted. diff --git a/listen_test.go b/listen_test.go index 6c9e6ba7b6..2f6f67183c 100644 --- a/listen_test.go +++ b/listen_test.go @@ -16,7 +16,7 @@ import ( "testing" "time" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp/fasthttputil" ) @@ -24,14 +24,14 @@ import ( func Test_App_Listen(t *testing.T) { app := New(Config{DisableStartupMessage: true}) - utils.AssertEqual(t, false, app.Listen(":99999") == nil) + require.False(t, app.Listen(":99999") == nil) go func() { time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) + require.Nil(t, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.Listen(":4003")) + require.Nil(t, app.Listen(":4003")) } // go test -run Test_App_Listen_Prefork @@ -40,7 +40,7 @@ func Test_App_Listen_Prefork(t *testing.T) { app := New(Config{DisableStartupMessage: true, Prefork: true}) - utils.AssertEqual(t, nil, app.Listen(":99999")) + require.Nil(t, app.Listen(":99999")) } // go test -run Test_App_ListenTLS @@ -48,16 +48,16 @@ func Test_App_ListenTLS(t *testing.T) { app := New() // invalid port - utils.AssertEqual(t, false, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") == nil) + require.False(t, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") == nil) // missing perm/cert file - utils.AssertEqual(t, false, app.ListenTLS(":0", "", "./.github/testdata/ssl.key") == nil) + require.False(t, app.ListenTLS(":0", "", "./.github/testdata/ssl.key") == nil) go func() { time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) + require.Nil(t, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) + require.Nil(t, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) } // go test -run Test_App_ListenTLS_Prefork @@ -67,9 +67,9 @@ func Test_App_ListenTLS_Prefork(t *testing.T) { app := New(Config{DisableStartupMessage: true, Prefork: true}) // invalid key file content - utils.AssertEqual(t, false, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.tmpl") == nil) + require.False(t, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.tmpl") == nil) - utils.AssertEqual(t, nil, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) + require.Nil(t, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) } // go test -run Test_App_ListenMutualTLS @@ -77,16 +77,16 @@ func Test_App_ListenMutualTLS(t *testing.T) { app := New() // invalid port - utils.AssertEqual(t, false, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem") == nil) + require.False(t, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem") == nil) // missing perm/cert file - utils.AssertEqual(t, false, app.ListenMutualTLS(":0", "", "./.github/testdata/ssl.key", "") == nil) + require.False(t, app.ListenMutualTLS(":0", "", "./.github/testdata/ssl.key", "") == nil) go func() { time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) + require.Nil(t, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) + require.Nil(t, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) } // go test -run Test_App_ListenMutualTLS_Prefork @@ -96,9 +96,9 @@ func Test_App_ListenMutualTLS_Prefork(t *testing.T) { app := New(Config{DisableStartupMessage: true, Prefork: true}) // invalid key file content - utils.AssertEqual(t, false, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.html", "") == nil) + require.False(t, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.html", "") == nil) - utils.AssertEqual(t, nil, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) + require.Nil(t, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) } // go test -run Test_App_Listener @@ -107,11 +107,11 @@ func Test_App_Listener(t *testing.T) { go func() { time.Sleep(500 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) + require.Nil(t, app.Shutdown()) }() ln := fasthttputil.NewInmemoryListener() - utils.AssertEqual(t, nil, app.Listener(ln)) + require.Nil(t, app.Listener(ln)) } // go test -run Test_App_Listener_Prefork @@ -121,28 +121,28 @@ func Test_App_Listener_Prefork(t *testing.T) { app := New(Config{DisableStartupMessage: true, Prefork: true}) ln := fasthttputil.NewInmemoryListener() - utils.AssertEqual(t, nil, app.Listener(ln)) + require.Nil(t, app.Listener(ln)) } func Test_App_Listener_TLS_Listener(t *testing.T) { // Create tls certificate cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") if err != nil { - utils.AssertEqual(t, nil, err) + require.NoError(t, err) } config := &tls.Config{Certificates: []tls.Certificate{cer}} ln, err := tls.Listen(NetworkTCP4, ":0", config) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) app := New() go func() { time.Sleep(time.Millisecond * 500) - utils.AssertEqual(t, nil, app.Shutdown()) + require.Nil(t, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.Listener(ln)) + require.Nil(t, app.Listener(ln)) } func captureOutput(f func()) string { @@ -181,11 +181,11 @@ func Test_App_Master_Process_Show_Startup_Message(t *testing.T) { startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) }) fmt.Println(startupMessage) - utils.AssertEqual(t, true, strings.Contains(startupMessage, "https://127.0.0.1:3000")) - utils.AssertEqual(t, true, strings.Contains(startupMessage, "(bound on host 0.0.0.0 and port 3000)")) - utils.AssertEqual(t, true, strings.Contains(startupMessage, "Child PIDs")) - utils.AssertEqual(t, true, strings.Contains(startupMessage, "11111, 22222, 33333, 44444, 55555, 60000")) - utils.AssertEqual(t, true, strings.Contains(startupMessage, "Prefork ........ Enabled")) + require.True(t, strings.Contains(startupMessage, "https://127.0.0.1:3000")) + require.True(t, strings.Contains(startupMessage, "(bound on host 0.0.0.0 and port 3000)")) + require.True(t, strings.Contains(startupMessage, "Child PIDs")) + require.True(t, strings.Contains(startupMessage, "11111, 22222, 33333, 44444, 55555, 60000")) + require.True(t, strings.Contains(startupMessage, "Prefork ........ Enabled")) } func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { @@ -194,8 +194,8 @@ func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { app.startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) }) fmt.Println(startupMessage) - utils.AssertEqual(t, "Test App v1.0.1", app.Config().AppName) - utils.AssertEqual(t, true, strings.Contains(startupMessage, app.Config().AppName)) + require.Equal(t, "Test App v1.0.1", app.Config().AppName) + require.True(t, strings.Contains(startupMessage, app.Config().AppName)) } func Test_App_Master_Process_Show_Startup_MessageWithAppNameNonAscii(t *testing.T) { @@ -205,7 +205,7 @@ func Test_App_Master_Process_Show_Startup_MessageWithAppNameNonAscii(t *testing. app.startupMessage(":3000", false, "") }) fmt.Println(startupMessage) - utils.AssertEqual(t, true, strings.Contains(startupMessage, "│ Serveur de vérification des données │")) + require.True(t, strings.Contains(startupMessage, "│ Serveur de vérification des données │")) } func Test_App_print_Route(t *testing.T) { @@ -215,10 +215,10 @@ func Test_App_print_Route(t *testing.T) { app.printRoutesMessage() }) fmt.Println(printRoutesMessage) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "routeName")) + require.True(t, strings.Contains(printRoutesMessage, "GET")) + require.True(t, strings.Contains(printRoutesMessage, "/")) + require.True(t, strings.Contains(printRoutesMessage, "emptyHandler")) + require.True(t, strings.Contains(printRoutesMessage, "routeName")) } func Test_App_print_Route_with_group(t *testing.T) { @@ -234,14 +234,14 @@ func Test_App_print_Route_with_group(t *testing.T) { app.printRoutesMessage() }) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "GET")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "emptyHandler")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "POST")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "PUT")) - utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber/*")) + require.True(t, strings.Contains(printRoutesMessage, "GET")) + require.True(t, strings.Contains(printRoutesMessage, "/")) + require.True(t, strings.Contains(printRoutesMessage, "emptyHandler")) + require.True(t, strings.Contains(printRoutesMessage, "/v1/test")) + require.True(t, strings.Contains(printRoutesMessage, "POST")) + require.True(t, strings.Contains(printRoutesMessage, "/v1/test/fiber")) + require.True(t, strings.Contains(printRoutesMessage, "PUT")) + require.True(t, strings.Contains(printRoutesMessage, "/v1/test/fiber/*")) } func emptyHandler(c Ctx) error { diff --git a/middleware/basicauth/basicauth_test.go b/middleware/basicauth/basicauth_test.go index 7e89d0e8ef..7656c9eb6e 100644 --- a/middleware/basicauth/basicauth_test.go +++ b/middleware/basicauth/basicauth_test.go @@ -9,7 +9,7 @@ import ( b64 "encoding/base64" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -25,8 +25,8 @@ func Test_BasicAuth_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } func Test_Middleware_BasicAuth(t *testing.T) { @@ -80,15 +80,15 @@ func Test_Middleware_BasicAuth(t *testing.T) { req := httptest.NewRequest("GET", "/testauth", nil) req.Header.Add("Authorization", "Basic "+creds) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, tt.statusCode, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, tt.statusCode, resp.StatusCode) if tt.statusCode == 200 { - utils.AssertEqual(t, fmt.Sprintf("%s%s", tt.username, tt.password), string(body)) + require.Equal(t, fmt.Sprintf("%s%s", tt.username, tt.password), string(body)) } } } @@ -120,5 +120,5 @@ func Benchmark_Middleware_BasicAuth(b *testing.B) { h(fctx) } - utils.AssertEqual(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) + require.Equal(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) } diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go index 0ab9df5a6c..2aa9eb366f 100644 --- a/middleware/cache/cache_test.go +++ b/middleware/cache/cache_test.go @@ -17,6 +17,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/internal/storage/memory" "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -35,11 +36,11 @@ func Test_Cache_CacheControl(t *testing.T) { }) _, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "public, max-age=10", resp.Header.Get(fiber.HeaderCacheControl)) + require.NoError(t, err) + require.Equal(t, "public, max-age=10", resp.Header.Get(fiber.HeaderCacheControl)) } func Test_Cache_Expired(t *testing.T) { @@ -53,17 +54,17 @@ func Test_Cache_Expired(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) // Sleep until the cache is expired time.Sleep(3 * time.Second) respCached, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) bodyCached, err := io.ReadAll(respCached.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) if bytes.Equal(body, bodyCached) { t.Errorf("Cache should have expired: %s, %s", body, bodyCached) @@ -71,9 +72,9 @@ func Test_Cache_Expired(t *testing.T) { // Next response should be also cached respCachedNextRound, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) bodyCachedNextRound, err := io.ReadAll(respCachedNextRound.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) if !bytes.Equal(bodyCachedNextRound, bodyCached) { t.Errorf("Cache should not have expired: %s, %s", bodyCached, bodyCachedNextRound) @@ -93,18 +94,18 @@ func Test_Cache(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) cachedReq := httptest.NewRequest("GET", "/", nil) cachedResp, err := app.Test(cachedReq) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) cachedBody, err := io.ReadAll(cachedResp.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) - utils.AssertEqual(t, cachedBody, body) + require.Equal(t, cachedBody, body) } func Test_Cache_WithSeveralRequests(t *testing.T) { @@ -125,18 +126,18 @@ func Test_Cache_WithSeveralRequests(t *testing.T) { for i := 0; i < 10; i++ { func(id int) { rsp, err := app.Test(httptest.NewRequest(http.MethodGet, fmt.Sprintf("/%d", id), nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) defer rsp.Body.Close() idFromServ, err := io.ReadAll(rsp.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) a, err := strconv.Atoi(string(idFromServ)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) // SomeTimes,The id is not equal with a - utils.AssertEqual(t, id, a) + require.Equal(t, id, a) }(i) } } @@ -156,18 +157,18 @@ func Test_Cache_Invalid_Expiration(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) cachedReq := httptest.NewRequest("GET", "/", nil) cachedResp, err := app.Test(cachedReq) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) cachedBody, err := io.ReadAll(cachedResp.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) - utils.AssertEqual(t, cachedBody, body) + require.Equal(t, cachedBody, body) } func Test_Cache_Invalid_Method(t *testing.T) { @@ -186,28 +187,28 @@ func Test_Cache_Invalid_Method(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("POST", "/?cache=123", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "123", string(body)) + require.NoError(t, err) + require.Equal(t, "123", string(body)) resp, err = app.Test(httptest.NewRequest("POST", "/?cache=12345", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "12345", string(body)) + require.NoError(t, err) + require.Equal(t, "12345", string(body)) resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=123", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "123", string(body)) + require.NoError(t, err) + require.Equal(t, "123", string(body)) resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=12345", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "123", string(body)) + require.NoError(t, err) + require.Equal(t, "123", string(body)) } func Test_Cache_NothingToCache(t *testing.T) { @@ -222,16 +223,16 @@ func Test_Cache_NothingToCache(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) time.Sleep(500 * time.Millisecond) respCached, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) bodyCached, err := io.ReadAll(respCached.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) if bytes.Equal(body, bodyCached) { t.Errorf("Cache should have expired: %s, %s", body, bodyCached) @@ -259,23 +260,23 @@ func Test_Cache_CustomNext(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) respCached, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) bodyCached, err := io.ReadAll(respCached.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, bytes.Equal(body, bodyCached)) - utils.AssertEqual(t, true, respCached.Header.Get(fiber.HeaderCacheControl) != "") + 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)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) errRespCached, err := app.Test(httptest.NewRequest("GET", "/error", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, errRespCached.Header.Get(fiber.HeaderCacheControl) == "") + require.NoError(t, err) + require.True(t, errRespCached.Header.Get(fiber.HeaderCacheControl) == "") } func Test_CustomKey(t *testing.T) { @@ -294,8 +295,8 @@ func Test_CustomKey(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) _, err := app.Test(req) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, called) + require.NoError(t, err) + require.True(t, called) } func Test_CustomExpiration(t *testing.T) { @@ -317,20 +318,20 @@ func Test_CustomExpiration(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, called) - utils.AssertEqual(t, 1, newCacheTime) + require.NoError(t, err) + require.True(t, called) + require.Equal(t, 1, newCacheTime) // Sleep until the cache is expired time.Sleep(1 * time.Second) cachedResp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) cachedBody, err := io.ReadAll(cachedResp.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) if bytes.Equal(body, cachedBody) { t.Errorf("Cache should have expired: %s, %s", body, cachedBody) @@ -338,9 +339,9 @@ func Test_CustomExpiration(t *testing.T) { // Next response should be cached cachedRespNextRound, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) cachedBodyNextRound, err := io.ReadAll(cachedRespNextRound.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) if !bytes.Equal(cachedBodyNextRound, cachedBody) { t.Errorf("Cache should not have expired: %s, %s", cachedBodyNextRound, cachedBody) @@ -362,13 +363,13 @@ func Test_AdditionalE2EResponseHeaders(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "foobar", resp.Header.Get("X-Foobar")) + require.NoError(t, err) + require.Equal(t, "foobar", resp.Header.Get("X-Foobar")) req = httptest.NewRequest("GET", "/", nil) resp, err = app.Test(req) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "foobar", resp.Header.Get("X-Foobar")) + require.NoError(t, err) + require.Equal(t, "foobar", resp.Header.Get("X-Foobar")) } func Test_CacheHeader(t *testing.T) { @@ -396,20 +397,20 @@ func Test_CacheHeader(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) + require.NoError(t, err) + require.Equal(t, cacheMiss, resp.Header.Get("X-Cache")) resp, err = app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, cacheHit, resp.Header.Get("X-Cache")) + require.NoError(t, err) + require.Equal(t, cacheHit, resp.Header.Get("X-Cache")) resp, err = app.Test(httptest.NewRequest("POST", "/?cache=12345", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, cacheUnreachable, resp.Header.Get("X-Cache")) + require.NoError(t, err) + require.Equal(t, cacheUnreachable, resp.Header.Get("X-Cache")) errRespCached, err := app.Test(httptest.NewRequest("GET", "/error", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, cacheUnreachable, errRespCached.Header.Get("X-Cache")) + require.NoError(t, err) + require.Equal(t, cacheUnreachable, errRespCached.Header.Get("X-Cache")) } func Test_Cache_WithHead(t *testing.T) { @@ -425,18 +426,18 @@ func Test_Cache_WithHead(t *testing.T) { req := httptest.NewRequest("HEAD", "/", nil) resp, err := app.Test(req) - utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) + require.Equal(t, cacheMiss, resp.Header.Get("X-Cache")) cachedReq := httptest.NewRequest("HEAD", "/", nil) cachedResp, err := app.Test(cachedReq) - utils.AssertEqual(t, cacheHit, cachedResp.Header.Get("X-Cache")) + require.Equal(t, cacheHit, cachedResp.Header.Get("X-Cache")) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) cachedBody, err := io.ReadAll(cachedResp.Body) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) - utils.AssertEqual(t, cachedBody, body) + require.Equal(t, cachedBody, body) } func Test_Cache_WithHeadThenGet(t *testing.T) { @@ -449,32 +450,32 @@ func Test_Cache_WithHeadThenGet(t *testing.T) { }) headResp, err := app.Test(httptest.NewRequest("HEAD", "/?cache=123", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) headBody, err := io.ReadAll(headResp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "", string(headBody)) - utils.AssertEqual(t, cacheMiss, headResp.Header.Get("X-Cache")) + 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)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) headBody, err = io.ReadAll(headResp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "", string(headBody)) - utils.AssertEqual(t, cacheHit, headResp.Header.Get("X-Cache")) + 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)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) getBody, err := io.ReadAll(getResp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "123", string(getBody)) - utils.AssertEqual(t, cacheMiss, getResp.Header.Get("X-Cache")) + 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)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) getBody, err = io.ReadAll(getResp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "123", string(getBody)) - utils.AssertEqual(t, cacheHit, getResp.Header.Get("X-Cache")) + require.NoError(t, err) + require.Equal(t, "123", string(getBody)) + require.Equal(t, cacheHit, getResp.Header.Get("X-Cache")) } func Test_CustomCacheHeader(t *testing.T) { @@ -491,8 +492,8 @@ func Test_CustomCacheHeader(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, cacheMiss, resp.Header.Get("Cache-Status")) + require.NoError(t, err) + require.Equal(t, cacheMiss, resp.Header.Get("Cache-Status")) } // Because time points are updated once every X milliseconds, entries in tests can often have @@ -538,8 +539,8 @@ func Test_Cache_MaxBytesOrder(t *testing.T) { for idx, tcase := range cases { rsp, err := app.Test(httptest.NewRequest("GET", tcase[0], nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, tcase[1], rsp.Header.Get("X-Cache"), fmt.Sprintf("Case %v", idx)) + require.NoError(t, err) + require.Equal(t, tcase[1], rsp.Header.Get("X-Cache"), fmt.Sprintf("Case %v", idx)) } } @@ -572,8 +573,8 @@ func Test_Cache_MaxBytesSizes(t *testing.T) { for idx, tcase := range cases { rsp, err := app.Test(httptest.NewRequest("GET", tcase[0], nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, tcase[1], rsp.Header.Get("X-Cache"), fmt.Sprintf("Case %v", idx)) + require.NoError(t, err) + require.Equal(t, tcase[1], rsp.Header.Get("X-Cache"), fmt.Sprintf("Case %v", idx)) } } @@ -601,8 +602,8 @@ func Benchmark_Cache(b *testing.B) { h(fctx) } - utils.AssertEqual(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) - utils.AssertEqual(b, true, len(fctx.Response.Body()) > 30000) + require.Equal(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) + require.True(b, len(fctx.Response.Body()) > 30000) } // go test -v -run=^$ -bench=Benchmark_Cache_Storage -benchmem -count=4 @@ -631,8 +632,8 @@ func Benchmark_Cache_Storage(b *testing.B) { h(fctx) } - utils.AssertEqual(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) - utils.AssertEqual(b, true, len(fctx.Response.Body()) > 30000) + require.Equal(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) + require.True(b, len(fctx.Response.Body()) > 30000) } func Benchmark_Cache_AdditionalHeaders(b *testing.B) { @@ -659,8 +660,8 @@ func Benchmark_Cache_AdditionalHeaders(b *testing.B) { h(fctx) } - utils.AssertEqual(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) - utils.AssertEqual(b, []byte("foobar"), fctx.Response.Header.Peek("X-Foobar")) + require.Equal(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) + require.Equal(b, []byte("foobar"), fctx.Response.Header.Peek("X-Foobar")) } func Benchmark_Cache_MaxSize(b *testing.B) { @@ -691,7 +692,7 @@ func Benchmark_Cache_MaxSize(b *testing.B) { h(fctx) } - utils.AssertEqual(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) + require.Equal(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) }) } } diff --git a/middleware/compress/compress_test.go b/middleware/compress/compress_test.go index 697c49218d..4609eafc88 100644 --- a/middleware/compress/compress_test.go +++ b/middleware/compress/compress_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) var filedata []byte @@ -37,14 +37,14 @@ func Test_Compress_Gzip(t *testing.T) { req.Header.Set("Accept-Encoding", "gzip") resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, "gzip", resp.Header.Get(fiber.HeaderContentEncoding)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.Equal(t, "gzip", resp.Header.Get(fiber.HeaderContentEncoding)) // Validate that the file size has shrunk body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, len(body) < len(filedata)) + require.NoError(t, err) + require.True(t, len(body) < len(filedata)) } // go test -run Test_Compress_Different_Level @@ -65,14 +65,14 @@ func Test_Compress_Different_Level(t *testing.T) { req.Header.Set("Accept-Encoding", "gzip") resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, "gzip", resp.Header.Get(fiber.HeaderContentEncoding)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.Equal(t, "gzip", resp.Header.Get(fiber.HeaderContentEncoding)) // Validate that the file size has shrunk body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, len(body) < len(filedata)) + require.NoError(t, err) + require.True(t, len(body) < len(filedata)) }) } } @@ -90,14 +90,14 @@ func Test_Compress_Deflate(t *testing.T) { req.Header.Set("Accept-Encoding", "deflate") resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, "deflate", resp.Header.Get(fiber.HeaderContentEncoding)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.Equal(t, "deflate", resp.Header.Get(fiber.HeaderContentEncoding)) // Validate that the file size has shrunk body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, len(body) < len(filedata)) + require.NoError(t, err) + require.True(t, len(body) < len(filedata)) } func Test_Compress_Brotli(t *testing.T) { @@ -113,14 +113,14 @@ func Test_Compress_Brotli(t *testing.T) { req.Header.Set("Accept-Encoding", "br") resp, err := app.Test(req, 10000) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, "br", resp.Header.Get(fiber.HeaderContentEncoding)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.Equal(t, "br", resp.Header.Get(fiber.HeaderContentEncoding)) // Validate that the file size has shrunk body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, len(body) < len(filedata)) + require.NoError(t, err) + require.True(t, len(body) < len(filedata)) } func Test_Compress_Disabled(t *testing.T) { @@ -136,14 +136,14 @@ func Test_Compress_Disabled(t *testing.T) { req.Header.Set("Accept-Encoding", "br") resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderContentEncoding)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.Equal(t, "", resp.Header.Get(fiber.HeaderContentEncoding)) // Validate the file size is not shrunk body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, len(body) == len(filedata)) + require.NoError(t, err) + require.True(t, len(body) == len(filedata)) } func Test_Compress_Next_Error(t *testing.T) { @@ -159,13 +159,13 @@ func Test_Compress_Next_Error(t *testing.T) { req.Header.Set("Accept-Encoding", "gzip") resp, err := app.Test(req) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 500, resp.StatusCode, "Status code") - utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderContentEncoding)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 500, resp.StatusCode, "Status code") + require.Equal(t, "", resp.Header.Get(fiber.HeaderContentEncoding)) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "next error", string(body)) + require.NoError(t, err) + require.Equal(t, "next error", string(body)) } // go test -run Test_Compress_Next @@ -178,6 +178,6 @@ func Test_Compress_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } diff --git a/middleware/cors/cors_test.go b/middleware/cors/cors_test.go index 64b019006e..84e7bd9b73 100644 --- a/middleware/cors/cors_test.go +++ b/middleware/cors/cors_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -33,18 +33,18 @@ func testDefaultOrEmptyConfig(t *testing.T, app *fiber.App) { ctx.Request.Header.SetMethod(fiber.MethodGet) h(ctx) - utils.AssertEqual(t, "*", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) - utils.AssertEqual(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowCredentials))) - utils.AssertEqual(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlExposeHeaders))) + require.Equal(t, "*", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + require.Equal(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowCredentials))) + require.Equal(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlExposeHeaders))) // Test default OPTIONS (preflight) response headers ctx = &fasthttp.RequestCtx{} ctx.Request.Header.SetMethod(fiber.MethodOptions) h(ctx) - utils.AssertEqual(t, "GET,POST,HEAD,PUT,DELETE,PATCH", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowMethods))) - utils.AssertEqual(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowHeaders))) - utils.AssertEqual(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlMaxAge))) + require.Equal(t, "GET,POST,HEAD,PUT,DELETE,PATCH", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowMethods))) + require.Equal(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowHeaders))) + require.Equal(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlMaxAge))) } // go test -run -v Test_CORS_Wildcard @@ -72,18 +72,18 @@ func Test_CORS_Wildcard(t *testing.T) { handler(ctx) // Check result - utils.AssertEqual(t, "localhost", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) - utils.AssertEqual(t, "true", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowCredentials))) - utils.AssertEqual(t, "3600", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlMaxAge))) - utils.AssertEqual(t, "Authentication", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowHeaders))) + require.Equal(t, "localhost", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + require.Equal(t, "true", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowCredentials))) + require.Equal(t, "3600", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlMaxAge))) + require.Equal(t, "Authentication", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowHeaders))) // Test non OPTIONS (preflight) response headers ctx = &fasthttp.RequestCtx{} ctx.Request.Header.SetMethod(fiber.MethodGet) handler(ctx) - utils.AssertEqual(t, "true", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowCredentials))) - utils.AssertEqual(t, "X-Request-ID", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlExposeHeaders))) + require.Equal(t, "true", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowCredentials))) + require.Equal(t, "X-Request-ID", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlExposeHeaders))) } // go test -run -v Test_CORS_Subdomain @@ -106,7 +106,7 @@ func Test_CORS_Subdomain(t *testing.T) { handler(ctx) // Allow-Origin header should be "" because http://google.com does not satisfy http://*.example.com - utils.AssertEqual(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + require.Equal(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) ctx.Request.Reset() ctx.Response.Reset() @@ -118,7 +118,7 @@ func Test_CORS_Subdomain(t *testing.T) { handler(ctx) - utils.AssertEqual(t, "http://test.example.com", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + require.Equal(t, "http://test.example.com", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) } func Test_CORS_AllowOriginScheme(t *testing.T) { @@ -215,9 +215,9 @@ func Test_CORS_AllowOriginScheme(t *testing.T) { handler(ctx) if tt.shouldAllowOrigin { - utils.AssertEqual(t, tt.reqOrigin, string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + require.Equal(t, tt.reqOrigin, string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) } else { - utils.AssertEqual(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) + require.Equal(t, "", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlAllowOrigin))) } } } @@ -232,6 +232,6 @@ func Test_CORS_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } diff --git a/middleware/csrf/csrf_test.go b/middleware/csrf/csrf_test.go index e95fa7aac3..b83b7fe379 100644 --- a/middleware/csrf/csrf_test.go +++ b/middleware/csrf/csrf_test.go @@ -7,6 +7,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -34,7 +35,7 @@ func Test_CSRF(t *testing.T) { ctx.Response.Reset() ctx.Request.Header.SetMethod("POST") h(ctx) - utils.AssertEqual(t, 403, ctx.Response.StatusCode()) + require.Equal(t, 403, ctx.Response.StatusCode()) // Empty/invalid CSRF token ctx.Request.Reset() @@ -42,7 +43,7 @@ func Test_CSRF(t *testing.T) { ctx.Request.Header.SetMethod("POST") ctx.Request.Header.Set("X-CSRF-Token", "johndoe") h(ctx) - utils.AssertEqual(t, 403, ctx.Response.StatusCode()) + require.Equal(t, 403, ctx.Response.StatusCode()) // Valid CSRF token ctx.Request.Reset() @@ -57,7 +58,7 @@ func Test_CSRF(t *testing.T) { ctx.Request.Header.SetMethod("POST") ctx.Request.Header.Set("X-CSRF-Token", token) h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) + require.Equal(t, 200, ctx.Response.StatusCode()) } } @@ -71,13 +72,13 @@ func Test_CSRF_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } func Test_CSRF_Invalid_KeyLookup(t *testing.T) { defer func() { - utils.AssertEqual(t, "[CSRF] KeyLookup must in the form of :", recover()) + require.Equal(t, "[CSRF] KeyLookup must in the form of :", recover()) }() app := fiber.New() @@ -109,7 +110,7 @@ func Test_CSRF_From_Form(t *testing.T) { ctx.Request.Header.SetMethod("POST") ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationForm) h(ctx) - utils.AssertEqual(t, 403, ctx.Response.StatusCode()) + require.Equal(t, 403, ctx.Response.StatusCode()) // Generate CSRF token ctx.Request.Reset() @@ -123,7 +124,7 @@ func Test_CSRF_From_Form(t *testing.T) { ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationForm) ctx.Request.SetBodyString("_csrf=" + token) h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) + require.Equal(t, 200, ctx.Response.StatusCode()) } func Test_CSRF_From_Query(t *testing.T) { @@ -142,7 +143,7 @@ func Test_CSRF_From_Query(t *testing.T) { ctx.Request.Header.SetMethod("POST") ctx.Request.SetRequestURI("/?_csrf=" + utils.UUID()) h(ctx) - utils.AssertEqual(t, 403, ctx.Response.StatusCode()) + require.Equal(t, 403, ctx.Response.StatusCode()) // Generate CSRF token ctx.Request.Reset() @@ -158,8 +159,8 @@ func Test_CSRF_From_Query(t *testing.T) { ctx.Request.SetRequestURI("/?_csrf=" + token) ctx.Request.Header.SetMethod("POST") h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) - utils.AssertEqual(t, "OK", string(ctx.Response.Body())) + require.Equal(t, 200, ctx.Response.StatusCode()) + require.Equal(t, "OK", string(ctx.Response.Body())) } func Test_CSRF_From_Param(t *testing.T) { @@ -178,7 +179,7 @@ func Test_CSRF_From_Param(t *testing.T) { ctx.Request.Header.SetMethod("POST") ctx.Request.SetRequestURI("/" + utils.UUID()) h(ctx) - utils.AssertEqual(t, 403, ctx.Response.StatusCode()) + require.Equal(t, 403, ctx.Response.StatusCode()) // Generate CSRF token ctx.Request.Reset() @@ -194,8 +195,8 @@ func Test_CSRF_From_Param(t *testing.T) { ctx.Request.SetRequestURI("/" + token) ctx.Request.Header.SetMethod("POST") h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) - utils.AssertEqual(t, "OK", string(ctx.Response.Body())) + require.Equal(t, 200, ctx.Response.StatusCode()) + require.Equal(t, "OK", string(ctx.Response.Body())) } func Test_CSRF_From_Cookie(t *testing.T) { @@ -215,7 +216,7 @@ func Test_CSRF_From_Cookie(t *testing.T) { ctx.Request.SetRequestURI("/") ctx.Request.Header.Set(fiber.HeaderCookie, "csrf="+utils.UUID()+";") h(ctx) - utils.AssertEqual(t, 403, ctx.Response.StatusCode()) + require.Equal(t, 403, ctx.Response.StatusCode()) // Generate CSRF token ctx.Request.Reset() @@ -232,15 +233,15 @@ func Test_CSRF_From_Cookie(t *testing.T) { ctx.Request.Header.Set(fiber.HeaderCookie, "csrf="+token+";") ctx.Request.SetRequestURI("/") h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) - utils.AssertEqual(t, "OK", string(ctx.Response.Body())) + require.Equal(t, 200, ctx.Response.StatusCode()) + require.Equal(t, "OK", string(ctx.Response.Body())) } func Test_CSRF_ErrorHandler_InvalidToken(t *testing.T) { app := fiber.New() errHandler := func(ctx fiber.Ctx, err error) error { - utils.AssertEqual(t, errTokenNotFound, err) + require.Equal(t, errTokenNotFound, err) return ctx.Status(419).Send([]byte("invalid CSRF token")) } @@ -263,15 +264,15 @@ func Test_CSRF_ErrorHandler_InvalidToken(t *testing.T) { ctx.Request.Header.SetMethod("POST") ctx.Request.Header.Set("X-CSRF-Token", "johndoe") h(ctx) - utils.AssertEqual(t, 419, ctx.Response.StatusCode()) - utils.AssertEqual(t, "invalid CSRF token", string(ctx.Response.Body())) + require.Equal(t, 419, ctx.Response.StatusCode()) + require.Equal(t, "invalid CSRF token", string(ctx.Response.Body())) } func Test_CSRF_ErrorHandler_EmptyToken(t *testing.T) { app := fiber.New() errHandler := func(ctx fiber.Ctx, err error) error { - utils.AssertEqual(t, errMissingHeader, err) + require.Equal(t, errMissingHeader, err) return ctx.Status(419).Send([]byte("empty CSRF token")) } @@ -293,6 +294,6 @@ func Test_CSRF_ErrorHandler_EmptyToken(t *testing.T) { ctx.Response.Reset() ctx.Request.Header.SetMethod("POST") h(ctx) - utils.AssertEqual(t, 419, ctx.Response.StatusCode()) - utils.AssertEqual(t, "empty CSRF token", string(ctx.Response.Body())) + require.Equal(t, 419, ctx.Response.StatusCode()) + require.Equal(t, "empty CSRF token", string(ctx.Response.Body())) } diff --git a/middleware/encryptcookie/encryptcookie_test.go b/middleware/encryptcookie/encryptcookie_test.go index 2695781f8a..c4d400e94c 100644 --- a/middleware/encryptcookie/encryptcookie_test.go +++ b/middleware/encryptcookie/encryptcookie_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -36,39 +36,39 @@ func Test_Middleware_Encrypt_Cookie(t *testing.T) { ctx := &fasthttp.RequestCtx{} ctx.Request.Header.SetMethod("GET") h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) - utils.AssertEqual(t, "value=", string(ctx.Response.Body())) + require.Equal(t, 200, ctx.Response.StatusCode()) + require.Equal(t, "value=", string(ctx.Response.Body())) // Test invalid cookie ctx = &fasthttp.RequestCtx{} ctx.Request.Header.SetMethod("GET") ctx.Request.Header.SetCookie("test", "Invalid") h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) - utils.AssertEqual(t, "value=", string(ctx.Response.Body())) + require.Equal(t, 200, ctx.Response.StatusCode()) + require.Equal(t, "value=", string(ctx.Response.Body())) ctx.Request.Header.SetCookie("test", "ixQURE2XOyZUs0WAOh2ehjWcP7oZb07JvnhWOsmeNUhPsj4+RyI=") h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) - utils.AssertEqual(t, "value=", string(ctx.Response.Body())) + require.Equal(t, 200, ctx.Response.StatusCode()) + require.Equal(t, "value=", string(ctx.Response.Body())) // Test valid cookie ctx = &fasthttp.RequestCtx{} ctx.Request.Header.SetMethod("POST") h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) + require.Equal(t, 200, ctx.Response.StatusCode()) encryptedCookie := fasthttp.Cookie{} encryptedCookie.SetKey("test") - utils.AssertEqual(t, true, ctx.Response.Header.Cookie(&encryptedCookie), "Get cookie value") + require.True(t, ctx.Response.Header.Cookie(&encryptedCookie), "Get cookie value") decryptedCookieValue, _ := DecryptCookie(string(encryptedCookie.Value()), testKey) - utils.AssertEqual(t, "SomeThing", decryptedCookieValue) + require.Equal(t, "SomeThing", decryptedCookieValue) ctx = &fasthttp.RequestCtx{} ctx.Request.Header.SetMethod("GET") ctx.Request.Header.SetCookie("test", string(encryptedCookie.Value())) h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) - utils.AssertEqual(t, "value=SomeThing", string(ctx.Response.Body())) + require.Equal(t, 200, ctx.Response.StatusCode()) + require.Equal(t, "value=SomeThing", string(ctx.Response.Body())) } func Test_Encrypt_Cookie_Next(t *testing.T) { @@ -90,8 +90,8 @@ func Test_Encrypt_Cookie_Next(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "SomeThing", resp.Cookies()[0].Value) + require.NoError(t, err) + require.Equal(t, "SomeThing", resp.Cookies()[0].Value) } func Test_Encrypt_Cookie_Except(t *testing.T) { @@ -122,18 +122,18 @@ func Test_Encrypt_Cookie_Except(t *testing.T) { ctx := &fasthttp.RequestCtx{} ctx.Request.Header.SetMethod("GET") h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) + require.Equal(t, 200, ctx.Response.StatusCode()) rawCookie := fasthttp.Cookie{} rawCookie.SetKey("test1") - utils.AssertEqual(t, true, ctx.Response.Header.Cookie(&rawCookie), "Get cookie value") - utils.AssertEqual(t, "SomeThing", string(rawCookie.Value())) + require.True(t, ctx.Response.Header.Cookie(&rawCookie), "Get cookie value") + require.Equal(t, "SomeThing", string(rawCookie.Value())) encryptedCookie := fasthttp.Cookie{} encryptedCookie.SetKey("test2") - utils.AssertEqual(t, true, ctx.Response.Header.Cookie(&encryptedCookie), "Get cookie value") + require.True(t, ctx.Response.Header.Cookie(&encryptedCookie), "Get cookie value") decryptedCookieValue, _ := DecryptCookie(string(encryptedCookie.Value()), testKey) - utils.AssertEqual(t, "SomeThing", decryptedCookieValue) + require.Equal(t, "SomeThing", decryptedCookieValue) } func Test_Encrypt_Cookie_Custom_Encryptor(t *testing.T) { @@ -167,18 +167,18 @@ func Test_Encrypt_Cookie_Custom_Encryptor(t *testing.T) { ctx := &fasthttp.RequestCtx{} ctx.Request.Header.SetMethod("POST") h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) + require.Equal(t, 200, ctx.Response.StatusCode()) encryptedCookie := fasthttp.Cookie{} encryptedCookie.SetKey("test") - utils.AssertEqual(t, true, ctx.Response.Header.Cookie(&encryptedCookie), "Get cookie value") + require.True(t, ctx.Response.Header.Cookie(&encryptedCookie), "Get cookie value") decodedBytes, _ := base64.StdEncoding.DecodeString(string(encryptedCookie.Value())) - utils.AssertEqual(t, "SomeThing", string(decodedBytes)) + require.Equal(t, "SomeThing", string(decodedBytes)) ctx = &fasthttp.RequestCtx{} ctx.Request.Header.SetMethod("GET") ctx.Request.Header.SetCookie("test", string(encryptedCookie.Value())) h(ctx) - utils.AssertEqual(t, 200, ctx.Response.StatusCode()) - utils.AssertEqual(t, "value=SomeThing", string(ctx.Response.Body())) + require.Equal(t, 200, ctx.Response.StatusCode()) + require.Equal(t, "value=SomeThing", string(ctx.Response.Body())) } diff --git a/middleware/etag/etag_test.go b/middleware/etag/etag_test.go index 44b834bc4b..4768eb5793 100644 --- a/middleware/etag/etag_test.go +++ b/middleware/etag/etag_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -21,8 +21,8 @@ func Test_ETag_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } // go test -run Test_ETag_SkipError @@ -36,8 +36,8 @@ func Test_ETag_SkipError(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusForbidden, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusForbidden, resp.StatusCode) } // go test -run Test_ETag_NotStatusOK @@ -51,8 +51,8 @@ func Test_ETag_NotStatusOK(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusCreated, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusCreated, resp.StatusCode) } // go test -run Test_ETag_NoBody @@ -66,8 +66,8 @@ func Test_ETag_NoBody(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) } // go test -run Test_ETag_NewEtag @@ -104,19 +104,19 @@ func testETagNewEtag(t *testing.T, headerIfNoneMatch, matched bool) { } resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) if !headerIfNoneMatch || !matched { - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - utils.AssertEqual(t, `"13-1831710635"`, resp.Header.Get(fiber.HeaderETag)) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + require.Equal(t, `"13-1831710635"`, resp.Header.Get(fiber.HeaderETag)) return } if matched { - utils.AssertEqual(t, fiber.StatusNotModified, resp.StatusCode) + require.Equal(t, fiber.StatusNotModified, resp.StatusCode) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 0, len(b)) + require.NoError(t, err) + require.Equal(t, 0, len(b)) } } @@ -154,19 +154,19 @@ func testETagWeakEtag(t *testing.T, headerIfNoneMatch, matched bool) { } resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) if !headerIfNoneMatch || !matched { - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - utils.AssertEqual(t, `W/"13-1831710635"`, resp.Header.Get(fiber.HeaderETag)) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + require.Equal(t, `W/"13-1831710635"`, resp.Header.Get(fiber.HeaderETag)) return } if matched { - utils.AssertEqual(t, fiber.StatusNotModified, resp.StatusCode) + require.Equal(t, fiber.StatusNotModified, resp.StatusCode) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 0, len(b)) + require.NoError(t, err) + require.Equal(t, 0, len(b)) } } @@ -208,19 +208,19 @@ func testETagCustomEtag(t *testing.T, headerIfNoneMatch, matched bool) { } resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) if !headerIfNoneMatch || !matched { - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - utils.AssertEqual(t, `"custom"`, resp.Header.Get(fiber.HeaderETag)) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + require.Equal(t, `"custom"`, resp.Header.Get(fiber.HeaderETag)) return } if matched { - utils.AssertEqual(t, fiber.StatusNotModified, resp.StatusCode) + require.Equal(t, fiber.StatusNotModified, resp.StatusCode) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 0, len(b)) + require.NoError(t, err) + require.Equal(t, 0, len(b)) } } @@ -241,8 +241,8 @@ func Test_ETag_CustomEtagPut(t *testing.T) { req := httptest.NewRequest("PUT", "/", nil) req.Header.Set(fiber.HeaderIfMatch, `"non-match"`) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusPreconditionFailed, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusPreconditionFailed, resp.StatusCode) } // go test -v -run=^$ -bench=Benchmark_Etag -benchmem -count=4 @@ -268,6 +268,6 @@ func Benchmark_Etag(b *testing.B) { h(fctx) } - utils.AssertEqual(b, 200, fctx.Response.Header.StatusCode()) - utils.AssertEqual(b, `"13-1831710635"`, string(fctx.Response.Header.Peek(fiber.HeaderETag))) + require.Equal(b, 200, fctx.Response.Header.StatusCode()) + require.Equal(b, `"13-1831710635"`, string(fctx.Response.Header.Peek(fiber.HeaderETag))) } diff --git a/middleware/expvar/expvar_test.go b/middleware/expvar/expvar_test.go index 2bbc56e4f4..98e1a48560 100644 --- a/middleware/expvar/expvar_test.go +++ b/middleware/expvar/expvar_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) func Test_Non_Expvar_Path(t *testing.T) { @@ -20,12 +20,12 @@ func Test_Non_Expvar_Path(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "escaped", string(b)) + require.NoError(t, err) + require.Equal(t, "escaped", string(b)) } func Test_Expvar_Index(t *testing.T) { @@ -38,14 +38,14 @@ func Test_Expvar_Index(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/debug/vars", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) - utils.AssertEqual(t, fiber.MIMEApplicationJSONCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) + require.Equal(t, fiber.MIMEApplicationJSONCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, bytes.Contains(b, []byte("cmdline"))) - utils.AssertEqual(t, true, bytes.Contains(b, []byte("memstat"))) + require.NoError(t, err) + require.True(t, bytes.Contains(b, []byte("cmdline"))) + require.True(t, bytes.Contains(b, []byte("memstat"))) } func Test_Expvar_Filter(t *testing.T) { @@ -58,14 +58,14 @@ func Test_Expvar_Filter(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/debug/vars?r=cmd", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) - utils.AssertEqual(t, fiber.MIMEApplicationJSONCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) + require.Equal(t, fiber.MIMEApplicationJSONCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, bytes.Contains(b, []byte("cmdline"))) - utils.AssertEqual(t, false, bytes.Contains(b, []byte("memstat"))) + require.NoError(t, err) + require.True(t, bytes.Contains(b, []byte("cmdline"))) + require.False(t, bytes.Contains(b, []byte("memstat"))) } func Test_Expvar_Other_Path(t *testing.T) { @@ -78,8 +78,8 @@ func Test_Expvar_Other_Path(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/debug/vars/302", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 302, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 302, resp.StatusCode) } // go test -run Test_Expvar_Next @@ -95,6 +95,6 @@ func Test_Expvar_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/debug/vars", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 404, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 404, resp.StatusCode) } diff --git a/middleware/favicon/favicon_test.go b/middleware/favicon/favicon_test.go index 9d9e1984ce..3db323cd7e 100644 --- a/middleware/favicon/favicon_test.go +++ b/middleware/favicon/favicon_test.go @@ -5,10 +5,10 @@ import ( "os" "testing" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" ) // go test -run Test_Middleware_Favicon @@ -23,21 +23,21 @@ func Test_Middleware_Favicon(t *testing.T) { // Skip Favicon middleware resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, fiber.StatusOK, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest("GET", "/favicon.ico", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, fiber.StatusNoContent, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, fiber.StatusNoContent, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest("OPTIONS", "/favicon.ico", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, fiber.StatusOK, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest("PUT", "/favicon.ico", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, fiber.StatusMethodNotAllowed, resp.StatusCode, "Status code") - utils.AssertEqual(t, "GET, HEAD, OPTIONS", resp.Header.Get(fiber.HeaderAllow)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, fiber.StatusMethodNotAllowed, resp.StatusCode, "Status code") + require.Equal(t, "GET, HEAD, OPTIONS", resp.Header.Get(fiber.HeaderAllow)) } // go test -run Test_Middleware_Favicon_Not_Found @@ -67,10 +67,10 @@ func Test_Middleware_Favicon_Found(t *testing.T) { resp, err := app.Test(httptest.NewRequest("GET", "/favicon.ico", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") - utils.AssertEqual(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) - utils.AssertEqual(t, "public, max-age=31536000", resp.Header.Get(fiber.HeaderCacheControl), "CacheControl Control") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, fiber.StatusOK, resp.StatusCode, "Status code") + require.Equal(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) + require.Equal(t, "public, max-age=31536000", resp.Header.Get(fiber.HeaderCacheControl), "CacheControl Control") } // go test -run Test_Middleware_Favicon_FileSystem @@ -83,10 +83,10 @@ func Test_Middleware_Favicon_FileSystem(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/favicon.ico", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") - utils.AssertEqual(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) - utils.AssertEqual(t, "public, max-age=31536000", resp.Header.Get(fiber.HeaderCacheControl), "CacheControl Control") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, fiber.StatusOK, resp.StatusCode, "Status code") + require.Equal(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) + require.Equal(t, "public, max-age=31536000", resp.Header.Get(fiber.HeaderCacheControl), "CacheControl Control") } // go test -run Test_Middleware_Favicon_CacheControl @@ -99,10 +99,10 @@ func Test_Middleware_Favicon_CacheControl(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/favicon.ico", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") - utils.AssertEqual(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) - utils.AssertEqual(t, "public, max-age=100", resp.Header.Get(fiber.HeaderCacheControl), "CacheControl Control") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, fiber.StatusOK, resp.StatusCode, "Status code") + require.Equal(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) + require.Equal(t, "public, max-age=100", resp.Header.Get(fiber.HeaderCacheControl), "CacheControl Control") } // go test -v -run=^$ -bench=Benchmark_Middleware_Favicon -benchmem -count=4 @@ -134,6 +134,6 @@ func Test_Favicon_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } diff --git a/middleware/filesystem/filesystem_test.go b/middleware/filesystem/filesystem_test.go index 6cae378734..215a9e6620 100644 --- a/middleware/filesystem/filesystem_test.go +++ b/middleware/filesystem/filesystem_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) // go test -run Test_FileSystem @@ -118,12 +118,12 @@ func Test_FileSystem(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { resp, err := app.Test(httptest.NewRequest("GET", tt.url, nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, tt.statusCode, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, tt.statusCode, resp.StatusCode) if tt.contentType != "" { ct := resp.Header.Get("Content-Type") - utils.AssertEqual(t, tt.contentType, ct) + require.Equal(t, tt.contentType, ct) } }) } @@ -140,8 +140,8 @@ func Test_FileSystem_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } func Test_FileSystem_NonGetAndHead(t *testing.T) { @@ -152,8 +152,8 @@ func Test_FileSystem_NonGetAndHead(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest(fiber.MethodPost, "/test", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 404, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 404, resp.StatusCode) } func Test_FileSystem_Head(t *testing.T) { @@ -165,13 +165,13 @@ func Test_FileSystem_Head(t *testing.T) { req, _ := http.NewRequest(fiber.MethodHead, "/test", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) } func Test_FileSystem_NoRoot(t *testing.T) { defer func() { - utils.AssertEqual(t, "filesystem: Root cannot be nil", recover()) + require.Equal(t, "filesystem: Root cannot be nil", recover()) }() app := fiber.New() @@ -188,8 +188,8 @@ func Test_FileSystem_UsingParam(t *testing.T) { req, _ := http.NewRequest(fiber.MethodHead, "/index", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) } func Test_FileSystem_UsingParam_NonFile(t *testing.T) { @@ -201,6 +201,6 @@ func Test_FileSystem_UsingParam_NonFile(t *testing.T) { req, _ := http.NewRequest(fiber.MethodHead, "/template", nil) resp, err := app.Test(req) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 404, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 404, resp.StatusCode) } diff --git a/middleware/helmet/helmet_test.go b/middleware/helmet/helmet_test.go index 56df76f6f6..0b13b5a0d9 100644 --- a/middleware/helmet/helmet_test.go +++ b/middleware/helmet/helmet_test.go @@ -9,7 +9,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) func Test_Default(t *testing.T) { @@ -22,13 +22,13 @@ func Test_Default(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "1; mode=block", resp.Header.Get(fiber.HeaderXXSSProtection)) - utils.AssertEqual(t, "nosniff", resp.Header.Get(fiber.HeaderXContentTypeOptions)) - utils.AssertEqual(t, "SAMEORIGIN", resp.Header.Get(fiber.HeaderXFrameOptions)) - utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderContentSecurityPolicy)) - utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderReferrerPolicy)) - utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderPermissionsPolicy)) + require.NoError(t, err) + require.Equal(t, "1; mode=block", resp.Header.Get(fiber.HeaderXXSSProtection)) + require.Equal(t, "nosniff", resp.Header.Get(fiber.HeaderXContentTypeOptions)) + require.Equal(t, "SAMEORIGIN", resp.Header.Get(fiber.HeaderXFrameOptions)) + require.Equal(t, "", resp.Header.Get(fiber.HeaderContentSecurityPolicy)) + require.Equal(t, "", resp.Header.Get(fiber.HeaderReferrerPolicy)) + require.Equal(t, "", resp.Header.Get(fiber.HeaderPermissionsPolicy)) } func Test_Filter(t *testing.T) { @@ -49,12 +49,12 @@ func Test_Filter(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "no-referrer", resp.Header.Get(fiber.HeaderReferrerPolicy)) + require.NoError(t, err) + require.Equal(t, "no-referrer", resp.Header.Get(fiber.HeaderReferrerPolicy)) resp, err = app.Test(httptest.NewRequest("GET", "/filter", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderReferrerPolicy)) + require.NoError(t, err) + require.Equal(t, "", resp.Header.Get(fiber.HeaderReferrerPolicy)) } func Test_ContentSecurityPolicy(t *testing.T) { @@ -69,8 +69,8 @@ func Test_ContentSecurityPolicy(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "default-src 'none'", resp.Header.Get(fiber.HeaderContentSecurityPolicy)) + require.NoError(t, err) + require.Equal(t, "default-src 'none'", resp.Header.Get(fiber.HeaderContentSecurityPolicy)) } func Test_ContentSecurityPolicyReportOnly(t *testing.T) { @@ -86,9 +86,9 @@ func Test_ContentSecurityPolicyReportOnly(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "default-src 'none'", resp.Header.Get(fiber.HeaderContentSecurityPolicyReportOnly)) - utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderContentSecurityPolicy)) + require.NoError(t, err) + require.Equal(t, "default-src 'none'", resp.Header.Get(fiber.HeaderContentSecurityPolicyReportOnly)) + require.Equal(t, "", resp.Header.Get(fiber.HeaderContentSecurityPolicy)) } func Test_PermissionsPolicy(t *testing.T) { @@ -103,6 +103,6 @@ func Test_PermissionsPolicy(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "microphone=()", resp.Header.Get(fiber.HeaderPermissionsPolicy)) + require.NoError(t, err) + require.Equal(t, "microphone=()", resp.Header.Get(fiber.HeaderPermissionsPolicy)) } diff --git a/middleware/limiter/limiter_test.go b/middleware/limiter/limiter_test.go index 02ddd2bdd9..9cae095c11 100644 --- a/middleware/limiter/limiter_test.go +++ b/middleware/limiter/limiter_test.go @@ -10,7 +10,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/internal/storage/memory" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -34,12 +34,12 @@ func Test_Limiter_Concurrency_Store(t *testing.T) { singleRequest := func(wg *sync.WaitGroup) { defer wg.Done() resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "Hello tester!", string(body)) + require.NoError(t, err) + require.Equal(t, "Hello tester!", string(body)) } for i := 0; i <= 49; i++ { @@ -50,14 +50,14 @@ func Test_Limiter_Concurrency_Store(t *testing.T) { wg.Wait() resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 429, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 429, resp.StatusCode) time.Sleep(3 * time.Second) resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) } // go test -run Test_Limiter_Concurrency -race -v @@ -79,12 +79,12 @@ func Test_Limiter_Concurrency(t *testing.T) { singleRequest := func(wg *sync.WaitGroup) { defer wg.Done() resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "Hello tester!", string(body)) + require.NoError(t, err) + require.Equal(t, "Hello tester!", string(body)) } for i := 0; i <= 49; i++ { @@ -95,14 +95,14 @@ func Test_Limiter_Concurrency(t *testing.T) { wg.Wait() resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 429, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 429, resp.StatusCode) time.Sleep(3 * time.Second) resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) } // go test -run Test_Limiter_No_Skip_Choices -v @@ -124,16 +124,16 @@ func Test_Limiter_No_Skip_Choices(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 400, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 400, resp.StatusCode) resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 429, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 429, resp.StatusCode) } // go test -run Test_Limiter_Skip_Failed_Requests -v @@ -154,22 +154,22 @@ func Test_Limiter_Skip_Failed_Requests(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 400, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 400, resp.StatusCode) resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/success", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 429, resp.StatusCode) + 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)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) } // go test -run Test_Limiter_Skip_Successful_Requests -v @@ -192,22 +192,22 @@ func Test_Limiter_Skip_Successful_Requests(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/success", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 400, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 400, resp.StatusCode) resp, err = app.Test(httptest.NewRequest(http.MethodGet, "/fail", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 429, resp.StatusCode) + 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)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 400, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 400, resp.StatusCode) } // go test -v -run=^$ -bench=Benchmark_Limiter_Custom_Store -benchmem -count=4 @@ -247,8 +247,8 @@ func Test_Limiter_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } func Test_Limiter_Headers(t *testing.T) { @@ -269,7 +269,7 @@ func Test_Limiter_Headers(t *testing.T) { app.Handler()(fctx) - utils.AssertEqual(t, "50", string(fctx.Response.Header.Peek("X-RateLimit-Limit"))) + require.Equal(t, "50", string(fctx.Response.Header.Peek("X-RateLimit-Limit"))) if v := string(fctx.Response.Header.Peek("X-RateLimit-Remaining")); v == "" { t.Errorf("The X-RateLimit-Remaining header is not set correctly - value is an empty string.") } @@ -321,11 +321,11 @@ func Test_Sliding_Window(t *testing.T) { singleRequest := func(shouldFail bool) { resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) if shouldFail { - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 429, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 429, resp.StatusCode) } else { - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) } } diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 6a2974872b..243c80c6d7 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -12,7 +12,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/requestid" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) @@ -34,9 +34,9 @@ func Test_Logger(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) - utils.AssertEqual(t, "some random error", buf.String()) + require.NoError(t, err) + require.Equal(t, fiber.StatusInternalServerError, resp.StatusCode) + require.Equal(t, "some random error", buf.String()) } // go test -run Test_Logger_locals @@ -66,23 +66,23 @@ func Test_Logger_locals(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - utils.AssertEqual(t, "johndoe", buf.String()) + 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)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - utils.AssertEqual(t, "55", buf.String()) + 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)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - utils.AssertEqual(t, "", buf.String()) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + require.Equal(t, "", buf.String()) } // go test -run Test_Logger_Next @@ -95,8 +95,8 @@ func Test_Logger_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } // go test -run Test_Logger_ErrorTimeZone @@ -107,8 +107,8 @@ func Test_Logger_ErrorTimeZone(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } type fakeOutput int @@ -127,10 +127,10 @@ func Test_Logger_ErrorOutput(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) - utils.AssertEqual(t, 2, int(*o)) + require.Equal(t, 2, int(*o)) } // go test -run Test_Logger_All @@ -148,11 +148,11 @@ func Test_Logger_All(t *testing.T) { colors := app.Config().ColorScheme resp, err := app.Test(httptest.NewRequest("GET", "/?foo=bar", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) expected := fmt.Sprintf("%dHost=example.comhttpHTTP/1.10.0.0.0example.com/?foo=bar/%s%s%s%s%s%s%s%s%sCannot GET /", os.Getpid(), colors.Black, colors.Red, colors.Green, colors.Yellow, colors.Blue, colors.Magenta, colors.Cyan, colors.White, colors.Reset) - utils.AssertEqual(t, expected, buf.String()) + require.Equal(t, expected, buf.String()) } // go test -run Test_Query_Params @@ -167,11 +167,11 @@ func Test_Query_Params(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/?foo=bar&baz=moz", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) expected := "foo=bar&baz=moz" - utils.AssertEqual(t, expected, buf.String()) + require.Equal(t, expected, buf.String()) } // go test -run Test_Response_Body @@ -194,18 +194,18 @@ func Test_Response_Body(t *testing.T) { }) _, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) expectedGetResponse := "Sample response body" - utils.AssertEqual(t, expectedGetResponse, buf.String()) + require.Equal(t, expectedGetResponse, buf.String()) buf.Reset() // Reset buffer to test POST _, err = app.Test(httptest.NewRequest("POST", "/test", nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) expectedPostResponse := "Post in test" - utils.AssertEqual(t, expectedPostResponse, buf.String()) + require.Equal(t, expectedPostResponse, buf.String()) } // go test -run Test_Logger_AppendUint @@ -225,9 +225,9 @@ func Test_Logger_AppendUint(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - utils.AssertEqual(t, "0 5 200", buf.String()) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + require.Equal(t, "0 5 200", buf.String()) } // go test -run Test_Logger_Data_Race -race @@ -256,10 +256,10 @@ func Test_Logger_Data_Race(t *testing.T) { resp2, err2 = app.Test(httptest.NewRequest("GET", "/", nil)) wg.Wait() - utils.AssertEqual(t, nil, err1) - utils.AssertEqual(t, fiber.StatusOK, resp1.StatusCode) - utils.AssertEqual(t, nil, err2) - utils.AssertEqual(t, fiber.StatusOK, resp2.StatusCode) + require.Nil(t, err1) + require.Equal(t, fiber.StatusOK, resp1.StatusCode) + require.Nil(t, err2) + require.Equal(t, fiber.StatusOK, resp2.StatusCode) } // go test -v -run=^$ -bench=Benchmark_Logger -benchmem -count=4 @@ -287,7 +287,7 @@ func Benchmark_Logger(b *testing.B) { h(fctx) } - utils.AssertEqual(b, 200, fctx.Response.Header.StatusCode()) + require.Equal(b, 200, fctx.Response.Header.StatusCode()) } // go test -run Test_Response_Header @@ -312,9 +312,9 @@ func Test_Response_Header(t *testing.T) { resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - utils.AssertEqual(t, "Hello fiber!", buf.String()) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + require.Equal(t, "Hello fiber!", buf.String()) } // go test -run Test_Req_Header @@ -334,9 +334,9 @@ func Test_Req_Header(t *testing.T) { headerReq.Header.Add("test", "Hello fiber!") resp, err := app.Test(headerReq) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - utils.AssertEqual(t, "Hello fiber!", buf.String()) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + require.Equal(t, "Hello fiber!", buf.String()) } // go test -run Test_ReqHeader_Header @@ -356,7 +356,7 @@ func Test_ReqHeader_Header(t *testing.T) { reqHeaderReq.Header.Add("test", "Hello fiber!") resp, err := app.Test(reqHeaderReq) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - utils.AssertEqual(t, "Hello fiber!", buf.String()) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + require.Equal(t, "Hello fiber!", buf.String()) } diff --git a/middleware/pprof/pprof_test.go b/middleware/pprof/pprof_test.go index 4305cf2999..b67663189d 100644 --- a/middleware/pprof/pprof_test.go +++ b/middleware/pprof/pprof_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) func Test_Non_Pprof_Path(t *testing.T) { @@ -20,12 +20,12 @@ func Test_Non_Pprof_Path(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "escaped", string(b)) + require.NoError(t, err) + require.Equal(t, "escaped", string(b)) } func Test_Pprof_Index(t *testing.T) { @@ -38,13 +38,13 @@ func Test_Pprof_Index(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/debug/pprof/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) - utils.AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) + require.Equal(t, fiber.MIMETextHTMLCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, bytes.Contains(b, []byte("/debug/pprof/"))) + require.NoError(t, err) + require.True(t, bytes.Contains(b, []byte("/debug/pprof/"))) } func Test_Pprof_Subs(t *testing.T) { @@ -68,8 +68,8 @@ func Test_Pprof_Subs(t *testing.T) { target += "?seconds=1" } resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, target, nil), 5000) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 200, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 200, resp.StatusCode) }) } } @@ -84,8 +84,8 @@ func Test_Pprof_Other(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/debug/pprof/302", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 302, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 302, resp.StatusCode) } // go test -run Test_Pprof_Next @@ -101,6 +101,6 @@ func Test_Pprof_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/debug/pprof/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 404, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, 404, resp.StatusCode) } diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index 2acf06d2cd..05005a928b 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -13,6 +13,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/internal/tlstest" "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) func createProxyTestServer(handler fiber.Handler, t *testing.T) (*fiber.App, string) { @@ -22,10 +23,10 @@ func createProxyTestServer(handler fiber.Handler, t *testing.T) (*fiber.App, str target.Get("/", handler) ln, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) go func() { - utils.AssertEqual(t, nil, target.Listener(ln)) + require.Nil(t, target.Listener(ln)) }() time.Sleep(2 * time.Second) @@ -40,7 +41,7 @@ func Test_Proxy_Empty_Upstream_Servers(t *testing.T) { defer func() { if r := recover(); r != nil { - utils.AssertEqual(t, "Servers cannot be empty", r) + require.Equal(t, "Servers cannot be empty", r) } }() app := fiber.New() @@ -60,8 +61,8 @@ func Test_Proxy_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } // go test -run Test_Proxy @@ -73,8 +74,8 @@ func Test_Proxy(t *testing.T) { ) resp, err := target.Test(httptest.NewRequest("GET", "/", nil), 2000) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusTeapot, resp.StatusCode) app := fiber.New(fiber.Config{DisableStartupMessage: true}) @@ -83,8 +84,8 @@ func Test_Proxy(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) req.Host = addr resp, err = app.Test(req) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusTeapot, resp.StatusCode) } // go test -run Test_Proxy_Balancer_WithTlsConfig @@ -92,10 +93,10 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) { t.Parallel() serverTLSConf, _, err := tlstest.GetTLSConfigs() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) ln, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) ln = tls.NewListener(ln, serverTLSConf) @@ -114,13 +115,13 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) { TlsConfig: clientTLSConf, })) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() code, body, errs := fiber.Get("https://" + addr + "/tlsbalaner").TLSConfig(clientTLSConf).String() - utils.AssertEqual(t, 0, len(errs)) - utils.AssertEqual(t, fiber.StatusOK, code) - utils.AssertEqual(t, "tls balancer", body) + require.Equal(t, 0, len(errs)) + require.Equal(t, fiber.StatusOK, code) + require.Equal(t, "tls balancer", body) } // go test -run Test_Proxy_Forward_WithTlsConfig_To_Http @@ -132,10 +133,10 @@ func Test_Proxy_Forward_WithTlsConfig_To_Http(t *testing.T) { }, t) proxyServerTLSConf, _, err := tlstest.GetTLSConfigs() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) proxyServerLn, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) proxyServerLn = tls.NewListener(proxyServerLn, proxyServerTLSConf) @@ -145,16 +146,16 @@ func Test_Proxy_Forward_WithTlsConfig_To_Http(t *testing.T) { app.Use(Forward("http://" + targetAddr)) - go func() { utils.AssertEqual(t, nil, app.Listener(proxyServerLn)) }() + go func() { require.Nil(t, app.Listener(proxyServerLn)) }() code, body, errs := fiber.Get("https://" + proxyAddr). InsecureSkipVerify(). Timeout(5 * time.Second). String() - utils.AssertEqual(t, 0, len(errs)) - utils.AssertEqual(t, fiber.StatusOK, code) - utils.AssertEqual(t, "hello from target", body) + require.Equal(t, 0, len(errs)) + require.Equal(t, fiber.StatusOK, code) + require.Equal(t, "hello from target", body) } // go test -run Test_Proxy_Forward @@ -170,12 +171,12 @@ func Test_Proxy_Forward(t *testing.T) { app.Use(Forward("http://" + addr)) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "forwarded", string(b)) + require.NoError(t, err) + require.Equal(t, "forwarded", string(b)) } // go test -run Test_Proxy_Forward_WithTlsConfig @@ -183,10 +184,10 @@ func Test_Proxy_Forward_WithTlsConfig(t *testing.T) { t.Parallel() serverTLSConf, _, err := tlstest.GetTLSConfigs() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) ln, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0") - utils.AssertEqual(t, nil, err) + require.NoError(t, err) ln = tls.NewListener(ln, serverTLSConf) @@ -203,13 +204,13 @@ func Test_Proxy_Forward_WithTlsConfig(t *testing.T) { WithTlsConfig(clientTLSConf) app.Use(Forward("https://" + addr + "/tlsfwd")) - go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + go func() { require.Nil(t, app.Listener(ln)) }() code, body, errs := fiber.Get("https://" + addr).TLSConfig(clientTLSConf).String() - utils.AssertEqual(t, 0, len(errs)) - utils.AssertEqual(t, fiber.StatusOK, code) - utils.AssertEqual(t, "tls forward", body) + require.Equal(t, 0, len(errs)) + require.Equal(t, fiber.StatusOK, code) + require.Equal(t, "tls forward", body) } // go test -run Test_Proxy_Modify_Response @@ -230,12 +231,12 @@ func Test_Proxy_Modify_Response(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "modified response", string(b)) + require.NoError(t, err) + require.Equal(t, "modified response", string(b)) } // go test -run Test_Proxy_Modify_Request @@ -257,12 +258,12 @@ func Test_Proxy_Modify_Request(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "modified request", string(b)) + require.NoError(t, err) + require.Equal(t, "modified request", string(b)) } // go test -run Test_Proxy_Timeout_Slow_Server @@ -281,12 +282,12 @@ func Test_Proxy_Timeout_Slow_Server(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil), 5000) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "fiber is awesome", string(b)) + require.NoError(t, err) + require.Equal(t, "fiber is awesome", string(b)) } // go test -run Test_Proxy_With_Timeout @@ -305,12 +306,12 @@ func Test_Proxy_With_Timeout(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil), 2000) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusInternalServerError, resp.StatusCode) b, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "timeout", string(b)) + require.NoError(t, err) + require.Equal(t, "timeout", string(b)) } // go test -run Test_Proxy_Buffer_Size_Response @@ -327,8 +328,8 @@ func Test_Proxy_Buffer_Size_Response(t *testing.T) { app.Use(Balancer(Config{Servers: []string{addr}})) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusInternalServerError, resp.StatusCode) app = fiber.New() app.Use(Balancer(Config{ @@ -337,8 +338,8 @@ func Test_Proxy_Buffer_Size_Response(t *testing.T) { })) resp, err = app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) } // go test -race -run Test_Proxy_Do_RestoreOriginalURL @@ -353,15 +354,15 @@ func Test_Proxy_Do_RestoreOriginalURL(t *testing.T) { if err := Do(c, "/proxy"); err != nil { return err } - utils.AssertEqual(t, originalURL, c.OriginalURL()) + require.Equal(t, originalURL, c.OriginalURL()) return c.SendString("ok") }) _, err1 := app.Test(httptest.NewRequest("GET", "/test", nil)) // This test requires multiple requests due to zero allocation used in fiber _, err2 := app.Test(httptest.NewRequest("GET", "/test", nil)) - utils.AssertEqual(t, nil, err1) - utils.AssertEqual(t, nil, err2) + require.Nil(t, err1) + require.Nil(t, err2) } func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) { @@ -376,7 +377,7 @@ func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) { path := c.OriginalURL() url := strings.TrimPrefix(path, "/") - utils.AssertEqual(t, "http://"+addr, url) + require.Equal(t, "http://"+addr, url) if err := Do(c, url); err != nil { return err } @@ -385,8 +386,8 @@ func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/http://"+addr, nil)) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) s, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "hello world", string(s)) + require.NoError(t, err) + require.Equal(t, "hello world", string(s)) } diff --git a/middleware/recover/recover_test.go b/middleware/recover/recover_test.go index b7046e4247..b837a9d46d 100644 --- a/middleware/recover/recover_test.go +++ b/middleware/recover/recover_test.go @@ -5,14 +5,14 @@ import ( "testing" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) // go test -run Test_Recover func Test_Recover(t *testing.T) { app := fiber.New(fiber.Config{ ErrorHandler: func(c fiber.Ctx, err error) error { - utils.AssertEqual(t, "Hi, I'm an error!", err.Error()) + require.Equal(t, "Hi, I'm an error!", err.Error()) return c.SendStatus(fiber.StatusTeapot) }, }) @@ -24,8 +24,8 @@ func Test_Recover(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/panic", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusTeapot, resp.StatusCode) } // go test -run Test_Recover_Next @@ -38,8 +38,8 @@ func Test_Recover_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } func Test_Recover_EnableStackTrace(t *testing.T) { @@ -53,6 +53,6 @@ func Test_Recover_EnableStackTrace(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/panic", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusInternalServerError, resp.StatusCode) } diff --git a/middleware/requestid/requestid_test.go b/middleware/requestid/requestid_test.go index 64237714f3..f011373515 100644 --- a/middleware/requestid/requestid_test.go +++ b/middleware/requestid/requestid_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) // go test -run Test_RequestID @@ -19,19 +19,19 @@ func Test_RequestID(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) reqid := resp.Header.Get(fiber.HeaderXRequestID) - utils.AssertEqual(t, 36, len(reqid)) + require.Equal(t, 36, len(reqid)) req := httptest.NewRequest("GET", "/", nil) req.Header.Add(fiber.HeaderXRequestID, reqid) resp, err = app.Test(req) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - utils.AssertEqual(t, reqid, resp.Header.Get(fiber.HeaderXRequestID)) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + require.Equal(t, reqid, resp.Header.Get(fiber.HeaderXRequestID)) } // go test -run Test_RequestID_Next @@ -44,9 +44,9 @@ func Test_RequestID_Next(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, resp.Header.Get(fiber.HeaderXRequestID), "") - utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, resp.Header.Get(fiber.HeaderXRequestID), "") + require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } // go test -run Test_RequestID_Locals @@ -70,6 +70,6 @@ func Test_RequestID_Locals(t *testing.T) { }) _, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, reqId, ctxVal) + require.NoError(t, err) + require.Equal(t, reqId, ctxVal) } diff --git a/middleware/session/session.go b/middleware/session/session.go index 65b8c3f239..a45a506132 100644 --- a/middleware/session/session.go +++ b/middleware/session/session.go @@ -14,7 +14,7 @@ import ( type Session struct { id string // session id fresh bool // if new session - ctx fiber.Ctx // fiber context + ctx fiber.Ctx // fiber context config *Store // store configuration data *data // key value data byteBuffer *bytes.Buffer // byte buffer for the en- and decode diff --git a/middleware/session/session_test.go b/middleware/session/session_test.go index 9c0ea74973..57628755a4 100644 --- a/middleware/session/session_test.go +++ b/middleware/session/session_test.go @@ -6,7 +6,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/internal/storage/memory" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -28,55 +28,55 @@ func Test_Session(t *testing.T) { // get session sess, err := store.Get(ctx) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, sess.Fresh()) + require.NoError(t, err) + require.True(t, sess.Fresh()) // get keys keys := sess.Keys() - utils.AssertEqual(t, []string{}, keys) + require.Equal(t, []string{}, keys) // get value name := sess.Get("name") - utils.AssertEqual(t, nil, name) + require.Nil(t, name) // set value sess.Set("name", "john") // get value name = sess.Get("name") - utils.AssertEqual(t, "john", name) + require.Equal(t, "john", name) keys = sess.Keys() - utils.AssertEqual(t, []string{"name"}, keys) + require.Equal(t, []string{"name"}, keys) // delete key sess.Delete("name") // get value name = sess.Get("name") - utils.AssertEqual(t, nil, name) + require.Nil(t, name) // get keys keys = sess.Keys() - utils.AssertEqual(t, []string{}, keys) + require.Equal(t, []string{}, keys) // get id id := sess.ID() - utils.AssertEqual(t, "123", id) + require.Equal(t, "123", id) // save the old session first err = sess.Save() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) // requesting entirely new context to prevent falsy tests ctx = app.NewCtx(&fasthttp.RequestCtx{}) sess, err = store.Get(ctx) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, sess.Fresh()) + require.NoError(t, err) + require.True(t, sess.Fresh()) // this id should be randomly generated as session key was deleted - utils.AssertEqual(t, 36, len(sess.ID())) + require.Equal(t, 36, len(sess.ID())) // when we use the original session for the second time // the session be should be same if the session is not expired @@ -85,9 +85,9 @@ func Test_Session(t *testing.T) { // request the server with the old session ctx.Request().Header.SetCookie(store.sessionName, id) sess, err = store.Get(ctx) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, false, sess.Fresh()) - utils.AssertEqual(t, sess.id, id) + require.NoError(t, err) + require.False(t, sess.Fresh()) + require.Equal(t, sess.id, id) } // go test -run Test_Session_Types @@ -108,8 +108,8 @@ func Test_Session_Types(t *testing.T) { // get session sess, err := store.Get(ctx) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, true, sess.Fresh()) + require.NoError(t, err) + require.True(t, sess.Fresh()) // the session string is no longer be 123 newSessionIDString := sess.ID() @@ -166,34 +166,34 @@ func Test_Session_Types(t *testing.T) { // save session err = sess.Save() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) // get session sess, err = store.Get(ctx) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, false, sess.Fresh()) + require.NoError(t, err) + require.False(t, sess.Fresh()) // get value - utils.AssertEqual(t, vuser, sess.Get("vuser").(User)) - utils.AssertEqual(t, vbool, sess.Get("vbool").(bool)) - utils.AssertEqual(t, vstring, sess.Get("vstring").(string)) - utils.AssertEqual(t, vint, sess.Get("vint").(int)) - utils.AssertEqual(t, vint8, sess.Get("vint8").(int8)) - utils.AssertEqual(t, vint16, sess.Get("vint16").(int16)) - utils.AssertEqual(t, vint32, sess.Get("vint32").(int32)) - utils.AssertEqual(t, vint64, sess.Get("vint64").(int64)) - utils.AssertEqual(t, vuint, sess.Get("vuint").(uint)) - utils.AssertEqual(t, vuint8, sess.Get("vuint8").(uint8)) - utils.AssertEqual(t, vuint16, sess.Get("vuint16").(uint16)) - utils.AssertEqual(t, vuint32, sess.Get("vuint32").(uint32)) - utils.AssertEqual(t, vuint64, sess.Get("vuint64").(uint64)) - utils.AssertEqual(t, vuintptr, sess.Get("vuintptr").(uintptr)) - utils.AssertEqual(t, vbyte, sess.Get("vbyte").(byte)) - utils.AssertEqual(t, vrune, sess.Get("vrune").(rune)) - utils.AssertEqual(t, vfloat32, sess.Get("vfloat32").(float32)) - utils.AssertEqual(t, vfloat64, sess.Get("vfloat64").(float64)) - utils.AssertEqual(t, vcomplex64, sess.Get("vcomplex64").(complex64)) - utils.AssertEqual(t, vcomplex128, sess.Get("vcomplex128").(complex128)) + require.Equal(t, vuser, sess.Get("vuser").(User)) + require.Equal(t, vbool, sess.Get("vbool").(bool)) + require.Equal(t, vstring, sess.Get("vstring").(string)) + require.Equal(t, vint, sess.Get("vint").(int)) + require.Equal(t, vint8, sess.Get("vint8").(int8)) + require.Equal(t, vint16, sess.Get("vint16").(int16)) + require.Equal(t, vint32, sess.Get("vint32").(int32)) + require.Equal(t, vint64, sess.Get("vint64").(int64)) + require.Equal(t, vuint, sess.Get("vuint").(uint)) + require.Equal(t, vuint8, sess.Get("vuint8").(uint8)) + require.Equal(t, vuint16, sess.Get("vuint16").(uint16)) + require.Equal(t, vuint32, sess.Get("vuint32").(uint32)) + require.Equal(t, vuint64, sess.Get("vuint64").(uint64)) + require.Equal(t, vuintptr, sess.Get("vuintptr").(uintptr)) + require.Equal(t, vbyte, sess.Get("vbyte").(byte)) + require.Equal(t, vrune, sess.Get("vrune").(rune)) + require.Equal(t, vfloat32, sess.Get("vfloat32").(float32)) + require.Equal(t, vfloat64, sess.Get("vfloat64").(float64)) + require.Equal(t, vcomplex64, sess.Get("vcomplex64").(complex64)) + require.Equal(t, vcomplex128, sess.Get("vcomplex128").(complex128)) } // go test -run Test_Session_Store_Reset @@ -209,7 +209,7 @@ func Test_Session_Store_Reset(t *testing.T) { // get session sess, _ := store.Get(ctx) // make sure its new - utils.AssertEqual(t, true, sess.Fresh()) + require.True(t, sess.Fresh()) // set value & save sess.Set("hello", "world") ctx.Request().Header.SetCookie(store.sessionName, sess.ID()) @@ -220,8 +220,8 @@ func Test_Session_Store_Reset(t *testing.T) { // make sure the session is recreated sess, _ = store.Get(ctx) - utils.AssertEqual(t, true, sess.Fresh()) - utils.AssertEqual(t, nil, sess.Get("hello")) + require.True(t, sess.Fresh()) + require.Nil(t, sess.Get("hello")) } // go test -run Test_Session_Save @@ -243,7 +243,7 @@ func Test_Session_Save(t *testing.T) { // save session err := sess.Save() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) }) t.Run("save to header", func(t *testing.T) { @@ -263,9 +263,9 @@ func Test_Session_Save(t *testing.T) { // save session err := sess.Save() - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, store.getSessionID(ctx), string(ctx.Response().Header.Peek(store.sessionName))) - utils.AssertEqual(t, store.getSessionID(ctx), string(ctx.Request().Header.Peek(store.sessionName))) + require.NoError(t, err) + require.Equal(t, store.getSessionID(ctx), string(ctx.Response().Header.Peek(store.sessionName))) + require.Equal(t, store.getSessionID(ctx), string(ctx.Request().Header.Peek(store.sessionName))) }) } @@ -290,18 +290,18 @@ func Test_Session_Save_Expiration(t *testing.T) { // save session err := sess.Save() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) // here you need to get the old session yet sess, _ = store.Get(ctx) - utils.AssertEqual(t, "john", sess.Get("name")) + require.Equal(t, "john", sess.Get("name")) // just to make sure the session has been expired time.Sleep(time.Second * 5) // here you should get a new session sess, _ = store.Get(ctx) - utils.AssertEqual(t, nil, sess.Get("name")) + require.Nil(t, sess.Get("name")) }) } @@ -323,7 +323,7 @@ func Test_Session_Reset(t *testing.T) { sess.Set("name", "fenny") sess.Destroy() name := sess.Get("name") - utils.AssertEqual(t, nil, name) + require.Nil(t, name) }) t.Run("reset from header", func(t *testing.T) { @@ -345,9 +345,9 @@ func Test_Session_Reset(t *testing.T) { sess, _ = store.Get(ctx) err := sess.Destroy() - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "", string(ctx.Response().Header.Peek(store.sessionName))) - utils.AssertEqual(t, "", string(ctx.Request().Header.Peek(store.sessionName))) + require.NoError(t, err) + require.Equal(t, "", string(ctx.Response().Header.Peek(store.sessionName))) + require.Equal(t, "", string(ctx.Request().Header.Peek(store.sessionName))) }) } @@ -356,11 +356,11 @@ func Test_Session_Custom_Config(t *testing.T) { t.Parallel() store := New(Config{Expiration: time.Hour, KeyGenerator: func() string { return "very random" }}) - utils.AssertEqual(t, time.Hour, store.Expiration) - utils.AssertEqual(t, "very random", store.KeyGenerator()) + require.Equal(t, time.Hour, store.Expiration) + require.Equal(t, "very random", store.KeyGenerator()) store = New(Config{Expiration: 0}) - utils.AssertEqual(t, ConfigDefault.Expiration, store.Expiration) + require.Equal(t, ConfigDefault.Expiration, store.Expiration) } // go test -run Test_Session_Cookie @@ -378,7 +378,7 @@ func Test_Session_Cookie(t *testing.T) { sess.Save() // cookie should be set on Save ( even if empty data ) - utils.AssertEqual(t, 84, len(ctx.Response().Header.PeekCookie(store.sessionName))) + require.Equal(t, 84, len(ctx.Response().Header.PeekCookie(store.sessionName))) } // go test -run Test_Session_Cookie_In_Response @@ -393,15 +393,15 @@ func Test_Session_Cookie_In_Response(t *testing.T) { // get session sess, _ := store.Get(ctx) sess.Set("id", "1") - utils.AssertEqual(t, true, sess.Fresh()) + require.True(t, sess.Fresh()) sess.Save() sess, _ = store.Get(ctx) sess.Set("name", "john") - utils.AssertEqual(t, true, sess.Fresh()) + require.True(t, sess.Fresh()) - utils.AssertEqual(t, "1", sess.Get("id")) - utils.AssertEqual(t, "john", sess.Get("name")) + require.Equal(t, "1", sess.Get("id")) + require.Equal(t, "john", sess.Get("name")) } // go test -run Test_Session_Deletes_Single_Key @@ -414,21 +414,21 @@ func Test_Session_Deletes_Single_Key(t *testing.T) { ctx := app.NewCtx(&fasthttp.RequestCtx{}) sess, err := store.Get(ctx) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) ctx.Request().Header.SetCookie(store.sessionName, sess.ID()) sess.Set("id", "1") - utils.AssertEqual(t, nil, sess.Save()) + require.Nil(t, sess.Save()) sess, err = store.Get(ctx) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) sess.Delete("id") - utils.AssertEqual(t, nil, sess.Save()) + require.Nil(t, sess.Save()) sess, err = store.Get(ctx) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, false, sess.Fresh()) - utils.AssertEqual(t, nil, sess.Get("id")) + require.NoError(t, err) + require.False(t, sess.Fresh()) + require.Nil(t, sess.Get("id")) } // go test -run Test_Session_Regenerate @@ -446,29 +446,29 @@ func Test_Session_Regenerate(t *testing.T) { // now the session is in the storage freshSession, err := store.Get(ctx) - utils.AssertEqual(t, nil, err) + require.NoError(t, err) originalSessionUUIDString = freshSession.ID() err = freshSession.Save() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) // set cookie ctx.Request().Header.SetCookie(store.sessionName, originalSessionUUIDString) // as the session is in the storage, session.fresh should be false acquiredSession, err := store.Get(ctx) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, false, acquiredSession.Fresh()) + require.NoError(t, err) + require.False(t, acquiredSession.Fresh()) err = acquiredSession.Regenerate() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) if acquiredSession.ID() == originalSessionUUIDString { t.Fatal("regenerate should generate another different id") } // acquiredSession.fresh should be true after regenerating - utils.AssertEqual(t, true, acquiredSession.Fresh()) + require.True(t, acquiredSession.Fresh()) }) } diff --git a/middleware/session/store_test.go b/middleware/session/store_test.go index c72d0891b8..b579e59379 100644 --- a/middleware/session/store_test.go +++ b/middleware/session/store_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -25,7 +25,7 @@ func TestStore_getSessionID(t *testing.T) { // set cookie ctx.Request().Header.SetCookie(store.sessionName, expectedID) - utils.AssertEqual(t, expectedID, store.getSessionID(ctx)) + require.Equal(t, expectedID, store.getSessionID(ctx)) }) t.Run("from header", func(t *testing.T) { @@ -39,7 +39,7 @@ func TestStore_getSessionID(t *testing.T) { // set header ctx.Request().Header.Set(store.sessionName, expectedID) - utils.AssertEqual(t, expectedID, store.getSessionID(ctx)) + require.Equal(t, expectedID, store.getSessionID(ctx)) }) t.Run("from url query", func(t *testing.T) { @@ -53,7 +53,7 @@ func TestStore_getSessionID(t *testing.T) { // set url parameter ctx.Request().SetRequestURI(fmt.Sprintf("/path?%s=%s", store.sessionName, expectedID)) - utils.AssertEqual(t, expectedID, store.getSessionID(ctx)) + require.Equal(t, expectedID, store.getSessionID(ctx)) }) } @@ -73,7 +73,7 @@ func TestStore_Get(t *testing.T) { ctx.Request().Header.SetCookie(store.sessionName, unexpectedID) acquiredSession, err := store.Get(ctx) - utils.AssertEqual(t, err, nil) + require.NoError(t, err) if acquiredSession.ID() != unexpectedID { t.Fatal("server should not accept the unexpectedID which is not in the store") diff --git a/middleware/skip/skip_test.go b/middleware/skip/skip_test.go index b8598bbffa..4373e18e37 100644 --- a/middleware/skip/skip_test.go +++ b/middleware/skip/skip_test.go @@ -6,7 +6,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/middleware/skip" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) // go test -run Test_Skip @@ -17,8 +17,8 @@ func Test_Skip(t *testing.T) { app.Get("/", helloWorldHandler) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) } // go test -run Test_SkipFalse @@ -29,8 +29,8 @@ func Test_SkipFalse(t *testing.T) { app.Get("/", helloWorldHandler) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusTeapot, resp.StatusCode) } // go test -run Test_SkipNilFunc @@ -41,8 +41,8 @@ func Test_SkipNilFunc(t *testing.T) { app.Get("/", helloWorldHandler) resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) + require.NoError(t, err) + require.Equal(t, fiber.StatusTeapot, resp.StatusCode) } func helloWorldHandler(c fiber.Ctx) error { diff --git a/path_test.go b/path_test.go index d60308b928..455e97fabf 100644 --- a/path_test.go +++ b/path_test.go @@ -8,7 +8,7 @@ import ( "fmt" "testing" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) // go test -race -run Test_Path_parseRoute @@ -16,7 +16,7 @@ func Test_Path_parseRoute(t *testing.T) { var rp routeParser rp = parseRoute("/shop/product/::filter/color::color/size::size") - utils.AssertEqual(t, routeParser{ + require.Equal(t, routeParser{ segs: []*routeSegment{ {Const: "/shop/product/:", Length: 15}, {IsParam: true, ParamName: "filter", ComparePart: "/color:", PartCount: 1}, @@ -29,7 +29,7 @@ func Test_Path_parseRoute(t *testing.T) { }, rp) rp = parseRoute("/api/v1/:param/abc/*") - utils.AssertEqual(t, routeParser{ + require.Equal(t, routeParser{ segs: []*routeSegment{ {Const: "/api/v1/", Length: 8}, {IsParam: true, ParamName: "param", ComparePart: "/abc", PartCount: 1}, @@ -41,7 +41,7 @@ func Test_Path_parseRoute(t *testing.T) { }, rp) rp = parseRoute("/v1/some/resource/name\\:customVerb") - utils.AssertEqual(t, routeParser{ + require.Equal(t, routeParser{ segs: []*routeSegment{ {Const: "/v1/some/resource/name:customVerb", Length: 33, IsLast: true}, }, @@ -49,7 +49,7 @@ func Test_Path_parseRoute(t *testing.T) { }, rp) rp = parseRoute("/v1/some/resource/:name\\:customVerb") - utils.AssertEqual(t, routeParser{ + require.Equal(t, routeParser{ segs: []*routeSegment{ {Const: "/v1/some/resource/", Length: 18}, {IsParam: true, ParamName: "name", ComparePart: ":customVerb", PartCount: 1}, @@ -60,7 +60,7 @@ func Test_Path_parseRoute(t *testing.T) { // heavy test with escaped charaters rp = parseRoute("/v1/some/resource/name\\\\:customVerb?\\?/:param/*") - utils.AssertEqual(t, routeParser{ + require.Equal(t, routeParser{ segs: []*routeSegment{ {Const: "/v1/some/resource/name:customVerb??/", Length: 36}, {IsParam: true, ParamName: "param", ComparePart: "/", PartCount: 1}, @@ -72,7 +72,7 @@ func Test_Path_parseRoute(t *testing.T) { }, rp) rp = parseRoute("/api/*/:param/:param2") - utils.AssertEqual(t, routeParser{ + require.Equal(t, routeParser{ segs: []*routeSegment{ {Const: "/api/", Length: 5, HasOptionalSlash: true}, {IsParam: true, ParamName: "*1", IsGreedy: true, IsOptional: true, ComparePart: "/", PartCount: 2}, @@ -86,7 +86,7 @@ func Test_Path_parseRoute(t *testing.T) { }, rp) rp = parseRoute("/test:optional?:optional2?") - utils.AssertEqual(t, routeParser{ + require.Equal(t, routeParser{ segs: []*routeSegment{ {Const: "/test", Length: 5}, {IsParam: true, ParamName: "optional", IsOptional: true, Length: 1}, @@ -96,7 +96,7 @@ func Test_Path_parseRoute(t *testing.T) { }, rp) rp = parseRoute("/config/+.json") - utils.AssertEqual(t, routeParser{ + require.Equal(t, routeParser{ segs: []*routeSegment{ {Const: "/config/", Length: 8}, {IsParam: true, ParamName: "+1", IsGreedy: true, IsOptional: false, ComparePart: ".json", PartCount: 1}, @@ -107,7 +107,7 @@ func Test_Path_parseRoute(t *testing.T) { }, rp) rp = parseRoute("/api/:day.:month?.:year?") - utils.AssertEqual(t, routeParser{ + require.Equal(t, routeParser{ segs: []*routeSegment{ {Const: "/api/", Length: 5}, {IsParam: true, ParamName: "day", IsOptional: false, ComparePart: ".", PartCount: 2}, @@ -120,7 +120,7 @@ func Test_Path_parseRoute(t *testing.T) { }, rp) rp = parseRoute("/*v1*/proxy") - utils.AssertEqual(t, routeParser{ + require.Equal(t, routeParser{ segs: []*routeSegment{ {Const: "/", Length: 1, HasOptionalSlash: true}, {IsParam: true, ParamName: "*1", IsGreedy: true, IsOptional: true, ComparePart: "v1", PartCount: 1}, @@ -131,6 +131,7 @@ func Test_Path_parseRoute(t *testing.T) { params: []string{"*1", "*2"}, wildCardCount: 2, }, rp) + } // go test -race -run Test_Path_matchParams @@ -147,9 +148,9 @@ func Test_Path_matchParams(t *testing.T) { parser := parseRoute(r) for _, c := range cases { match := parser.getMatch(c.url, c.url, &ctxParams, c.partialCheck) - utils.AssertEqual(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) + require.Equal(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) if match && len(c.params) > 0 { - utils.AssertEqual(t, c.params[0:len(c.params)], ctxParams[0:len(c.params)], fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) + require.Equal(t, c.params[0:len(c.params)], ctxParams[0:len(c.params)], fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) } } } @@ -554,25 +555,25 @@ func Test_Path_matchParams(t *testing.T) { func Test_Utils_GetTrimmedParam(t *testing.T) { t.Parallel() res := GetTrimmedParam("") - utils.AssertEqual(t, "", res) + require.Equal(t, "", res) res = GetTrimmedParam("*") - utils.AssertEqual(t, "*", res) + require.Equal(t, "*", res) res = GetTrimmedParam(":param") - utils.AssertEqual(t, "param", res) + require.Equal(t, "param", res) res = GetTrimmedParam(":param1?") - utils.AssertEqual(t, "param1", res) + require.Equal(t, "param1", res) res = GetTrimmedParam("noParam") - utils.AssertEqual(t, "noParam", res) + require.Equal(t, "noParam", res) } func Test_Utils_RemoveEscapeChar(t *testing.T) { t.Parallel() res := RemoveEscapeChar(":test\\:bla") - utils.AssertEqual(t, ":test:bla", res) + require.Equal(t, ":test:bla", res) res = RemoveEscapeChar("\\abc") - utils.AssertEqual(t, "abc", res) + require.Equal(t, "abc", res) res = RemoveEscapeChar("noEscapeChar") - utils.AssertEqual(t, "noEscapeChar", res) + require.Equal(t, "noEscapeChar", res) } // go test -race -run Test_Path_matchParams @@ -599,9 +600,9 @@ func Benchmark_Path_matchParams(t *testing.B) { matchRes = true } } - utils.AssertEqual(t, c.match, matchRes, fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) + require.Equal(t, c.match, matchRes, fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) if matchRes && len(c.params) > 0 { - utils.AssertEqual(t, c.params[0:len(c.params)-1], ctxParams[0:len(c.params)-1], fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) + require.Equal(t, c.params[0:len(c.params)-1], ctxParams[0:len(c.params)-1], fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) } }) @@ -770,9 +771,9 @@ func Test_Path_matchParams0(t *testing.T) { parser := parseRoute(r) for _, c := range cases { match := parser.getMatch(c.url, c.url, &ctxParams, c.partialCheck) - utils.AssertEqual(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) + require.Equal(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) if match && len(c.params) > 0 { - utils.AssertEqual(t, c.params[0:len(c.params)], ctxParams[0:len(c.params)], fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) + require.Equal(t, c.params[0:len(c.params)], ctxParams[0:len(c.params)], fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) } } } diff --git a/prefork_test.go b/prefork_test.go index f318ac37b9..f6c04b9b2a 100644 --- a/prefork_test.go +++ b/prefork_test.go @@ -11,7 +11,7 @@ import ( "testing" "time" - "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" ) func Test_App_Prefork_Child_Process(t *testing.T) { @@ -24,28 +24,28 @@ func Test_App_Prefork_Child_Process(t *testing.T) { app := New() err := app.prefork(NetworkTCP4, "invalid", nil) - utils.AssertEqual(t, false, err == nil) + require.False(t, err == nil) go func() { time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) + require.Nil(t, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.prefork(NetworkTCP6, "[::1]:", nil)) + require.Nil(t, app.prefork(NetworkTCP6, "[::1]:", nil)) // Create tls certificate cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") if err != nil { - utils.AssertEqual(t, nil, err) + require.NoError(t, err) } config := &tls.Config{Certificates: []tls.Certificate{cer}} go func() { time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) + require.Nil(t, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.prefork(NetworkTCP4, "127.0.0.1:", config)) + require.Nil(t, app.prefork(NetworkTCP4, "127.0.0.1:", config)) } func Test_App_Prefork_Master_Process(t *testing.T) { @@ -56,15 +56,15 @@ func Test_App_Prefork_Master_Process(t *testing.T) { go func() { time.Sleep(1000 * time.Millisecond) - utils.AssertEqual(t, nil, app.Shutdown()) + require.Nil(t, app.Shutdown()) }() - utils.AssertEqual(t, nil, app.prefork(NetworkTCP4, ":3000", nil)) + require.Nil(t, app.prefork(NetworkTCP4, ":3000", nil)) dummyChildCmd = "invalid" err := app.prefork(NetworkTCP4, "127.0.0.1:", nil) - utils.AssertEqual(t, false, err == nil) + require.False(t, err == nil) } func Test_App_Prefork_Child_Process_Never_Show_Startup_Message(t *testing.T) { @@ -75,27 +75,27 @@ func Test_App_Prefork_Child_Process_Never_Show_Startup_Message(t *testing.T) { defer func() { os.Stdout = rescueStdout }() r, w, err := os.Pipe() - utils.AssertEqual(t, nil, err) + require.NoError(t, err) os.Stdout = w New().startupProcess().startupMessage(":3000", false, "") - utils.AssertEqual(t, nil, w.Close()) + require.Nil(t, w.Close()) out, err := io.ReadAll(r) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, 0, len(out)) + require.NoError(t, err) + require.Equal(t, 0, len(out)) } func setupIsChild(t *testing.T) { t.Helper() - utils.AssertEqual(t, nil, os.Setenv(envPreforkChildKey, envPreforkChildVal)) + require.Nil(t, os.Setenv(envPreforkChildKey, envPreforkChildVal)) } func teardownIsChild(t *testing.T) { t.Helper() - utils.AssertEqual(t, nil, os.Setenv(envPreforkChildKey, "")) + require.Nil(t, os.Setenv(envPreforkChildKey, "")) } diff --git a/router_test.go b/router_test.go index 7d56072d54..6a53e34e93 100644 --- a/router_test.go +++ b/router_test.go @@ -17,6 +17,7 @@ import ( "testing" "github.com/gofiber/fiber/v3/utils" + "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) @@ -40,21 +41,21 @@ func Test_Route_Match_SameLength(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/:param", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, ":param", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, ":param", app.getString(body)) // with param resp, err = app.Test(httptest.NewRequest(MethodGet, "/test", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "test", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "test", app.getString(body)) } func Test_Route_Match_Star(t *testing.T) { @@ -65,21 +66,21 @@ func Test_Route_Match_Star(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/*", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "*", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "*", app.getString(body)) // with param resp, err = app.Test(httptest.NewRequest(MethodGet, "/test", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "test", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "test", app.getString(body)) // without parameter route := Route{ @@ -89,18 +90,18 @@ func Test_Route_Match_Star(t *testing.T) { } params := [maxParams]string{} match := route.match("", "", ¶ms) - utils.AssertEqual(t, true, match) - utils.AssertEqual(t, [maxParams]string{}, params) + require.True(t, match) + require.Equal(t, [maxParams]string{}, params) // with parameter match = route.match("/favicon.ico", "/favicon.ico", ¶ms) - utils.AssertEqual(t, true, match) - utils.AssertEqual(t, [maxParams]string{"favicon.ico"}, params) + require.True(t, match) + require.Equal(t, [maxParams]string{"favicon.ico"}, params) // without parameter again match = route.match("", "", ¶ms) - utils.AssertEqual(t, true, match) - utils.AssertEqual(t, [maxParams]string{}, params) + require.True(t, match) + require.Equal(t, [maxParams]string{}, params) } func Test_Route_Match_Root(t *testing.T) { @@ -111,12 +112,12 @@ func Test_Route_Match_Root(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "root", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "root", app.getString(body)) } func Test_Route_Match_Parser(t *testing.T) { @@ -129,21 +130,21 @@ func Test_Route_Match_Parser(t *testing.T) { return c.SendString(c.Params("*")) }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/foo/bar", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "bar", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "bar", app.getString(body)) // with star resp, err = app.Test(httptest.NewRequest(MethodGet, "/Foobar/test", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "test", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "test", app.getString(body)) } func Test_Route_Match_Middleware(t *testing.T) { @@ -154,21 +155,21 @@ func Test_Route_Match_Middleware(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/foo/*", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "*", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "*", app.getString(body)) // with param resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo/bar/fasel", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "bar/fasel", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "bar/fasel", app.getString(body)) } func Test_Route_Match_UnescapedPath(t *testing.T) { @@ -179,22 +180,22 @@ func Test_Route_Match_UnescapedPath(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/cr%C3%A9er", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "test", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "test", app.getString(body)) // without special chars resp, err = app.Test(httptest.NewRequest(MethodGet, "/créer", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") // check deactivated behavior app.config.UnescapePath = false resp, err = app.Test(httptest.NewRequest(MethodGet, "/cr%C3%A9er", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusNotFound, resp.StatusCode, "Status code") } func Test_Route_Match_WithEscapeChar(t *testing.T) { @@ -215,30 +216,30 @@ func Test_Route_Match_WithEscapeChar(t *testing.T) { // check static route resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/some/resource/name:customVerb", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "static", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "static", app.getString(body)) // check group route resp, err = app.Test(httptest.NewRequest(MethodGet, "/v2/:firstVerb/:customVerb", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "group", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "group", app.getString(body)) // check param route resp, err = app.Test(httptest.NewRequest(MethodGet, "/v3/awesome/name:customVerb", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "awesome", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "awesome", app.getString(body)) } func Test_Route_Match_Middleware_HasPrefix(t *testing.T) { @@ -249,12 +250,12 @@ func Test_Route_Match_Middleware_HasPrefix(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/foo/bar", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "middleware", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "middleware", app.getString(body)) } func Test_Route_Match_Middleware_Root(t *testing.T) { @@ -265,19 +266,19 @@ func Test_Route_Match_Middleware_Root(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/everything", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, "middleware", app.getString(body)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, "middleware", app.getString(body)) } func Test_Router_Register_Missing_Handler(t *testing.T) { app := New() defer func() { if err := recover(); err != nil { - utils.AssertEqual(t, "missing handler in route: /doe\n", fmt.Sprintf("%v", err)) + require.Equal(t, "missing handler in route: /doe\n", fmt.Sprintf("%v", err)) } }() app.register("USE", "/doe") @@ -286,11 +287,11 @@ func Test_Router_Register_Missing_Handler(t *testing.T) { func Test_Ensure_Router_Interface_Implementation(t *testing.T) { var app any = (*App)(nil) _, ok := app.(Router) - utils.AssertEqual(t, true, ok) + require.True(t, ok) var group any = (*Group)(nil) _, ok = group.(Router) - utils.AssertEqual(t, true, ok) + require.True(t, ok) } func Test_Router_Handler_Catch_Error(t *testing.T) { @@ -307,7 +308,7 @@ func Test_Router_Handler_Catch_Error(t *testing.T) { app.Handler()(c) - utils.AssertEqual(t, StatusInternalServerError, c.Response.Header.StatusCode()) + require.Equal(t, StatusInternalServerError, c.Response.Header.StatusCode()) } func Test_Route_Static_Root(t *testing.T) { @@ -318,31 +319,31 @@ func Test_Route_Static_Root(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/style.css", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, true, strings.Contains(app.getString(body), "color")) + require.NoError(t, err, "app.Test(req)") + require.True(t, strings.Contains(app.getString(body), "color")) app = New() app.Static("/", dir) resp, err = app.Test(httptest.NewRequest(MethodGet, "/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 404, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 404, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/style.css", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, true, strings.Contains(app.getString(body), "color")) + require.NoError(t, err, "app.Test(req)") + require.True(t, strings.Contains(app.getString(body), "color")) } func Test_Route_Static_HasPrefix(t *testing.T) { @@ -353,20 +354,20 @@ func Test_Route_Static_HasPrefix(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/static", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/static/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/static/style.css", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err := io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, true, strings.Contains(app.getString(body), "color")) + require.NoError(t, err, "app.Test(req)") + require.True(t, strings.Contains(app.getString(body), "color")) app = New() app.Static("/static/", dir, Static{ @@ -374,58 +375,58 @@ func Test_Route_Static_HasPrefix(t *testing.T) { }) resp, err = app.Test(httptest.NewRequest(MethodGet, "/static", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/static/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/static/style.css", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, true, strings.Contains(app.getString(body), "color")) + require.NoError(t, err, "app.Test(req)") + require.True(t, strings.Contains(app.getString(body), "color")) app = New() app.Static("/static", dir) resp, err = app.Test(httptest.NewRequest(MethodGet, "/static", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 404, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 404, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/static/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 404, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 404, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/static/style.css", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, true, strings.Contains(app.getString(body), "color")) + require.NoError(t, err, "app.Test(req)") + require.True(t, strings.Contains(app.getString(body), "color")) app = New() app.Static("/static/", dir) resp, err = app.Test(httptest.NewRequest(MethodGet, "/static", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 404, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 404, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/static/", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 404, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 404, resp.StatusCode, "Status code") resp, err = app.Test(httptest.NewRequest(MethodGet, "/static/style.css", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") body, err = io.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, true, strings.Contains(app.getString(body), "color")) + require.NoError(t, err, "app.Test(req)") + require.True(t, strings.Contains(app.getString(body), "color")) } ////////////////////////////////////////////// @@ -460,9 +461,9 @@ func Benchmark_App_MethodNotAllowed(b *testing.B) { appHandler(c) } b.StopTimer() - utils.AssertEqual(b, 405, c.Response.StatusCode()) - utils.AssertEqual(b, "GET, HEAD", string(c.Response.Header.Peek("Allow"))) - utils.AssertEqual(b, utils.StatusMessage(StatusMethodNotAllowed), string(c.Response.Body())) + require.Equal(b, 405, c.Response.StatusCode()) + require.Equal(b, "GET, HEAD", string(c.Response.Header.Peek("Allow"))) + require.Equal(b, utils.StatusMessage(StatusMethodNotAllowed), string(c.Response.Body())) } // go test -v ./... -run=^$ -bench=Benchmark_Router_NotFound -benchmem -count=4 @@ -482,8 +483,8 @@ func Benchmark_Router_NotFound(b *testing.B) { for n := 0; n < b.N; n++ { appHandler(c) } - utils.AssertEqual(b, 404, c.Response.StatusCode()) - utils.AssertEqual(b, "Cannot DELETE /this/route/does/not/exist", string(c.Response.Body())) + require.Equal(b, 404, c.Response.StatusCode()) + require.Equal(b, "Cannot DELETE /this/route/does/not/exist", string(c.Response.Body())) } // go test -v ./... -run=^$ -bench=Benchmark_Router_Handler -benchmem -count=4 @@ -597,9 +598,9 @@ func Benchmark_Router_Next(b *testing.B) { c.indexRoute = -1 res, err = app.next(c, false) } - utils.AssertEqual(b, nil, err) - utils.AssertEqual(b, true, res) - utils.AssertEqual(b, 4, c.indexRoute) + require.NoError(b, err) + require.True(b, res) + require.Equal(b, 4, c.indexRoute) } // go test -v ./... -run=^$ -bench=Benchmark_Route_Match -benchmem -count=4 @@ -627,8 +628,8 @@ func Benchmark_Route_Match(b *testing.B) { match = route.match("/user/keys/1337", "/user/keys/1337", ¶ms) } - utils.AssertEqual(b, true, match) - utils.AssertEqual(b, []string{"1337"}, params[0:len(parsed.params)]) + require.True(b, match) + require.Equal(b, []string{"1337"}, params[0:len(parsed.params)]) } // go test -v ./... -run=^$ -bench=Benchmark_Route_Match_Star -benchmem -count=4 @@ -657,8 +658,8 @@ func Benchmark_Route_Match_Star(b *testing.B) { match = route.match("/user/keys/bla", "/user/keys/bla", ¶ms) } - utils.AssertEqual(b, true, match) - utils.AssertEqual(b, []string{"user/keys/bla"}, params[0:len(parsed.params)]) + require.True(b, match) + require.Equal(b, []string{"user/keys/bla"}, params[0:len(parsed.params)]) } // go test -v ./... -run=^$ -bench=Benchmark_Route_Match_Root -benchmem -count=4 @@ -688,8 +689,8 @@ func Benchmark_Route_Match_Root(b *testing.B) { match = route.match("/", "/", ¶ms) } - utils.AssertEqual(b, true, match) - utils.AssertEqual(b, []string{}, params[0:len(parsed.params)]) + require.True(b, match) + require.Equal(b, []string{}, params[0:len(parsed.params)]) } // go test -v ./... -run=^$ -bench=Benchmark_Router_Handler_CaseSensitive -benchmem -count=4 @@ -778,8 +779,8 @@ func Benchmark_Router_Github_API(b *testing.B) { app.ReleaseCtx(ctx) } - utils.AssertEqual(b, nil, err) - utils.AssertEqual(b, true, match) + require.NoError(b, err) + require.True(b, match) } } diff --git a/utils/assertions.go b/utils/assertions.go deleted file mode 100644 index d124ad9e60..0000000000 --- a/utils/assertions.go +++ /dev/null @@ -1,67 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import ( - "bytes" - "fmt" - "log" - "path/filepath" - "reflect" - "runtime" - "testing" - "text/tabwriter" -) - -// AssertEqual checks if values are equal -func AssertEqual(tb testing.TB, expected, actual any, description ...string) { - if tb != nil { - tb.Helper() - } - - if reflect.DeepEqual(expected, actual) { - return - } - - aType := "" - bType := "" - - if expected != nil { - aType = reflect.TypeOf(expected).String() - } - if actual != nil { - bType = reflect.TypeOf(actual).String() - } - - testName := "AssertEqual" - if tb != nil { - testName = tb.Name() - } - - _, file, line, _ := runtime.Caller(1) - - var buf bytes.Buffer - w := tabwriter.NewWriter(&buf, 0, 0, 5, ' ', 0) - fmt.Fprintf(w, "\nTest:\t%s", testName) - fmt.Fprintf(w, "\nTrace:\t%s:%d", filepath.Base(file), line) - if len(description) > 0 { - fmt.Fprintf(w, "\nDescription:\t%s", description[0]) - } - fmt.Fprintf(w, "\nExpect:\t%v\t(%s)", expected, aType) - fmt.Fprintf(w, "\nResult:\t%v\t(%s)", actual, bType) - - result := "" - if err := w.Flush(); err != nil { - result = err.Error() - } else { - result = buf.String() - } - - if tb != nil { - tb.Fatal(result) - } else { - log.Fatal(result) - } -} diff --git a/utils/assertions_test.go b/utils/assertions_test.go deleted file mode 100644 index 9b886aae65..0000000000 --- a/utils/assertions_test.go +++ /dev/null @@ -1,14 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import "testing" - -func Test_AssertEqual(t *testing.T) { - t.Parallel() - AssertEqual(nil, []string{}, []string{}) - AssertEqual(t, []string{}, []string{}) - AssertEqual(t, []byte("aq"), []byte("aq")) -} diff --git a/utils/bytes_test.go b/utils/bytes_test.go index 06ab1c4a8e..4dd1c5d137 100644 --- a/utils/bytes_test.go +++ b/utils/bytes_test.go @@ -7,15 +7,18 @@ package utils import ( "bytes" "testing" + + "github.com/stretchr/testify/require" ) func Test_ToLowerBytes(t *testing.T) { t.Parallel() - AssertEqual(t, []byte("/my/name/is/:param/*"), ToLowerBytes([]byte("/MY/NAME/IS/:PARAM/*"))) - AssertEqual(t, []byte("/my1/name/is/:param/*"), ToLowerBytes([]byte("/MY1/NAME/IS/:PARAM/*"))) - AssertEqual(t, []byte("/my2/name/is/:param/*"), ToLowerBytes([]byte("/MY2/NAME/IS/:PARAM/*"))) - AssertEqual(t, []byte("/my3/name/is/:param/*"), ToLowerBytes([]byte("/MY3/NAME/IS/:PARAM/*"))) - AssertEqual(t, []byte("/my4/name/is/:param/*"), ToLowerBytes([]byte("/MY4/NAME/IS/:PARAM/*"))) + + require.Equal(t, []byte("/my/name/is/:param/*"), ToLowerBytes([]byte("/MY/NAME/IS/:PARAM/*"))) + require.Equal(t, []byte("/my1/name/is/:param/*"), ToLowerBytes([]byte("/MY1/NAME/IS/:PARAM/*"))) + require.Equal(t, []byte("/my2/name/is/:param/*"), ToLowerBytes([]byte("/MY2/NAME/IS/:PARAM/*"))) + require.Equal(t, []byte("/my3/name/is/:param/*"), ToLowerBytes([]byte("/MY3/NAME/IS/:PARAM/*"))) + require.Equal(t, []byte("/my4/name/is/:param/*"), ToLowerBytes([]byte("/MY4/NAME/IS/:PARAM/*"))) } func Benchmark_ToLowerBytes(b *testing.B) { @@ -26,23 +29,24 @@ func Benchmark_ToLowerBytes(b *testing.B) { for n := 0; n < b.N; n++ { res = ToLowerBytes(path) } - AssertEqual(b, want, res) + require.Equal(b, bytes.Equal(want, res), true) }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = bytes.ToLower(path) } - AssertEqual(b, want, res) + require.Equal(b, bytes.Equal(want, res), true) }) } func Test_ToUpperBytes(t *testing.T) { t.Parallel() - AssertEqual(t, []byte("/MY/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my/name/is/:param/*"))) - AssertEqual(t, []byte("/MY1/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my1/name/is/:param/*"))) - AssertEqual(t, []byte("/MY2/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my2/name/is/:param/*"))) - AssertEqual(t, []byte("/MY3/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my3/name/is/:param/*"))) - AssertEqual(t, []byte("/MY4/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my4/name/is/:param/*"))) + + require.Equal(t, []byte("/MY/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my/name/is/:param/*"))) + require.Equal(t, []byte("/MY1/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my1/name/is/:param/*"))) + require.Equal(t, []byte("/MY2/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my2/name/is/:param/*"))) + require.Equal(t, []byte("/MY3/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my3/name/is/:param/*"))) + require.Equal(t, []byte("/MY4/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my4/name/is/:param/*"))) } func Benchmark_ToUpperBytes(b *testing.B) { @@ -53,12 +57,12 @@ func Benchmark_ToUpperBytes(b *testing.B) { for n := 0; n < b.N; n++ { res = ToUpperBytes(path) } - AssertEqual(b, want, res) + require.Equal(b, want, res) }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = bytes.ToUpper(path) } - AssertEqual(b, want, res) + require.Equal(b, want, res) }) } diff --git a/utils/byteseq_test.go b/utils/byteseq_test.go index dfd0757703..790329245a 100644 --- a/utils/byteseq_test.go +++ b/utils/byteseq_test.go @@ -4,6 +4,8 @@ import ( "bytes" "strings" "testing" + + "github.com/stretchr/testify/require" ) func Benchmark_EqualFoldBytes(b *testing.B) { @@ -14,34 +16,16 @@ func Benchmark_EqualFoldBytes(b *testing.B) { for n := 0; n < b.N; n++ { res = EqualFold(left, right) } - AssertEqual(b, true, res) + require.True(b, res) }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = bytes.EqualFold(left, right) } - AssertEqual(b, true, res) + require.True(b, res) }) } -func Test_EqualFoldBytes(t *testing.T) { - t.Parallel() - res := EqualFold([]byte("/MY/NAME/IS/:PARAM/*"), []byte("/my/name/is/:param/*")) - AssertEqual(t, true, res) - res = EqualFold([]byte("/MY1/NAME/IS/:PARAM/*"), []byte("/MY1/NAME/IS/:PARAM/*")) - AssertEqual(t, true, res) - res = EqualFold([]byte("/my2/name/is/:param/*"), []byte("/my2/name")) - AssertEqual(t, false, res) - res = EqualFold([]byte("/dddddd"), []byte("eeeeee")) - AssertEqual(t, false, res) - res = EqualFold([]byte("\na"), []byte("*A")) - AssertEqual(t, false, res) - res = EqualFold([]byte("/MY3/NAME/IS/:PARAM/*"), []byte("/my3/name/is/:param/*")) - AssertEqual(t, true, res) - res = EqualFold([]byte("/MY4/NAME/IS/:PARAM/*"), []byte("/my4/nAME/IS/:param/*")) - AssertEqual(t, true, res) -} - // go test -v -run=^$ -bench=Benchmark_EqualFold -benchmem -count=4 ./utils/ func Benchmark_EqualFold(b *testing.B) { var res bool @@ -49,13 +33,13 @@ func Benchmark_EqualFold(b *testing.B) { for n := 0; n < b.N; n++ { res = EqualFold(upperStr, lowerStr) } - AssertEqual(b, true, res) + require.True(b, res) }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = strings.EqualFold(upperStr, lowerStr) } - AssertEqual(b, true, res) + require.True(b, res) }) } @@ -78,9 +62,9 @@ func Test_EqualFold(t *testing.T) { for _, tc := range testCases { res := EqualFold[string](tc.S1, tc.S2) - AssertEqual(t, tc.Expected, res, "string") + require.Equal(t, tc.Expected, res, "string") res = EqualFold[[]byte]([]byte(tc.S1), []byte(tc.S2)) - AssertEqual(t, tc.Expected, res, "bytes") + require.Equal(t, tc.Expected, res, "bytes") } } diff --git a/utils/common_test.go b/utils/common_test.go index bf108d24e9..4c427df5af 100644 --- a/utils/common_test.go +++ b/utils/common_test.go @@ -8,23 +8,25 @@ import ( "crypto/rand" "fmt" "testing" + + "github.com/stretchr/testify/require" ) func Test_FunctionName(t *testing.T) { t.Parallel() - AssertEqual(t, "github.com/gofiber/fiber/v3/utils.Test_UUID", FunctionName(Test_UUID)) + require.Equal(t, "github.com/gofiber/fiber/v3/utils.Test_UUID", FunctionName(Test_UUID)) - AssertEqual(t, "github.com/gofiber/fiber/v3/utils.Test_FunctionName.func1", FunctionName(func() {})) + require.Equal(t, "github.com/gofiber/fiber/v3/utils.Test_FunctionName.func1", FunctionName(func() {})) dummyint := 20 - AssertEqual(t, "int", FunctionName(dummyint)) + require.Equal(t, "int", FunctionName(dummyint)) } func Test_UUID(t *testing.T) { t.Parallel() res := UUID() - AssertEqual(t, 36, len(res)) - AssertEqual(t, true, res != "00000000-0000-0000-0000-000000000000") + require.Equal(t, 36, len(res)) + require.True(t, res != "00000000-0000-0000-0000-000000000000") } func Test_UUID_Concurrency(t *testing.T) { @@ -42,14 +44,14 @@ func Test_UUID_Concurrency(t *testing.T) { res = <-ch results[res] = res } - AssertEqual(t, iterations, len(results)) + require.Equal(t, iterations, len(results)) } func Test_UUIDv4(t *testing.T) { t.Parallel() res := UUIDv4() - AssertEqual(t, 36, len(res)) - AssertEqual(t, true, res != "00000000-0000-0000-0000-000000000000") + require.Equal(t, 36, len(res)) + require.True(t, res != "00000000-0000-0000-0000-000000000000") } func Test_UUIDv4_Concurrency(t *testing.T) { @@ -67,7 +69,7 @@ func Test_UUIDv4_Concurrency(t *testing.T) { res = <-ch results[res] = res } - AssertEqual(t, iterations, len(results)) + require.Equal(t, iterations, len(results)) } // go test -v -run=^$ -bench=Benchmark_UUID -benchmem -count=2 @@ -78,7 +80,7 @@ func Benchmark_UUID(b *testing.B) { for n := 0; n < b.N; n++ { res = UUID() } - AssertEqual(b, 36, len(res)) + require.Equal(b, 36, len(res)) }) b.Run("default", func(b *testing.B) { rnd := make([]byte, 16) @@ -86,32 +88,32 @@ func Benchmark_UUID(b *testing.B) { for n := 0; n < b.N; n++ { res = fmt.Sprintf("%x-%x-%x-%x-%x", rnd[0:4], rnd[4:6], rnd[6:8], rnd[8:10], rnd[10:]) } - AssertEqual(b, 36, len(res)) + require.Equal(b, 36, len(res)) }) } func Test_ConvertToBytes(t *testing.T) { t.Parallel() - AssertEqual(t, 0, ConvertToBytes("")) - AssertEqual(t, 42, ConvertToBytes("42")) - AssertEqual(t, 42, ConvertToBytes("42b")) - AssertEqual(t, 42, ConvertToBytes("42B")) - AssertEqual(t, 42, ConvertToBytes("42 b")) - AssertEqual(t, 42, ConvertToBytes("42 B")) - - AssertEqual(t, 42*1000, ConvertToBytes("42k")) - AssertEqual(t, 42*1000, ConvertToBytes("42K")) - AssertEqual(t, 42*1000, ConvertToBytes("42kb")) - AssertEqual(t, 42*1000, ConvertToBytes("42KB")) - AssertEqual(t, 42*1000, ConvertToBytes("42 kb")) - AssertEqual(t, 42*1000, ConvertToBytes("42 KB")) - - AssertEqual(t, 42*1000000, ConvertToBytes("42M")) - AssertEqual(t, int(42.5*1000000), ConvertToBytes("42.5MB")) - AssertEqual(t, 42*1000000000, ConvertToBytes("42G")) - - AssertEqual(t, 0, ConvertToBytes("string")) - AssertEqual(t, 0, ConvertToBytes("MB")) + require.Equal(t, 0, ConvertToBytes("")) + require.Equal(t, 42, ConvertToBytes("42")) + require.Equal(t, 42, ConvertToBytes("42b")) + require.Equal(t, 42, ConvertToBytes("42B")) + require.Equal(t, 42, ConvertToBytes("42 b")) + require.Equal(t, 42, ConvertToBytes("42 B")) + + require.Equal(t, 42*1000, ConvertToBytes("42k")) + require.Equal(t, 42*1000, ConvertToBytes("42K")) + require.Equal(t, 42*1000, ConvertToBytes("42kb")) + require.Equal(t, 42*1000, ConvertToBytes("42KB")) + require.Equal(t, 42*1000, ConvertToBytes("42 kb")) + require.Equal(t, 42*1000, ConvertToBytes("42 KB")) + + require.Equal(t, 42*1000000, ConvertToBytes("42M")) + require.Equal(t, int(42.5*1000000), ConvertToBytes("42.5MB")) + require.Equal(t, 42*1000000000, ConvertToBytes("42G")) + + require.Equal(t, 0, ConvertToBytes("string")) + require.Equal(t, 0, ConvertToBytes("MB")) } // go test -v -run=^$ -bench=Benchmark_ConvertToBytes -benchmem -count=2 @@ -121,6 +123,6 @@ func Benchmark_ConvertToBytes(b *testing.B) { for n := 0; n < b.N; n++ { res = ConvertToBytes("42B") } - AssertEqual(b, 42, res) + require.Equal(b, 42, res) }) } diff --git a/utils/convert_test.go b/utils/convert_test.go index 59ce625e53..722d53f18f 100644 --- a/utils/convert_test.go +++ b/utils/convert_test.go @@ -4,12 +4,16 @@ package utils -import "testing" +import ( + "testing" + + "github.com/stretchr/testify/require" +) func Test_UnsafeString(t *testing.T) { t.Parallel() res := UnsafeString([]byte("Hello, World!")) - AssertEqual(t, "Hello, World!", res) + require.Equal(t, "Hello, World!", res) } // go test -v -run=^$ -bench=UnsafeString -benchmem -count=2 @@ -21,20 +25,20 @@ func Benchmark_UnsafeString(b *testing.B) { for n := 0; n < b.N; n++ { res = UnsafeString(hello) } - AssertEqual(b, "Hello, World!", res) + require.Equal(b, "Hello, World!", res) }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = string(hello) } - AssertEqual(b, "Hello, World!", res) + require.Equal(b, "Hello, World!", res) }) } func Test_UnsafeBytes(t *testing.T) { t.Parallel() res := UnsafeBytes("Hello, World!") - AssertEqual(t, []byte("Hello, World!"), res) + require.Equal(t, []byte("Hello, World!"), res) } // go test -v -run=^$ -bench=UnsafeBytes -benchmem -count=4 @@ -46,30 +50,30 @@ func Benchmark_UnsafeBytes(b *testing.B) { for n := 0; n < b.N; n++ { res = UnsafeBytes(hello) } - AssertEqual(b, []byte("Hello, World!"), res) + require.Equal(b, []byte("Hello, World!"), res) }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = []byte(hello) } - AssertEqual(b, []byte("Hello, World!"), res) + require.Equal(b, []byte("Hello, World!"), res) }) } func Test_CopyString(t *testing.T) { t.Parallel() res := CopyString("Hello, World!") - AssertEqual(t, "Hello, World!", res) + require.Equal(t, "Hello, World!", res) } func Test_ToString(t *testing.T) { t.Parallel() res := ToString([]byte("Hello, World!")) - AssertEqual(t, "Hello, World!", res) + require.Equal(t, "Hello, World!", res) res = ToString(true) - AssertEqual(t, "true", res) + require.Equal(t, "true", res) res = ToString(uint(100)) - AssertEqual(t, "100", res) + require.Equal(t, "100", res) } // go test -v -run=^$ -bench=ToString -benchmem -count=2 diff --git a/utils/http_test.go b/utils/http_test.go index 2abe6c51a4..e06e89057b 100644 --- a/utils/http_test.go +++ b/utils/http_test.go @@ -8,24 +8,26 @@ import ( "mime" "net/http" "testing" + + "github.com/stretchr/testify/require" ) func Test_GetMIME(t *testing.T) { t.Parallel() res := GetMIME(".json") - AssertEqual(t, "application/json", res) + require.Equal(t, "application/json", res) res = GetMIME(".xml") - AssertEqual(t, "application/xml", res) + require.Equal(t, "application/xml", res) res = GetMIME("xml") - AssertEqual(t, "application/xml", res) + require.Equal(t, "application/xml", res) res = GetMIME("unknown") - AssertEqual(t, MIMEOctetStream, res) + require.Equal(t, MIMEOctetStream, res) // empty case res = GetMIME("") - AssertEqual(t, "", res) + require.Equal(t, "", res) } // go test -v -run=^$ -bench=Benchmark_GetMIME -benchmem -count=2 @@ -39,7 +41,7 @@ func Benchmark_GetMIME(b *testing.B) { res = GetMIME(".exe") res = GetMIME(".json") } - AssertEqual(b, "application/json", res) + require.Equal(b, "application/json", res) }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { @@ -49,7 +51,7 @@ func Benchmark_GetMIME(b *testing.B) { res = mime.TypeByExtension(".exe") res = mime.TypeByExtension(".json") } - AssertEqual(b, "application/json", res) + require.Equal(b, "application/json", res) }) } @@ -57,25 +59,25 @@ func Test_ParseVendorSpecificContentType(t *testing.T) { t.Parallel() cType := ParseVendorSpecificContentType("application/json") - AssertEqual(t, "application/json", cType) + require.Equal(t, "application/json", cType) cType = ParseVendorSpecificContentType("multipart/form-data; boundary=dart-http-boundary-ZnVy.ICWq+7HOdsHqWxCFa8g3D.KAhy+Y0sYJ_lBADypu8po3_X") - AssertEqual(t, "multipart/form-data", cType) + require.Equal(t, "multipart/form-data", cType) cType = ParseVendorSpecificContentType("multipart/form-data") - AssertEqual(t, "multipart/form-data", cType) + require.Equal(t, "multipart/form-data", cType) cType = ParseVendorSpecificContentType("application/vnd.api+json; version=1") - AssertEqual(t, "application/json", cType) + require.Equal(t, "application/json", cType) cType = ParseVendorSpecificContentType("application/vnd.api+json") - AssertEqual(t, "application/json", cType) + require.Equal(t, "application/json", cType) cType = ParseVendorSpecificContentType("application/vnd.dummy+x-www-form-urlencoded") - AssertEqual(t, "application/x-www-form-urlencoded", cType) + require.Equal(t, "application/x-www-form-urlencoded", cType) cType = ParseVendorSpecificContentType("something invalid") - AssertEqual(t, "something invalid", cType) + require.Equal(t, "something invalid", cType) } func Benchmark_ParseVendorSpecificContentType(b *testing.B) { @@ -84,42 +86,42 @@ func Benchmark_ParseVendorSpecificContentType(b *testing.B) { for n := 0; n < b.N; n++ { cType = ParseVendorSpecificContentType("application/vnd.api+json; version=1") } - AssertEqual(b, "application/json", cType) + require.Equal(b, "application/json", cType) }) b.Run("defaultContentType", func(b *testing.B) { for n := 0; n < b.N; n++ { cType = ParseVendorSpecificContentType("application/json") } - AssertEqual(b, "application/json", cType) + require.Equal(b, "application/json", cType) }) } func Test_StatusMessage(t *testing.T) { t.Parallel() res := StatusMessage(204) - AssertEqual(t, "No Content", res) + require.Equal(t, "No Content", res) res = StatusMessage(404) - AssertEqual(t, "Not Found", res) + require.Equal(t, "Not Found", res) res = StatusMessage(426) - AssertEqual(t, "Upgrade Required", res) + require.Equal(t, "Upgrade Required", res) res = StatusMessage(511) - AssertEqual(t, "Network Authentication Required", res) + require.Equal(t, "Network Authentication Required", res) res = StatusMessage(1337) - AssertEqual(t, "", res) + require.Equal(t, "", res) res = StatusMessage(-1) - AssertEqual(t, "", res) + require.Equal(t, "", res) res = StatusMessage(0) - AssertEqual(t, "", res) + require.Equal(t, "", res) res = StatusMessage(600) - AssertEqual(t, "", res) + require.Equal(t, "", res) } // go test -run=^$ -bench=Benchmark_StatusMessage -benchmem -count=2 @@ -129,12 +131,12 @@ func Benchmark_StatusMessage(b *testing.B) { for n := 0; n < b.N; n++ { res = StatusMessage(http.StatusNotExtended) } - AssertEqual(b, "Not Extended", res) + require.Equal(b, "Not Extended", res) }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = http.StatusText(http.StatusNotExtended) } - AssertEqual(b, "Not Extended", res) + require.Equal(b, "Not Extended", res) }) } diff --git a/utils/json_test.go b/utils/json_test.go index 1a79e489c0..3c756aca24 100644 --- a/utils/json_test.go +++ b/utils/json_test.go @@ -3,6 +3,8 @@ package utils import ( "encoding/json" "testing" + + "github.com/stretchr/testify/require" ) type sampleStructure struct { @@ -21,9 +23,9 @@ func Test_GolangJSONEncoder(t *testing.T) { ) raw, err := jsonEncoder(ss) - AssertEqual(t, err, nil) + require.NoError(t, err) - AssertEqual(t, string(raw), importantString) + require.Equal(t, string(raw), importantString) } func Test_DefaultJSONEncoder(t *testing.T) { @@ -38,9 +40,9 @@ func Test_DefaultJSONEncoder(t *testing.T) { ) raw, err := jsonEncoder(ss) - AssertEqual(t, err, nil) + require.NoError(t, err) - AssertEqual(t, string(raw), importantString) + require.Equal(t, string(raw), importantString) } func Test_DefaultJSONDecoder(t *testing.T) { @@ -53,6 +55,6 @@ func Test_DefaultJSONDecoder(t *testing.T) { ) err := jsonDecoder(importantString, &ss) - AssertEqual(t, err, nil) - AssertEqual(t, "Hello World", ss.ImportantString) + require.NoError(t, err) + require.Equal(t, "Hello World", ss.ImportantString) } diff --git a/utils/strings_test.go b/utils/strings_test.go index b0d5176753..37abc96efb 100644 --- a/utils/strings_test.go +++ b/utils/strings_test.go @@ -7,11 +7,13 @@ package utils import ( "strings" "testing" + + "github.com/stretchr/testify/require" ) func Test_ToUpper(t *testing.T) { t.Parallel() - AssertEqual(t, "/MY/NAME/IS/:PARAM/*", ToUpper("/my/name/is/:param/*")) + require.Equal(t, "/MY/NAME/IS/:PARAM/*", ToUpper("/my/name/is/:param/*")) } const ( @@ -26,23 +28,23 @@ func Benchmark_ToUpper(b *testing.B) { for n := 0; n < b.N; n++ { res = ToUpper(largeStr) } - AssertEqual(b, upperStr, res) + require.Equal(b, upperStr, res) }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = strings.ToUpper(largeStr) } - AssertEqual(b, upperStr, res) + require.Equal(b, upperStr, res) }) } func Test_ToLower(t *testing.T) { t.Parallel() - AssertEqual(t, "/my/name/is/:param/*", ToLower("/MY/NAME/IS/:PARAM/*")) - AssertEqual(t, "/my1/name/is/:param/*", ToLower("/MY1/NAME/IS/:PARAM/*")) - AssertEqual(t, "/my2/name/is/:param/*", ToLower("/MY2/NAME/IS/:PARAM/*")) - AssertEqual(t, "/my3/name/is/:param/*", ToLower("/MY3/NAME/IS/:PARAM/*")) - AssertEqual(t, "/my4/name/is/:param/*", ToLower("/MY4/NAME/IS/:PARAM/*")) + require.Equal(t, "/my/name/is/:param/*", ToLower("/MY/NAME/IS/:PARAM/*")) + require.Equal(t, "/my1/name/is/:param/*", ToLower("/MY1/NAME/IS/:PARAM/*")) + require.Equal(t, "/my2/name/is/:param/*", ToLower("/MY2/NAME/IS/:PARAM/*")) + require.Equal(t, "/my3/name/is/:param/*", ToLower("/MY3/NAME/IS/:PARAM/*")) + require.Equal(t, "/my4/name/is/:param/*", ToLower("/MY4/NAME/IS/:PARAM/*")) } func Benchmark_ToLower(b *testing.B) { @@ -51,12 +53,12 @@ func Benchmark_ToLower(b *testing.B) { for n := 0; n < b.N; n++ { res = ToLower(largeStr) } - AssertEqual(b, lowerStr, res) + require.Equal(b, lowerStr, res) }) b.Run("default", func(b *testing.B) { for n := 0; n < b.N; n++ { res = strings.ToLower(largeStr) } - AssertEqual(b, lowerStr, res) + require.Equal(b, lowerStr, res) }) } diff --git a/utils/xml_test.go b/utils/xml_test.go index bbb11708d6..e4699e68f5 100644 --- a/utils/xml_test.go +++ b/utils/xml_test.go @@ -3,6 +3,8 @@ package utils import ( "encoding/xml" "testing" + + "github.com/stretchr/testify/require" ) type serversXMLStructure struct { @@ -33,9 +35,9 @@ func Test_GolangXMLEncoder(t *testing.T) { ) raw, err := xmlEncoder(ss) - AssertEqual(t, err, nil) + require.NoError(t, err) - AssertEqual(t, string(raw), xmlString) + require.Equal(t, string(raw), xmlString) } func Test_DefaultXMLEncoder(t *testing.T) { @@ -53,7 +55,7 @@ func Test_DefaultXMLEncoder(t *testing.T) { ) raw, err := xmlEncoder(ss) - AssertEqual(t, err, nil) + require.NoError(t, err) - AssertEqual(t, string(raw), xmlString) + require.Equal(t, string(raw), xmlString) } From 80a6fdc2153fac726d175cc1d8ec3f78e3720f9d Mon Sep 17 00:00:00 2001 From: Trim21 Date: Mon, 22 Aug 2022 14:01:40 +0800 Subject: [PATCH 046/141] client: fix `Agent` use after free (#2037) * fix * fix * fix --- client.go | 29 ++++++++++++++++++++--------- client_test.go | 3 +-- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/client.go b/client.go index 97762dcdaf..deedc0f56c 100644 --- a/client.go +++ b/client.go @@ -741,9 +741,14 @@ func (a *Agent) RetryIf(retryIf RetryIfFunc) *Agent { /************************** End Agent Setting **************************/ // 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) { defer a.release() + return a.bytes() +} +func (a *Agent) bytes() (code int, body []byte, errs []error) { if errs = append(errs, a.errs...); len(errs) > 0 { return } @@ -802,16 +807,22 @@ func printDebugInfo(req *Request, resp *Response, w io.Writer) { } // String returns the status code, string body and errors of url. +// +// it's not safe to use Agent after calling [Agent.String] func (a *Agent) String() (int, string, []error) { - code, body, errs := a.Bytes() + defer a.release() + code, body, errs := a.bytes() return code, utils.UnsafeString(body), errs } // Struct returns the status code, bytes body and errors of url. // And bytes body will be unmarshalled to given v. +// +// it's not safe to use Agent after calling [Agent.Struct] func (a *Agent) Struct(v interface{}) (code int, body []byte, errs []error) { - if code, body, errs = a.Bytes(); len(errs) > 0 { + defer a.release() + if code, body, errs = a.bytes(); len(errs) > 0 { return } @@ -856,8 +867,12 @@ func (a *Agent) reset() { } var ( - clientPool sync.Pool - agentPool sync.Pool + clientPool sync.Pool + agentPool = sync.Pool{ + New: func() interface{} { + return &Agent{req: &Request{}} + }, + } responsePool sync.Pool argsPool sync.Pool formFilePool sync.Pool @@ -895,11 +910,7 @@ func ReleaseClient(c *Client) { // no longer needed. This allows Agent recycling, reduces GC pressure // and usually improves performance. func AcquireAgent() *Agent { - v := agentPool.Get() - if v == nil { - return &Agent{req: &Request{}} - } - return v.(*Agent) + return agentPool.Get().(*Agent) } // ReleaseAgent returns a acquired via AcquireAgent to Agent pool. diff --git a/client_test.go b/client_test.go index f9de9367de..980ac1f955 100644 --- a/client_test.go +++ b/client_test.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/tls" "encoding/base64" + "encoding/json" "errors" "fmt" "io" @@ -16,8 +17,6 @@ import ( "testing" "time" - "encoding/json" - "github.com/gofiber/fiber/v2/internal/tlstest" "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp/fasthttputil" From 917263c9e4f53376036af94d192cdb2fe9d91e3e Mon Sep 17 00:00:00 2001 From: wernerr Date: Mon, 22 Aug 2022 08:47:55 +0200 Subject: [PATCH 047/141] Improve pull request template --- .github/pull_request_template.md | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 6dcf949744..5c34c3748c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,11 +1,31 @@ -**Please provide enough information so that others can review your pull request:** +## Description - +Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. +Explain the *details* for making this change. What existing problem does the pull request solve? -**Explain the *details* for making this change. What existing problem does the pull request solve?** +Fixes # (issue) - +## Type of change -**Commit formatting** +Please delete options that are not relevant. -Use emojis on commit messages so it provides an easy way of identifying the purpose or intention of a commit. Check out the emoji cheatsheet here: https://gitmoji.carloscuesta.me/ \ No newline at end of file +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +## Checklist: + +- [ ] For new functionalities I follow the inspiration of the express js framework and built them similar in usage +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation - https://github.com/gofiber/docs for https://docs.gofiber.io/ +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] If new dependencies exist, I have checked that they are really necessary and agreed with the maintainers/community (we want to have as few dependencies as possible) +- [ ] I tried to make my code as fast as possible with as few allocations as possible +- [ ] For new code I have written benchmarks so that they can be analyzed and improved + +## Commit formatting: + +Use emojis on commit messages so it provides an easy way of identifying the purpose or intention of a commit. Check out the emoji cheatsheet here: https://gitmoji.carloscuesta.me/ From f9e87097ab728d433d04c618b0140e9da5c4c0d9 Mon Sep 17 00:00:00 2001 From: wernerr Date: Mon, 22 Aug 2022 08:53:22 +0200 Subject: [PATCH 048/141] Improve pull request template --- .github/pull_request_template.md | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 6dcf949744..5c34c3748c 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,11 +1,31 @@ -**Please provide enough information so that others can review your pull request:** +## Description - +Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. +Explain the *details* for making this change. What existing problem does the pull request solve? -**Explain the *details* for making this change. What existing problem does the pull request solve?** +Fixes # (issue) - +## Type of change -**Commit formatting** +Please delete options that are not relevant. -Use emojis on commit messages so it provides an easy way of identifying the purpose or intention of a commit. Check out the emoji cheatsheet here: https://gitmoji.carloscuesta.me/ \ No newline at end of file +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +## Checklist: + +- [ ] For new functionalities I follow the inspiration of the express js framework and built them similar in usage +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation - https://github.com/gofiber/docs for https://docs.gofiber.io/ +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] If new dependencies exist, I have checked that they are really necessary and agreed with the maintainers/community (we want to have as few dependencies as possible) +- [ ] I tried to make my code as fast as possible with as few allocations as possible +- [ ] For new code I have written benchmarks so that they can be analyzed and improved + +## Commit formatting: + +Use emojis on commit messages so it provides an easy way of identifying the purpose or intention of a commit. Check out the emoji cheatsheet here: https://gitmoji.carloscuesta.me/ From 8540d0afdc56966e1256574f65aa8cf572fa4c3c Mon Sep 17 00:00:00 2001 From: George Bolo Date: Tue, 23 Aug 2022 02:32:21 -0400 Subject: [PATCH 049/141] fixes #2016 - make IP() and IPs() more reliable (#2020) * fixes #2016 - make IP() and IPs() more reliable * improve the performance of IP validation functionality * refactor IP validation and make it a configuration option --- app.go | 7 ++ ctx.go | 87 ++++++++++++++++++++---- ctx_test.go | 186 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 261 insertions(+), 19 deletions(-) diff --git a/app.go b/app.go index f311617e3a..284fea2d91 100644 --- a/app.go +++ b/app.go @@ -369,6 +369,13 @@ type Config struct { trustedProxiesMap map[string]struct{} trustedProxyRanges []*net.IPNet + // If set to true, c.IP() and c.IPs() will validate IP addresses before returning them. + // Also, c.IP() will return only the first valid IP rather than just the raw header + // WARNING: this has a performance cost associated with it. + // + // Default: false + EnableIPValidation bool `json:"enable_ip_validation"` + // If set to true, will print all routes with their method, path and handler. // Default: false EnablePrintRoutes bool `json:"enable_print_routes"` diff --git a/ctx.go b/ctx.go index d8818ff855..e58c6b889e 100644 --- a/ctx.go +++ b/ctx.go @@ -661,33 +661,94 @@ func (c *Ctx) Port() string { } // IP returns the remote IP address of the request. +// If ProxyHeader and IP Validation is configured, it will parse that header and return the first valid IP address. // Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. func (c *Ctx) IP() string { if c.IsProxyTrusted() && len(c.app.config.ProxyHeader) > 0 { - return c.Get(c.app.config.ProxyHeader) + return c.extractIPFromHeader(c.app.config.ProxyHeader) } return c.fasthttp.RemoteIP().String() } -// IPs returns an string slice of IP addresses specified in the X-Forwarded-For request header. -func (c *Ctx) IPs() (ips []string) { - header := c.fasthttp.Request.Header.Peek(HeaderXForwardedFor) - if len(header) == 0 { - return +// validateIPIfEnabled will return the input IP when validation is disabled. +// when validation is enabled, it will return an empty string if the input is not a valid IP. +func (c *Ctx) validateIPIfEnabled(ip string) string { + if c.app.config.EnableIPValidation && net.ParseIP(ip) == nil { + return "" } - ips = make([]string, bytes.Count(header, []byte(","))+1) - var commaPos, i int + return ip +} + +// extractIPsFromHeader will return a slice of IPs it found given a header name in the order they appear. +// When IP validation is enabled, any invalid IPs will be omitted. +func (c *Ctx) extractIPsFromHeader(header string) (ipsFound []string) { + headerValue := c.Get(header) + + // try to gather IPs in the input with minimal allocations to improve performance + ips := make([]string, bytes.Count([]byte(headerValue), []byte(","))+1) + var commaPos, i, validCount int for { - commaPos = bytes.IndexByte(header, ',') + commaPos = bytes.IndexByte([]byte(headerValue), ',') if commaPos != -1 { - ips[i] = utils.Trim(c.app.getString(header[:commaPos]), ' ') - header, i = header[commaPos+1:], i+1 + ips[i] = c.validateIPIfEnabled(utils.Trim(headerValue[:commaPos], ' ')) + if ips[i] != "" { + validCount++ + } + headerValue, i = headerValue[commaPos+1:], i+1 } else { - ips[i] = utils.Trim(c.app.getString(header), ' ') - return + ips[i] = c.validateIPIfEnabled(utils.Trim(headerValue, ' ')) + if ips[i] != "" { + validCount++ + } + break } } + + // filter out any invalid IP(s) that we found + if len(ips) == validCount { + ipsFound = ips + } else { + ipsFound = make([]string, validCount) + var validIndex int + for n := range ips { + if ips[n] != "" { + ipsFound[validIndex] = ips[n] + validIndex++ + } + } + } + return +} + +// extractIPFromHeader will attempt to pull the real client IP from the given header when IP validation is enabled. +// currently, it will return the first valid IP address in header. +// when IP validation is disabled, it will simply return the value of the header without any inspection. +func (c *Ctx) extractIPFromHeader(header string) string { + if c.app.config.EnableIPValidation { + // extract all IPs from the header's value + ips := c.extractIPsFromHeader(header) + + // since X-Forwarded-For has no RFC, it's really up to the proxy to decide whether to append + // or prepend IPs to this list. For example, the AWS ALB will prepend but the F5 BIG-IP will append ;( + // for now lets just go with the first value in the list... + if len(ips) > 0 { + return ips[0] + } + + // return the IP from the stack if we could not find any valid Ips + return c.fasthttp.RemoteIP().String() + } + + // default behaviour if IP validation is not enabled is just to return whatever value is + // in the proxy header. Even if it is empty or invalid + return c.Get(c.app.config.ProxyHeader) +} + +// IPs returns a string slice of IP addresses specified in the X-Forwarded-For request header. +// When IP validation is enabled, only valid IPs are returned. +func (c *Ctx) IPs() (ips []string) { + return c.extractIPsFromHeader(HeaderXForwardedFor) } // Is returns the matching content type, diff --git a/ctx_test.go b/ctx_test.go index 825cbb8966..59b563db14 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -1100,19 +1100,86 @@ func Test_Ctx_PortInHandler(t *testing.T) { // go test -run Test_Ctx_IP func Test_Ctx_IP(t *testing.T) { t.Parallel() + app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) + + // default behaviour will return the remote IP from the stack + utils.AssertEqual(t, "0.0.0.0", c.IP()) + + // X-Forwarded-For is set, but it is ignored because proxyHeader is not set + c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.1") utils.AssertEqual(t, "0.0.0.0", c.IP()) } // go test -run Test_Ctx_IP_ProxyHeader func Test_Ctx_IP_ProxyHeader(t *testing.T) { t.Parallel() - app := New(Config{ProxyHeader: "Real-Ip"}) - c := app.AcquireCtx(&fasthttp.RequestCtx{}) - defer app.ReleaseCtx(c) - utils.AssertEqual(t, "", c.IP()) + + // make sure that the same behaviour exists for different proxy header names + proxyHeaderNames := []string{"Real-Ip", HeaderXForwardedFor} + + for _, proxyHeaderName := range proxyHeaderNames { + app := New(Config{ProxyHeader: proxyHeaderName}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + + c.Request().Header.Set(proxyHeaderName, "0.0.0.1") + utils.AssertEqual(t, "0.0.0.1", c.IP()) + + // without IP validation we return the full string + c.Request().Header.Set(proxyHeaderName, "0.0.0.1, 0.0.0.2") + utils.AssertEqual(t, "0.0.0.1, 0.0.0.2", c.IP()) + + // without IP validation we return invalid IPs + c.Request().Header.Set(proxyHeaderName, "invalid, 0.0.0.2, 0.0.0.3") + utils.AssertEqual(t, "invalid, 0.0.0.2, 0.0.0.3", c.IP()) + + // when proxy header is enabled but the value is empty, without IP validation we return an empty string + c.Request().Header.Set(proxyHeaderName, "") + utils.AssertEqual(t, "", c.IP()) + + // without IP validation we return an invalid IP + c.Request().Header.Set(proxyHeaderName, "not-valid-ip") + utils.AssertEqual(t, "not-valid-ip", c.IP()) + + app.ReleaseCtx(c) + } +} + +// go test -run Test_Ctx_IP_ProxyHeader +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 + proxyHeaderNames := []string{"Real-Ip", HeaderXForwardedFor} + + for _, proxyHeaderName := range proxyHeaderNames { + app := New(Config{EnableIPValidation: true, ProxyHeader: proxyHeaderName}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + + // when proxy header & validation is enabled and the value is a valid IP, we return it + c.Request().Header.Set(proxyHeaderName, "0.0.0.1") + utils.AssertEqual(t, "0.0.0.1", c.IP()) + + // when proxy header & validation is enabled and the value is a list of IPs, we return the first valid IP + c.Request().Header.Set(proxyHeaderName, "0.0.0.1, 0.0.0.2") + utils.AssertEqual(t, "0.0.0.1", c.IP()) + + c.Request().Header.Set(proxyHeaderName, "invalid, 0.0.0.2, 0.0.0.3") + utils.AssertEqual(t, "0.0.0.2", c.IP()) + + // when proxy header & validation is enabled but the value is empty, we will ignore the header + c.Request().Header.Set(proxyHeaderName, "") + utils.AssertEqual(t, "0.0.0.0", c.IP()) + + // when proxy header & validation is enabled but the value is not an IP, we will ignore the header + // and return the IP of the caller + c.Request().Header.Set(proxyHeaderName, "not-valid-ip") + utils.AssertEqual(t, "0.0.0.0", c.IP()) + + app.ReleaseCtx(c) + } } // go test -run Test_Ctx_IP_UntrustedProxy @@ -1141,14 +1208,65 @@ func Test_Ctx_IPs(t *testing.T) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) + + // normal happy path test case + c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, 127.0.0.2, 127.0.0.3") + utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs()) + + // inconsistent space formatting + c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1,127.0.0.2 ,127.0.0.3") + utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs()) + + // invalid IPs are allowed to be returned + c.Request().Header.Set(HeaderXForwardedFor, "invalid, 127.0.0.1, 127.0.0.2") + utils.AssertEqual(t, []string{"invalid", "127.0.0.1", "127.0.0.2"}, c.IPs()) + c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.2") + utils.AssertEqual(t, []string{"127.0.0.1", "invalid", "127.0.0.2"}, c.IPs()) + + // ensure that the ordering of IPs in the header is maintained + c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.3, 127.0.0.1, 127.0.0.2") + utils.AssertEqual(t, []string{"127.0.0.3", "127.0.0.1", "127.0.0.2"}, c.IPs()) + + // empty header + c.Request().Header.Set(HeaderXForwardedFor, "") + utils.AssertEqual(t, 0, len(c.IPs())) + + // missing header + c.Request() + utils.AssertEqual(t, 0, len(c.IPs())) +} + +func Test_Ctx_IPs_With_IP_Validation(t *testing.T) { + t.Parallel() + app := New(Config{EnableIPValidation: true}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + + // normal happy path test case c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, 127.0.0.2, 127.0.0.3") utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs()) + // inconsistent space formatting c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1,127.0.0.2 ,127.0.0.3") utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2", "127.0.0.3"}, c.IPs()) + // invalid IPs are in the header + c.Request().Header.Set(HeaderXForwardedFor, "invalid, 127.0.0.1, 127.0.0.2") + utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2"}, c.IPs()) + c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.2") + utils.AssertEqual(t, []string{"127.0.0.1", "127.0.0.2"}, c.IPs()) + + // ensure that the ordering of IPs in the header is maintained + c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.3, 127.0.0.1, 127.0.0.2") + utils.AssertEqual(t, []string{"127.0.0.3", "127.0.0.1", "127.0.0.2"}, c.IPs()) + + // empty header c.Request().Header.Set(HeaderXForwardedFor, "") utils.AssertEqual(t, 0, len(c.IPs())) + + // missing header + c.Request() + utils.AssertEqual(t, 0, len(c.IPs())) } // go test -v -run=^$ -bench=Benchmark_Ctx_IPs -benchmem -count=4 @@ -1156,14 +1274,70 @@ func Benchmark_Ctx_IPs(b *testing.B) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, 127.0.0.1, 127.0.0.1") + c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.1") + var res []string + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + res = c.IPs() + } + utils.AssertEqual(b, []string{"127.0.0.1", "invalid", "127.0.0.1"}, res) +} + +func Benchmark_Ctx_IPs_With_IP_Validation(b *testing.B) { + app := New(Config{EnableIPValidation: true}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1, invalid, 127.0.0.1") var res []string b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { res = c.IPs() } - utils.AssertEqual(b, []string{"127.0.0.1", "127.0.0.1", "127.0.0.1"}, res) + utils.AssertEqual(b, []string{"127.0.0.1", "127.0.0.1"}, res) +} + +func Benchmark_Ctx_IP_With_ProxyHeader(b *testing.B) { + app := New(Config{ProxyHeader: HeaderXForwardedFor}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1") + var res string + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + res = c.IP() + } + utils.AssertEqual(b, "127.0.0.1", res) +} + +func Benchmark_Ctx_IP_With_ProxyHeader_and_IP_Validation(b *testing.B) { + app := New(Config{ProxyHeader: HeaderXForwardedFor, EnableIPValidation: true}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1") + var res string + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + res = c.IP() + } + utils.AssertEqual(b, "127.0.0.1", res) +} + +func Benchmark_Ctx_IP(b *testing.B) { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Request() + var res string + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + res = c.IP() + } + utils.AssertEqual(b, "0.0.0.0", res) } // go test -run Test_Ctx_Is From 9c98a1fb37c29673032562d1569d904daae06273 Mon Sep 17 00:00:00 2001 From: Jinquan Wang <35188480+wangjq4214@users.noreply.github.com> Date: Wed, 24 Aug 2022 15:16:19 +0800 Subject: [PATCH 050/141] =?UTF-8?q?=F0=9F=90=9B=20bug:=20Case=20sensitivit?= =?UTF-8?q?y=20for=20parameters=20in=20GetRouteURL=20(#2010)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 bug: Case sensitivity for parameters in GetRouteURL * ✏️ perf: error spell --- ctx.go | 20 +++++++++------ ctx_test.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 75 insertions(+), 17 deletions(-) diff --git a/ctx.go b/ctx.go index e58c6b889e..f956c3b626 100644 --- a/ctx.go +++ b/ctx.go @@ -922,7 +922,7 @@ func (c *Ctx) Params(key string, defaultValue ...string) string { if len(key) != len(c.route.Params[i]) { continue } - if c.route.Params[i] == key { + if c.route.Params[i] == key || (!c.app.config.CaseSensitive && utils.EqualFold(c.route.Params[i], key)) { // in case values are not here if len(c.values) <= i || len(c.values[i]) == 0 { break @@ -1243,20 +1243,24 @@ func (c *Ctx) Bind(vars Map) error { func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { buf := bytebufferpool.Get() for _, segment := range route.routeParser.segs { + if !segment.IsParam { + _, err := buf.WriteString(segment.Const) + if err != nil { + return "", err + } + continue + } + for key, val := range params { - if segment.IsParam && (key == segment.ParamName || (segment.IsGreedy && len(key) == 1 && isInCharset(key[0], greedyParameters))) { + isSame := key == segment.ParamName || (!c.app.config.CaseSensitive && utils.EqualFold(key, segment.ParamName)) + isGreedy := (segment.IsGreedy && len(key) == 1 && isInCharset(key[0], greedyParameters)) + if isSame || isGreedy { _, err := buf.WriteString(utils.ToString(val)) if err != nil { return "", err } } } - if !segment.IsParam { - _, err := buf.WriteString(segment.Const) - if err != nil { - return "", err - } - } } location := buf.String() // release buffer diff --git a/ctx_test.go b/ctx_test.go index 59b563db14..273dd14e25 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -1587,6 +1587,11 @@ func Test_Ctx_Params(t *testing.T) { utils.AssertEqual(t, "", c.Params("optional")) return nil }) + app.Get("/test5/:id/:Id", func(c *Ctx) error { + utils.AssertEqual(t, "first", c.Params("id")) + utils.AssertEqual(t, "first", c.Params("Id")) + return nil + }) resp, err := app.Test(httptest.NewRequest(MethodGet, "/test/john", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") @@ -1602,6 +1607,32 @@ func Test_Ctx_Params(t *testing.T) { resp, err = app.Test(httptest.NewRequest(MethodGet, "/test4", nil)) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + resp, err = app.Test(httptest.NewRequest(MethodGet, "/test5/first/second", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") +} + +func Test_Ctx_Params_Case_Sensitive(t *testing.T) { + t.Parallel() + app := New(Config{CaseSensitive: true}) + app.Get("/test/:User", func(c *Ctx) error { + utils.AssertEqual(t, "john", c.Params("User")) + utils.AssertEqual(t, "", c.Params("user")) + return nil + }) + app.Get("/test2/:id/:Id", func(c *Ctx) error { + utils.AssertEqual(t, "first", c.Params("id")) + utils.AssertEqual(t, "second", c.Params("Id")) + return nil + }) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/test/john", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + resp, err = app.Test(httptest.NewRequest(MethodGet, "/test2/first/second", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") } // go test -race -run Test_Ctx_AllParams @@ -3090,16 +3121,39 @@ func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) { // go test -run Test_Ctx_Get_Location_From_Route_name func Test_Ctx_Get_Location_From_Route_name(t *testing.T) { - app := New() - c := app.AcquireCtx(&fasthttp.RequestCtx{}) - defer app.ReleaseCtx(c) - app.Get("/user/:name", func(c *Ctx) error { - return c.SendString(c.Params("name")) - }).Name("User") + t.Run("case insensitive", func(t *testing.T) { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + app.Get("/user/:name", func(c *Ctx) error { + return c.SendString(c.Params("name")) + }).Name("User") - location, err := c.GetRouteURL("User", Map{"name": "fiber"}) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "/user/fiber", location) + location, err := c.GetRouteURL("User", Map{"name": "fiber"}) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "/user/fiber", location) + + location, err = c.GetRouteURL("User", Map{"Name": "fiber"}) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "/user/fiber", location) + }) + + t.Run("case sensitive",func(t *testing.T) { + app := New(Config{CaseSensitive: true}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + app.Get("/user/:name", func(c *Ctx) error { + return c.SendString(c.Params("name")) + }).Name("User") + + location, err := c.GetRouteURL("User", Map{"name": "fiber"}) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "/user/fiber", location) + + location, err = c.GetRouteURL("User", Map{"Name": "fiber"}) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "/user/", location) + }) } // go test -run Test_Ctx_Get_Location_From_Route_name_Optional_greedy From 4d28b1e200f8f86cebfc969ea94a0feddbecd8e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Wed, 24 Aug 2022 10:47:37 +0300 Subject: [PATCH 051/141] :bug: bug: make tlsHandler public to use it with Listener (#2034) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :bug: bug: ClientHelloInfo support for app.Listener * :bug: bug: ClientHelloInfo support for app.Listener * fix * make tlshandler public * update * 🐛 bug: make tlsHandler public to use it with Listener #2034 Co-authored-by: wernerr --- app.go | 10 +++++++++- app_test.go | 15 +++++++++++++++ ctx.go | 6 +++--- ctx_test.go | 2 +- listen.go | 24 +++++++++++++++++++----- 5 files changed, 47 insertions(+), 10 deletions(-) diff --git a/app.go b/app.go index 284fea2d91..a79e28410b 100644 --- a/app.go +++ b/app.go @@ -115,7 +115,7 @@ type App struct { latestRoute *Route latestGroup *Group // TLS handler - tlsHandler *tlsHandler + tlsHandler *TLSHandler } // Config is a struct holding the server settings. @@ -570,6 +570,14 @@ func (app *App) handleTrustedProxy(ipAddress string) { } } +// You can use SetTLSHandler to use ClientHelloInfo when using TLS with Listener. +func (app *App) SetTLSHandler(tlsHandler *TLSHandler) { + // Attach the tlsHandler to the config + app.mutex.Lock() + app.tlsHandler = tlsHandler + app.mutex.Unlock() +} + // Mount attaches another app instance as a sub-router along a routing path. // It's very useful to split up a large API as many independent routers and // compose them as a single service using Mount. The fiber's error handler and diff --git a/app_test.go b/app_test.go index 716970fbf2..b20e0c455c 100644 --- a/app_test.go +++ b/app_test.go @@ -6,6 +6,7 @@ package fiber import ( "bytes" + "crypto/tls" "errors" "fmt" "io" @@ -1560,3 +1561,17 @@ func Test_App_Test_no_timeout_infinitely(t *testing.T) { t.FailNow() } } + +func Test_App_SetTLSHandler(t *testing.T) { + tlsHandler := &TLSHandler{clientHelloInfo: &tls.ClientHelloInfo{ + ServerName: "example.golang", + }} + + app := New() + app.SetTLSHandler(tlsHandler) + + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + + utils.AssertEqual(t, "example.golang", c.ClientHelloInfo().ServerName) +} diff --git a/ctx.go b/ctx.go index f956c3b626..ed80c48ca9 100644 --- a/ctx.go +++ b/ctx.go @@ -68,13 +68,13 @@ type Ctx struct { viewBindMap *dictpool.Dict // Default view map to bind template engine } -// tlsHandle object -type tlsHandler struct { +// TLSHandler object +type TLSHandler struct { clientHelloInfo *tls.ClientHelloInfo } // GetClientInfo Callback function to set CHI -func (t *tlsHandler) GetClientInfo(info *tls.ClientHelloInfo) (*tls.Certificate, error) { +func (t *TLSHandler) GetClientInfo(info *tls.ClientHelloInfo) (*tls.Certificate, error) { t.clientHelloInfo = info return nil, nil } diff --git a/ctx_test.go b/ctx_test.go index 273dd14e25..31f47e7a29 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -1462,7 +1462,7 @@ func Test_Ctx_ClientHelloInfo(t *testing.T) { PSSWithSHA256 = 0x0804 VersionTLS13 = 0x0304 ) - app.tlsHandler = &tlsHandler{clientHelloInfo: &tls.ClientHelloInfo{ + app.tlsHandler = &TLSHandler{clientHelloInfo: &tls.ClientHelloInfo{ ServerName: "example.golang", SignatureSchemes: []tls.SignatureScheme{PSSWithSHA256}, SupportedVersions: []uint16{VersionTLS13}, diff --git a/listen.go b/listen.go index 4d8d25b72d..ca55bb06a3 100644 --- a/listen.go +++ b/listen.go @@ -31,16 +31,20 @@ func (app *App) Listener(ln net.Listener) error { addr, tlsConfig := lnMetadata(app.config.Network, ln) return app.prefork(app.config.Network, addr, tlsConfig) } + // prepare the server for the start app.startupProcess() + // Print startup message if !app.config.DisableStartupMessage { app.startupMessage(ln.Addr().String(), getTlsConfig(ln) != nil, "") } + // Print routes if app.config.EnablePrintRoutes { app.printRoutesMessage() } + // Start listening return app.server.Serve(ln) } @@ -54,21 +58,26 @@ func (app *App) Listen(addr string) error { if app.config.Prefork { return app.prefork(app.config.Network, addr, nil) } + // Setup listener ln, err := net.Listen(app.config.Network, addr) if err != nil { return err } + // prepare the server for the start app.startupProcess() + // Print startup message if !app.config.DisableStartupMessage { app.startupMessage(ln.Addr().String(), false, "") } + // Print routes if app.config.EnablePrintRoutes { app.printRoutesMessage() } + // Start listening return app.server.Serve(ln) } @@ -82,12 +91,14 @@ func (app *App) ListenTLS(addr, certFile, keyFile string) error { if len(certFile) == 0 || len(keyFile) == 0 { return errors.New("tls: provide a valid cert or key path") } + // Set TLS config with handler cert, err := tls.LoadX509KeyPair(certFile, keyFile) if err != nil { return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) } - tlsHandler := &tlsHandler{} + + tlsHandler := &TLSHandler{} config := &tls.Config{ MinVersion: tls.VersionTLS12, Certificates: []tls.Certificate{ @@ -95,6 +106,7 @@ func (app *App) ListenTLS(addr, certFile, keyFile string) error { }, GetCertificate: tlsHandler.GetClientInfo, } + // Prefork is supported if app.config.Prefork { return app.prefork(app.config.Network, addr, config) @@ -103,23 +115,25 @@ func (app *App) ListenTLS(addr, certFile, keyFile string) error { // Setup listener ln, err := net.Listen(app.config.Network, addr) ln = tls.NewListener(ln, config) - if err != nil { return err } + // prepare the server for the start app.startupProcess() + // Print startup message if !app.config.DisableStartupMessage { app.startupMessage(ln.Addr().String(), true, "") } + // Print routes if app.config.EnablePrintRoutes { app.printRoutesMessage() } // Attach the tlsHandler to the config - app.tlsHandler = tlsHandler + app.SetTLSHandler(tlsHandler) // Start listening return app.server.Serve(ln) @@ -147,7 +161,7 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) clientCertPool := x509.NewCertPool() clientCertPool.AppendCertsFromPEM(clientCACert) - tlsHandler := &tlsHandler{} + tlsHandler := &TLSHandler{} config := &tls.Config{ MinVersion: tls.VersionTLS12, ClientAuth: tls.RequireAndVerifyClientCert, @@ -183,7 +197,7 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) } // Attach the tlsHandler to the config - app.tlsHandler = tlsHandler + app.SetTLSHandler(tlsHandler) // Start listening return app.server.Serve(ln) From 6c6661a32d71d9c8ebc928fec1dcfac7a1c3a839 Mon Sep 17 00:00:00 2001 From: micziz <99438936+micziz@users.noreply.github.com> Date: Wed, 24 Aug 2022 12:50:45 +0200 Subject: [PATCH 052/141] =?UTF-8?q?=F0=9F=93=9D=20docs:=20Update=20Italian?= =?UTF-8?q?=20readme=20(#2042)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update README_it.md --- .github/README_it.md | 98 ++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/.github/README_it.md b/.github/README_it.md index f42bba9f46..8c39316f74 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -82,7 +82,7 @@ Fiber è un framework web inspirato a Express costruito sopra Fasthttp, un motore HTTP molto veloce per Go. Progettato per semplificare le cose per uno sviluppo veloce con zero allocazione di memoria e le prestazioni in mente.

    -## ⚡️ Avvio rapido +## ⚡️ Inizia velocemente ```go package main @@ -111,9 +111,9 @@ Questi test sono stati eseguiti da [TechEmpower](https://www.techempower.com/ben ## ⚙️ Installazione -Assicurati di avere Go ([download](https://go.dev/dl/)) installato. Versione `1.14` o superiore. +Assicurati di avere Go ([per scaricalro](https://go.dev/dl/)) installato. Devi avere la versione `1.14` o superiore. -Inizializza il tuo progetto creando una cartella e successivamente lanciando `go mod init github.com/your/repo` ([per maggiori info](https://go.dev/blog/using-go-modules)) da dentro la cartella. Dopo installa Fiber con il comando [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them): +Inizializza il tuo progetto creando una cartella e successivamente usando il comando `go mod init github.com/la-tua/repo` ([per maggiori informazioni](https://go.dev/blog/using-go-modules)) dentro la cartella. Dopodiche installa Fiber con il comando [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them): ```bash go get -u github.com/gofiber/fiber/v2 @@ -139,7 +139,7 @@ go get -u github.com/gofiber/fiber/v2 I nuovi gopher che passano da [Node.js](https://nodejs.org/en/about/) a [Go](https://go.dev/doc/) hanno a che fare con una curva di apprendimento prima di poter iniziare a creare le proprie applicazioni web o microservizi. Fiber, come **web framework** , è stato creato con l'idea di **minimalismo** e seguendo lo '**UNIX way**' , così i nuovi gopher posso entrare rapidamente nel mondo di Go con un caldo e fidato benvenuto. -Fiber è **inspirato** da Express, il web framework più popolare su internet. Abbiamo combiniamo la **facilità** di Express e **le pure prestazioni** di Go. Se hai mai implementato una applicazione web in Node.js (_utilizzando Express o simili_), allora i tanti metodi e principi ti saranno **molto familiari**. +Fiber è **ispirato** da Express, il web framework più popolare su internet. Abbiamo combiniamo la **facilità** di Express e **le prestazioni** di Go. Se hai mai implementato una applicazione web in Node.js (_utilizzando Express o simili_), allora i tanti metodi e principi ti saranno **molto familiari**. ## ⚠️ Limitazioni @@ -148,7 +148,7 @@ Fiber è **inspirato** da Express, il web framework più popolare su internet. A ## 👀 Esempi -Qui sotto trovi molti dei più comuni esempi. Se vuoi vedere ulteriori esempi, visita il nostro [Recipes repository](https://github.com/gofiber/recipes) o la nostra [documentazione API](https://docs.gofiber.io) . +Qui sotto trovi molti dei più comuni esempi. Se vuoi vedere ulteriori esempi, visita il nostro [repository delle ricette](https://github.com/gofiber/recipes) o la nostra [documentazione API](https://docs.gofiber.io) . #### 📖 [**Routing di base**](https://docs.gofiber.io/#basic-routing) @@ -191,7 +191,7 @@ func main() { ``` -#### 📖 [**Route Naming**](https://docs.gofiber.io/api/app#name) +#### 📖 [**Dare nomi alle Route**](https://docs.gofiber.io/api/app#name) ```go func main() { @@ -275,17 +275,17 @@ func main() {
    📚 Mostra altri esempi -### View engine +### Motori di template -📖 [Config](https://docs.gofiber.io/api/fiber#config) -📖 [Engines](https://github.com/gofiber/template) +📖 [Configurazione](https://docs.gofiber.io/api/fiber#config) +📖 [Motori](https://github.com/gofiber/template) 📖 [Render](https://docs.gofiber.io/api/ctx#render) -Fiber usa di default [html/template](https://pkg.go.dev/html/template/) quando nessun view engine è stato impostato. +Fiber usa di default [html/template](https://pkg.go.dev/html/template/) quando nessun motore template è stato impostato. -Se vuoi eseguire parzialmente o utilizzare un engine differente come [amber](https://github.com/eknkc/amber), [handlebars](https://github.com/aymerick/raymond), [mustache](https://github.com/cbroglie/mustache) o [pug](https://github.com/Joker/jade) ecc.. +Se vuoi eseguire parzialmente o utilizzare un motore differente come [amber](https://github.com/eknkc/amber), [handlebars](https://github.com/aymerick/raymond), [mustache](https://github.com/cbroglie/mustache) o [pug](https://github.com/Joker/jade) ecc.. -Dai un'occhiata al pacchetto [Template](https://github.com/gofiber/template) che supporta multipli view engine. +Dai un'occhiata al pacchetto [Template](https://github.com/gofiber/template) che supporta multipli motore template. ```go package main @@ -315,7 +315,7 @@ func main() { ### Raggruppare le route -📖 [Group](https://docs.gofiber.io/api/app#group) +📖 [Gruppi](https://docs.gofiber.io/api/app#group) ```go func middleware(c *fiber.Ctx) error { @@ -460,7 +460,7 @@ func main() { } ``` -### WebSocket Upgrade +### WebSocket 📖 [Websocket](https://github.com/gofiber/websocket) @@ -494,7 +494,7 @@ func main() { } ``` -### Server-Sent Events +### Eventi dal server 📖 [More Info](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) @@ -535,7 +535,7 @@ func main() { } ``` -### Recover middleware +### Recupera middleware 📖 [Recover](https://docs.gofiber.io/api/middleware/recover) @@ -560,7 +560,7 @@ func main() {
    -### Utilizzare Trusted Proxy +### Utilizzare i Proxy fidati 📖 [Config](https://docs.gofiber.io/api/fiber#config) @@ -591,26 +591,26 @@ Qui una lista dei middleware inclusi con Fiber. | Middleware | Descrizione | | :------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Basic auth middleware provides an HTTP basic authentication. It calls the next handler for valid credentials and 401 Unauthorized for missing or invalid credentials. | -| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Intercept and cache responses | -| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Compression middleware for Fiber, it supports `deflate`, `gzip` and `brotli` by default. | -| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | -| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | -| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | -| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | -| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | -| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | -| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | FileSystem middleware for Fiber, special thanks and credits to Alireza Salary | -| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Rate-limiting middleware for Fiber. Use to limit repeated requests to public APIs and/or endpoints such as password reset. | -| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP request/response logger. | -| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Monitor middleware that reports server metrics, inspired by express-status-monitor | -| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Special thanks to Matthew Lee \(@mthli\) | -| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Allows you to proxy requests to a multiple servers | -| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Recover middleware recovers from panics anywhere in the stack chain and handles the control to the centralized[ ErrorHandler](https://docs.gofiber.io/guide/error-handling). | -| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Adds a requestid to every request. | -| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | -| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | -| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Middleware basico di autenticazione usando http. Chiama il suo handler se le credenziali sono guiste e il codice 401 Unauthorized per credenziali mancanti o invailde. | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Intercetta e mette nella cache la risposta | +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Middlewere di compressione per Fiber, supporta `deflate`, `gzip` e `brotli` di default. | +| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Ti permette di usare cross-origin resource sharing \(CORS\) con tante optzioni. | +| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Ti protegge da attachi CSRF. | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Middleware che encrypta i valori dei cookie. | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Middlewere che permette alle cache di essere piu efficienti e salvare banda, come un web server non deve rimandare il messagio pieno se il contenuto non è cambiato. | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Middleware che serve via il suo runtime server HTTP varianti esposte in formato JSON. | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignora favicon dai logs o serve dalla memoria se un filepath si dà. | +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Middleware per il FileSystem per Fiber, grazie tante e crediti a Alireza Salary | +| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Middelwere per Rate-limiting per Fiber. Usato per limitare richieste continue agli APIs publici e/o endpoints come un password reset. | +| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | Logger HTTP per richiesta/risposta. | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Middleware per monitorare che riporta metriche server, ispirato da express-status-monitor | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Grazie tante a Matthew Lee \(@mthli\) | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Ti permette di fare richieste proxy a multipli server. | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Middleware per recuperare dai attachi di panico da tutte le parti nella stack chain e da il controllo al centralizzato[ ErrorHandler](https://docs.gofiber.io/guide/error-handling). | +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Aggiunge un requestid a ogni richiesta. | +| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Middelwere per sessioni. NOTA: Questo middleware usa il nostro Storage package. | +| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Middleware che salta un wrapped handler se un predicate è vero. | +| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Aggiunge un tempo massimo per una richiesta e lo manda a ErrorHandler se si supera. | ## 🧬 Middleware Esterni @@ -618,26 +618,26 @@ La lista dei moduli middleware hostati esternamente e mantenuti dal [team di Fib | Middleware | Descrizione | | :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | -| [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | -| [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | -| [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | +| [adaptor](https://github.com/gofiber/adaptor) | Converte gli handler net/http a/da i request handlers di Fiber, grazie tante a @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Aiuta a mettere sicurezza alla tua app usando vari header HTTP. | +| [jwt](https://github.com/gofiber/jwt) | Usa JSON Web Token \(JWT\) auth. | +| [keyauth](https://github.com/gofiber/keyauth) | Usa auth basato su chiavi. | +| [redirect](https://github.com/gofiber/redirect) | Middleware per reinderizzare | +| [rewrite](https://github.com/gofiber/rewrite) | Riscrive la path all URL con le regole date. Puo essere di aiuto per compatibilita o per creare link puliti e piu descrittivi. | +| [storage](https://github.com/gofiber/storage) | Dirver di storage che implementa la interfaccia Storage, fatto per essere usato con vari Fiber middleware. | +| [template](https://github.com/gofiber/template) | Questo pachetto contiene 8 motori template che possono essere usati con Fiber `v1.10.x`. Versione di go neccesaria: 1.13+. | +| [websocket](https://github.com/gofiber/websocket) | Basato su Fasthttp WebSocket per Fiber con supporto per Locals! | ## 🕶️ Awesome List -For more articles, middlewares, examples or tools check our [awesome list](https://github.com/gofiber/awesome-fiber). +Per piu articoli, middlewares, esempi o attrezzi puoi usare la [awesome list](https://github.com/gofiber/awesome-fiber). ## 👍 Contribuire Se vuoi dirci **grazie** e/o supportare lo sviluppo di `Fiber`: 1. Aggiungi una [stella GitHub](https://github.com/gofiber/fiber/stargazers) al progetto. -2. Tweeta del progetto [su Twitter](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber). +2. Twitta del progetto [su Twitter](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber). 3. Scrivi una recensione o un tutorial su [Medium](https://medium.com/), [Dev.to](https://dev.to/) o sul tuo blog personale. 4. Supporta il progetto donando una [tazza di caffè](https://buymeacoff.ee/fenny). @@ -666,7 +666,7 @@ Fiber è un progetto open source che va avanti grazie alle donazioni per pagare | ![](https://avatars.githubusercontent.com/u/31022056?s=25) | [@marvinjwendt](https://github.com/marvinjwendt) | ☕ x 1 | | ![](https://avatars.githubusercontent.com/u/31921460?s=25) | [@toishy](https://github.com/toishy) | ☕ x 1 | -## ‎‍💻 Code Contributor +## ‎‍💻 Contributori Code Contributors From b9704571093276371144b44e20961f676fb0cfc0 Mon Sep 17 00:00:00 2001 From: GalvinGao Date: Wed, 24 Aug 2022 08:09:33 -0400 Subject: [PATCH 053/141] docs: change support claim up to go 1.19 (#2043) --- .github/README.md | 2 +- .github/README_de.md | 2 +- .github/README_es.md | 2 +- .github/README_fr.md | 2 +- .github/README_he.md | 2 +- .github/README_id.md | 2 +- .github/README_it.md | 2 +- .github/README_ja.md | 2 +- .github/README_ko.md | 2 +- .github/README_nl.md | 2 +- .github/README_pt.md | 2 +- .github/README_ru.md | 2 +- .github/README_sa.md | 2 +- .github/README_tr.md | 2 +- .github/README_zh-CN.md | 2 +- .github/README_zh-TW.md | 2 +- 16 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/README.md b/.github/README.md index a3c045acba..85406ea913 100644 --- a/.github/README.md +++ b/.github/README.md @@ -148,7 +148,7 @@ Fiber is **inspired** by Express, the most popular web framework on the Internet We **listen** to our users in [issues](https://github.com/gofiber/fiber/issues), Discord [channel](https://gofiber.io/discord) _and all over the Internet_ to create a **fast**, **flexible** and **friendly** Go web framework for **any** task, **deadline** and developer **skill**! Just like Express does in the JavaScript world. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Examples diff --git a/.github/README_de.md b/.github/README_de.md index 094dd85aa1..8964567e1b 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -142,7 +142,7 @@ Neue Gopher, welche von [Node.js](https://nodejs.org/en/about/) zu [Go](https:// Fiber ist **inspiriert** von Express.js, dem beliebtesten Web-Framework im Internet. Wir haben die **Leichtigkeit** von Express und die **Rohleistung** von Go kombiniert. Wenn du jemals eine Webanwendung mit Node.js implementiert hast (_mit Express.js oder ähnlichem_), werden dir viele Methoden und Prinzipien **sehr vertraut** vorkommen. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Beispiele diff --git a/.github/README_es.md b/.github/README_es.md index b5098d739a..29416f26e4 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -142,7 +142,7 @@ Los nuevos gophers que hacen el cambio de [Node.js](https://nodejs.org/en/about/ Fiber está **inspirado** en Expressjs, el framework web más popular en Internet. Combinamos la **facilidad** de Express y **el rendimiento bruto** de Go. Si alguna vez ha implementado una aplicación web en Node.js ( _utilizando Express.js o similar_ ), muchos métodos y principios le parecerán **muy comunes** . ## ⚠️ Limitantes -* Debido a que Fiber utiliza unsafe, la biblioteca no siempre será compatible con la última versión de Go. Fiber 2.29.0 ha sido probado con las versiones de Go 1.14 a 1.18. +* Debido a que Fiber utiliza unsafe, la biblioteca no siempre será compatible con la última versión de Go. Fiber 2.29.0 ha sido probado con las versiones de Go 1.14 a 1.19. * Fiber no es compatible con interfaces net/http. Esto significa que no lo podrá usar en proyectos como qglgen, go-swagger, u otros que son parte del ecosistema net/http. ## 👀 Ejemplos diff --git a/.github/README_fr.md b/.github/README_fr.md index e2d3caae37..ebb24e34ad 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -142,7 +142,7 @@ Les nouveaux gophers qui passent de [Node.js](https://nodejs.org/en/about/) à [ Fiber est **inspiré** par Express, le framework web le plus populaire d'Internet. Nous avons combiné la **facilité** d'Express, et la **performance brute** de Go. Si vous avez déja développé une application web en Node.js (_en utilisant Express ou équivalent_), alors de nombreuses méthodes et principes vous sembleront **familiers**. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Exemples diff --git a/.github/README_he.md b/.github/README_he.md index 4aa0dd28e1..c687038739 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -187,7 +187,7 @@ Fiber נוצרה **בהשראת** Express, ה-web framework הפופולרית
    ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 דוגמאות diff --git a/.github/README_id.md b/.github/README_id.md index e43d0e7491..6e3e41c560 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -145,7 +145,7 @@ Kami **mendengarkan** para pengguna di [GitHub Issues](https://github.com/gofibe ## ⚠️ Limitasi -* Karena penggunaan Fiber yang tidak aman, perpustakaan mungkin tidak selalu kompatibel dengan versi Go terbaru. Fiber 2.29.0 telah diuji dengan Go versi 1.14 hingga 1.18. +* Karena penggunaan Fiber yang tidak aman, perpustakaan mungkin tidak selalu kompatibel dengan versi Go terbaru. Fiber 2.29.0 telah diuji dengan Go versi 1.14 hingga 1.19. * Fiber tidak kompatibel dengan antarmuka net/http. Ini berarti kamu tidak akan dapat menggunakan proyek seperti gqlgen, go-swagger, atau lainnya yang merupakan bagian dari ekosistem net/http. ## 👀 Contoh diff --git a/.github/README_it.md b/.github/README_it.md index 8c39316f74..a07aa84d85 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -143,7 +143,7 @@ Fiber è **ispirato** da Express, il web framework più popolare su internet. Ab ## ⚠️ Limitazioni -* Dato che Fiber utilizza unsafe, la libreria non sempre potrebbe essere compatibile con l'ultima versione di Go. Fiber 2.29.0 è stato testato con la versioni 1.14 alla 1.18 di Go. +* Dato che Fiber utilizza unsafe, la libreria non sempre potrebbe essere compatibile con l'ultima versione di Go. Fiber 2.29.0 è stato testato con la versioni 1.14 alla 1.19 di Go. * Fiber non è compatibile con le interfacce net/http. Questo significa che non è possibile utilizzare progetti come qglgen, go-swagger, o altri che fanno parte dell'ecosistema net/http. ## 👀 Esempi diff --git a/.github/README_ja.md b/.github/README_ja.md index b9a437b60e..6d2cda5293 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -147,7 +147,7 @@ Fiber は人気の高い Web フレームワークである Expressjs に**イ もしも、Web アプリケーションを Express 等の Node.js フレームワークで実装した経験があれば、多くの方法や原理がとても**馴染み深い**でしょう。 ## ⚠️ 制限時効 -* Fiberはunsafeパッケージを使用しているため、最新のGoバージョンと互換性がない場合があります。Fiber 2.29.0 は、Go のバージョン 1.14 から 1.18 でテストされています。 +* Fiberはunsafeパッケージを使用しているため、最新のGoバージョンと互換性がない場合があります。Fiber 2.29.0 は、Go のバージョン 1.14 から 1.19 でテストされています。 * Fiberはnet/httpインターフェースと互換性がありません。つまり、gqlgenやgo-swaggerなど、net/httpのエコシステムの一部であるプロジェクトを使用することができません。 ## 👀 例 diff --git a/.github/README_ko.md b/.github/README_ko.md index 11463c4e3c..6b4ba34fdb 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -144,7 +144,7 @@ Fiber는 인터넷에서 가장 인기있는 웹 프레임워크인 Express에 우리는 **어떤한** 작업, **마감일정**, 개발자의 **기술**이던간에 **빠르고**, **유연하고**, **익숙한** Go 웹 프레임워크를 만들기 위해 사용자들의 [이슈들](https://github.com/gofiber/fiber/issues)을(그리고 모든 인터넷을 통해) **듣고 있습니다**! Express가 자바스크립트 세계에서 하는 것 처럼요. ## ⚠️ 한계점 -* Fiber는 unsafe 패키지를 사용하기 때문에 최신 Go버전과 호환되지 않을 수 있습니다.Fiber 2.29.0은 Go 버전 1.14에서 1.18로 테스트되고 있습니다. +* Fiber는 unsafe 패키지를 사용하기 때문에 최신 Go버전과 호환되지 않을 수 있습니다.Fiber 2.29.0은 Go 버전 1.14에서 1.19로 테스트되고 있습니다. * Fiber는 net/http 인터페이스와 호환되지 않습니다.즉, gqlgen이나 go-swagger 등 net/http 생태계의 일부인 프로젝트를 사용할 수 없습니다. ## 👀 예제 diff --git a/.github/README_nl.md b/.github/README_nl.md index 9bc3dd228c..d7ffd4daa1 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -144,7 +144,7 @@ Fiber is **geïnspireerd** door Express, het populairste webframework op interne We **luisteren** naar onze gebruikers in [issues](https://github.com/gofiber/fiber/issues) (_en overal op het internet_) om een **snelle**, **flexibele** en **vriendelijk** Go web framework te maken voor **elke** taak, **deadline** en ontwikkelaar **vaardigheid**! Net zoals Express dat doet in de JavaScript-wereld. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Voorbeelden diff --git a/.github/README_pt.md b/.github/README_pt.md index 714f553eb4..f15f4e3a94 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -142,7 +142,7 @@ Os novos gophers que mudaram do [Node.js](https://nodejs.org/en/about/) para o [ O Fiber é **inspirado** no Express, o framework web mais popular da Internet. Combinamos a **facilidade** do Express e com o **desempenho bruto** do Go. Se você já implementou um aplicativo web com Node.js ( _usando Express.js ou similar_ ), então muitos métodos e princípios parecerão **muito familiares** para você. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Exemplos diff --git a/.github/README_ru.md b/.github/README_ru.md index 5fc5191d33..eaa171eeac 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -144,7 +144,7 @@ Fiber **вдохновлен** Express, самым популярным веб Мы **прислушиваемся** к нашим пользователям в [issues](https://github.com/gofiber/fiber/issues), Discord [канале](https://gofiber.io/discord) _и в остальном Интернете_, чтобы создать **быстрый**, **гибкий** и **дружелюбный** веб фреймворк на Go для **любых** задач, **дедлайнов** и **уровней** разработчиков! Как это делает Express в мире JavaScript. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Примеры diff --git a/.github/README_sa.md b/.github/README_sa.md index 34199e355a..6e90ca81ee 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -158,7 +158,7 @@ Fiber هو **مستوحى** من Express, إطار الويب الأكثر شع ** و تطوير **مهارات**! فقط مثل Express تفعل لـ JavaScript عالم. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.18. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 أمثلة diff --git a/.github/README_tr.md b/.github/README_tr.md index ed7e7337cb..3866ff7c13 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -143,7 +143,7 @@ Fiber, internet üzerinde en popüler web framework'ü olan Express'ten **esinle ## ⚠️ Sınırlamalar -- Fiber unsafe kullanımı sebebiyle Go'nun son sürümüyle her zaman uyumlu olmayabilir. Fiber 2.29.0, Go 1.14 ile 1.18 sürümleriyle test edildi. +- Fiber unsafe kullanımı sebebiyle Go'nun son sürümüyle her zaman uyumlu olmayabilir. Fiber 2.29.0, Go 1.14 ile 1.19 sürümleriyle test edildi. - Fiber net/http arabirimiyle uyumlu değildir. Yani gqlgen veya go-swagger gibi net/http ekosisteminin parçası olan projeleri kullanamazsınız. ## 👀 Örnekler diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index 8024583e82..5369548a48 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -144,7 +144,7 @@ go get -u github.com/gofiber/fiber/v2 我们会**倾听**用户在[issues](https://github.com/gofiber/fiber/issues)和 Discord [channel](https://gofiber.io/discord)和在互联网上的所有诉求,为了创建一个能让有着任何技术栈的开发者都能在deadline前完成任务的**迅速**,**灵活**以及**友好**的`Go web`框架,就像`Express`在`JavaScript`世界中一样。 ## ⚠️ 限制 -* 由于 Fiber 使用了 unsafe 特性,导致其可能与最新的 Go 版本不兼容。Fiber 2.29.0 已经在 Go 1.14 到 1.18 上测试过。 +* 由于 Fiber 使用了 unsafe 特性,导致其可能与最新的 Go 版本不兼容。Fiber 2.29.0 已经在 Go 1.14 到 1.19 上测试过。 * Fiber 与 net/http 接口不兼容。也就是说你无法使用 gqlen,go-swagger 或者任何其他属于 net/http 生态的项目。 ## 👀 示例 diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index 38249ab47b..1d3907cb76 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -144,7 +144,7 @@ Fiber **受到** 網路上最流行的 Web 框架 ExpressJS**啟發**,結合 E 有什麼問題請發[issues](https://github.com/gofiber/fiber/issues)或加入 Discord [channel](https://gofiber.io/discord)討論,我們想要創造**快速**、**彈性**、**友善**的社群給**任何人**使用!就像 Express 那樣。 ## 限制 -* 由於 Fiber 使用了 unsafe,該庫可能並不總是與最新的 Go 版本兼容。 Fiber 2.29.0 已經用 Go 版本 1.14 到 1.18 進行了測試。 +* 由於 Fiber 使用了 unsafe,該庫可能並不總是與最新的 Go 版本兼容。 Fiber 2.29.0 已經用 Go 版本 1.14 到 1.19 進行了測試。 * Fiber 與 net/http 接口不兼容。 這意味著您將無法使用 gqlgen、go-swagger 或任何其他屬於 net/http 生態系統的項目。 ## 👀 範例 From 235ff1649c56b92e0a42fd96174ee99734c22cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Thu, 25 Aug 2022 19:27:15 +0300 Subject: [PATCH 054/141] :memo: docs: convert gitter, telegram links to discord --- .github/CODE_OF_CONDUCT.md | 2 +- .github/CONTRIBUTING.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md index 3d1f2b5781..c8d7ee4458 100644 --- a/.github/CODE_OF_CONDUCT.md +++ b/.github/CODE_OF_CONDUCT.md @@ -36,7 +36,7 @@ This Code of Conduct applies within all community spaces, and also applies when ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [Gitter](https://gitter.im/gofiber/community). All complaints will be reviewed and investigated promptly and fairly. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at [Discord](https://gofiber.io/discord). All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index da8566f7d9..7a84e21e40 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing -When contributing to this repository, please first discuss the change you wish to make via our [Telegram](https://t.me/gofiber) group, by creating an [issue](https://github.com/gofiber/fiber/issues) or any other method with the owners of this repository before making a change. +When contributing to this repository, please first discuss the change you wish to make via our [Discord](https://gofiber.io/discord) server, by creating an [issue](https://github.com/gofiber/fiber/issues) or any other method with the owners of this repository before making a change. Please note: we have a [code of conduct](https://github.com/gofiber/fiber/blob/master/.github/CODE_OF_CONDUCT.md), please follow it in all your interactions with the `Fiber` project. @@ -24,4 +24,4 @@ If you want to say **thank you** and/or support the active development of `Fiber 1. Add a [GitHub Star](https://github.com/gofiber/fiber/stargazers) to the project. 2. Tweet about the project [on your Twitter](https://twitter.com/intent/tweet?text=%F0%9F%9A%80%20Fiber%20%E2%80%94%20is%20an%20Express.js%20inspired%20web%20framework%20build%20on%20Fasthttp%20for%20%23Go%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber). 3. Write a review or tutorial on [Medium](https://medium.com/), [Dev.to](https://dev.to/) or personal blog. -4. Support the project by donating a [cup of coffee](https://buymeacoff.ee/fenny). \ No newline at end of file +4. Support the project by donating a [cup of coffee](https://buymeacoff.ee/fenny). From 01ea139da4f0fd77e102898337846d7371f33439 Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Fri, 26 Aug 2022 14:32:46 +0430 Subject: [PATCH 055/141] :mag_right: fix unhandled errors (#2048) fix unhandled errors --- app_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app_test.go b/app_test.go index b20e0c455c..fcb623bdfb 100644 --- a/app_test.go +++ b/app_test.go @@ -499,17 +499,20 @@ func Test_App_Order(t *testing.T) { app := New() app.Get("/test", func(c *Ctx) error { - c.Write([]byte("1")) + _, err := c.Write([]byte("1")) + utils.AssertEqual(t, nil, err) return c.Next() }) app.All("/test", func(c *Ctx) error { - c.Write([]byte("2")) + _, err := c.Write([]byte("2")) + utils.AssertEqual(t, nil, err) return c.Next() }) app.Use(func(c *Ctx) error { - c.Write([]byte("3")) + _, err := c.Write([]byte("3")) + utils.AssertEqual(t, nil, err) return nil }) From ffb2d4cb1a6417d94a36237e9b579782e6ea9185 Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Fri, 26 Aug 2022 16:40:46 +0430 Subject: [PATCH 056/141] fix unhandled error in cache package tests (#2049) * fix unhandled errors * fix unhandled error in cache package test * omit variable type * omit variable type * rename variable because collide with the imported package name --- internal/go-ole/connect.go | 4 ++-- internal/schema/decoder.go | 12 ++++++------ middleware/cache/cache_test.go | 6 +++++- middleware/session/session_test.go | 6 +++--- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/internal/go-ole/connect.go b/internal/go-ole/connect.go index b2ac2ec67a..b910f0e329 100644 --- a/internal/go-ole/connect.go +++ b/internal/go-ole/connect.go @@ -44,8 +44,8 @@ func (c *Connection) Release() { // Load COM object from list of programIDs or strings. func (c *Connection) Load(names ...string) (errors []error) { - var tempErrors []error = make([]error, len(names)) - var numErrors int = 0 + var tempErrors = make([]error, len(names)) + var numErrors = 0 for _, name := range names { err := c.Create(name) if err != nil { diff --git a/internal/schema/decoder.go b/internal/schema/decoder.go index 9d44822202..b63c45e1dc 100644 --- a/internal/schema/decoder.go +++ b/internal/schema/decoder.go @@ -74,19 +74,19 @@ func (d *Decoder) Decode(dst interface{}, src map[string][]string) error { } v = v.Elem() t := v.Type() - errors := MultiError{} + multiError := MultiError{} for path, values := range src { if parts, err := d.cache.parsePath(path, t); err == nil { if err = d.decode(v, path, parts, values); err != nil { - errors[path] = err + multiError[path] = err } } else if !d.ignoreUnknownKeys { - errors[path] = UnknownKeyError{Key: path} + multiError[path] = UnknownKeyError{Key: path} } } - errors.merge(d.checkRequired(t, src)) - if len(errors) > 0 { - return errors + multiError.merge(d.checkRequired(t, src)) + if len(multiError) > 0 { + return multiError } return nil } diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go index 4c7e155a36..f991d24b0a 100644 --- a/middleware/cache/cache_test.go +++ b/middleware/cache/cache_test.go @@ -5,6 +5,7 @@ package cache import ( "bytes" "fmt" + "io" "io/ioutil" "math" "net/http" @@ -126,7 +127,10 @@ func Test_Cache_WithSeveralRequests(t *testing.T) { rsp, err := app.Test(httptest.NewRequest(http.MethodGet, fmt.Sprintf("/%d", id), nil)) utils.AssertEqual(t, nil, err) - defer rsp.Body.Close() + defer func(Body io.ReadCloser) { + err := Body.Close() + utils.AssertEqual(t, nil, err) + }(rsp.Body) idFromServ, err := ioutil.ReadAll(rsp.Body) utils.AssertEqual(t, nil, err) diff --git a/middleware/session/session_test.go b/middleware/session/session_test.go index 4afef2458d..19bf121fa0 100644 --- a/middleware/session/session_test.go +++ b/middleware/session/session_test.go @@ -127,9 +127,9 @@ func Test_Session_Types(t *testing.T) { Name: "John", } // set value - var vbool bool = true - var vstring string = "str" - var vint int = 13 + var vbool = true + var vstring = "str" + var vint = 13 var vint8 int8 = 13 var vint16 int16 = 13 var vint32 int32 = 13 From aef7ea53b32aa336b7c41cb1ca1a5faa816be784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Fri, 26 Aug 2022 15:16:06 +0300 Subject: [PATCH 057/141] :bug: bug: fix route constraints problems (#2033) * :bug: bug: fix route constraints problems * escape support for data * exactLen -> len --- helpers.go | 31 +++++++++++++++++-------------- path.go | 46 ++++++++++++++++++++++++++++++++++++---------- path_test.go | 52 +++++++++++++++++++++++++++------------------------- 3 files changed, 80 insertions(+), 49 deletions(-) diff --git a/helpers.go b/helpers.go index f5e1ee55d8..b4046b4730 100644 --- a/helpers.go +++ b/helpers.go @@ -698,18 +698,21 @@ const ( // Route Constraints const ( - ConstraintInt = "int" - ConstraintBool = "bool" - ConstraintFloat = "float" - ConstraintAlpha = "alpha" - ConstraintGuid = "guid" - ConstraintMinLen = "minLen" - ConstraintMaxLen = "maxLen" - ConstraintExactLen = "exactLen" - ConstraintBetweenLen = "betweenLen" - ConstraintMin = "min" - ConstraintMax = "max" - ConstraintRange = "range" - ConstraintDatetime = "datetime" - ConstraintRegex = "regex" + ConstraintInt = "int" + ConstraintBool = "bool" + ConstraintFloat = "float" + ConstraintAlpha = "alpha" + ConstraintGuid = "guid" + ConstraintMinLen = "minLen" + ConstraintMaxLen = "maxLen" + ConstraintLen = "len" + ConstraintBetweenLen = "betweenLen" + ConstraintMinLenLower = "minlen" + ConstraintMaxLenLower = "maxlen" + ConstraintBetweenLenLower = "betweenlen" + ConstraintMin = "min" + ConstraintMax = "max" + ConstraintRange = "range" + ConstraintDatetime = "datetime" + ConstraintRegex = "regex" ) diff --git a/path.go b/path.go index 16d1b2a37d..db2818dd00 100644 --- a/path.go +++ b/path.go @@ -7,6 +7,7 @@ package fiber import ( + "fmt" "regexp" "strconv" "strings" @@ -79,7 +80,7 @@ const ( guidConstraint minLenConstraint maxLenConstraint - exactLenConstraint + lenConstraint betweenLenConstraint minConstraint maxConstraint @@ -261,7 +262,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r if hasConstraint := (parameterConstraintStart != -1 && parameterConstraintEnd != -1); hasConstraint { constraintString := pattern[parameterConstraintStart+1 : parameterConstraintEnd] - userconstraints := strings.Split(constraintString, string(parameterConstraintSeparatorChars)) + userconstraints := splitNonEscaped(constraintString, string(parameterConstraintSeparatorChars)) constraints = make([]*Constraint, 0, len(userconstraints)) for _, c := range userconstraints { @@ -272,7 +273,15 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r if start != -1 && end != -1 { constraint := &Constraint{ ID: getParamConstraintType(c[:start]), - Data: strings.Split(RemoveEscapeChar(c[start+1:end]), string(parameterConstraintDataSeparatorChars)), + Data: splitNonEscaped(c[start+1:end], string(parameterConstraintDataSeparatorChars)), + } + + // remove escapes from data + if len(constraint.Data) == 1 { + constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) + } else if len(constraint.Data) == 2 { + constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) + constraint.Data[1] = RemoveEscapeChar(constraint.Data[1]) } // Precompile regex if has regex constraint @@ -384,6 +393,21 @@ func findNextNonEscapedCharsetPosition(search string, charset []byte) int { return pos } +// splitNonEscaped slices s into all substrings separated by sep and returns a slice of the substrings between those separators +// This function also takes a care of escape char when splitting. +func splitNonEscaped(s, sep string) []string { + var result []string + i := findNextNonEscapedCharsetPosition(s, []byte(sep)) + + for i > -1 { + result = append(result, s[:i]) + s = s[i+len(sep):] + i = findNextNonEscapedCharsetPosition(s, []byte(sep)) + } + + return append(result, s) +} + // getMatch parses the passed url and tries to match it against the route segments and determine the parameter positions func (routeParser *routeParser) getMatch(detectionPath, path string, params *[maxParams]string, partialCheck bool) bool { var i, paramsIterator, partLen int @@ -526,13 +550,13 @@ func getParamConstraintType(constraintPart string) TypeConstraint { return alphaConstraint case ConstraintGuid: return guidConstraint - case ConstraintMinLen: + case ConstraintMinLen, ConstraintMinLenLower: return minLenConstraint - case ConstraintMaxLen: + case ConstraintMaxLen, ConstraintMaxLenLower: return maxLenConstraint - case ConstraintExactLen: - return exactLenConstraint - case ConstraintBetweenLen: + case ConstraintLen: + return lenConstraint + case ConstraintBetweenLen, ConstraintBetweenLenLower: return betweenLenConstraint case ConstraintMin: return minConstraint @@ -555,7 +579,7 @@ func (c *Constraint) CheckConstraint(param string) bool { var num int // check data exists - needOneData := []TypeConstraint{minLenConstraint, maxLenConstraint, exactLenConstraint, minConstraint, maxConstraint, datetimeConstraint, regexConstraint} + needOneData := []TypeConstraint{minLenConstraint, maxLenConstraint, lenConstraint, minConstraint, maxConstraint, datetimeConstraint, regexConstraint} needTwoData := []TypeConstraint{betweenLenConstraint, rangeConstraint} for _, data := range needOneData { @@ -570,6 +594,8 @@ func (c *Constraint) CheckConstraint(param string) bool { } } + fmt.Print(c.Data) + // check constraints switch c.ID { case intConstraint: @@ -598,7 +624,7 @@ func (c *Constraint) CheckConstraint(param string) bool { if len(param) > data { return false } - case exactLenConstraint: + case lenConstraint: data, _ := strconv.Atoi(c.Data[0]) if len(param) != data { diff --git a/path_test.go b/path_test.go index 7ad514098f..4ea2a1ec1b 100644 --- a/path_test.go +++ b/path_test.go @@ -475,7 +475,7 @@ func Test_Path_matchParams(t *testing.T) { {url: "/api/v1/123", params: []string{"123"}, match: true}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) - testCase("/api/v1/:param", []testparams{ + testCase("/api/v1/:param", []testparams{ {url: "/api/v1/ent", params: []string{"ent"}, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: false}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, @@ -549,6 +549,19 @@ func Test_Path_matchParams(t *testing.T) { {url: "/api/v1/25", params: []string{"25"}, match: true}, {url: "/api/v1/true", params: []string{"true"}, match: false}, }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/87283827683", params: []string{"87283827683"}, match: true}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: true}, + }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/1200", params: []string{"1200"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, + }) } func Test_Utils_GetTrimmedParam(t *testing.T) { @@ -681,7 +694,7 @@ func Benchmark_Path_matchParams(t *testing.B) { {url: "/api/v1/123", params: []string{"123"}, match: true}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) - benchCase("/api/v1/:param", []testparams{ + benchCase("/api/v1/:param", []testparams{ {url: "/api/v1/ent", params: []string{"ent"}, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: false}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, @@ -755,28 +768,17 @@ func Benchmark_Path_matchParams(t *testing.B) { {url: "/api/v1/25", params: []string{"25"}, match: true}, {url: "/api/v1/true", params: []string{"true"}, match: false}, }) -} - -func Test_Path_matchParams0(t *testing.T) { - t.Parallel() - type testparams struct { - url string - params []string - match bool - partialCheck bool - } - var ctxParams [maxParams]string - testCase := func(r string, cases []testparams) { - parser := parseRoute(r) - for _, c := range cases { - match := parser.getMatch(c.url, c.url, &ctxParams, c.partialCheck) - utils.AssertEqual(t, c.match, match, fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) - if match && len(c.params) > 0 { - utils.AssertEqual(t, c.params[0:len(c.params)], ctxParams[0:len(c.params)], fmt.Sprintf("route: '%s', url: '%s'", r, c.url)) - } - } - } - testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true}, + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: true}, + {url: "/api/v1/87283827683", params: []string{"87283827683"}, match: true}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: true}, + }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/25", params: []string{"25"}, match: true}, + {url: "/api/v1/1200", params: []string{"1200"}, match: true}, + {url: "/api/v1/true", params: []string{"true"}, match: false}, }) } From b86f79eca4325c90aded51ec220e8e551420491d Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Fri, 26 Aug 2022 18:16:45 +0430 Subject: [PATCH 058/141] handle file error on closing (#2050) * fix unhandled errors * fix unhandled error in cache package test * omit variable type * omit variable type * rename variable because collide with the imported package name * handle file error on closing --- internal/gopsutil/common/common.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/internal/gopsutil/common/common.go b/internal/gopsutil/common/common.go index dc7a927b26..5c7adace0e 100644 --- a/internal/gopsutil/common/common.go +++ b/internal/gopsutil/common/common.go @@ -13,6 +13,7 @@ import ( "errors" "fmt" "io/ioutil" + "log" "net/url" "os" "os/exec" @@ -122,7 +123,12 @@ func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) { if err != nil { return []string{""}, err } - defer f.Close() + defer func(f *os.File) { + err := f.Close() + if err != nil { + log.Fatalln(err) + } + }(f) var ret []string @@ -204,7 +210,12 @@ func ReadInts(filename string) ([]int64, error) { if err != nil { return []int64{}, err } - defer f.Close() + defer func(f *os.File) { + err := f.Close() + if err != nil { + log.Fatalln(err) + } + }(f) var ret []int64 From 506f0b21c5cf963294978df8569e9b8acff03481 Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Sun, 28 Aug 2022 21:15:56 +0430 Subject: [PATCH 059/141] fix unhandled error in `common_linux.go` (#2056) * fix unhandled errors * fix unhandled error in cache package test * omit variable type * omit variable type * rename variable because collide with the imported package name * handle file error on closing * fix unhandled in common_linux.go --- internal/gopsutil/common/common_linux.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/gopsutil/common/common_linux.go b/internal/gopsutil/common/common_linux.go index 9234f767d7..a89c8e28ac 100644 --- a/internal/gopsutil/common/common_linux.go +++ b/internal/gopsutil/common/common_linux.go @@ -6,6 +6,7 @@ package common import ( "context" "fmt" + "log" "os" "os/exec" "path/filepath" @@ -37,7 +38,12 @@ func NumProcs() (uint64, error) { if err != nil { return 0, err } - defer f.Close() + defer func(f *os.File) { + err := f.Close() + if err != nil { + log.Fatalln(err) + } + }(f) list, err := f.Readdirnames(-1) if err != nil { From 6272d759ebe1c21755713d529f842665a7993eb9 Mon Sep 17 00:00:00 2001 From: Jason McNeil Date: Sun, 28 Aug 2022 13:57:47 -0300 Subject: [PATCH 060/141] =?UTF-8?q?=F0=9F=9A=80=20[Feature]:=20middleware/?= =?UTF-8?q?csrf=20custom=20extractor=20(#2052)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(middleware/csrf): allow custom Extractor * test: update Test_CSRF_From_Custom * docs: add comma * docs: update KeyLookup docs --- middleware/csrf/README.md | 14 ++++++++++- middleware/csrf/config.go | 40 ++++++++++++++++++------------- middleware/csrf/csrf.go | 2 +- middleware/csrf/csrf_test.go | 44 +++++++++++++++++++++++++++++++++++ middleware/csrf/extractors.go | 10 ++++---- 5 files changed, 87 insertions(+), 23 deletions(-) diff --git a/middleware/csrf/README.md b/middleware/csrf/README.md index 8ab8171cc8..ac57428875 100644 --- a/middleware/csrf/README.md +++ b/middleware/csrf/README.md @@ -49,9 +49,12 @@ app.Use(csrf.New(csrf.Config{ CookieSameSite: "Lax", Expiration: 1 * time.Hour, KeyGenerator: utils.UUID, + Extractor: func(c *fiber.Ctx) (string, error) { ... }, })) ``` +Note: KeyLookup will be ignored if Extractor is explicitly set. + ### Custom Storage/Database You can use any storage from our [storage](https://github.com/gofiber/storage/) package. @@ -74,7 +77,7 @@ type Config struct { Next func(c *fiber.Ctx) bool // KeyLookup is a string in the form of ":" that is used - // to extract token from the request. + // to create an Extractor that extracts the token from the request. // Possible values: // - "header:" // - "query:" @@ -82,6 +85,8 @@ type Config struct { // - "form:" // - "cookie:" // + // Ignored if an Extractor is explicitly set. + // // Optional. Default: "header:X-CSRF-Token" KeyLookup string @@ -133,6 +138,13 @@ type Config struct { // // Optional. Default: utils.UUID KeyGenerator func() string + + // Extractor returns the csrf token + // + // If set this will be used in place of an Extractor based on KeyLookup. + // + // Optional. Default will create an Extractor based on KeyLookup. + Extractor func(c *fiber.Ctx) (string, error) } ``` diff --git a/middleware/csrf/config.go b/middleware/csrf/config.go index 4d9cd7b8a1..5e45c7b83d 100644 --- a/middleware/csrf/config.go +++ b/middleware/csrf/config.go @@ -18,7 +18,7 @@ type Config struct { Next func(c *fiber.Ctx) bool // KeyLookup is a string in the form of ":" that is used - // to extract token from the request. + // to create an Extractor that extracts the token from the request. // Possible values: // - "header:" // - "query:" @@ -26,6 +26,8 @@ type Config struct { // - "form:" // - "cookie:" // + // Ignored if an Extractor is explicitly set. + // // Optional. Default: "header:X-CSRF-Token" KeyLookup string @@ -92,8 +94,12 @@ type Config struct { // Optional. Default: DefaultErrorHandler ErrorHandler fiber.ErrorHandler - // extractor returns the csrf token from the request based on KeyLookup - extractor func(c *fiber.Ctx) (string, error) + // Extractor returns the csrf token + // + // If set this will be used in place of an Extractor based on KeyLookup. + // + // Optional. Default will create an Extractor based on KeyLookup. + Extractor func(c *fiber.Ctx) (string, error) } // ConfigDefault is the default config @@ -104,7 +110,7 @@ var ConfigDefault = Config{ Expiration: 1 * time.Hour, KeyGenerator: utils.UUID, ErrorHandler: defaultErrorHandler, - extractor: csrfFromHeader("X-Csrf-Token"), + Extractor: CsrfFromHeader("X-Csrf-Token"), } // default ErrorHandler that process return error from fiber.Handler @@ -174,18 +180,20 @@ func configDefault(config ...Config) Config { panic("[CSRF] KeyLookup must in the form of :") } - // By default we extract from a header - cfg.extractor = csrfFromHeader(textproto.CanonicalMIMEHeaderKey(selectors[1])) - - switch selectors[0] { - case "form": - cfg.extractor = csrfFromForm(selectors[1]) - case "query": - cfg.extractor = csrfFromQuery(selectors[1]) - case "param": - cfg.extractor = csrfFromParam(selectors[1]) - case "cookie": - cfg.extractor = csrfFromCookie(selectors[1]) + if cfg.Extractor == nil { + // By default we extract from a header + cfg.Extractor = CsrfFromHeader(textproto.CanonicalMIMEHeaderKey(selectors[1])) + + switch selectors[0] { + case "form": + cfg.Extractor = CsrfFromForm(selectors[1]) + case "query": + cfg.Extractor = CsrfFromQuery(selectors[1]) + case "param": + cfg.Extractor = CsrfFromParam(selectors[1]) + case "cookie": + cfg.Extractor = CsrfFromCookie(selectors[1]) + } } return cfg diff --git a/middleware/csrf/csrf.go b/middleware/csrf/csrf.go index 1de2aec5d6..e7ad4f2a72 100644 --- a/middleware/csrf/csrf.go +++ b/middleware/csrf/csrf.go @@ -39,7 +39,7 @@ func New(config ...Config) fiber.Handler { // Assume that anything not defined as 'safe' by RFC7231 needs protection // Extract token from client request i.e. header, query, param, form or cookie - token, err = cfg.extractor(c) + token, err = cfg.Extractor(c) if err != nil { return cfg.ErrorHandler(c, err) } diff --git a/middleware/csrf/csrf_test.go b/middleware/csrf/csrf_test.go index 082b3519a1..90650bf327 100644 --- a/middleware/csrf/csrf_test.go +++ b/middleware/csrf/csrf_test.go @@ -236,6 +236,50 @@ func Test_CSRF_From_Cookie(t *testing.T) { utils.AssertEqual(t, "OK", string(ctx.Response.Body())) } +func Test_CSRF_From_Custom(t *testing.T) { + app := fiber.New() + + extractor := func(c *fiber.Ctx) (string, error) { + body := string(c.Body()) + // Generate the correct extractor to get the token from the correct location + selectors := strings.Split(body, "=") + + if len(selectors) != 2 || selectors[1] == "" { + return "", errMissingParam + } + return selectors[1], nil + } + + app.Use(New(Config{Extractor: extractor})) + + app.Post("/", func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + h := app.Handler() + ctx := &fasthttp.RequestCtx{} + + // Invalid CSRF token + ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMETextPlain) + h(ctx) + utils.AssertEqual(t, 403, ctx.Response.StatusCode()) + + // Generate CSRF token + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod("GET") + h(ctx) + token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + token = strings.Split(strings.Split(token, ";")[0], "=")[1] + + ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMETextPlain) + ctx.Request.SetBodyString("_csrf=" + token) + h(ctx) + utils.AssertEqual(t, 200, ctx.Response.StatusCode()) +} + func Test_CSRF_ErrorHandler_InvalidToken(t *testing.T) { app := fiber.New() diff --git a/middleware/csrf/extractors.go b/middleware/csrf/extractors.go index 2b577b1233..ab669bb47a 100644 --- a/middleware/csrf/extractors.go +++ b/middleware/csrf/extractors.go @@ -15,7 +15,7 @@ var ( ) // csrfFromParam returns a function that extracts token from the url param string. -func csrfFromParam(param string) func(c *fiber.Ctx) (string, error) { +func CsrfFromParam(param string) func(c *fiber.Ctx) (string, error) { return func(c *fiber.Ctx) (string, error) { token := c.Params(param) if token == "" { @@ -26,7 +26,7 @@ func csrfFromParam(param string) func(c *fiber.Ctx) (string, error) { } // csrfFromForm returns a function that extracts a token from a multipart-form. -func csrfFromForm(param string) func(c *fiber.Ctx) (string, error) { +func CsrfFromForm(param string) func(c *fiber.Ctx) (string, error) { return func(c *fiber.Ctx) (string, error) { token := c.FormValue(param) if token == "" { @@ -37,7 +37,7 @@ func csrfFromForm(param string) func(c *fiber.Ctx) (string, error) { } // csrfFromCookie returns a function that extracts token from the cookie header. -func csrfFromCookie(param string) func(c *fiber.Ctx) (string, error) { +func CsrfFromCookie(param string) func(c *fiber.Ctx) (string, error) { return func(c *fiber.Ctx) (string, error) { token := c.Cookies(param) if token == "" { @@ -48,7 +48,7 @@ func csrfFromCookie(param string) func(c *fiber.Ctx) (string, error) { } // csrfFromHeader returns a function that extracts token from the request header. -func csrfFromHeader(param string) func(c *fiber.Ctx) (string, error) { +func CsrfFromHeader(param string) func(c *fiber.Ctx) (string, error) { return func(c *fiber.Ctx) (string, error) { token := c.Get(param) if token == "" { @@ -59,7 +59,7 @@ func csrfFromHeader(param string) func(c *fiber.Ctx) (string, error) { } // csrfFromQuery returns a function that extracts token from the query string. -func csrfFromQuery(param string) func(c *fiber.Ctx) (string, error) { +func CsrfFromQuery(param string) func(c *fiber.Ctx) (string, error) { return func(c *fiber.Ctx) (string, error) { token := c.Query(param) if token == "" { From 0734c9d3fc6f7d0663d6af6e48194ef0d8fae3ac Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Mon, 29 Aug 2022 16:42:12 +0430 Subject: [PATCH 061/141] :mag: fix unhandled errors in helpers_test.go (#2058) * fix unhandled errors * fix unhandled error in cache package test * omit variable type * omit variable type * rename variable because collide with the imported package name * handle file error on closing * fix unhandled in common_linux.go * fix unhandled errors in helpers_test.go --- helpers_test.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/helpers_test.go b/helpers_test.go index 7b45540de7..f1bfbb2ec8 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -22,7 +22,8 @@ func Test_Utils_ETag(t *testing.T) { t.Run("Not Status OK", func(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.SendString("Hello, World!") + err := c.SendString("Hello, World!") + utils.AssertEqual(t, nil, err) c.Status(201) setETag(c, false) utils.AssertEqual(t, "", string(c.Response().Header.Peek(HeaderETag))) @@ -38,7 +39,8 @@ func Test_Utils_ETag(t *testing.T) { t.Run("Has HeaderIfNoneMatch", func(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.SendString("Hello, World!") + err := c.SendString("Hello, World!") + utils.AssertEqual(t, nil, err) c.Request().Header.Set(HeaderIfNoneMatch, `"13-1831710635"`) setETag(c, false) utils.AssertEqual(t, 304, c.Response().StatusCode()) @@ -49,7 +51,8 @@ func Test_Utils_ETag(t *testing.T) { t.Run("No HeaderIfNoneMatch", func(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.SendString("Hello, World!") + err := c.SendString("Hello, World!") + utils.AssertEqual(t, nil, err) setETag(c, false) utils.AssertEqual(t, `"13-1831710635"`, string(c.Response().Header.Peek(HeaderETag))) }) @@ -60,7 +63,8 @@ func Benchmark_Utils_ETag(b *testing.B) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.SendString("Hello, World!") + err := c.SendString("Hello, World!") + utils.AssertEqual(b, nil, err) for n := 0; n < b.N; n++ { setETag(c, false) } @@ -73,7 +77,8 @@ func Test_Utils_ETag_Weak(t *testing.T) { t.Run("Set Weak", func(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.SendString("Hello, World!") + err := c.SendString("Hello, World!") + utils.AssertEqual(t, nil, err) setETag(c, true) utils.AssertEqual(t, `W/"13-1831710635"`, string(c.Response().Header.Peek(HeaderETag))) }) From d9257c2c1d400c1fcc251184ccf32e985f1ce104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emre=20Savc=C4=B1?= Date: Tue, 30 Aug 2022 09:30:13 +0300 Subject: [PATCH 062/141] =?UTF-8?q?=F0=9F=9A=80=20[Feature]:=20add=20envva?= =?UTF-8?q?r=20expose=20middleware=20(#2054)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add envvar expose middleware * fix readme remove redundant word * change hard coded json marshal to app config marshaller * update readme * update all lang readme * change t.setenv to os.setenv as backward compability reason * change io.readall to ioutil.readall * Update envvar.go * remove get method that only used in test * remove unused method from envvar struct * remove path variable from config, update readme, add new test case Co-authored-by: RW --- .github/README.md | 3 +- .github/README_ckb.md | 1 + .github/README_de.md | 1 + .github/README_es.md | 1 + .github/README_fa.md | 1 + .github/README_fr.md | 1 + .github/README_he.md | 1 + .github/README_id.md | 1 + .github/README_it.md | 41 ++++++------ .github/README_ja.md | 1 + .github/README_ko.md | 1 + .github/README_nl.md | 1 + .github/README_pt.md | 1 + .github/README_ru.md | 1 + .github/README_sa.md | 1 + .github/README_tr.md | 25 ++++---- .github/README_zh-CN.md | 1 + .github/README_zh-TW.md | 1 + middleware/envvar/README.md | 83 ++++++++++++++++++++++++ middleware/envvar/envvar.go | 73 ++++++++++++++++++++++ middleware/envvar/envvar_test.go | 104 +++++++++++++++++++++++++++++++ 21 files changed, 311 insertions(+), 33 deletions(-) create mode 100644 middleware/envvar/README.md create mode 100644 middleware/envvar/envvar.go create mode 100644 middleware/envvar/envvar_test.go diff --git a/.github/README.md b/.github/README.md index 85406ea913..c04982642c 100644 --- a/.github/README.md +++ b/.github/README.md @@ -595,13 +595,14 @@ func main() { Here is a list of middleware that are included within the Fiber framework. | Middleware | Description | -| :------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +|:---------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Basic auth middleware provides an HTTP basic authentication. It calls the next handler for valid credentials and 401 Unauthorized for missing or invalid credentials. | | [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Intercept and cache responses | | [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Compression middleware for Fiber, it supports `deflate`, `gzip` and `brotli` by default. | | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_ckb.md b/.github/README_ckb.md index 8fd7ae8dbb..1f1bfd36d7 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -601,6 +601,7 @@ func main() { | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_de.md b/.github/README_de.md index 8964567e1b..083fb17a20 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -571,6 +571,7 @@ Hier finden Sie eine Liste der Middleware, die im Fiber-Framework enthalten ist. | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_es.md b/.github/README_es.md index 29416f26e4..1803de9fdd 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -571,6 +571,7 @@ Aquí está una lista del middleware incluido en el marco web Fiber. | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_fa.md b/.github/README_fa.md index 90332c3c75..fb555ed4f1 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -686,6 +686,7 @@ func main() { | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) |فعال سازی هدر های cross-origin با گزینه های مختلف.| | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) |در برابر حملات CSRF ایمنی ایجاد میکند.| | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) |مقادیر کوکی هارا رمزنگاری میکند.| +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | با ارائه تنظیمات اختیاری، متغیرهای محیط را در معرض دید قرار دهید. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | میدلور ETag به کش ها اجازه میدهد کارآمد تر عمل کرده و در پهنای باند صرفه جویی کنند. به عنوان یک وب سرور نیازی به دادن پاسخ کامل نیست اگر محتوا تغییر نکرده باشد. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | میدلور Expvar میتواند متغیر هایی را تعریف کرده و مقادیر انها را در زمان اجرا با فرمت JSON به شما نشان دهد. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | جلوگیری و یا کش کردن درخواست های favicon در صورتی که مسیر یک فایل را داده باشید.| diff --git a/.github/README_fr.md b/.github/README_fr.md index ebb24e34ad..b2fc37324b 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -573,6 +573,7 @@ Here is a list of middleware that are included within the Fiber framework. | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_he.md b/.github/README_he.md index c687038739..7b19957e0e 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -689,6 +689,7 @@ Here is a list of middleware that are included within the Fiber framework. | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_id.md b/.github/README_id.md index 6e3e41c560..285644b8fa 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -574,6 +574,7 @@ Kumpulan `middleware` yang ada didalam kerangka kerja Fiber. | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_it.md b/.github/README_it.md index a07aa84d85..b2e6307aea 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -589,28 +589,29 @@ func main() { Qui una lista dei middleware inclusi con Fiber. -| Middleware | Descrizione | -| :------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Middleware basico di autenticazione usando http. Chiama il suo handler se le credenziali sono guiste e il codice 401 Unauthorized per credenziali mancanti o invailde. | -| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Intercetta e mette nella cache la risposta | -| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Middlewere di compressione per Fiber, supporta `deflate`, `gzip` e `brotli` di default. | +| Middleware | Descrizione | +| :------------------------------------------------------------------------------------- |:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Middleware basico di autenticazione usando http. Chiama il suo handler se le credenziali sono guiste e il codice 401 Unauthorized per credenziali mancanti o invailde. | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Intercetta e mette nella cache la risposta | +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Middlewere di compressione per Fiber, supporta `deflate`, `gzip` e `brotli` di default. | | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Ti permette di usare cross-origin resource sharing \(CORS\) con tante optzioni. | -| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Ti protegge da attachi CSRF. | -| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Middleware che encrypta i valori dei cookie. | -| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Middlewere che permette alle cache di essere piu efficienti e salvare banda, come un web server non deve rimandare il messagio pieno se il contenuto non è cambiato. | -| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Middleware che serve via il suo runtime server HTTP varianti esposte in formato JSON. | -| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignora favicon dai logs o serve dalla memoria se un filepath si dà. | -| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Middleware per il FileSystem per Fiber, grazie tante e crediti a Alireza Salary | -| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Middelwere per Rate-limiting per Fiber. Usato per limitare richieste continue agli APIs publici e/o endpoints come un password reset. | -| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | Logger HTTP per richiesta/risposta. | -| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Middleware per monitorare che riporta metriche server, ispirato da express-status-monitor | -| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Grazie tante a Matthew Lee \(@mthli\) | -| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Ti permette di fare richieste proxy a multipli server. | -| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Middleware per recuperare dai attachi di panico da tutte le parti nella stack chain e da il controllo al centralizzato[ ErrorHandler](https://docs.gofiber.io/guide/error-handling). | -| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Aggiunge un requestid a ogni richiesta. | +| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Ti protegge da attachi CSRF. | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Middleware che encrypta i valori dei cookie. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Esporre le variabili di ambiente fornendo una configurazione facoltativa. | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Middlewere che permette alle cache di essere piu efficienti e salvare banda, come un web server non deve rimandare il messagio pieno se il contenuto non è cambiato. | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Middleware che serve via il suo runtime server HTTP varianti esposte in formato JSON. | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignora favicon dai logs o serve dalla memoria se un filepath si dà. | +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Middleware per il FileSystem per Fiber, grazie tante e crediti a Alireza Salary | +| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Middelwere per Rate-limiting per Fiber. Usato per limitare richieste continue agli APIs publici e/o endpoints come un password reset. | +| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | Logger HTTP per richiesta/risposta. | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Middleware per monitorare che riporta metriche server, ispirato da express-status-monitor | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Grazie tante a Matthew Lee \(@mthli\) | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Ti permette di fare richieste proxy a multipli server. | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Middleware per recuperare dai attachi di panico da tutte le parti nella stack chain e da il controllo al centralizzato[ ErrorHandler](https://docs.gofiber.io/guide/error-handling). | +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Aggiunge un requestid a ogni richiesta. | | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Middelwere per sessioni. NOTA: Questo middleware usa il nostro Storage package. | -| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Middleware che salta un wrapped handler se un predicate è vero. | -| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Aggiunge un tempo massimo per una richiesta e lo manda a ErrorHandler se si supera. | +| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Middleware che salta un wrapped handler se un predicate è vero. | +| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Aggiunge un tempo massimo per una richiesta e lo manda a ErrorHandler se si supera. | ## 🧬 Middleware Esterni diff --git a/.github/README_ja.md b/.github/README_ja.md index 6d2cda5293..8243b29236 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -576,6 +576,7 @@ func main() { | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_ko.md b/.github/README_ko.md index 6b4ba34fdb..00cea2806e 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -577,6 +577,7 @@ Fiber 프레임워크에 포함되는 미들웨어 목록입니다. | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_nl.md b/.github/README_nl.md index d7ffd4daa1..000a378797 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -577,6 +577,7 @@ Here is a list of middleware that are included within the Fiber framework. | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_pt.md b/.github/README_pt.md index f15f4e3a94..0079eb632d 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -571,6 +571,7 @@ Here is a list of middleware that are included within the Fiber framework. | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_ru.md b/.github/README_ru.md index eaa171eeac..12cd17cf2b 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -578,6 +578,7 @@ func main() { | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_sa.md b/.github/README_sa.md index 6e90ca81ee..b3655d8d0a 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -642,6 +642,7 @@ Here is a list of middleware that are included within the Fiber framework. | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Enable cross-origin resource sharing \(CORS\) with various options. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protect from CSRF exploits. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignore favicon from logs or serve from memory if a file path is provided. | diff --git a/.github/README_tr.md b/.github/README_tr.md index 3866ff7c13..7f53a7f7d9 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -564,26 +564,27 @@ func main() { Fiber'a dahil edilen middlewareların bir listesi aşağıda verilmiştir. | Middleware | Açıklama | -| :------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| :------------------------------------------------------------------------------------- |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Basic auth middleware'ı, bir HTTP Basic auth sağlar. Geçerli kimlik bilgileri için sonraki handlerı ve eksik veya geçersiz kimlik bilgileri için 401 döndürür. | -| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Reponseları durdur ve önbelleğe al. | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Reponseları durdur ve önbelleğe al. | | [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Fiber için sıkıştırma middleware, varsayılan olarak `deflate`, `gzip` ve `brotli`yi destekler. | | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Çeşitli seçeneklerle başlangıçlar arası kaynak paylaşımını \(CORS\) etkinleştirin. | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | CSRF exploitlerinden korunun. | -| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware'ı cookie değerlerini şifreler. | -| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware'ı sayfa içeriği değişmediyse bant genişliğini daha verimli kullanmak için tam sayfa içeriğini tekrar göndermez. | -| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware, HTTP serverinin bazı runtime değişkenlerini JSON formatında sunar. | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware'ı cookie değerlerini şifreler. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Environment değişkenlerini belirtilen ayarlara göre dışarıya açar. | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware'ı sayfa içeriği değişmediyse bant genişliğini daha verimli kullanmak için tam sayfa içeriğini tekrar göndermez. | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware, HTTP serverinin bazı runtime değişkenlerini JSON formatında sunar. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Bir dosya yolu sağlanmışsa, loglardaki favicon'u yoksayar veya bellekten sunar. | -| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Fiber için FileSystem middleware, Alireza Salary'e özel teşekkürler. | +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Fiber için FileSystem middleware, Alireza Salary'e özel teşekkürler. | | [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Fiber için hız sınırlayıcı middleware'i. Açık API'lere ve/veya parola sıfırlama gibi endpointlere yönelik tekrarlanan istekleri sınırlamak için kullanın. | | [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP istek/yanıt logger'ı. | -| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Monitor middleware'ı sunucu metriklerini rapor eder, express-status-monitor'den esinlenildi. | -| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Matthew Lee'ye özel teşekkürler \(@mthli\). | -| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Birden çok sunucuya proxy istekleri yapmanızı sağlar. | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Monitor middleware'ı sunucu metriklerini rapor eder, express-status-monitor'den esinlenildi. | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Matthew Lee'ye özel teşekkürler \(@mthli\). | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Birden çok sunucuya proxy istekleri yapmanızı sağlar. | | [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Recover middleware'i, stack chain'ini herhangi bir yerindeki paniklerden kurtulur ve kontrolü merkezileştirilmiş [ErrorHandler'e](https://docs.gofiber.io/guide/error-handling) verir. | -| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Her requeste id verir. | -| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session için middleware. NOTE: Bu middleware Fiber'in Storage yapısını kullanır. | -| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware'ı verilen koşul `true` olduğunda handler'ı atlar ve işlemez. | +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Her requeste id verir. | +| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session için middleware. NOTE: Bu middleware Fiber'in Storage yapısını kullanır. | +| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware'ı verilen koşul `true` olduğunda handler'ı atlar ve işlemez. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Bir request için maksimum süre ekler ve aşılırsa ErrorHandler'a iletir. | ## 🧬 Harici Middlewarelar diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index 5369548a48..b78811e868 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -573,6 +573,7 @@ func main() { | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | 使用各种选项启用跨源资源共享\(CORS\). | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | 保护来自CSRF的漏洞. | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | 加密 cookie 值的加密中间件. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | 通过提供可选配置来公开环境变量。. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | 让缓存更加高效并且节省带宽,让web服务不再需要重新响应整个响应体如果响应内容未变更. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | 通过其HTTP服务器运行时间提供JSON格式的暴露变体. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | 如果提供了文件路径,则忽略日志中的图标或从内存中服务. | diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index 1d3907cb76..a7f2dfe8ea 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -573,6 +573,7 @@ func main() { | [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | 使用各種選項啟用跨域資源共享 \(CORS\)。 | | [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | 防止 CSRF 漏洞利用。 | | [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware which encrypts cookie values. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expose environment variables with providing an optional config. | | [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | ETag middleware that lets caches be more efficient and save bandwidth, as a web server does not need to resend a full response if the content has not changed. | | [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware that serves via its HTTP server runtime exposed variants in the JSON format. | | [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | 如果提供了文件路徑,則忽略日誌中的網站圖標或從內存中提供服務。 | diff --git a/middleware/envvar/README.md b/middleware/envvar/README.md new file mode 100644 index 0000000000..ce69522a2e --- /dev/null +++ b/middleware/envvar/README.md @@ -0,0 +1,83 @@ +# Exposing Environment Variables Middleware + +EnvVar middleware for [Fiber](https://github.com/gofiber/fiber) that can be used to expose environment variables with various options. + +## Table of Contents + +- [Environment Variables (EnvVar) Middleware](#environment-variables-envvar-middleware) + - [Table of Contents](#table-of-contents) + - [Signatures](#signatures) + - [Examples](#examples) + - [Default Config](#default-config) + - [Custom Config](#custom-config) + - [Response](#http-response) + - [Config](#config) + - [Default Config](#default-config-1) + +## Signatures + +```go +func New(config ...Config) fiber.Handler +``` + +## Examples + +First import the middleware from Fiber, + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/envvar" +) +``` + +Then create a Fiber app with `app := fiber.New()`. + +**Note**: You need to provide a path to use envvar middleware. + +### Default Config + +```go +app.Use("/expose/envvars", envvar.New()) +``` + +### Custom Config + +```go +app.Use("/expose/envvars", envvar.New(envvar.Config{ + ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, + ExcludeVars: map[string]string{"excludeKey": ""}} +})) +``` + +### Response + +Http response contract: +``` +{ + "vars": { + "someEnvVariable": "someValue", + "anotherEnvVariable": "anotherValue", + } +} + +``` + +## Config + +```go +// Config defines the config for middleware. +type Config struct { + // ExportVars specifies the environment variables that should export + ExportVars map[string]string + // ExcludeVars specifies the environment variables that should not export + ExcludeVars map[string]string +} + +``` + +## Default Config + +```go +Config{} +``` diff --git a/middleware/envvar/envvar.go b/middleware/envvar/envvar.go new file mode 100644 index 0000000000..373eac5035 --- /dev/null +++ b/middleware/envvar/envvar.go @@ -0,0 +1,73 @@ +package envvar + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" + "os" + "strings" +) + +// Config defines the config for middleware. +type Config struct { + // ExportVars specifies the environment variables that should export + ExportVars map[string]string + // ExcludeVars specifies the environment variables that should not export + ExcludeVars map[string]string +} + +type EnvVar struct { + Vars map[string]string `json:"vars"` +} + +func (envVar *EnvVar) set(key, val string) { + envVar.Vars[key] = val +} + +var defaultConfig = Config{} + +func New(config ...Config) fiber.Handler { + var cfg = defaultConfig + if len(config) > 0 { + cfg = config[0] + } + + return func(c *fiber.Ctx) error { + if c.Method() != fiber.MethodGet { + return fiber.ErrMethodNotAllowed + } + + envVar := newEnvVar(cfg) + varsByte, err := c.App().Config().JSONEncoder(envVar) + if err != nil { + c.Response().SetBodyRaw(utils.UnsafeBytes(err.Error())) + c.Response().SetStatusCode(fiber.StatusInternalServerError) + return nil + } + c.Response().SetBodyRaw(varsByte) + c.Response().SetStatusCode(fiber.StatusOK) + c.Response().Header.Set("Content-Type", "application/json; charset=utf-8") + return nil + } +} + +func newEnvVar(cfg Config) *EnvVar { + vars := &EnvVar{Vars: make(map[string]string)} + + if len(cfg.ExportVars) > 0 { + for key, defaultVal := range cfg.ExportVars { + vars.set(key, defaultVal) + if envVal, exists := os.LookupEnv(key); exists { + vars.set(key, envVal) + } + } + } else { + for _, envVal := range os.Environ() { + keyVal := strings.Split(envVal, "=") + if _, exists := cfg.ExcludeVars[keyVal[0]]; !exists { + vars.set(keyVal[0], keyVal[1]) + } + } + } + + return vars +} diff --git a/middleware/envvar/envvar_test.go b/middleware/envvar/envvar_test.go new file mode 100644 index 0000000000..edb74d5f59 --- /dev/null +++ b/middleware/envvar/envvar_test.go @@ -0,0 +1,104 @@ +package envvar + +import ( + "encoding/json" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" + "io/ioutil" + "net/http" + "os" + "testing" +) + +func TestEnvVarStructWithExportVarsExcludeVars(t *testing.T) { + os.Setenv("testKey", "testEnvValue") + os.Setenv("anotherEnvKey", "anotherEnvVal") + os.Setenv("excludeKey", "excludeEnvValue") + defer os.Unsetenv("testKey") + defer os.Unsetenv("anotherEnvKey") + defer os.Unsetenv("excludeKey") + + vars := newEnvVar(Config{ + ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, + ExcludeVars: map[string]string{"excludeKey": ""}}) + + utils.AssertEqual(t, vars.Vars["testKey"], "testEnvValue") + utils.AssertEqual(t, vars.Vars["testDefaultKey"], "testDefaultVal") + utils.AssertEqual(t, vars.Vars["excludeKey"], "") + utils.AssertEqual(t, vars.Vars["anotherEnvKey"], "") +} + +func TestEnvVarHandler(t *testing.T) { + os.Setenv("testKey", "testVal") + defer os.Unsetenv("testKey") + + expectedEnvVarResponse, _ := json.Marshal( + struct { + Vars map[string]string `json:"vars"` + }{ + map[string]string{"testKey": "testVal"}, + }) + + app := fiber.New() + app.Use("/envvars", New(Config{ + ExportVars: map[string]string{"testKey": ""}})) + + req, _ := http.NewRequest("GET", "http://localhost/envvars", nil) + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + + respBody, err := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + + utils.AssertEqual(t, expectedEnvVarResponse, respBody) +} + +func TestEnvVarHandlerNotMatched(t *testing.T) { + app := fiber.New() + app.Use("/envvars", New(Config{ + ExportVars: map[string]string{"testKey": ""}})) + + app.Get("/another-path", func(ctx *fiber.Ctx) error { + ctx.SendString("OK") + return nil + }) + + req, _ := http.NewRequest("GET", "http://localhost/another-path", nil) + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + + respBody, err := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + + utils.AssertEqual(t, []byte("OK"), respBody) +} + +func TestEnvVarHandlerDefaultConfig(t *testing.T) { + os.Setenv("testEnvKey", "testEnvVal") + defer os.Unsetenv("testEnvKey") + + app := fiber.New() + app.Use("/envvars", New()) + + req, _ := http.NewRequest("GET", "http://localhost/envvars", nil) + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + + respBody, err := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + + var envVars EnvVar + utils.AssertEqual(t, nil, json.Unmarshal(respBody, &envVars)) + val := envVars.Vars["testEnvKey"] + utils.AssertEqual(t, "testEnvVal", val) +} + +func TestEnvVarHandlerMethod(t *testing.T) { + app := fiber.New() + app.Use("/envvars", New()) + + req, _ := http.NewRequest("POST", "http://localhost/envvars", nil) + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusMethodNotAllowed, resp.StatusCode) +} From 0ebc9113d5ebfd76702d89cc717b44b8995221b3 Mon Sep 17 00:00:00 2001 From: RW Date: Tue, 30 Aug 2022 08:36:17 +0200 Subject: [PATCH 063/141] Update README.md --- middleware/envvar/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/envvar/README.md b/middleware/envvar/README.md index ce69522a2e..b9880e9cba 100644 --- a/middleware/envvar/README.md +++ b/middleware/envvar/README.md @@ -10,7 +10,7 @@ EnvVar middleware for [Fiber](https://github.com/gofiber/fiber) that can be used - [Examples](#examples) - [Default Config](#default-config) - [Custom Config](#custom-config) - - [Response](#http-response) + - [Response](#response) - [Config](#config) - [Default Config](#default-config-1) From 349772d69ab76d596a6522b8f751dffc08ef0d69 Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Tue, 30 Aug 2022 11:08:16 +0430 Subject: [PATCH 064/141] fix unhandled errors and remove unused parameter (#2061) * fix unhandled errors * fix unhandled error in cache package test * omit variable type * omit variable type * rename variable because collide with the imported package name * handle file error on closing * fix unhandled in common_linux.go * fix unhandled errors in helpers_test.go * fix unhandled errors in listen_test.go * remove unused parameter in emptyHandler method --- listen_test.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/listen_test.go b/listen_test.go index 11d1908bd4..978e953a4d 100644 --- a/listen_test.go +++ b/listen_test.go @@ -166,12 +166,18 @@ func captureOutput(f func()) string { go func() { var buf bytes.Buffer wg.Done() - io.Copy(&buf, reader) + _, err := io.Copy(&buf, reader) + if err != nil { + panic(err) + } out <- buf.String() }() wg.Wait() f() - writer.Close() + err = writer.Close() + if err != nil { + panic(err) + } return <-out } @@ -244,6 +250,6 @@ func Test_App_print_Route_with_group(t *testing.T) { utils.AssertEqual(t, true, strings.Contains(printRoutesMessage, "/v1/test/fiber/*")) } -func emptyHandler(c *Ctx) error { +func emptyHandler(_ *Ctx) error { return nil } From e72ea32dd2515014d01b4094c47449dc8c42ba93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Tue, 30 Aug 2022 09:40:58 +0300 Subject: [PATCH 065/141] :bug: bug: fix regex constraints (#2059) --- path.go | 39 +++++++++++++++++++++++++-------------- path_test.go | 16 ++++++++++++++-- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/path.go b/path.go index db2818dd00..2b9e0e4524 100644 --- a/path.go +++ b/path.go @@ -7,7 +7,6 @@ package fiber import ( - "fmt" "regexp" "strconv" "strings" @@ -250,7 +249,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r // find constraint part if exists in the parameter part and remove it if parameterEndPosition > 0 { parameterConstraintStart = findNextNonEscapedCharsetPosition(pattern[0:parameterEndPosition], parameterConstraintStartChars) - parameterConstraintEnd = findNextNonEscapedCharsetPosition(pattern[0:parameterEndPosition+1], parameterConstraintEndChars) + parameterConstraintEnd = findLastCharsetPosition(pattern[0:parameterEndPosition+1], parameterConstraintEndChars) } // cut params part @@ -262,12 +261,12 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r if hasConstraint := (parameterConstraintStart != -1 && parameterConstraintEnd != -1); hasConstraint { constraintString := pattern[parameterConstraintStart+1 : parameterConstraintEnd] - userconstraints := splitNonEscaped(constraintString, string(parameterConstraintSeparatorChars)) - constraints = make([]*Constraint, 0, len(userconstraints)) + userConstraints := splitNonEscaped(constraintString, string(parameterConstraintSeparatorChars)) + constraints = make([]*Constraint, 0, len(userConstraints)) - for _, c := range userconstraints { + for _, c := range userConstraints { start := findNextNonEscapedCharsetPosition(c, parameterConstraintDataStartChars) - end := findNextNonEscapedCharsetPosition(c, parameterConstraintDataEndChars) + end := findLastCharsetPosition(c, parameterConstraintDataEndChars) // Assign constraint if start != -1 && end != -1 { @@ -277,11 +276,13 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r } // remove escapes from data - if len(constraint.Data) == 1 { - constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) - } else if len(constraint.Data) == 2 { - constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) - constraint.Data[1] = RemoveEscapeChar(constraint.Data[1]) + if constraint.ID != regexConstraint { + if len(constraint.Data) == 1 { + constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) + } else if len(constraint.Data) == 2 { + constraint.Data[0] = RemoveEscapeChar(constraint.Data[0]) + constraint.Data[1] = RemoveEscapeChar(constraint.Data[1]) + } } // Precompile regex if has regex constraint @@ -346,6 +347,18 @@ func findNextCharsetPosition(search string, charset []byte) int { return nextPosition } +// findNextCharsetPosition search the last char position from the charset +func findLastCharsetPosition(search string, charset []byte) int { + lastPosition := -1 + for _, char := range charset { + if pos := strings.LastIndexByte(search, char); pos != -1 && (pos < lastPosition || lastPosition == -1) { + lastPosition = pos + } + } + + return lastPosition +} + // findNextCharsetPositionConstraint search the next char position from the charset // unlike findNextCharsetPosition, it takes care of constraint start-end chars to parse route pattern func findNextCharsetPositionConstraint(search string, charset []byte) int { @@ -363,7 +376,7 @@ func findNextCharsetPositionConstraint(search string, charset []byte) int { if char == paramConstraintEnd { constraintEnd = pos } - //fmt.Println(string(char)) + if pos != -1 && (pos < nextPosition || nextPosition == -1) { if pos > constraintStart && pos < constraintEnd { nextPosition = pos @@ -594,8 +607,6 @@ func (c *Constraint) CheckConstraint(param string) bool { } } - fmt.Print(c.Data) - // check constraints switch c.ID { case intConstraint: diff --git a/path_test.go b/path_test.go index 4ea2a1ec1b..ca02ea2b15 100644 --- a/path_test.go +++ b/path_test.go @@ -520,12 +520,18 @@ func Test_Path_matchParams(t *testing.T) { {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, {url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true}, }) - testCase("/api/v1/:param", []testparams{ + testCase("/api/v1/:param", []testparams{ {url: "/api/v1/ent", params: []string{"ent"}, match: false}, {url: "/api/v1/15", params: []string{"15"}, match: false}, {url: "/api/v1/peach", params: []string{"peach"}, match: true}, {url: "/api/v1/p34ch", params: []string{"p34ch"}, match: false}, }) + testCase("/api/v1/:param", []testparams{ + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/15", params: []string{"15"}, match: false}, + {url: "/api/v1/2022-08-27", params: []string{"2022-08-27"}, match: true}, + {url: "/api/v1/2022/08-27", params: []string{"p34ch"}, match: false}, + }) testCase("/api/v1/:param", []testparams{ {url: "/api/v1/entity", params: []string{"entity"}, match: false}, {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, @@ -739,12 +745,18 @@ func Benchmark_Path_matchParams(t *testing.B) { {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, {url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true}, }) - benchCase("/api/v1/:param", []testparams{ + benchCase("/api/v1/:param", []testparams{ {url: "/api/v1/ent", params: []string{"ent"}, match: false}, {url: "/api/v1/15", params: []string{"15"}, match: false}, {url: "/api/v1/peach", params: []string{"peach"}, match: true}, {url: "/api/v1/p34ch", params: []string{"p34ch"}, match: false}, }) + benchCase("/api/v1/:param", []testparams{ + {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/15", params: []string{"15"}, match: false}, + {url: "/api/v1/2022-08-27", params: []string{"2022-08-27"}, match: true}, + {url: "/api/v1/2022/08-27", params: []string{"p34ch"}, match: false}, + }) benchCase("/api/v1/:param", []testparams{ {url: "/api/v1/entity", params: []string{"entity"}, match: false}, {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, From cbfcac2c0a911bb1d47deadea71d7667115a0bfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Tue, 30 Aug 2022 14:01:24 +0300 Subject: [PATCH 066/141] :bug: bug: remove prefork support from custom listeners (#2060) * :bug: bug: remove prefork support from custom listeners * Update listen_test.go --- helpers.go | 31 ------------------------------- helpers_test.go | 44 -------------------------------------------- listen.go | 11 +++++------ listen_test.go | 10 ---------- 4 files changed, 5 insertions(+), 91 deletions(-) diff --git a/helpers.go b/helpers.go index b4046b4730..73e882e3b5 100644 --- a/helpers.go +++ b/helpers.go @@ -24,37 +24,6 @@ import ( "github.com/valyala/fasthttp" ) -/* #nosec */ -// lnMetadata will close the listener and return the addr and tls config -func lnMetadata(network string, ln net.Listener) (addr string, cfg *tls.Config) { - // Get addr - addr = ln.Addr().String() - - // Close listener - if err := ln.Close(); err != nil { - return - } - - // Wait for the listener to be closed - var closed bool - for i := 0; i < 10; i++ { - conn, err := net.DialTimeout(network, addr, 3*time.Second) - if err != nil || conn == nil { - closed = true - break - } - _ = conn.Close() - time.Sleep(100 * time.Millisecond) - } - if !closed { - panic("listener: " + addr + ": Only one usage of each socket address (protocol/network address/port) is normally permitted.") - } - - cfg = getTlsConfig(ln) - - return -} - /* #nosec */ // getTlsConfig returns a net listener's tls config func getTlsConfig(ln net.Listener) *tls.Config { diff --git a/helpers_test.go b/helpers_test.go index f1bfbb2ec8..e72e727256 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -5,9 +5,7 @@ package fiber import ( - "crypto/tls" "fmt" - "net" "strings" "testing" "time" @@ -260,48 +258,6 @@ func Benchmark_Utils_IsNoCache(b *testing.B) { utils.AssertEqual(b, true, ok) } -func Test_Utils_lnMetadata(t *testing.T) { - t.Run("closed listen", func(t *testing.T) { - ln, err := net.Listen(NetworkTCP, ":0") - utils.AssertEqual(t, nil, err) - - utils.AssertEqual(t, nil, ln.Close()) - - addr, config := lnMetadata(NetworkTCP, ln) - - utils.AssertEqual(t, ln.Addr().String(), addr) - utils.AssertEqual(t, true, config == nil) - }) - - t.Run("non tls", func(t *testing.T) { - ln, err := net.Listen(NetworkTCP, ":0") - - utils.AssertEqual(t, nil, err) - - addr, config := lnMetadata(NetworkTCP4, ln) - - utils.AssertEqual(t, ln.Addr().String(), addr) - utils.AssertEqual(t, true, config == nil) - }) - - t.Run("tls", func(t *testing.T) { - cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") - utils.AssertEqual(t, nil, err) - - config := &tls.Config{Certificates: []tls.Certificate{cer}} - - ln, err := net.Listen(NetworkTCP4, ":0") - utils.AssertEqual(t, nil, err) - - ln = tls.NewListener(ln, config) - - addr, config := lnMetadata(NetworkTCP4, ln) - - utils.AssertEqual(t, ln.Addr().String(), addr) - utils.AssertEqual(t, true, config != nil) - }) -} - // go test -v -run=^$ -bench=Benchmark_SlashRecognition -benchmem -count=4 func Benchmark_SlashRecognition(b *testing.B) { search := "wtf/1234" diff --git a/listen.go b/listen.go index ca55bb06a3..c82afc3a20 100644 --- a/listen.go +++ b/listen.go @@ -26,12 +26,6 @@ import ( // Listener can be used to pass a custom listener. func (app *App) Listener(ln net.Listener) error { - // Prefork is supported for custom listeners - if app.config.Prefork { - addr, tlsConfig := lnMetadata(app.config.Network, ln) - return app.prefork(app.config.Network, addr, tlsConfig) - } - // prepare the server for the start app.startupProcess() @@ -45,6 +39,11 @@ func (app *App) Listener(ln net.Listener) error { app.printRoutesMessage() } + // Prefork is not supported for custom listeners + if app.config.Prefork { + fmt.Println("[Warning] Prefork isn't supported for custom listeners.") + } + // Start listening return app.server.Serve(ln) } diff --git a/listen_test.go b/listen_test.go index 978e953a4d..8e4691d65f 100644 --- a/listen_test.go +++ b/listen_test.go @@ -114,16 +114,6 @@ func Test_App_Listener(t *testing.T) { utils.AssertEqual(t, nil, app.Listener(ln)) } -// go test -run Test_App_Listener_Prefork -func Test_App_Listener_Prefork(t *testing.T) { - testPreforkMaster = true - - app := New(Config{DisableStartupMessage: true, Prefork: true}) - - ln := fasthttputil.NewInmemoryListener() - utils.AssertEqual(t, nil, app.Listener(ln)) -} - func Test_App_Listener_TLS_Listener(t *testing.T) { // Create tls certificate cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") From 6026560c93447b4cb85d812c862b491d7e25405b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Garc=C3=ADa=20Rosales?= Date: Tue, 30 Aug 2022 14:44:44 +0200 Subject: [PATCH 067/141] =?UTF-8?q?Sugerencia=20de=20cambios=20en=20traduc?= =?UTF-8?q?ci=C3=B3n=20al=20espa=C3=B1ol=20(#2064)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Saludos soy de Perú y me gustaría contribuir con una traducción más precisa al español mi lenguaje nativo. --- .github/README_es.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README_es.md b/.github/README_es.md index 1803de9fdd..7bfca8e1ed 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -79,7 +79,7 @@

    -Fiber es un framework web inspirado en Express construido sobre Fasthttp, el motor HTTP más rápido para Go. Diseñado para facilitar las cosas para un desarrollo rápido con cero asignación de memoria y rendimiento en mente. +Fiber es un framework web inspirado en Express construido sobre Fasthttp, el motor HTTP más rápido para Go. Diseñado para facilitar las cosas y tener un menor tiempo de desarrollo con cero asignación de memoria y pensado para un mejor rendimiento.

    ## ⚡️ Inicio rápido From ec96d161a0c5083834e844939ad61e88aa68fd75 Mon Sep 17 00:00:00 2001 From: RW Date: Tue, 30 Aug 2022 14:48:31 +0200 Subject: [PATCH 068/141] Fix csrf middleware behavior with header key lookup (#2063) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛 [Bug]: Strange CSRF middleware behavior with header KeyLookup configuration #2045 --- middleware/csrf/config.go | 6 +- middleware/csrf/csrf_test.go | 114 ++++++++++++++++++++++++++++++++++- middleware/csrf/manager.go | 7 ++- 3 files changed, 120 insertions(+), 7 deletions(-) diff --git a/middleware/csrf/config.go b/middleware/csrf/config.go index 5e45c7b83d..94c5287b65 100644 --- a/middleware/csrf/config.go +++ b/middleware/csrf/config.go @@ -102,15 +102,17 @@ type Config struct { Extractor func(c *fiber.Ctx) (string, error) } +const HeaderName = "X-Csrf-Token" + // ConfigDefault is the default config var ConfigDefault = Config{ - KeyLookup: "header:X-Csrf-Token", + KeyLookup: "header:" + HeaderName, CookieName: "csrf_", CookieSameSite: "Lax", Expiration: 1 * time.Hour, KeyGenerator: utils.UUID, ErrorHandler: defaultErrorHandler, - Extractor: CsrfFromHeader("X-Csrf-Token"), + Extractor: CsrfFromHeader(HeaderName), } // default ErrorHandler that process return error from fiber.Handler diff --git a/middleware/csrf/csrf_test.go b/middleware/csrf/csrf_test.go index 90650bf327..dcdabc4628 100644 --- a/middleware/csrf/csrf_test.go +++ b/middleware/csrf/csrf_test.go @@ -40,7 +40,7 @@ func Test_CSRF(t *testing.T) { ctx.Request.Reset() ctx.Response.Reset() ctx.Request.Header.SetMethod("POST") - ctx.Request.Header.Set("X-CSRF-Token", "johndoe") + ctx.Request.Header.Set(HeaderName, "johndoe") h(ctx) utils.AssertEqual(t, 403, ctx.Response.StatusCode()) @@ -55,7 +55,7 @@ func Test_CSRF(t *testing.T) { ctx.Request.Reset() ctx.Response.Reset() ctx.Request.Header.SetMethod("POST") - ctx.Request.Header.Set("X-CSRF-Token", token) + ctx.Request.Header.Set(HeaderName, token) h(ctx) utils.AssertEqual(t, 200, ctx.Response.StatusCode()) } @@ -305,7 +305,7 @@ func Test_CSRF_ErrorHandler_InvalidToken(t *testing.T) { ctx.Request.Reset() ctx.Response.Reset() ctx.Request.Header.SetMethod("POST") - ctx.Request.Header.Set("X-CSRF-Token", "johndoe") + ctx.Request.Header.Set(HeaderName, "johndoe") h(ctx) utils.AssertEqual(t, 419, ctx.Response.StatusCode()) utils.AssertEqual(t, "invalid CSRF token", string(ctx.Response.Body())) @@ -340,3 +340,111 @@ func Test_CSRF_ErrorHandler_EmptyToken(t *testing.T) { utils.AssertEqual(t, 419, ctx.Response.StatusCode()) utils.AssertEqual(t, "empty CSRF token", string(ctx.Response.Body())) } + +// TODO: use this test case and make the unsafe header value bug from https://github.com/gofiber/fiber/issues/2045 reproducible and permanently fixed/tested by this testcase +//func Test_CSRF_UnsafeHeaderValue(t *testing.T) { +// app := fiber.New() +// +// app.Use(New()) +// app.Get("/", func(c *fiber.Ctx) error { +// return c.SendStatus(fiber.StatusOK) +// }) +// app.Get("/test", func(c *fiber.Ctx) error { +// return c.SendStatus(fiber.StatusOK) +// }) +// app.Post("/", func(c *fiber.Ctx) error { +// return c.SendStatus(fiber.StatusOK) +// }) +// +// resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/", nil)) +// utils.AssertEqual(t, nil, err) +// utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) +// +// var token string +// for _, c := range resp.Cookies() { +// if c.Name != ConfigDefault.CookieName { +// continue +// } +// token = c.Value +// break +// } +// +// fmt.Println("token", token) +// +// getReq := httptest.NewRequest(http.MethodGet, "/", nil) +// getReq.Header.Set(HeaderName, token) +// resp, err = app.Test(getReq) +// +// getReq = httptest.NewRequest(http.MethodGet, "/test", nil) +// getReq.Header.Set("X-Requested-With", "XMLHttpRequest") +// getReq.Header.Set(fiber.HeaderCacheControl, "no") +// getReq.Header.Set(HeaderName, token) +// +// resp, err = app.Test(getReq) +// +// getReq.Header.Set(fiber.HeaderAccept, "*/*") +// getReq.Header.Del(HeaderName) +// resp, err = app.Test(getReq) +// +// postReq := httptest.NewRequest(http.MethodPost, "/", nil) +// postReq.Header.Set("X-Requested-With", "XMLHttpRequest") +// postReq.Header.Set(HeaderName, token) +// resp, err = app.Test(postReq) +//} + +// go test -v -run=^$ -bench=Benchmark_Middleware_CSRF_Check -benchmem -count=4 +func Benchmark_Middleware_CSRF_Check(b *testing.B) { + app := fiber.New() + + app.Use(New()) + app.Get("/", func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusTeapot) + }) + + fctx := &fasthttp.RequestCtx{} + h := app.Handler() + ctx := &fasthttp.RequestCtx{} + + // Generate CSRF token + ctx.Request.Header.SetMethod("GET") + h(ctx) + token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + token = strings.Split(strings.Split(token, ";")[0], "=")[1] + + ctx.Request.Header.SetMethod("POST") + ctx.Request.Header.Set(HeaderName, token) + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + h(fctx) + } + + utils.AssertEqual(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) +} + +// go test -v -run=^$ -bench=Benchmark_Middleware_CSRF_GenerateToken -benchmem -count=4 +func Benchmark_Middleware_CSRF_GenerateToken(b *testing.B) { + app := fiber.New() + + app.Use(New()) + app.Get("/", func(c *fiber.Ctx) error { + return c.SendStatus(fiber.StatusTeapot) + }) + + fctx := &fasthttp.RequestCtx{} + h := app.Handler() + ctx := &fasthttp.RequestCtx{} + + // Generate CSRF token + ctx.Request.Header.SetMethod("GET") + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + h(fctx) + } + + utils.AssertEqual(b, fiber.StatusTeapot, fctx.Response.Header.StatusCode()) +} diff --git a/middleware/csrf/manager.go b/middleware/csrf/manager.go index dd70c2ed1c..13f0ccb657 100644 --- a/middleware/csrf/manager.go +++ b/middleware/csrf/manager.go @@ -6,6 +6,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/memory" + "github.com/gofiber/fiber/v2/utils" ) // go:generate msgp @@ -88,7 +89,8 @@ func (m *manager) set(key string, it *item, exp time.Duration) { _ = m.storage.Set(key, raw, exp) } } else { - m.memory.Set(key, it, exp) + // the key is crucial in crsf and sometimes a reference to another value which can be reused later(pool/unsafe values concept), so a copy is made here + m.memory.Set(utils.CopyString(key), it, exp) } } @@ -97,7 +99,8 @@ 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) + // the key is crucial in crsf and sometimes a reference to another value which can be reused later(pool/unsafe values concept), so a copy is made here + m.memory.Set(utils.CopyString(key), raw, exp) } } From 1f18ae35490c4a8fd0a88cc39f5f6808957206bb Mon Sep 17 00:00:00 2001 From: RW Date: Tue, 30 Aug 2022 18:30:41 +0200 Subject: [PATCH 069/141] prepare release v2.37.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index a79e28410b..bd3708b842 100644 --- a/app.go +++ b/app.go @@ -30,7 +30,7 @@ import ( ) // Version of current fiber package -const Version = "2.37.0-rc.1" +const Version = "2.37.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From b7500a8d08d0c1cda76344517a1cb0d534dcd846 Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Fri, 2 Sep 2022 10:31:55 +0430 Subject: [PATCH 070/141] unhandled error in hooks test (#2070) * fix unhandled errors * fix unhandled error in cache package test * omit variable type * omit variable type * rename variable because collide with the imported package name * handle file error on closing * fix unhandled in common_linux.go * fix unhandled errors in helpers_test.go * fix unhandled errors in listen_test.go * remove unused parameter in emptyHandler method * refactor path.go * unhandled error in hooks test --- hooks_test.go | 16 ++++++++++------ path.go | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/hooks_test.go b/hooks_test.go index 5a800920d2..5ed9d82145 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -42,7 +42,8 @@ func Test_Hook_OnName(t *testing.T) { defer bytebufferpool.Put(buf) app.Hooks().OnName(func(r Route) error { - buf.WriteString(r.Name) + _, err := buf.WriteString(r.Name) + utils.AssertEqual(t, nil, err) return nil }) @@ -84,8 +85,8 @@ func Test_Hook_OnGroup(t *testing.T) { defer bytebufferpool.Put(buf) app.Hooks().OnGroup(func(g Group) error { - buf.WriteString(g.Prefix) - + _, err := buf.WriteString(g.Prefix) + utils.AssertEqual(t, nil, err) return nil }) @@ -104,7 +105,8 @@ func Test_Hook_OnGroupName(t *testing.T) { defer bytebufferpool.Put(buf) app.Hooks().OnGroupName(func(g Group) error { - buf.WriteString(g.name) + _, err := buf.WriteString(g.name) + utils.AssertEqual(t, nil, err) return nil }) @@ -143,7 +145,8 @@ func Test_Hook_OnShutdown(t *testing.T) { defer bytebufferpool.Put(buf) app.Hooks().OnShutdown(func() error { - buf.WriteString("shutdowning") + _, err := buf.WriteString("shutdowning") + utils.AssertEqual(t, nil, err) return nil }) @@ -163,7 +166,8 @@ func Test_Hook_OnListen(t *testing.T) { defer bytebufferpool.Put(buf) app.Hooks().OnListen(func() error { - buf.WriteString("ready") + _, err := buf.WriteString("ready") + utils.AssertEqual(t, nil, err) return nil }) diff --git a/path.go b/path.go index 2b9e0e4524..27f09f5f9e 100644 --- a/path.go +++ b/path.go @@ -60,7 +60,7 @@ const ( paramConstraintDataSeparator byte = ',' // separator of datas of type constraint for a parameter ) -// parameter constraint types +// TypeConstraint parameter constraint types type TypeConstraint int16 type Constraint struct { @@ -259,7 +259,7 @@ func (routeParser *routeParser) analyseParameterPart(pattern string) (string, *r // Check has constraint var constraints []*Constraint - if hasConstraint := (parameterConstraintStart != -1 && parameterConstraintEnd != -1); hasConstraint { + if hasConstraint := parameterConstraintStart != -1 && parameterConstraintEnd != -1; hasConstraint { constraintString := pattern[parameterConstraintStart+1 : parameterConstraintEnd] userConstraints := splitNonEscaped(constraintString, string(parameterConstraintSeparatorChars)) constraints = make([]*Constraint, 0, len(userConstraints)) From 87faeda5c16998a3a185c3d27cb4dc62ccb24dbd Mon Sep 17 00:00:00 2001 From: Fufu Date: Sun, 4 Sep 2022 01:03:51 +0800 Subject: [PATCH 071/141] =?UTF-8?q?=F0=9F=90=9B=20Fix:=20EnvVar=20middlewa?= =?UTF-8?q?re=20parses=20base64=20incorrectly=20(#2069)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix: EnvVar middleware parses base64 incorrectly * Chore: fix typo in README.md * Chore: standardize and simplify response --- middleware/envvar/README.md | 22 +++++++++--------- middleware/envvar/envvar.go | 16 +++++-------- middleware/envvar/envvar_test.go | 40 ++++++++++++++++++++++++++++++-- 3 files changed, 55 insertions(+), 23 deletions(-) diff --git a/middleware/envvar/README.md b/middleware/envvar/README.md index b9880e9cba..a16923d048 100644 --- a/middleware/envvar/README.md +++ b/middleware/envvar/README.md @@ -44,10 +44,12 @@ app.Use("/expose/envvars", envvar.New()) ### Custom Config ```go -app.Use("/expose/envvars", envvar.New(envvar.Config{ - ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, - ExcludeVars: map[string]string{"excludeKey": ""}} -})) +app.Use("/expose/envvars", envvar.New( + envvar.Config{ + ExportVars: map[string]string{"testKey": "", "testDefaultKey": "testDefaultVal"}, + ExcludeVars: map[string]string{"excludeKey": ""}, + }), +) ``` ### Response @@ -57,10 +59,9 @@ Http response contract: { "vars": { "someEnvVariable": "someValue", - "anotherEnvVariable": "anotherValue", + "anotherEnvVariable": "anotherValue" } } - ``` ## Config @@ -68,12 +69,11 @@ Http response contract: ```go // Config defines the config for middleware. type Config struct { - // ExportVars specifies the environment variables that should export - ExportVars map[string]string - // ExcludeVars specifies the environment variables that should not export - ExcludeVars map[string]string + // ExportVars specifies the environment variables that should export + ExportVars map[string]string + // ExcludeVars specifies the environment variables that should not export + ExcludeVars map[string]string } - ``` ## Default Config diff --git a/middleware/envvar/envvar.go b/middleware/envvar/envvar.go index 373eac5035..c8ed80de3d 100644 --- a/middleware/envvar/envvar.go +++ b/middleware/envvar/envvar.go @@ -1,10 +1,10 @@ package envvar import ( - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/utils" "os" "strings" + + "github.com/gofiber/fiber/v2" ) // Config defines the config for middleware. @@ -39,14 +39,10 @@ func New(config ...Config) fiber.Handler { envVar := newEnvVar(cfg) varsByte, err := c.App().Config().JSONEncoder(envVar) if err != nil { - c.Response().SetBodyRaw(utils.UnsafeBytes(err.Error())) - c.Response().SetStatusCode(fiber.StatusInternalServerError) - return nil + return c.Status(fiber.StatusInternalServerError).SendString(err.Error()) } - c.Response().SetBodyRaw(varsByte) - c.Response().SetStatusCode(fiber.StatusOK) - c.Response().Header.Set("Content-Type", "application/json; charset=utf-8") - return nil + c.Set(fiber.HeaderContentType, fiber.MIMEApplicationJSONCharsetUTF8) + return c.Send(varsByte) } } @@ -62,7 +58,7 @@ func newEnvVar(cfg Config) *EnvVar { } } else { for _, envVal := range os.Environ() { - keyVal := strings.Split(envVal, "=") + keyVal := strings.SplitN(envVal, "=", 2) if _, exists := cfg.ExcludeVars[keyVal[0]]; !exists { vars.set(keyVal[0], keyVal[1]) } diff --git a/middleware/envvar/envvar_test.go b/middleware/envvar/envvar_test.go index edb74d5f59..6e90e99cba 100644 --- a/middleware/envvar/envvar_test.go +++ b/middleware/envvar/envvar_test.go @@ -2,12 +2,13 @@ package envvar import ( "encoding/json" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/utils" "io/ioutil" "net/http" "os" "testing" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" ) func TestEnvVarStructWithExportVarsExcludeVars(t *testing.T) { @@ -102,3 +103,38 @@ func TestEnvVarHandlerMethod(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusMethodNotAllowed, resp.StatusCode) } + +func TestEnvVarHandlerSpecialValue(t *testing.T) { + testEnvKey := "testEnvKey" + fakeBase64 := "testBase64:TQ==" + os.Setenv(testEnvKey, fakeBase64) + defer os.Unsetenv(testEnvKey) + + app := fiber.New() + app.Use("/envvars", New()) + app.Use("/envvars/export", New(Config{ExportVars: map[string]string{testEnvKey: ""}})) + + req, _ := http.NewRequest("GET", "http://localhost/envvars", nil) + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + + respBody, err := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + + var envVars EnvVar + utils.AssertEqual(t, nil, json.Unmarshal(respBody, &envVars)) + val := envVars.Vars[testEnvKey] + utils.AssertEqual(t, fakeBase64, val) + + req, _ = http.NewRequest("GET", "http://localhost/envvars/export", nil) + resp, err = app.Test(req) + utils.AssertEqual(t, nil, err) + + respBody, err = ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + + var envVarsExport EnvVar + utils.AssertEqual(t, nil, json.Unmarshal(respBody, &envVarsExport)) + val = envVarsExport.Vars[testEnvKey] + utils.AssertEqual(t, fakeBase64, val) +} From c8b1879b11d4f09fbba3fef9f741f9bc2f28db64 Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Mon, 5 Sep 2022 15:41:09 +0430 Subject: [PATCH 072/141] fix unhandled errors in app_test.go (#2071) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix unhandled errors * fix unhandled error in cache package test * omit variable type * omit variable type * rename variable because collide with the imported package name * handle file error on closing * fix unhandled in common_linux.go * fix unhandled errors in helpers_test.go * fix unhandled errors in listen_test.go * remove unused parameter in emptyHandler method * refactor path.go * unhandled error in hooks test * fix unhandled errors in app_test.go * fix unhandled errors in ctx_test.go * ✨ fix unhandled errors in helpers_test.go * revert app_test.go --- app_test.go | 3 ++- ctx_test.go | 27 +++++++++++++++++---------- helpers_test.go | 9 ++++++--- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/app_test.go b/app_test.go index fcb623bdfb..d186193ae3 100644 --- a/app_test.go +++ b/app_test.go @@ -1378,7 +1378,8 @@ func Test_App_New_Test_Parallel(t *testing.T) { t.Run("Test_App_New_Test_Parallel_2", func(t *testing.T) { t.Parallel() app := New(Config{Immutable: true}) - app.Test(httptest.NewRequest("GET", "/", nil)) + _, err := app.Test(httptest.NewRequest("GET", "/", nil)) + utils.AssertEqual(t, nil, err) }) } diff --git a/ctx_test.go b/ctx_test.go index 31f47e7a29..caf037877d 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -755,35 +755,42 @@ func Test_Ctx_Format(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) c.Request().Header.Set(HeaderAccept, MIMETextPlain) - c.Format([]byte("Hello, World!")) + err := c.Format([]byte("Hello, World!")) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "Hello, World!", string(c.Response().Body())) c.Request().Header.Set(HeaderAccept, MIMETextHTML) - c.Format("Hello, World!") + err = c.Format("Hello, World!") + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

    Hello, World!

    ", string(c.Response().Body())) c.Request().Header.Set(HeaderAccept, MIMEApplicationJSON) - c.Format("Hello, World!") + err = c.Format("Hello, World!") + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, `"Hello, World!"`, string(c.Response().Body())) c.Request().Header.Set(HeaderAccept, MIMETextPlain) - c.Format(complex(1, 1)) + err = c.Format(complex(1, 1)) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "(1+1i)", string(c.Response().Body())) c.Request().Header.Set(HeaderAccept, MIMEApplicationXML) - c.Format("Hello, World!") + err = c.Format("Hello, World!") + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, `Hello, World!`, string(c.Response().Body())) - err := c.Format(complex(1, 1)) + err = c.Format(complex(1, 1)) utils.AssertEqual(t, true, err != nil) c.Request().Header.Set(HeaderAccept, MIMETextPlain) - c.Format(Map{}) + err = c.Format(Map{}) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "map[]", string(c.Response().Body())) type broken string c.Request().Header.Set(HeaderAccept, "broken/accept") - c.Format(broken("Hello, World!")) + err = c.Format(broken("Hello, World!")) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, `Hello, World!`, string(c.Response().Body())) } @@ -3137,8 +3144,8 @@ func Test_Ctx_Get_Location_From_Route_name(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "/user/fiber", location) }) - - t.Run("case sensitive",func(t *testing.T) { + + t.Run("case sensitive", func(t *testing.T) { app := New(Config{CaseSensitive: true}) c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) diff --git a/helpers_test.go b/helpers_test.go index e72e727256..03c1e687a9 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -84,7 +84,8 @@ func Test_Utils_ETag_Weak(t *testing.T) { t.Run("Match Weak ETag", func(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.SendString("Hello, World!") + err := c.SendString("Hello, World!") + utils.AssertEqual(t, nil, err) c.Request().Header.Set(HeaderIfNoneMatch, `W/"13-1831710635"`) setETag(c, true) utils.AssertEqual(t, 304, c.Response().StatusCode()) @@ -95,7 +96,8 @@ func Test_Utils_ETag_Weak(t *testing.T) { t.Run("Not Match Weak ETag", func(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.SendString("Hello, World!") + err := c.SendString("Hello, World!") + utils.AssertEqual(t, nil, err) c.Request().Header.Set(HeaderIfNoneMatch, `W/"13-1831710635xx"`) setETag(c, true) utils.AssertEqual(t, `W/"13-1831710635"`, string(c.Response().Header.Peek(HeaderETag))) @@ -135,7 +137,8 @@ func Benchmark_Utils_ETag_Weak(b *testing.B) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.SendString("Hello, World!") + err := c.SendString("Hello, World!") + utils.AssertEqual(b, nil, err) for n := 0; n < b.N; n++ { setETag(c, true) } From 154e4f820183fd509b291cfc41577fd1f6341a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Mon, 5 Sep 2022 14:15:42 +0300 Subject: [PATCH 073/141] :sparkles: v3 (feature): refactor logger middleware (#1979) * :sparkles: v3 (feature): refactor logger middleware - Make middleware extandable for 3rd-party loggers. (https://github.com/gofiber/fiber/issues/1828) * :sparkles: v3 (feature): refactor logger middleware - Make middleware extandable for 3rd-party loggers. (https://github.com/gofiber/fiber/issues/1828) * :sparkles: v3 (feature): refactor logger middleware - add example for zerolog. * :sparkles: v3 (feature): refactor logger middleware - fix ToC. * merge --- middleware/logger/README.md | 69 ++++++++- middleware/logger/config.go | 40 ++++- middleware/logger/default_logger.go | 222 ++++++++++++++++++++++++++++ middleware/logger/logger.go | 202 ++----------------------- 4 files changed, 333 insertions(+), 200 deletions(-) create mode 100644 middleware/logger/default_logger.go diff --git a/middleware/logger/README.md b/middleware/logger/README.md index 3bc9e758c8..c6b99fb645 100644 --- a/middleware/logger/README.md +++ b/middleware/logger/README.md @@ -11,6 +11,7 @@ Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP r - [Logging Request ID](#logging-request-id) - [Changing TimeZone & TimeFormat](#changing-timezone--timeformat) - [Custom File Writer](#custom-file-writer) + - [Logging with Zerolog](#logging-with-zerolog) - [Config](#config) - [Default Config](#default-config-1) - [Constants](#constants) @@ -76,6 +77,43 @@ app.Use(logger.New(logger.Config{ })) ``` +### Logging with Zerolog +```go +package main + +import ( + "os" + + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/logger" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +func main() { + app := fiber.New() + + log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + + app.Use(logger.New(logger.Config{LoggerFunc: func(c fiber.Ctx, data logger.LoggerData, cfg logger.Config) error { + log.Info(). + Str("path", c.Path()). + Str("method", c.Method()). + Int("status", c.Response(). + StatusCode()). + Msg("new request") + + return nil + }})) + + app.Get("/", func(c fiber.Ctx) error { + return c.SendString("test") + }) + + app.Listen(":3000") +} +``` + ## Config ```go // Config defines the config for middleware. @@ -109,19 +147,38 @@ type Config struct { // // Default: os.Stderr Output io.Writer + + // You can define specific things before the returning the handler: colors, template, etc. + // + // Optional. Default: beforeHandlerFunc + BeforeHandlerFunc func(Config) + + // You can use custom loggers with Fiber by using this field. + // This field is really useful if you're using Zerolog, Zap, Logrus, apex/log etc. + // If you don't define anything for this field, it'll use classical logger of Fiber. + // + // Optional. Default: defaultLogger + LoggerFunc func(c fiber.Ctx, data LoggerData, cfg Config) error } ``` ## Default Config ```go +// ConfigDefault is the default config var ConfigDefault = Config{ - Next: nil, - Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", - TimeFormat: "15:04:05", - TimeZone: "Local", - TimeInterval: 500 * time.Millisecond, - Output: os.Stderr, + Next: nil, + Format: defaultFormat, + TimeFormat: "15:04:05", + TimeZone: "Local", + TimeInterval: 500 * time.Millisecond, + Output: os.Stdout, + BeforeHandlerFunc: beforeHandlerFunc, + LoggerFunc: defaultLogger, + enableColors: true, } + +// default logging format for Fiber's default logger +var defaultFormat = "[${time}] ${status} - ${latency} ${method} ${path}\n" ``` ## Constants diff --git a/middleware/logger/config.go b/middleware/logger/config.go index b8e6d4df9d..80dd3288c9 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -41,6 +41,18 @@ type Config struct { // Default: os.Stdout Output io.Writer + // You can define specific things before the returning the handler: colors, template, etc. + // + // Optional. Default: beforeHandlerFunc + BeforeHandlerFunc func(Config) + + // You can use custom loggers with Fiber by using this field. + // This field is really useful if you're using Zerolog, Zap, Logrus, apex/log etc. + // If you don't define anything for this field, it'll use default logger of Fiber. + // + // Optional. Default: defaultLogger + LoggerFunc func(c fiber.Ctx, data LoggerData, cfg Config) error + enableColors bool enableLatency bool timeZoneLocation *time.Location @@ -48,15 +60,20 @@ type Config struct { // ConfigDefault is the default config var ConfigDefault = Config{ - Next: nil, - Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", - TimeFormat: "15:04:05", - TimeZone: "Local", - TimeInterval: 500 * time.Millisecond, - Output: os.Stdout, - enableColors: true, + Next: nil, + Format: defaultFormat, + TimeFormat: "15:04:05", + TimeZone: "Local", + TimeInterval: 500 * time.Millisecond, + Output: os.Stdout, + BeforeHandlerFunc: beforeHandlerFunc, + LoggerFunc: defaultLogger, + enableColors: true, } +// default logging format for Fiber's default logger +var defaultFormat = "[${time}] ${status} - ${latency} ${method} ${path}\n" + // Function to check if the logger format is compatible for coloring func validCustomFormat(format string) bool { validTemplates := []string{"${status}", "${method}"} @@ -105,5 +122,14 @@ func configDefault(config ...Config) Config { if cfg.Output == nil { cfg.Output = ConfigDefault.Output } + + if cfg.BeforeHandlerFunc == nil { + cfg.BeforeHandlerFunc = ConfigDefault.BeforeHandlerFunc + } + + if cfg.LoggerFunc == nil { + cfg.LoggerFunc = ConfigDefault.LoggerFunc + } + return cfg } diff --git a/middleware/logger/default_logger.go b/middleware/logger/default_logger.go new file mode 100644 index 0000000000..822cb676d6 --- /dev/null +++ b/middleware/logger/default_logger.go @@ -0,0 +1,222 @@ +package logger + +import ( + "fmt" + "io" + "os" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/gofiber/fiber/v3" + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" + "github.com/valyala/bytebufferpool" + "github.com/valyala/fasthttp" + "github.com/valyala/fasttemplate" +) + +// LoggerData is a struct to define some variables to use in custom logger function. +type LoggerData struct { + Pid string + ErrPaddingStr string + ChainErr error + Start time.Time + Stop time.Time + Timestamp atomic.Value +} + +var tmpl *fasttemplate.Template + +// default logger for fiber +func defaultLogger(c fiber.Ctx, data LoggerData, cfg Config) error { + var mu sync.Mutex + + // Alias colors + colors := c.App().Config().ColorScheme + + // Get new buffer + buf := bytebufferpool.Get() + + // Default output when no custom Format or io.Writer is given + if cfg.enableColors && cfg.Format == defaultFormat { + // Format error if exist + formatErr := "" + if data.ChainErr != nil { + formatErr = colors.Red + " | " + data.ChainErr.Error() + colors.Reset + } + + // 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, + )) + + // Write buffer to output + _, _ = cfg.Output.Write(buf.Bytes()) + + // Put buffer back to pool + bytebufferpool.Put(buf) + + // End chain + return nil + } + + // Loop over template tags to replace it with the correct value + _, err := tmpl.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) { + switch tag { + case TagTime: + return buf.WriteString(data.Timestamp.Load().(string)) + case TagReferer: + return buf.WriteString(c.Get(fiber.HeaderReferer)) + case TagProtocol: + return buf.WriteString(c.Protocol()) + case TagScheme: + return buf.WriteString(c.Scheme()) + case TagPid: + return buf.WriteString(data.Pid) + case TagPort: + return buf.WriteString(c.Port()) + case TagIP: + return buf.WriteString(c.IP()) + case TagIPs: + return buf.WriteString(c.Get(fiber.HeaderXForwardedFor)) + case TagHost: + return buf.WriteString(c.Hostname()) + case TagPath: + return buf.WriteString(c.Path()) + case TagURL: + return buf.WriteString(c.OriginalURL()) + case TagUA: + return buf.WriteString(c.Get(fiber.HeaderUserAgent)) + case TagLatency: + return buf.WriteString(fmt.Sprintf("%7v", data.Stop.Sub(data.Start).Round(time.Millisecond))) + case TagBody: + return buf.Write(c.Body()) + case TagBytesReceived: + return appendInt(buf, len(c.Request().Body())) + case TagBytesSent: + return appendInt(buf, len(c.Response().Body())) + case TagRoute: + return buf.WriteString(c.Route().Path) + case TagStatus: + if cfg.enableColors { + return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset)) + } + return appendInt(buf, c.Response().StatusCode()) + case TagResBody: + return buf.Write(c.Response().Body()) + case TagReqHeaders: + out := make(map[string]string, 0) + if err := c.Bind().Header(&out); err != nil { + return 0, err + } + + reqHeaders := make([]string, 0) + for k, v := range out { + reqHeaders = append(reqHeaders, k+"="+v) + } + return buf.Write([]byte(strings.Join(reqHeaders, "&"))) + case TagQueryStringParams: + return buf.WriteString(c.Request().URI().QueryArgs().String()) + case TagMethod: + if cfg.enableColors { + return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) + } + return buf.WriteString(c.Method()) + case TagBlack: + return buf.WriteString(colors.Black) + case TagRed: + return buf.WriteString(colors.Red) + case TagGreen: + return buf.WriteString(colors.Green) + case TagYellow: + return buf.WriteString(colors.Yellow) + case TagBlue: + return buf.WriteString(colors.Blue) + case TagMagenta: + return buf.WriteString(colors.Magenta) + case TagCyan: + return buf.WriteString(colors.Cyan) + case TagWhite: + return buf.WriteString(colors.White) + case TagReset: + return buf.WriteString(colors.Reset) + case TagError: + if data.ChainErr != nil { + return buf.WriteString(data.ChainErr.Error()) + } + return buf.WriteString("-") + default: + // Check if we have a value tag i.e.: "reqHeader:x-key" + switch { + case strings.HasPrefix(tag, TagReqHeader): + return buf.WriteString(c.Get(tag[10:])) + case strings.HasPrefix(tag, TagRespHeader): + return buf.WriteString(c.GetRespHeader(tag[11:])) + case strings.HasPrefix(tag, TagQuery): + return buf.WriteString(c.Query(tag[6:])) + case strings.HasPrefix(tag, TagForm): + return buf.WriteString(c.FormValue(tag[5:])) + case strings.HasPrefix(tag, TagCookie): + return buf.WriteString(c.Cookies(tag[7:])) + case strings.HasPrefix(tag, TagLocals): + switch v := c.Locals(tag[7:]).(type) { + case []byte: + return buf.Write(v) + case string: + return buf.WriteString(v) + case nil: + return 0, nil + default: + return buf.WriteString(fmt.Sprintf("%v", v)) + } + } + } + return 0, nil + }) + // Also write errors to the buffer + if err != nil { + _, _ = buf.WriteString(err.Error()) + } + mu.Lock() + // Write buffer to output + if _, err := cfg.Output.Write(buf.Bytes()); err != nil { + // 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) + } + } + mu.Unlock() + // Put buffer back to pool + bytebufferpool.Put(buf) + + return nil +} + +// run something before returning the handler +func beforeHandlerFunc(cfg Config) { + // Create template parser + tmpl = fasttemplate.New(cfg.Format, "${", "}") + + // If colors are enabled, check terminal compatibility + if cfg.enableColors { + cfg.Output = colorable.NewColorableStdout() + if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) { + cfg.Output = colorable.NewNonColorable(os.Stdout) + } + } +} + +func appendInt(buf *bytebufferpool.ByteBuffer, v int) (int, error) { + old := len(buf.B) + buf.B = fasthttp.AppendUint(buf.B, v) + return len(buf.B) - old, nil +} diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 72f7876463..0baa0424b9 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -1,8 +1,6 @@ package logger import ( - "fmt" - "io" "os" "strconv" "strings" @@ -11,11 +9,6 @@ import ( "time" "github.com/gofiber/fiber/v3" - "github.com/mattn/go-colorable" - "github.com/mattn/go-isatty" - "github.com/valyala/bytebufferpool" - "github.com/valyala/fasthttp" - "github.com/valyala/fasttemplate" ) // Logger variables @@ -76,9 +69,6 @@ func New(config ...Config) fiber.Handler { // Check if format contains latency cfg.enableLatency = strings.Contains(cfg.Format, "${latency}") - // Create template parser - tmpl := fasttemplate.New(cfg.Format, "${", "}") - // Create correct timeformat var timestamp atomic.Value timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.TimeFormat)) @@ -99,19 +89,16 @@ func New(config ...Config) fiber.Handler { // Set variables var ( once sync.Once - mu sync.Mutex errHandler fiber.ErrorHandler ) - // If colors are enabled, check terminal compatibility - if cfg.enableColors { - cfg.Output = colorable.NewColorableStdout() - if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) { - cfg.Output = colorable.NewNonColorable(os.Stdout) - } - } + // Err padding errPadding := 15 errPaddingStr := strconv.Itoa(errPadding) + + // Before handling func + cfg.BeforeHandlerFunc(cfg) + // Return new handler return func(c fiber.Ctx) (err error) { // Don't execute middleware if Next returns true @@ -119,9 +106,6 @@ func New(config ...Config) fiber.Handler { return c.Next() } - // Alias colors - colors := c.App().Config().ColorScheme - // Set error handler once once.Do(func() { // get longested possible path @@ -160,174 +144,18 @@ func New(config ...Config) fiber.Handler { stop = time.Now() } - // Get new buffer - buf := bytebufferpool.Get() - - // Default output when no custom Format or io.Writer is given - if cfg.enableColors && cfg.Format == ConfigDefault.Format { - // Format error if exist - formatErr := "" - if chainErr != nil { - formatErr = colors.Red + " | " + chainErr.Error() + colors.Reset - } - - // Format log to buffer - _, _ = buf.WriteString(fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n", - timestamp.Load().(string), - statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset, - stop.Sub(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()) - - // Put buffer back to pool - bytebufferpool.Put(buf) - - // End chain - return nil - } - - // Loop over template tags to replace it with the correct value - _, err = tmpl.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) { - switch tag { - case TagTime: - return buf.WriteString(timestamp.Load().(string)) - case TagReferer: - return buf.WriteString(c.Get(fiber.HeaderReferer)) - case TagProtocol: - return buf.WriteString(c.Protocol()) - case TagScheme: - return buf.WriteString(c.Scheme()) - case TagPid: - return buf.WriteString(pid) - case TagPort: - return buf.WriteString(c.Port()) - case TagIP: - return buf.WriteString(c.IP()) - case TagIPs: - return buf.WriteString(c.Get(fiber.HeaderXForwardedFor)) - case TagHost: - return buf.WriteString(c.Hostname()) - case TagPath: - return buf.WriteString(c.Path()) - case TagURL: - return buf.WriteString(c.OriginalURL()) - case TagUA: - return buf.WriteString(c.Get(fiber.HeaderUserAgent)) - case TagLatency: - return buf.WriteString(fmt.Sprintf("%7v", stop.Sub(start).Round(time.Millisecond))) - case TagBody: - return buf.Write(c.Body()) - case TagBytesReceived: - return appendInt(buf, len(c.Request().Body())) - case TagBytesSent: - return appendInt(buf, len(c.Response().Body())) - case TagRoute: - return buf.WriteString(c.Route().Path) - case TagStatus: - if cfg.enableColors { - return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset)) - } - return appendInt(buf, c.Response().StatusCode()) - case TagResBody: - return buf.Write(c.Response().Body()) - case TagReqHeaders: - out := make(map[string]string, 0) - if err := c.Bind().Header(&out); err != nil { - return 0, err - } - - reqHeaders := make([]string, 0) - for k, v := range out { - reqHeaders = append(reqHeaders, k+"="+v) - } - return buf.Write([]byte(strings.Join(reqHeaders, "&"))) - case TagQueryStringParams: - return buf.WriteString(c.Request().URI().QueryArgs().String()) - case TagMethod: - if cfg.enableColors { - return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) - } - return buf.WriteString(c.Method()) - case TagBlack: - return buf.WriteString(colors.Black) - case TagRed: - return buf.WriteString(colors.Red) - case TagGreen: - return buf.WriteString(colors.Green) - case TagYellow: - return buf.WriteString(colors.Yellow) - case TagBlue: - return buf.WriteString(colors.Blue) - case TagMagenta: - return buf.WriteString(colors.Magenta) - case TagCyan: - return buf.WriteString(colors.Cyan) - case TagWhite: - return buf.WriteString(colors.White) - case TagReset: - return buf.WriteString(colors.Reset) - case TagError: - if chainErr != nil { - return buf.WriteString(chainErr.Error()) - } - return buf.WriteString("-") - default: - // Check if we have a value tag i.e.: "reqHeader:x-key" - switch { - case strings.HasPrefix(tag, TagReqHeader): - return buf.WriteString(c.Get(tag[10:])) - case strings.HasPrefix(tag, TagRespHeader): - return buf.WriteString(c.GetRespHeader(tag[11:])) - case strings.HasPrefix(tag, TagQuery): - return buf.WriteString(c.Query(tag[6:])) - case strings.HasPrefix(tag, TagForm): - return buf.WriteString(c.FormValue(tag[5:])) - case strings.HasPrefix(tag, TagCookie): - return buf.WriteString(c.Cookies(tag[7:])) - case strings.HasPrefix(tag, TagLocals): - switch v := c.Locals(tag[7:]).(type) { - case []byte: - return buf.Write(v) - case string: - return buf.WriteString(v) - case nil: - return 0, nil - default: - return buf.WriteString(fmt.Sprintf("%v", v)) - } - } - } - return 0, nil - }) - // Also write errors to the buffer - if err != nil { - _, _ = buf.WriteString(err.Error()) + // Logger instance + if err = cfg.LoggerFunc(c, LoggerData{ + Pid: pid, + ErrPaddingStr: errPaddingStr, + ChainErr: chainErr, + Start: start, + Stop: stop, + Timestamp: timestamp, + }, cfg); err != nil { + return err } - mu.Lock() - // Write buffer to output - if _, err := cfg.Output.Write(buf.Bytes()); err != nil { - // 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) - } - } - mu.Unlock() - // Put buffer back to pool - bytebufferpool.Put(buf) return nil } } - -func appendInt(buf *bytebufferpool.ByteBuffer, v int) (int, error) { - old := len(buf.B) - buf.B = fasthttp.AppendUint(buf.B, v) - return len(buf.B) - old, nil -} From 78c6197430a8ac5ceff51edde102a14c2c6681a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Mon, 5 Sep 2022 17:41:25 +0300 Subject: [PATCH 074/141] :recycle: v3 (enhancement): remove automatic HEAD method creation from Get() ref: https://github.com/gofiber/fiber/issues/2026 --- app.go | 2 +- app_test.go | 14 ++++--- client_test.go | 6 +-- group.go | 2 +- middleware/cache/cache.go | 2 +- middleware/cache/cache_test.go | 67 ---------------------------------- 6 files changed, 14 insertions(+), 79 deletions(-) diff --git a/app.go b/app.go index 500c10d434..2b416c170f 100644 --- a/app.go +++ b/app.go @@ -685,7 +685,7 @@ func (app *App) Use(args ...any) Router { // Get registers a route for GET methods that requests a representation // of the specified resource. Requests using GET should only retrieve data. func (app *App) Get(path string, handlers ...Handler) Router { - return app.Head(path, handlers...).Add(MethodGet, path, handlers...) + return app.Add(MethodGet, path, handlers...) } // Head registers a route for HEAD methods that asks for a response identical diff --git a/app_test.go b/app_test.go index 5a3dc3148c..31aa9c2789 100644 --- a/app_test.go +++ b/app_test.go @@ -83,12 +83,14 @@ func Test_App_MethodNotAllowed(t *testing.T) { resp, err = app.Test(httptest.NewRequest(MethodTrace, "/", nil)) require.NoError(t, err) require.Equal(t, 405, resp.StatusCode) - require.Equal(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow)) + require.Equal(t, "GET, POST, OPTIONS", resp.Header.Get(HeaderAllow)) resp, err = app.Test(httptest.NewRequest(MethodPatch, "/", nil)) require.NoError(t, err) require.Equal(t, 405, resp.StatusCode) - require.Equal(t, "GET, HEAD, POST, OPTIONS", resp.Header.Get(HeaderAllow)) + require.Equal(t, "GET, POST, OPTIONS", resp.Header.Get(HeaderAllow)) + + app.Head("/", testEmptyHandler) resp, err = app.Test(httptest.NewRequest(MethodPut, "/", nil)) require.NoError(t, err) @@ -313,7 +315,7 @@ func Test_App_Mount(t *testing.T) { resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") - require.Equal(t, uint32(2), app.handlersCount) + require.Equal(t, uint32(1), app.handlersCount) } func Test_App_Use_Params(t *testing.T) { @@ -962,7 +964,7 @@ func Test_App_Group_Mount(t *testing.T) { resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") - require.Equal(t, uint32(2), app.handlersCount) + require.Equal(t, uint32(1), app.handlersCount) } func Test_App_Group(t *testing.T) { @@ -1209,7 +1211,7 @@ func Test_App_Stack(t *testing.T) { stack := app.Stack() require.Equal(t, 9, len(stack)) require.Equal(t, 3, len(stack[methodInt(MethodGet)])) - require.Equal(t, 3, len(stack[methodInt(MethodHead)])) + require.Equal(t, 1, len(stack[methodInt(MethodHead)])) require.Equal(t, 2, len(stack[methodInt(MethodPost)])) require.Equal(t, 1, len(stack[methodInt(MethodPut)])) require.Equal(t, 1, len(stack[methodInt(MethodPatch)])) @@ -1228,7 +1230,7 @@ func Test_App_HandlersCount(t *testing.T) { app.Post("/path3", testEmptyHandler) count := app.HandlersCount() - require.Equal(t, uint32(4), count) + require.Equal(t, uint32(3), count) } // go test -run Test_App_ReadTimeout diff --git a/client_test.go b/client_test.go index 987b0c3cbb..ad5eecc10e 100644 --- a/client_test.go +++ b/client_test.go @@ -94,8 +94,8 @@ func Test_Client_Head(t *testing.T) { app := New(Config{DisableStartupMessage: true}) - app.Get("/", func(c Ctx) error { - return c.SendString(c.Hostname()) + app.Head("/", func(c Ctx) error { + return c.SendStatus(StatusAccepted) }) go func() { require.Nil(t, app.Listener(ln)) }() @@ -107,7 +107,7 @@ func Test_Client_Head(t *testing.T) { code, body, errs := a.String() - require.Equal(t, StatusOK, code) + require.Equal(t, StatusAccepted, code) require.Equal(t, "", body) require.Equal(t, 0, len(errs)) } diff --git a/group.go b/group.go index 8afd289dc3..502041de45 100644 --- a/group.go +++ b/group.go @@ -102,7 +102,7 @@ func (grp *Group) Use(args ...any) Router { // of the specified resource. Requests using GET should only retrieve data. func (grp *Group) Get(path string, handlers ...Handler) Router { path = getGroupPath(grp.Prefix, path) - return grp.app.Add(MethodHead, path, handlers...).Add(MethodGet, path, handlers...) + return grp.app.Add(MethodGet, path, handlers...) } // Head registers a route for HEAD methods that asks for a response identical diff --git a/middleware/cache/cache.go b/middleware/cache/cache.go index 7ea8a7c860..c9253f0af8 100644 --- a/middleware/cache/cache.go +++ b/middleware/cache/cache.go @@ -84,7 +84,7 @@ func New(config ...Config) fiber.Handler { // Return new handler return func(c fiber.Ctx) error { // Only cache GET and HEAD methods - if c.Method() != fiber.MethodGet && c.Method() != fiber.MethodHead { + if c.Method() != fiber.MethodGet { c.Set(cfg.CacheHeader, cacheUnreachable) return c.Next() } diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go index 18ac961450..9dba9571ad 100644 --- a/middleware/cache/cache_test.go +++ b/middleware/cache/cache_test.go @@ -416,73 +416,6 @@ func Test_CacheHeader(t *testing.T) { require.Equal(t, cacheUnreachable, errRespCached.Header.Get("X-Cache")) } -func Test_Cache_WithHead(t *testing.T) { - t.Parallel() - - app := fiber.New() - app.Use(New()) - - app.Get("/", func(c fiber.Ctx) error { - now := fmt.Sprintf("%d", time.Now().UnixNano()) - return c.SendString(now) - }) - - req := httptest.NewRequest("HEAD", "/", nil) - resp, err := app.Test(req) - require.NoError(t, err) - require.Equal(t, cacheMiss, resp.Header.Get("X-Cache")) - - cachedReq := httptest.NewRequest("HEAD", "/", nil) - cachedResp, err := app.Test(cachedReq) - require.NoError(t, err) - require.Equal(t, cacheHit, cachedResp.Header.Get("X-Cache")) - - body, err := io.ReadAll(resp.Body) - require.NoError(t, err) - cachedBody, err := io.ReadAll(cachedResp.Body) - require.NoError(t, err) - - require.Equal(t, cachedBody, body) -} - -func Test_Cache_WithHeadThenGet(t *testing.T) { - t.Parallel() - - app := fiber.New() - app.Use(New()) - app.Get("/", func(c fiber.Ctx) error { - return c.SendString(c.Query("cache")) - }) - - headResp, err := app.Test(httptest.NewRequest("HEAD", "/?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)) - 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)) - 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)) - require.NoError(t, err) - getBody, err = io.ReadAll(getResp.Body) - require.NoError(t, err) - require.Equal(t, "123", string(getBody)) - require.Equal(t, cacheHit, getResp.Header.Get("X-Cache")) -} - func Test_CustomCacheHeader(t *testing.T) { t.Parallel() From 90c1c4c5a30ab9c37c7c367ae491e47bfd2f4653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Mon, 5 Sep 2022 18:34:02 +0300 Subject: [PATCH 075/141] v3: fix benchmarks --- router_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router_test.go b/router_test.go index 6a53e34e93..bd66430bea 100644 --- a/router_test.go +++ b/router_test.go @@ -462,7 +462,7 @@ func Benchmark_App_MethodNotAllowed(b *testing.B) { } b.StopTimer() require.Equal(b, 405, c.Response.StatusCode()) - require.Equal(b, "GET, HEAD", string(c.Response.Header.Peek("Allow"))) + require.Equal(b, "GET", string(c.Response.Header.Peek("Allow"))) require.Equal(b, utils.StatusMessage(StatusMethodNotAllowed), string(c.Response.Body())) } From e3282f64085423e48cf5424910fb0652d80e198c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Tue, 6 Sep 2022 18:43:06 +0300 Subject: [PATCH 076/141] :recycle: v3 (enhancement): replace `c.Hostname()` -> `c.Host()` & add `c.Hostname()` that returns host without port --- app.go | 4 +- client_test.go | 6 +-- ctx.go | 18 +++++-- ctx_interface.go | 8 +++- ctx_test.go | 73 ++++++++++++++++++++++------- middleware/adaptor/adopter_test.go | 4 +- middleware/logger/default_logger.go | 2 +- 7 files changed, 86 insertions(+), 29 deletions(-) diff --git a/app.go b/app.go index 2b416c170f..7740d3aba6 100644 --- a/app.go +++ b/app.go @@ -349,12 +349,12 @@ type Config struct { // If request ip in TrustedProxies whitelist then: // 1. c.Scheme() get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header // 2. c.IP() get value from ProxyHeader header. - // 3. c.Hostname() get value from X-Forwarded-Host header + // 3. c.Host() and c.Hostname() get value from X-Forwarded-Host header // But if request ip NOT in Trusted Proxies whitelist then: // 1. c.Scheme() WON't get value from X-Forwarded-Proto, X-Forwarded-Protocol, X-Forwarded-Ssl or X-Url-Scheme header, // will return https in case when tls connection is handled by the app, of http otherwise // 2. c.IP() WON'T get value from ProxyHeader header, will return RemoteIP() from fasthttp context - // 3. c.Hostname() WON'T get value from X-Forwarded-Host header, fasthttp.Request.URI().Host() + // 3. c.Host() and c.Hostname() WON'T get value from X-Forwarded-Host header, fasthttp.Request.URI().Host() // will be used to get the hostname. // // Default: false diff --git a/client_test.go b/client_test.go index ad5eecc10e..9b866dc027 100644 --- a/client_test.go +++ b/client_test.go @@ -31,7 +31,7 @@ func Test_Client_Invalid_URL(t *testing.T) { app := New(Config{DisableStartupMessage: true}) app.Get("/", func(c Ctx) error { - return c.SendString(c.Hostname()) + return c.SendString(c.Host()) }) go func() { require.Nil(t, app.Listener(ln)) }() @@ -69,7 +69,7 @@ func Test_Client_Get(t *testing.T) { app := New(Config{DisableStartupMessage: true}) app.Get("/", func(c Ctx) error { - return c.SendString(c.Hostname()) + return c.SendString(c.Host()) }) go func() { require.Nil(t, app.Listener(ln)) }() @@ -394,7 +394,7 @@ func Test_Client_Agent_Host(t *testing.T) { app := New(Config{DisableStartupMessage: true}) app.Get("/", func(c Ctx) error { - return c.SendString(c.Hostname()) + return c.SendString(c.Host()) }) go func() { require.Nil(t, app.Listener(ln)) }() diff --git a/ctx.go b/ctx.go index 43f9835e82..5d6c57dfdb 100644 --- a/ctx.go +++ b/ctx.go @@ -210,7 +210,7 @@ func (c *DefaultCtx) BaseURL() string { if c.baseURI != "" { return c.baseURI } - c.baseURI = c.Scheme() + "://" + c.Hostname() + c.baseURI = c.Scheme() + "://" + c.Host() return c.baseURI } @@ -468,11 +468,11 @@ func (c *DefaultCtx) GetRespHeader(key string, defaultValue ...string) string { return defaultString(c.app.getString(c.fasthttp.Response.Header.Peek(key)), defaultValue) } -// Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header. +// Host contains the host derived from the X-Forwarded-Host or Host HTTP header. // Returned value is only valid within the handler. Do not store any references. // Make copies or use the Immutable setting instead. // Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. -func (c *DefaultCtx) Hostname() string { +func (c *DefaultCtx) Host() string { if c.IsProxyTrusted() { if host := c.Get(HeaderXForwardedHost); len(host) > 0 { return host @@ -481,6 +481,16 @@ func (c *DefaultCtx) Hostname() string { return c.app.getString(c.fasthttp.Request.URI().Host()) } +// Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header using the c.Host() method. +// Returned value is only valid within the handler. Do not store any references. +// Make copies or use the Immutable setting instead. +// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. +func (c *DefaultCtx) Hostname() string { + addr, _ := parseAddr(c.Host()) + + return addr +} + // Port returns the remote port of the request. func (c *DefaultCtx) Port() string { port := c.fasthttp.RemoteAddr().(*net.TCPAddr).Port @@ -1238,7 +1248,7 @@ func (c *DefaultCtx) Subdomains(offset ...int) []string { if len(offset) > 0 { o = offset[0] } - subdomains := strings.Split(c.Hostname(), ".") + subdomains := strings.Split(c.Host(), ".") l := len(subdomains) - o // Check index to avoid slice bounds out of range panic if l < 0 { diff --git a/ctx_interface.go b/ctx_interface.go index 9f24a4c4b1..3adcea498f 100644 --- a/ctx_interface.go +++ b/ctx_interface.go @@ -123,7 +123,13 @@ type Ctx interface { // Make copies or use the Immutable setting instead. GetRespHeader(key string, defaultValue ...string) string - // Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header. + // Host contains the host derived from the X-Forwarded-Host or Host HTTP header. + // Returned value is only valid within the handler. Do not store any references. + // Make copies or use the Immutable setting instead. + // Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. + Host() string + + // Hostname contains the hostname derived from the X-Forwarded-Host or Host HTTP header using the c.Host() method. // Returned value is only valid within the handler. Do not store any references. // Make copies or use the Immutable setting instead. // Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. diff --git a/ctx_test.go b/ctx_test.go index 7372c29713..82e21d39bf 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -772,18 +772,18 @@ func Test_Ctx_Get(t *testing.T) { require.Equal(t, "default", c.Get("unknown", "default")) } -// go test -run Test_Ctx_Hostname -func Test_Ctx_Hostname(t *testing.T) { +// go test -run Test_Ctx_Host +func Test_Ctx_Host(t *testing.T) { t.Parallel() app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") - require.Equal(t, "google.com", c.Hostname()) + require.Equal(t, "google.com", c.Host()) } -// go test -run Test_Ctx_Hostname_Untrusted -func Test_Ctx_Hostname_UntrustedProxy(t *testing.T) { +// go test -run Test_Ctx_Host_UntrustedProxy +func Test_Ctx_Host_UntrustedProxy(t *testing.T) { t.Parallel() // Don't trust any proxy { @@ -791,7 +791,7 @@ func Test_Ctx_Hostname_UntrustedProxy(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") - require.Equal(t, "google.com", c.Hostname()) + require.Equal(t, "google.com", c.Host()) app.ReleaseCtx(c) } // Trust to specific proxy list @@ -800,48 +800,89 @@ func Test_Ctx_Hostname_UntrustedProxy(t *testing.T) { c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") - require.Equal(t, "google.com", c.Hostname()) + require.Equal(t, "google.com", c.Host()) app.ReleaseCtx(c) } } -// go test -run Test_Ctx_Hostname_Trusted -func Test_Ctx_Hostname_TrustedProxy(t *testing.T) { +// go test -run Test_Ctx_Host_TrustedProxy +func Test_Ctx_Host_TrustedProxy(t *testing.T) { t.Parallel() { app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "0.8.0.1"}}) c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") - require.Equal(t, "google1.com", c.Hostname()) + require.Equal(t, "google1.com", c.Host()) app.ReleaseCtx(c) } } -// go test -run Test_Ctx_Hostname_UntrustedProxyRange -func Test_Ctx_Hostname_TrustedProxyRange(t *testing.T) { +// go test -run Test_Ctx_Host_TrustedProxyRange +func Test_Ctx_Host_TrustedProxyRange(t *testing.T) { t.Parallel() app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0/30"}}) c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") - require.Equal(t, "google1.com", c.Hostname()) + require.Equal(t, "google1.com", c.Host()) app.ReleaseCtx(c) } -// go test -run Test_Ctx_Hostname_UntrustedProxyRange -func Test_Ctx_Hostname_UntrustedProxyRange(t *testing.T) { +// go test -run Test_Ctx_Host_UntrustedProxyRange +func Test_Ctx_Host_UntrustedProxyRange(t *testing.T) { t.Parallel() app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"1.0.0.0/30"}}) c := app.NewCtx(&fasthttp.RequestCtx{}) c.Request().SetRequestURI("http://google.com/test") c.Request().Header.Set(HeaderXForwardedHost, "google1.com") - require.Equal(t, "google.com", c.Hostname()) + require.Equal(t, "google.com", c.Host()) app.ReleaseCtx(c) } +// go test -v -run=^$ -bench=Benchmark_Ctx_Host -benchmem -count=4 +func Benchmark_Ctx_Host(b *testing.B) { + app := New() + c := app.NewCtx(&fasthttp.RequestCtx{}) + c.Request().SetRequestURI("http://google.com/test") + var host string + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + host = c.Host() + } + require.Equal(b, "google.com", host) +} + +// go test -run Test_Ctx_Hostname +func Test_Ctx_Hostname(t *testing.T) { + t.Parallel() + app := New() + c := app.NewCtx(&fasthttp.RequestCtx{}) + + c.Request().SetRequestURI("http://google.com/test") + require.Equal(t, "google.com", c.Hostname()) + + c.Request().SetRequestURI("http://google.com:8080/test") + require.Equal(t, "google.com", c.Hostname()) +} + +// go test -v -run=^$ -bench=Benchmark_Ctx_Hostname -benchmem -count=4 +func Benchmark_Ctx_Hostname(b *testing.B) { + app := New() + c := app.NewCtx(&fasthttp.RequestCtx{}) + c.Request().SetRequestURI("http://google.com:8080/test") + var hostname string + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + hostname = c.Hostname() + } + require.Equal(b, "google.com", hostname) +} + // go test -run Test_Ctx_Port func Test_Ctx_Port(t *testing.T) { t.Parallel() diff --git a/middleware/adaptor/adopter_test.go b/middleware/adaptor/adopter_test.go index 6480bff8cf..8e9531e7b1 100644 --- a/middleware/adaptor/adopter_test.go +++ b/middleware/adaptor/adopter_test.go @@ -245,8 +245,8 @@ func testFiberToHandlerFunc(t *testing.T, checkDefaultPort bool, app ...*fiber.A if contentLength != expectedContentLength { t.Fatalf("unexpected contentLength %d. Expecting %d", contentLength, expectedContentLength) } - if c.Hostname() != expectedHost { - t.Fatalf("unexpected host %q. Expecting %q", c.Hostname(), expectedHost) + if c.Host() != expectedHost { + t.Fatalf("unexpected host %q. Expecting %q", c.Host(), expectedHost) } remoteAddr := c.Context().RemoteAddr().String() if remoteAddr != expectedRemoteAddr { diff --git a/middleware/logger/default_logger.go b/middleware/logger/default_logger.go index 822cb676d6..41442f7837 100644 --- a/middleware/logger/default_logger.go +++ b/middleware/logger/default_logger.go @@ -88,7 +88,7 @@ func defaultLogger(c fiber.Ctx, data LoggerData, cfg Config) error { case TagIPs: return buf.WriteString(c.Get(fiber.HeaderXForwardedFor)) case TagHost: - return buf.WriteString(c.Hostname()) + return buf.WriteString(c.Host()) case TagPath: return buf.WriteString(c.Path()) case TagURL: From 9fc80fcc92a9b6dcbbf5f0414c9baaf2e5d49d8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Sep 2022 17:53:45 +0200 Subject: [PATCH 077/141] Bump github.com/valyala/fasthttp from 1.39.0 to 1.40.0 (#2075) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Bump github.com/valyala/fasthttp from 1.39.0 to 1.40.0 Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.39.0 to 1.40.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.39.0...v1.40.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Add retry logic to tests Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: René Werner --- .github/workflows/test.yml | 6 +++++- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ceaf859e8e..000f7717ef 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,4 +35,8 @@ jobs: - name: Fetch Repository uses: actions/checkout@v3 - name: Run Test - run: go test ./... -v -race + uses: nick-fields/retry@v2 + with: + max_attempts: 3 + timeout_minutes: 15 + command: go test ./... -v -race diff --git a/go.mod b/go.mod index f998b13c97..30ed0e109b 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module github.com/gofiber/fiber/v2 go 1.16 require ( - github.com/valyala/fasthttp v1.39.0 + github.com/valyala/fasthttp v1.40.0 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 ) diff --git a/go.sum b/go.sum index 4b837f064b..b96abe70e2 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwc github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 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.39.0 h1:lW8mGeM7yydOqZKmwyMTaz/PH/A+CLgtmmcjv+OORfU= -github.com/valyala/fasthttp v1.39.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= +github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc= +github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= From e9db6c70d23f487a45a6a6b16040cf46e798d131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Tue, 6 Sep 2022 18:54:39 +0300 Subject: [PATCH 078/141] v3: fix logger benchmarks (#2074) * v3: fix logger benchmarks * fix datarace * fix datarace * fix datarace --- middleware/logger/README.md | 4 ++-- middleware/logger/config.go | 2 +- middleware/logger/default_logger.go | 9 ++++----- middleware/logger/logger.go | 25 ++++++++++++++++--------- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/middleware/logger/README.md b/middleware/logger/README.md index c6b99fb645..c77d4f6afb 100644 --- a/middleware/logger/README.md +++ b/middleware/logger/README.md @@ -95,7 +95,7 @@ func main() { log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) - app.Use(logger.New(logger.Config{LoggerFunc: func(c fiber.Ctx, data logger.LoggerData, cfg logger.Config) error { + app.Use(logger.New(logger.Config{LoggerFunc: func(c fiber.Ctx, data *logger.LoggerData, cfg logger.Config) error { log.Info(). Str("path", c.Path()). Str("method", c.Method()). @@ -158,7 +158,7 @@ type Config struct { // If you don't define anything for this field, it'll use classical logger of Fiber. // // Optional. Default: defaultLogger - LoggerFunc func(c fiber.Ctx, data LoggerData, cfg Config) error + LoggerFunc func(c fiber.Ctx, data *LoggerData, cfg Config) error } ``` diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 80dd3288c9..700f2c6942 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -51,7 +51,7 @@ type Config struct { // If you don't define anything for this field, it'll use default logger of Fiber. // // Optional. Default: defaultLogger - LoggerFunc func(c fiber.Ctx, data LoggerData, cfg Config) error + LoggerFunc func(c fiber.Ctx, data *LoggerData, cfg Config) error enableColors bool enableLatency bool diff --git a/middleware/logger/default_logger.go b/middleware/logger/default_logger.go index 41442f7837..7565f3dc5e 100644 --- a/middleware/logger/default_logger.go +++ b/middleware/logger/default_logger.go @@ -19,6 +19,7 @@ import ( // LoggerData is a struct to define some variables to use in custom logger function. type LoggerData struct { + mu sync.Mutex Pid string ErrPaddingStr string ChainErr error @@ -30,9 +31,7 @@ type LoggerData struct { var tmpl *fasttemplate.Template // default logger for fiber -func defaultLogger(c fiber.Ctx, data LoggerData, cfg Config) error { - var mu sync.Mutex - +func defaultLogger(c fiber.Ctx, data *LoggerData, cfg Config) error { // Alias colors colors := c.App().Config().ColorScheme @@ -185,7 +184,7 @@ func defaultLogger(c fiber.Ctx, data LoggerData, cfg Config) error { if err != nil { _, _ = buf.WriteString(err.Error()) } - mu.Lock() + data.mu.Lock() // Write buffer to output if _, err := cfg.Output.Write(buf.Bytes()); err != nil { // Write error to output @@ -194,7 +193,7 @@ func defaultLogger(c fiber.Ctx, data LoggerData, cfg Config) error { fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) } } - mu.Unlock() + data.mu.Unlock() // Put buffer back to pool bytebufferpool.Put(buf) diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 0baa0424b9..98f6894500 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -88,6 +88,7 @@ func New(config ...Config) fiber.Handler { // Set variables var ( + mu sync.Mutex once sync.Once errHandler fiber.ErrorHandler ) @@ -99,6 +100,13 @@ func New(config ...Config) fiber.Handler { // Before handling func cfg.BeforeHandlerFunc(cfg) + // Logger data + data := &LoggerData{ + Pid: pid, + ErrPaddingStr: errPaddingStr, + Timestamp: timestamp, + } + // Return new handler return func(c fiber.Ctx) (err error) { // Don't execute middleware if Next returns true @@ -144,17 +152,16 @@ func New(config ...Config) fiber.Handler { stop = time.Now() } - // Logger instance - if err = cfg.LoggerFunc(c, LoggerData{ - Pid: pid, - ErrPaddingStr: errPaddingStr, - ChainErr: chainErr, - Start: start, - Stop: stop, - Timestamp: timestamp, - }, cfg); err != nil { + // Logger instance & update some logger data fields + mu.Lock() + data.ChainErr = chainErr + data.Start = start + data.Stop = stop + + if err = cfg.LoggerFunc(c, data, cfg); err != nil { return err } + mu.Unlock() return nil } From e8c93e6153687be33638d8b5ecfebe186f70e569 Mon Sep 17 00:00:00 2001 From: Jinquan Wang <35188480+wangjq4214@users.noreply.github.com> Date: Wed, 7 Sep 2022 17:05:37 +0800 Subject: [PATCH 079/141] =?UTF-8?q?=F0=9F=90=9B=20[Fix]:=20unhandle=20in?= =?UTF-8?q?=20strictmode=20(#2055)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: unhandle in strictmode * 🐛 fix: error test * ✅ chore: add testcases for strictrouting * ✅ chore: fix test case --- app_test.go | 66 +++++++++++++++++++++++++++++++++++++++++++++++++ helpers.go | 2 +- helpers_test.go | 5 +--- 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/app_test.go b/app_test.go index d186193ae3..aadb70d7d3 100644 --- a/app_test.go +++ b/app_test.go @@ -423,6 +423,72 @@ func Test_App_Use_CaseSensitive(t *testing.T) { utils.AssertEqual(t, "/AbC", app.getString(body)) } +func Test_App_Not_Use_StrictRouting(t *testing.T) { + app := New() + + app.Use("/abc", func(c *Ctx) error { + return c.SendString(c.Path()) + }) + + g := app.Group("/foo") + g.Use("/", func(c *Ctx) error { + return c.SendString(c.Path()) + }) + + // wrong path in the requested route -> 404 + resp, err := app.Test(httptest.NewRequest(MethodGet, "/abc/", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + // right path in the requrested route -> 200 + resp, err = app.Test(httptest.NewRequest(MethodGet, "/abc", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + // wrong path with group in the requested route -> 404 + resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + // right path with group in the requrested route -> 200 + resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo/", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") +} + +func Test_App_Use_StrictRouting(t *testing.T) { + app := New(Config{StrictRouting: true}) + + app.Get("/abc", func(c *Ctx) error { + return c.SendString(c.Path()) + }) + + g := app.Group("/foo") + g.Get("/", func(c *Ctx) error { + return c.SendString(c.Path()) + }) + + // wrong path in the requested route -> 404 + resp, err := app.Test(httptest.NewRequest(MethodGet, "/abc/", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") + + // right path in the requrested route -> 200 + resp, err = app.Test(httptest.NewRequest(MethodGet, "/abc", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + // wrong path with group in the requested route -> 404 + resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusNotFound, resp.StatusCode, "Status code") + + // right path with group in the requrested route -> 200 + resp, err = app.Test(httptest.NewRequest(MethodGet, "/foo/", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") +} + func Test_App_Add_Method_Test(t *testing.T) { app := New() defer func() { diff --git a/helpers.go b/helpers.go index 73e882e3b5..c3360ad032 100644 --- a/helpers.go +++ b/helpers.go @@ -191,7 +191,7 @@ func setETag(c *Ctx, weak bool) { } func getGroupPath(prefix, path string) string { - if len(path) == 0 || path == "/" { + if len(path) == 0 { return prefix } diff --git a/helpers_test.go b/helpers_test.go index 03c1e687a9..8afb71bb48 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -148,14 +148,11 @@ func Benchmark_Utils_ETag_Weak(b *testing.B) { func Test_Utils_getGroupPath(t *testing.T) { t.Parallel() res := getGroupPath("/v1", "/") - utils.AssertEqual(t, "/v1", res) + utils.AssertEqual(t, "/v1/", res) res = getGroupPath("/v1/", "/") utils.AssertEqual(t, "/v1/", res) - res = getGroupPath("/v1", "/") - utils.AssertEqual(t, "/v1", res) - res = getGroupPath("/", "/") utils.AssertEqual(t, "/", res) From 3bbeb4ebe97aa90effe785b222f715795c43daf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Wed, 7 Sep 2022 12:57:39 +0200 Subject: [PATCH 080/141] Add changelog link to release drafter --- .github/release-drafter.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 41fa29a751..13f19e0e60 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -90,4 +90,7 @@ autolabeler: template: | $CHANGES + + **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...$COMPLETE + Thank you $CONTRIBUTORS for making this update possible. From 8dfbd9460871ad53c0277afcc4eb7b93fbce61b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Wed, 7 Sep 2022 12:59:08 +0200 Subject: [PATCH 081/141] Add changelog link to release drafter --- .github/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 13f19e0e60..d5b5b3278f 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -91,6 +91,6 @@ template: | $CHANGES - **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...$COMPLETE + **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...$RESOLVED_VERSION Thank you $CONTRIBUTORS for making this update possible. From 22239f621d794bbedf7c0da5a562883d26712b8e Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 7 Sep 2022 13:06:27 +0200 Subject: [PATCH 082/141] Update release-drafter.yml --- .github/release-drafter.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index d5b5b3278f..273d909fa8 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -90,7 +90,6 @@ autolabeler: template: | $CHANGES - **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...$RESOLVED_VERSION Thank you $CONTRIBUTORS for making this update possible. From aa2881b2b29a7cda35e233419c2ca930f83d5749 Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 7 Sep 2022 13:06:43 +0200 Subject: [PATCH 083/141] Update release-drafter.yml --- .github/release-drafter.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 41fa29a751..273d909fa8 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -90,4 +90,6 @@ autolabeler: template: | $CHANGES + **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...$RESOLVED_VERSION + Thank you $CONTRIBUTORS for making this update possible. From bb102259c4635efe76ed7e62e20d4c1f6a8f4b4a Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 7 Sep 2022 14:36:27 +0200 Subject: [PATCH 084/141] Update release-drafter.yml --- .github/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 273d909fa8..6df748953c 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -90,6 +90,6 @@ autolabeler: template: | $CHANGES - **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...$RESOLVED_VERSION + **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION Thank you $CONTRIBUTORS for making this update possible. From ca0f6630a167a8c3548a457d182d34d5379a187a Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 7 Sep 2022 14:36:37 +0200 Subject: [PATCH 085/141] Update release-drafter.yml --- .github/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 273d909fa8..6df748953c 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -90,6 +90,6 @@ autolabeler: template: | $CHANGES - **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...$RESOLVED_VERSION + **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION Thank you $CONTRIBUTORS for making this update possible. From 35753f7eac08d26def76d15695537157c338ef63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Thu, 8 Sep 2022 08:29:05 +0300 Subject: [PATCH 086/141] :bug: bug: fix constraints when to use multiple params (#2077) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :bug: bug: fix constraints when to use multiple params * test case route matching remove the parameters for negative routing matches, as they are unnecessary and should not be considered there Co-authored-by: René Werner --- path.go | 14 +-- path_test.go | 266 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 165 insertions(+), 115 deletions(-) diff --git a/path.go b/path.go index 27f09f5f9e..927a97aaca 100644 --- a/path.go +++ b/path.go @@ -362,23 +362,15 @@ func findLastCharsetPosition(search string, charset []byte) int { // findNextCharsetPositionConstraint search the next char position from the charset // unlike findNextCharsetPosition, it takes care of constraint start-end chars to parse route pattern func findNextCharsetPositionConstraint(search string, charset []byte) int { + constraintStart := findNextNonEscapedCharsetPosition(search, parameterConstraintStartChars) + constraintEnd := findNextNonEscapedCharsetPosition(search, parameterConstraintEndChars) nextPosition := -1 - constraintStart := -1 - constraintEnd := -1 for _, char := range charset { pos := strings.IndexByte(search, char) - if char == paramConstraintStart { - constraintStart = pos - } - - if char == paramConstraintEnd { - constraintEnd = pos - } - if pos != -1 && (pos < nextPosition || nextPosition == -1) { - if pos > constraintStart && pos < constraintEnd { + if (pos > constraintStart && pos > constraintEnd) || (pos < constraintStart && pos < constraintEnd) { nextPosition = pos } } diff --git a/path_test.go b/path_test.go index ca02ea2b15..5e89e1a875 100644 --- a/path_test.go +++ b/path_test.go @@ -432,128 +432,128 @@ func Test_Path_matchParams(t *testing.T) { {url: "/api/:test", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/true", params: []string{"true"}, match: true}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, {url: "/api/v1/8728382.5", params: []string{"8728382.5"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/#!?", params: []string{"#!?"}, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/#!?", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/f0fa66cc-d22e-445b-866d-1d76e776371d", params: []string{"f0fa66cc-d22e-445b-866d-1d76e776371d"}, match: true}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/123", params: []string{"123"}, match: false}, + {url: "/api/v1/123", params: nil, match: false}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, {url: "/api/v1/ent", params: []string{"ent"}, match: true}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: true}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, - {url: "/api/v1/123", params: []string{"123"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/123", params: nil, match: false}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/e", params: []string{"e"}, match: false}, + {url: "/api/v1/e", params: nil, match: false}, {url: "/api/v1/en", params: []string{"en"}, match: true}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: true}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/e", params: []string{"e"}, match: false}, + {url: "/api/v1/e", params: nil, match: false}, {url: "/api/v1/en", params: []string{"en"}, match: true}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: true}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, - {url: "/api/v1/1", params: []string{"1"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/1", params: nil, match: false}, {url: "/api/v1/5", params: []string{"5"}, match: true}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, {url: "/api/v1/1", params: []string{"1"}, match: true}, {url: "/api/v1/5", params: []string{"5"}, match: true}, - {url: "/api/v1/15", params: []string{"15"}, match: false}, + {url: "/api/v1/15", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, {url: "/api/v1/9", params: []string{"9"}, match: true}, {url: "/api/v1/5", params: []string{"5"}, match: true}, - {url: "/api/v1/15", params: []string{"15"}, match: false}, + {url: "/api/v1/15", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, - {url: "/api/v1/15", params: []string{"15"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/15", params: nil, match: false}, {url: "/api/v1/peach", params: []string{"peach"}, match: true}, - {url: "/api/v1/p34ch", params: []string{"p34ch"}, match: false}, + {url: "/api/v1/p34ch", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, - {url: "/api/v1/15", params: []string{"15"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/15", params: nil, match: false}, {url: "/api/v1/2022-08-27", params: []string{"2022-08-27"}, match: true}, - {url: "/api/v1/2022/08-27", params: []string{"p34ch"}, match: false}, + {url: "/api/v1/2022/08-27", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/87283827683", params: nil, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/87283827683", params: nil, match: false}, {url: "/api/v1/25", params: []string{"25"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, }) testCase("/api/v1/:param", []testparams{ {url: "/api/v1/entity", params: []string{"entity"}, match: true}, @@ -562,11 +562,40 @@ func Test_Path_matchParams(t *testing.T) { {url: "/api/v1/true", params: []string{"true"}, match: true}, }) testCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/87283827683", params: nil, match: false}, {url: "/api/v1/25", params: []string{"25"}, match: true}, {url: "/api/v1/1200", params: []string{"1200"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, + }) + testCase("/api/v1/:lang/videos/:page", []testparams{ + {url: "/api/v1/try/videos/200", params: nil, match: false}, + {url: "/api/v1/tr/videos/1800", params: nil, match: false}, + {url: "/api/v1/tr/videos/100", params: []string{"tr", "100"}, match: true}, + {url: "/api/v1/e/videos/10", params: nil, match: false}, + }) + testCase("/api/v1/:lang/:page", []testparams{ + {url: "/api/v1/try/200", params: nil, match: false}, + {url: "/api/v1/tr/1800", params: nil, match: false}, + {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, + {url: "/api/v1/e/10", params: nil, match: false}, + }) + testCase("/api/v1/:lang/:page", []testparams{ + {url: "/api/v1/try/200", params: []string{"try", "200"}, match: true}, + {url: "/api/v1/tr/1800", params: nil, match: false}, + {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, + {url: "/api/v1/e/10", params: nil, match: false}, + }) + testCase("/api/v1/:lang/:page", []testparams{ + {url: "/api/v1/try/200", params: nil, match: false}, + {url: "/api/v1/tr/1800", params: []string{"tr", "1800"}, match: true}, + {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, + {url: "/api/v1/e/10", params: nil, match: false}, + }) + testCase("/api/v1/:date/:regex", []testparams{ + {url: "/api/v1/2005-11-01/a", params: nil, match: false}, + {url: "/api/v1/2005-1101/paach", params: nil, match: false}, + {url: "/api/v1/2005-11-01/peach", params: []string{"2005-11-01", "peach"}, match: true}, }) } @@ -657,128 +686,128 @@ func Benchmark_Path_matchParams(t *testing.B) { {url: "/api/v1/", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/true", params: []string{"true"}, match: true}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, {url: "/api/v1/8728382.5", params: []string{"8728382.5"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/#!?", params: []string{"#!?"}, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/#!?", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/f0fa66cc-d22e-445b-866d-1d76e776371d", params: []string{"f0fa66cc-d22e-445b-866d-1d76e776371d"}, match: true}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ {url: "/api/v1/entity", params: []string{"entity"}, match: true}, - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/123", params: []string{"123"}, match: false}, + {url: "/api/v1/123", params: nil, match: false}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, {url: "/api/v1/ent", params: []string{"ent"}, match: true}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: true}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, - {url: "/api/v1/123", params: []string{"123"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/123", params: nil, match: false}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/e", params: []string{"e"}, match: false}, + {url: "/api/v1/e", params: nil, match: false}, {url: "/api/v1/en", params: []string{"en"}, match: true}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: true}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/e", params: []string{"e"}, match: false}, + {url: "/api/v1/e", params: nil, match: false}, {url: "/api/v1/en", params: []string{"en"}, match: true}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: true}, {url: "/api/v1/12345", params: []string{"12345"}, match: true}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, - {url: "/api/v1/1", params: []string{"1"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/1", params: nil, match: false}, {url: "/api/v1/5", params: []string{"5"}, match: true}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, {url: "/api/v1/1", params: []string{"1"}, match: true}, {url: "/api/v1/5", params: []string{"5"}, match: true}, - {url: "/api/v1/15", params: []string{"15"}, match: false}, + {url: "/api/v1/15", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, {url: "/api/v1/9", params: []string{"9"}, match: true}, {url: "/api/v1/5", params: []string{"5"}, match: true}, - {url: "/api/v1/15", params: []string{"15"}, match: false}, + {url: "/api/v1/15", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/2005-11-01", params: []string{"2005-11-01"}, match: true}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, - {url: "/api/v1/15", params: []string{"15"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/15", params: nil, match: false}, {url: "/api/v1/peach", params: []string{"peach"}, match: true}, - {url: "/api/v1/p34ch", params: []string{"p34ch"}, match: false}, + {url: "/api/v1/p34ch", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/ent", params: []string{"ent"}, match: false}, - {url: "/api/v1/15", params: []string{"15"}, match: false}, + {url: "/api/v1/ent", params: nil, match: false}, + {url: "/api/v1/15", params: nil, match: false}, {url: "/api/v1/2022-08-27", params: []string{"2022-08-27"}, match: true}, - {url: "/api/v1/2022/08-27", params: []string{"p34ch"}, match: false}, + {url: "/api/v1/2022/08-27", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/8728382", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: nil, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/87283827683", params: nil, match: false}, {url: "/api/v1/123", params: []string{"123"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/87283827683", params: nil, match: false}, {url: "/api/v1/25", params: []string{"25"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, }) benchCase("/api/v1/:param", []testparams{ {url: "/api/v1/entity", params: []string{"entity"}, match: true}, @@ -787,10 +816,39 @@ func Benchmark_Path_matchParams(t *testing.B) { {url: "/api/v1/true", params: []string{"true"}, match: true}, }) benchCase("/api/v1/:param", []testparams{ - {url: "/api/v1/entity", params: []string{"entity"}, match: false}, - {url: "/api/v1/87283827683", params: []string{"8728382"}, match: false}, + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/87283827683", params: nil, match: false}, {url: "/api/v1/25", params: []string{"25"}, match: true}, {url: "/api/v1/1200", params: []string{"1200"}, match: true}, - {url: "/api/v1/true", params: []string{"true"}, match: false}, + {url: "/api/v1/true", params: nil, match: false}, + }) + benchCase("/api/v1/:lang/videos/:page", []testparams{ + {url: "/api/v1/try/videos/200", params: nil, match: false}, + {url: "/api/v1/tr/videos/1800", params: nil, match: false}, + {url: "/api/v1/tr/videos/100", params: []string{"tr", "100"}, match: true}, + {url: "/api/v1/e/videos/10", params: nil, match: false}, + }) + benchCase("/api/v1/:lang/:page", []testparams{ + {url: "/api/v1/try/200", params: nil, match: false}, + {url: "/api/v1/tr/1800", params: nil, match: false}, + {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, + {url: "/api/v1/e/10", params: nil, match: false}, + }) + benchCase("/api/v1/:lang/:page", []testparams{ + {url: "/api/v1/try/200", params: []string{"try", "200"}, match: true}, + {url: "/api/v1/tr/1800", params: nil, match: false}, + {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, + {url: "/api/v1/e/10", params: nil, match: false}, + }) + benchCase("/api/v1/:lang/:page", []testparams{ + {url: "/api/v1/try/200", params: nil, match: false}, + {url: "/api/v1/tr/1800", params: []string{"tr", "1800"}, match: true}, + {url: "/api/v1/tr/100", params: []string{"tr", "100"}, match: true}, + {url: "/api/v1/e/10", params: nil, match: false}, + }) + benchCase("/api/v1/:date/:regex", []testparams{ + {url: "/api/v1/2005-11-01/a", params: nil, match: false}, + {url: "/api/v1/2005-1101/paach", params: nil, match: false}, + {url: "/api/v1/2005-11-01/peach", params: []string{"2005-11-01", "peach"}, match: true}, }) } From 012a2b16a5a391f76701431027224d69897e7fb2 Mon Sep 17 00:00:00 2001 From: RW Date: Thu, 8 Sep 2022 07:32:17 +0200 Subject: [PATCH 087/141] prepare release v2.37.1 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index bd3708b842..ee7b468bb6 100644 --- a/app.go +++ b/app.go @@ -30,7 +30,7 @@ import ( ) // Version of current fiber package -const Version = "2.37.0" +const Version = "2.37.1" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From 281e2f00463d7c7947cc37344ddab9d2dff3409d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Thu, 8 Sep 2022 08:57:05 +0300 Subject: [PATCH 088/141] :sparkles: v3 (feature): merge Listen methods & ListenConfig (#1930) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :sparkles: v3: new Start method for app * :sparkles: v3: new Start method for app * :sparkles: v3: new Start method for app * :sparkles: v3: new Start method for app * :sparkles: v3: new Start method for app * :sparkles: v3: new Start method for app * fix tests * improve graceful shutdown * update * Start -> Listen * rename test funcs. * Add Test_Listen_Graceful_Shutdown test. * add OnShutdownSuccess * fix tests * fix tests * split listen & listener * typo * Add retry logic to tests * Add retry logic to tests * Add retry logic to tests * Add retry logic to tests Co-authored-by: René Werner --- .github/workflows/test.yml | 6 +- app.go | 26 +-- app_test.go | 31 +-- client_test.go | 169 ++++++++++---- error.go | 13 +- hooks_test.go | 8 +- listen.go | 331 +++++++++++++++++----------- listen_test.go | 387 +++++++++++++++++++++++++-------- middleware/pprof/pprof_test.go | 8 +- middleware/proxy/proxy_test.go | 37 +++- prefork.go | 20 +- prefork_test.go | 14 +- 12 files changed, 714 insertions(+), 336 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ffd8b92709..1ffb54108b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,4 +35,8 @@ jobs: - name: Fetch Repository uses: actions/checkout@v3 - name: Run Test - run: go test ./... -v -race + uses: nick-fields/retry@v2 + with: + max_attempts: 3 + timeout_minutes: 15 + command: go test ./... -v -race diff --git a/app.go b/app.go index 7740d3aba6..861c321b68 100644 --- a/app.go +++ b/app.go @@ -124,11 +124,6 @@ type App struct { // Config is a struct holding the server settings. type Config struct { - // When set to true, this will spawn multiple Go processes listening on the same port. - // - // Default: false - Prefork bool `json:"prefork"` - // Enables the "Server: value" HTTP header. // // Default: "" @@ -270,11 +265,6 @@ type Config struct { // Default: false DisableHeaderNormalizing bool `json:"disable_header_normalizing"` - // When set to true, it will not print out the «Fiber» ASCII art and listening address. - // - // Default: false - DisableStartupMessage bool `json:"disable_startup_message"` - // This function allows to setup app name for the app // // Default: nil @@ -332,12 +322,6 @@ type Config struct { // Default: xml.Marshal XMLEncoder utils.XMLMarshal `json:"-"` - // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only) - // WARNING: When prefork is set to true, only "tcp4" and "tcp6" can be chose. - // - // Default: NetworkTCP4 - Network string - // If you find yourself behind some sort of proxy, like a load balancer, // then certain header information may be sent to you using special X-Forwarded-* headers or the Forwarded header. // For example, the Host HTTP header is usually used to return the requested host. @@ -374,10 +358,6 @@ type Config struct { // Default: false EnableIPValidation bool `json:"enable_ip_validation"` - // If set to true, will print all routes with their method, path and handler. - // Default: false - EnablePrintRoutes bool `json:"enable_print_routes"` - // You can define custom color scheme. They'll be used for startup message, route list and some middlewares. // // Optional. Default: DefaultColors @@ -533,9 +513,6 @@ func New(config ...Config) *App { if app.config.XMLEncoder == nil { app.config.XMLEncoder = xml.Marshal } - if app.config.Network == "" { - app.config.Network = NetworkTCP4 - } app.config.trustedProxiesMap = make(map[string]struct{}, len(app.config.TrustedProxies)) for _, ipAddress := range app.config.TrustedProxies { @@ -831,7 +808,8 @@ func (app *App) HandlersCount() uint32 { // Shutdown does not close keepalive connections so its recommended to set ReadTimeout to something else than 0. func (app *App) Shutdown() error { if app.hooks != nil { - defer app.hooks.executeOnShutdownHooks() + // TODO: check should be defered? + app.hooks.executeOnShutdownHooks() } app.mutex.Lock() diff --git a/app_test.go b/app_test.go index 31aa9c2789..1e14597346 100644 --- a/app_test.go +++ b/app_test.go @@ -599,16 +599,14 @@ func Test_App_New(t *testing.T) { func Test_App_Config(t *testing.T) { app := New(Config{ - DisableStartupMessage: true, + StrictRouting: true, }) - require.True(t, app.Config().DisableStartupMessage) + require.True(t, app.Config().StrictRouting) } func Test_App_Shutdown(t *testing.T) { t.Run("success", func(t *testing.T) { - app := New(Config{ - DisableStartupMessage: true, - }) + app := New() require.True(t, app.Shutdown() == nil) }) @@ -1098,7 +1096,6 @@ func Test_App_Deep_Group(t *testing.T) { // go test -run Test_App_Next_Method func Test_App_Next_Method(t *testing.T) { app := New() - app.config.DisableStartupMessage = true app.Use(func(c Ctx) error { require.Equal(t, MethodGet, c.Method()) @@ -1140,7 +1137,6 @@ func Test_NewError(t *testing.T) { // go test -run Test_Test_Timeout func Test_Test_Timeout(t *testing.T) { app := New() - app.config.DisableStartupMessage = true app.Get("/", testEmptyHandler) @@ -1166,7 +1162,6 @@ func (errorReader) Read([]byte) (int, error) { // go test -run Test_Test_DumpError func Test_Test_DumpError(t *testing.T) { app := New() - app.config.DisableStartupMessage = true app.Get("/", testEmptyHandler) @@ -1236,10 +1231,9 @@ func Test_App_HandlersCount(t *testing.T) { // go test -run Test_App_ReadTimeout func Test_App_ReadTimeout(t *testing.T) { app := New(Config{ - ReadTimeout: time.Nanosecond, - IdleTimeout: time.Minute, - DisableStartupMessage: true, - DisableKeepalive: true, + ReadTimeout: time.Nanosecond, + IdleTimeout: time.Minute, + DisableKeepalive: true, }) app.Get("/read-timeout", func(c Ctx) error { @@ -1266,14 +1260,12 @@ func Test_App_ReadTimeout(t *testing.T) { require.Nil(t, app.Shutdown()) }() - require.Nil(t, app.Listen(":4004")) + require.Nil(t, app.Listen(":4004", ListenConfig{DisableStartupMessage: true})) } // go test -run Test_App_BadRequest func Test_App_BadRequest(t *testing.T) { - app := New(Config{ - DisableStartupMessage: true, - }) + app := New() app.Get("/bad-request", func(c Ctx) error { return c.SendString("I should not be sent") @@ -1298,14 +1290,13 @@ func Test_App_BadRequest(t *testing.T) { require.Nil(t, app.Shutdown()) }() - require.Nil(t, app.Listen(":4005")) + require.Nil(t, app.Listen(":4005", ListenConfig{DisableStartupMessage: true})) } // go test -run Test_App_SmallReadBuffer func Test_App_SmallReadBuffer(t *testing.T) { app := New(Config{ - ReadBufferSize: 1, - DisableStartupMessage: true, + ReadBufferSize: 1, }) app.Get("/small-read-buffer", func(c Ctx) error { @@ -1322,7 +1313,7 @@ func Test_App_SmallReadBuffer(t *testing.T) { require.Nil(t, app.Shutdown()) }() - require.Nil(t, app.Listen(":4006")) + require.Nil(t, app.Listen(":4006", ListenConfig{DisableStartupMessage: true})) } func Test_App_Server(t *testing.T) { diff --git a/client_test.go b/client_test.go index 9b866dc027..e3d78dca6d 100644 --- a/client_test.go +++ b/client_test.go @@ -28,13 +28,17 @@ func Test_Client_Invalid_URL(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", func(c Ctx) error { return c.SendString(c.Host()) }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() a := Get("http://example.com\r\n\r\nGET /\r\n\r\n") @@ -66,13 +70,17 @@ func Test_Client_Get(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", func(c Ctx) error { return c.SendString(c.Host()) }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() for i := 0; i < 5; i++ { a := Get("http://example.com") @@ -92,14 +100,17 @@ func Test_Client_Head(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Head("/", func(c Ctx) error { return c.SendStatus(StatusAccepted) }) - go func() { require.Nil(t, app.Listener(ln)) }() - + go func() { + require.Nil(t, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() for i := 0; i < 5; i++ { a := Head("http://example.com") @@ -118,14 +129,18 @@ func Test_Client_Post(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Post("/", func(c Ctx) error { return c.Status(StatusCreated). SendString(c.FormValue("foo")) }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() for i := 0; i < 5; i++ { args := AcquireArgs() @@ -152,13 +167,17 @@ func Test_Client_Put(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Put("/", func(c Ctx) error { return c.SendString(c.FormValue("foo")) }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() for i := 0; i < 5; i++ { args := AcquireArgs() @@ -185,13 +204,17 @@ func Test_Client_Patch(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Patch("/", func(c Ctx) error { return c.SendString(c.FormValue("foo")) }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, nil, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() for i := 0; i < 5; i++ { args := AcquireArgs() @@ -218,14 +241,18 @@ func Test_Client_Delete(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Delete("/", func(c Ctx) error { return c.Status(StatusNoContent). SendString("deleted") }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() for i := 0; i < 5; i++ { args := AcquireArgs() @@ -249,13 +276,17 @@ func Test_Client_UserAgent(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", func(c Ctx) error { return c.Send(c.Request().Header.UserAgent()) }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, nil, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() t.Run("default", func(t *testing.T) { for i := 0; i < 5; i++ { @@ -391,13 +422,17 @@ func Test_Client_Agent_Host(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", func(c Ctx) error { return c.SendString(c.Host()) }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() a := Get("http://1.1.1.1:8080"). Host("example.com"). @@ -487,13 +522,17 @@ func Test_Client_Agent_Custom_Response(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", func(c Ctx) error { return c.SendString("custom") }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() for i := 0; i < 5; i++ { a := AcquireAgent() @@ -524,13 +563,17 @@ func Test_Client_Agent_Dest(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", func(c Ctx) error { return c.SendString("dest") }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, nil, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() t.Run("small dest", func(t *testing.T) { dest := []byte("de") @@ -592,9 +635,13 @@ func Test_Client_Agent_RetryIf(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() a := Post("http://example.com"). RetryIf(func(req *Request) bool { @@ -699,7 +746,7 @@ func Test_Client_Agent_MultipartForm(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Post("/", func(c Ctx) error { require.Equal(t, "multipart/form-data; boundary=myBoundary", c.Get(HeaderContentType)) @@ -711,7 +758,11 @@ func Test_Client_Agent_MultipartForm(t *testing.T) { return c.Send(c.Request().Body()) }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, nil, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() args := AcquireArgs() @@ -754,7 +805,7 @@ func Test_Client_Agent_MultipartForm_SendFiles(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Post("/", func(c Ctx) error { require.Equal(t, "multipart/form-data; boundary=myBoundary", c.Get(HeaderContentType)) @@ -781,7 +832,11 @@ func Test_Client_Agent_MultipartForm_SendFiles(t *testing.T) { return c.SendString("multipart form files") }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, nil, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() for i := 0; i < 5; i++ { ff := AcquireFormFile() @@ -885,14 +940,18 @@ func Test_Client_Agent_Timeout(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", func(c Ctx) error { time.Sleep(time.Millisecond * 200) return c.SendString("timeout") }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, nil, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() a := Get("http://example.com"). Timeout(time.Millisecond * 50) @@ -911,13 +970,17 @@ func Test_Client_Agent_Reuse(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", func(c Ctx) error { return c.SendString("reuse") }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, nil, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() a := Get("http://example.com"). Reuse() @@ -952,13 +1015,17 @@ func Test_Client_Agent_InsecureSkipVerify(t *testing.T) { ln = tls.NewListener(ln, serverTLSConf) - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", func(c Ctx) error { return c.SendString("ignore tls") }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, nil, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() code, body, errs := Get("https://" + ln.Addr().String()). InsecureSkipVerify(). @@ -981,13 +1048,17 @@ func Test_Client_Agent_TLS(t *testing.T) { ln = tls.NewListener(ln, serverTLSConf) - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", func(c Ctx) error { return c.SendString("tls") }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, nil, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() code, body, errs := Get("https://" + ln.Addr().String()). TLSConfig(clientTLSConf). @@ -1003,7 +1074,7 @@ func Test_Client_Agent_MaxRedirectsCount(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", func(c Ctx) error { if c.Request().URI().QueryArgs().Has("foo") { @@ -1015,7 +1086,11 @@ func Test_Client_Agent_MaxRedirectsCount(t *testing.T) { return c.SendString("redirect") }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, nil, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() t.Run("success", func(t *testing.T) { a := Get("http://example.com?foo"). @@ -1049,7 +1124,7 @@ func Test_Client_Agent_Struct(t *testing.T) { ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", func(c Ctx) error { return c.JSON(data{true}) @@ -1059,7 +1134,11 @@ func Test_Client_Agent_Struct(t *testing.T) { return c.SendString(`{"success"`) }) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, nil, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() t.Run("success", func(t *testing.T) { t.Parallel() @@ -1128,11 +1207,15 @@ func testAgent(t *testing.T, handler Handler, wrapAgent func(agent *Agent), exce ln := fasthttputil.NewInmemoryListener() - app := New(Config{DisableStartupMessage: true}) + app := New() app.Get("/", handler) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, nil, app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + })) + }() c := 1 if len(count) > 0 { diff --git a/error.go b/error.go index 87a6af38c9..6b08acef15 100644 --- a/error.go +++ b/error.go @@ -2,19 +2,24 @@ package fiber import ( errors "encoding/json" - goErrors "errors" + stdErrors "errors" "github.com/gofiber/fiber/v3/internal/schema" ) +// Graceful shutdown errors +var ( + ErrGracefulTimeout = stdErrors.New("shutdown: graceful timeout has been reached, exiting") +) + // Range errors var ( - ErrRangeMalformed = goErrors.New("range: malformed range header string") - ErrRangeUnsatisfiable = goErrors.New("range: unsatisfiable range") + ErrRangeMalformed = stdErrors.New("range: malformed range header string") + ErrRangeUnsatisfiable = stdErrors.New("range: unsatisfiable range") ) // Binder errors -var ErrCustomBinderNotFound = goErrors.New("binder: custom binder not found, please be sure to enter the right name") +var ErrCustomBinderNotFound = stdErrors.New("binder: custom binder not found, please be sure to enter the right name") // gorilla/schema errors type ( diff --git a/hooks_test.go b/hooks_test.go index ca46993738..611dd93eb7 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -158,9 +158,7 @@ func Test_Hook_OnShutdown(t *testing.T) { func Test_Hook_OnListen(t *testing.T) { t.Parallel() - app := New(Config{ - DisableStartupMessage: true, - }) + app := New() buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) @@ -176,8 +174,8 @@ func Test_Hook_OnListen(t *testing.T) { time.Sleep(1000 * time.Millisecond) require.Nil(t, app.Shutdown()) }() - require.Nil(t, app.Listen(":9000")) + require.Nil(t, app.Listen(":9000", ListenConfig{DisableStartupMessage: true})) require.Equal(t, "ready", buf.String()) } @@ -198,5 +196,5 @@ func Test_Hook_OnHook(t *testing.T) { return nil }) - require.Nil(t, app.prefork(NetworkTCP4, ":3000", nil)) + require.Nil(t, app.prefork(":3000", nil, ListenConfig{DisableStartupMessage: true, EnablePrefork: true})) } diff --git a/listen.go b/listen.go index 05f210cc60..d3ab216aae 100644 --- a/listen.go +++ b/listen.go @@ -5,10 +5,11 @@ package fiber import ( + "context" "crypto/tls" "crypto/x509" - "errors" "fmt" + "log" "net" "os" "path/filepath" @@ -23,96 +24,167 @@ import ( "github.com/mattn/go-isatty" ) -// Listener can be used to pass a custom listener. -func (app *App) Listener(ln net.Listener) error { - // prepare the server for the start - app.startupProcess() +// ListenConfig is a struct to customize startup of Fiber. +// +// TODO: Add timeout for graceful shutdown. +type ListenConfig struct { + // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only) + // WARNING: When prefork is set to true, only "tcp4" and "tcp6" can be chose. + // + // Default: NetworkTCP4 + ListenerNetwork string `json:"listener_network"` + + // CertFile is a path of certficate file. + // If you want to use TLS, you have to enter this field. + // + // Default : "" + CertFile string `json:"cert_file"` + + // KeyFile is a path of certficate's private key. + // If you want to use TLS, you have to enter this field. + // + // Default : "" + CertKeyFile string `json:"cert_key_file"` + + // CertClientFile is a path of client certficate. + // If you want to use mTLS, you have to enter this field. + // + // Default : "" + CertClientFile string `json:"cert_client_file"` + + // GracefulContext is a field to shutdown Fiber by given context gracefully. + // + // Default: nil + GracefulContext context.Context `json:"graceful_context"` + + // TLSConfigFunc allows customizing tls.Config as you want. + // + // Default: nil + TLSConfigFunc func(tlsConfig *tls.Config) `json:"tls_config_func"` + + // ListenerFunc allows accessing and customizing net.Listener. + // + // Default: nil + ListenerAddrFunc func(addr net.Addr) `json:"listener_addr_func"` + + // BeforeServeFunc allows customizing and accessing fiber app before serving the app. + // + // Default: nil + BeforeServeFunc func(app *App) error `json:"before_serve_func"` + + // When set to true, it will not print out the «Fiber» ASCII art and listening address. + // + // Default: false + DisableStartupMessage bool `json:"disable_startup_message"` + + // When set to true, this will spawn multiple Go processes listening on the same port. + // + // Default: false + EnablePrefork bool `json:"enable_prefork"` + + // If set to true, will print all routes with their method, path and handler. + // + // Default: false + EnablePrintRoutes bool `json:"enable_print_routes"` + + // OnShutdownError allows to customize error behavior when to graceful shutdown server by given signal. + // + // Default: Print error with log.Fatalf() + OnShutdownError func(err error) + + // OnShutdownSuccess allows to customize success behavior when to graceful shutdown server by given signal. + // + // Default: nil + OnShutdownSuccess func() +} - // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), getTlsConfig(ln) != nil, "") +// listenConfigDefault is a function to set default values of ListenConfig. +func listenConfigDefault(config ...ListenConfig) ListenConfig { + if len(config) < 1 { + return ListenConfig{ + ListenerNetwork: NetworkTCP4, + OnShutdownError: func(err error) { + log.Fatalf("shutdown: %v", err) + }, + } } - // Print routes - if app.config.EnablePrintRoutes { - app.printRoutesMessage() + cfg := config[0] + if cfg.ListenerNetwork == "" { + cfg.ListenerNetwork = NetworkTCP4 } - // Prefork is not supported for custom listeners - if app.config.Prefork { - fmt.Println("[Warning] Prefork isn't supported for custom listeners.") + if cfg.OnShutdownError == nil { + cfg.OnShutdownError = func(err error) { + log.Fatalf("shutdown: %v", err) + } } - // Start listening - return app.server.Serve(ln) + return cfg } // Listen serves HTTP requests from the given addr. +// You should enter custom ListenConfig to customize startup. (TLS, mTLS, prefork...) // // app.Listen(":8080") // app.Listen("127.0.0.1:8080") -func (app *App) Listen(addr string) error { - // Start prefork - if app.config.Prefork { - return app.prefork(app.config.Network, addr, nil) - } - - // Setup listener - ln, err := net.Listen(app.config.Network, addr) - if err != nil { - return err - } +// app.Listen(":8080", ListenConfig{EnablePrefork: true}) +func (app *App) Listen(addr string, config ...ListenConfig) error { + cfg := listenConfigDefault(config...) + + // Configure TLS + var tlsConfig *tls.Config = nil + if cfg.CertFile != "" && cfg.CertKeyFile != "" { + cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.CertKeyFile) + if err != nil { + return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", cfg.CertFile, cfg.CertKeyFile, err) + } - // prepare the server for the start - app.startupProcess() + tlsHandler := &TLSHandler{} + tlsConfig = &tls.Config{ + MinVersion: tls.VersionTLS12, + Certificates: []tls.Certificate{ + cert, + }, + GetCertificate: tlsHandler.GetClientInfo, + } - // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), false, "") - } + if cfg.CertClientFile != "" { + clientCACert, err := os.ReadFile(filepath.Clean(cfg.CertClientFile)) + if err != nil { + return err + } - // Print routes - if app.config.EnablePrintRoutes { - app.printRoutesMessage() - } + clientCertPool := x509.NewCertPool() + clientCertPool.AppendCertsFromPEM(clientCACert) - // Start listening - return app.server.Serve(ln) -} + tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert + tlsConfig.ClientCAs = clientCertPool + } -// ListenTLS serves HTTPS requests from the given addr. -// certFile and keyFile are the paths to TLS certificate and key file: -// -// app.ListenTLS(":8080", "./cert.pem", "./cert.key") -func (app *App) ListenTLS(addr, certFile, keyFile string) error { - // Check for valid cert/key path - if len(certFile) == 0 || len(keyFile) == 0 { - return errors.New("tls: provide a valid cert or key path") + // Attach the tlsHandler to the config + app.SetTLSHandler(tlsHandler) } - // Set TLS config with handler - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) + if cfg.TLSConfigFunc != nil { + cfg.TLSConfigFunc(tlsConfig) } - tlsHandler := &TLSHandler{} - config := &tls.Config{ - MinVersion: tls.VersionTLS12, - Certificates: []tls.Certificate{ - cert, - }, - GetCertificate: tlsHandler.GetClientInfo, + // Graceful shutdown + if cfg.GracefulContext != nil { + ctx, cancel := context.WithCancel(cfg.GracefulContext) + defer cancel() + + go app.gracefulShutdown(ctx, cfg) } - // Prefork is supported - if app.config.Prefork { - return app.prefork(app.config.Network, addr, config) + // Start prefork + if cfg.EnablePrefork { + return app.prefork(addr, tlsConfig, cfg) } - // Setup listener - ln, err := net.Listen(app.config.Network, addr) - ln = tls.NewListener(ln, config) + // Configure Listener + ln, err := app.createListener(addr, tlsConfig, cfg) if err != nil { return err } @@ -120,89 +192,85 @@ func (app *App) ListenTLS(addr, certFile, keyFile string) error { // prepare the server for the start app.startupProcess() - // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), true, "") - } + // Print startup message & routes + app.printMessages(cfg, ln) - // Print routes - if app.config.EnablePrintRoutes { - app.printRoutesMessage() + // Serve + if cfg.BeforeServeFunc != nil { + if err := cfg.BeforeServeFunc(app); err != nil { + return err + } } - // Attach the tlsHandler to the config - app.SetTLSHandler(tlsHandler) - - // Start listening return app.server.Serve(ln) } -// ListenMutualTLS serves HTTPS requests from the given addr. -// certFile, keyFile and clientCertFile are the paths to TLS certificate and key file: -// -// app.ListenMutualTLS(":8080", "./cert.pem", "./cert.key", "./client.pem") -func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) error { - // Check for valid cert/key path - if len(certFile) == 0 || len(keyFile) == 0 { - return errors.New("tls: provide a valid cert or key path") - } +// Listener serves HTTP requests from the given listener. +// You should enter custom ListenConfig to customize startup. (prefork, startup message, graceful shutdown...) +func (app *App) Listener(ln net.Listener, config ...ListenConfig) error { + cfg := listenConfigDefault(config...) - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) + // Graceful shutdown + if cfg.GracefulContext != nil { + ctx, cancel := context.WithCancel(cfg.GracefulContext) + defer cancel() + + go app.gracefulShutdown(ctx, cfg) } - clientCACert, err := os.ReadFile(filepath.Clean(clientCertFile)) - if err != nil { - return err + // prepare the server for the start + app.startupProcess() + + // Print startup message & routes + app.printMessages(cfg, ln) + + // Serve + if cfg.BeforeServeFunc != nil { + if err := cfg.BeforeServeFunc(app); err != nil { + return err + } } - clientCertPool := x509.NewCertPool() - clientCertPool.AppendCertsFromPEM(clientCACert) - tlsHandler := &TLSHandler{} - config := &tls.Config{ - MinVersion: tls.VersionTLS12, - ClientAuth: tls.RequireAndVerifyClientCert, - ClientCAs: clientCertPool, - Certificates: []tls.Certificate{ - cert, - }, - GetCertificate: tlsHandler.GetClientInfo, + // Prefork is not supported for custom listeners + if cfg.EnablePrefork { + fmt.Println("[Warning] Prefork isn't supported for custom listeners.") } - // Prefork is supported - if app.config.Prefork { - return app.prefork(app.config.Network, addr, config) + return app.server.Serve(ln) +} + +// Create listener function. +func (app *App) createListener(addr string, tlsConfig *tls.Config, cfg ListenConfig) (net.Listener, error) { + var listener net.Listener + var err error + + if tlsConfig != nil { + listener, err = tls.Listen(cfg.ListenerNetwork, addr, tlsConfig) + } else { + listener, err = net.Listen(cfg.ListenerNetwork, addr) } - // Setup listener - ln, err := tls.Listen(app.config.Network, addr, config) - if err != nil { - return err + if cfg.ListenerAddrFunc != nil { + cfg.ListenerAddrFunc(listener.Addr()) } - // prepare the server for the start - app.startupProcess() + return listener, err +} +func (app *App) printMessages(cfg ListenConfig, ln net.Listener) { // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(ln.Addr().String(), true, "") + if !cfg.DisableStartupMessage { + app.startupMessage(ln.Addr().String(), getTlsConfig(ln) != nil, "", cfg) } // Print routes - if app.config.EnablePrintRoutes { + if cfg.EnablePrintRoutes { app.printRoutesMessage() } - - // Attach the tlsHandler to the config - app.SetTLSHandler(tlsHandler) - - // Start listening - return app.server.Serve(ln) } // startupMessage prepares the startup message with the handler number, port, address and other information -func (app *App) startupMessage(addr string, tls bool, pids string) { +func (app *App) startupMessage(addr string, tls bool, pids string, cfg ListenConfig) { // ignore child processes if IsChild() { return @@ -259,7 +327,7 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { host, port := parseAddr(addr) if host == "" { - if app.config.Network == NetworkTCP6 { + if cfg.ListenerNetwork == NetworkTCP6 { host = "[::1]" } else { host = "0.0.0.0" @@ -272,12 +340,12 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { } isPrefork := "Disabled" - if app.config.Prefork { + if cfg.EnablePrefork { isPrefork = "Enabled" } procs := strconv.Itoa(runtime.GOMAXPROCS(0)) - if !app.config.Prefork { + if !cfg.EnablePrefork { procs = "1" } @@ -307,7 +375,7 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { ) var childPidsLogo string - if app.config.Prefork { + if cfg.EnablePrefork { var childPidsTemplate string childPidsTemplate += "%s" childPidsTemplate += " ┌───────────────────────────────────────────────────┐\n%s" @@ -444,3 +512,16 @@ func (app *App) printRoutesMessage() { _ = w.Flush() } + +// shutdown goroutine +func (app *App) gracefulShutdown(ctx context.Context, cfg ListenConfig) { + <-ctx.Done() + + if err := app.Shutdown(); err != nil { + cfg.OnShutdownError(err) + } + + if success := cfg.OnShutdownSuccess; success != nil { + success() + } +} diff --git a/listen_test.go b/listen_test.go index a096d6a839..09ddc09630 100644 --- a/listen_test.go +++ b/listen_test.go @@ -1,15 +1,14 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - package fiber import ( "bytes" + "context" "crypto/tls" + "errors" "fmt" "io" "log" + "net" "os" "strings" "sync" @@ -20,9 +19,9 @@ import ( "github.com/valyala/fasthttp/fasthttputil" ) -// go test -run Test_App_Listen -func Test_App_Listen(t *testing.T) { - app := New(Config{DisableStartupMessage: true}) +// go test -run Test_Listen +func Test_Listen(t *testing.T) { + app := New() require.False(t, app.Listen(":99999") == nil) @@ -31,78 +30,181 @@ func Test_App_Listen(t *testing.T) { require.Nil(t, app.Shutdown()) }() - require.Nil(t, app.Listen(":4003")) + require.Nil(t, app.Listen(":4003", ListenConfig{DisableStartupMessage: true})) +} + +// go test -run Test_Listen_Graceful_Shutdown +func Test_Listen_Graceful_Shutdown(t *testing.T) { + var mu sync.Mutex + var shutdown bool + + app := New() + + app.Get("/", func(c Ctx) error { + return c.SendString(c.Hostname()) + }) + + ln := fasthttputil.NewInmemoryListener() + + go func() { + ctx, cancel := context.WithTimeout(context.Background(), 250*time.Millisecond) + defer cancel() + + err := app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + GracefulContext: ctx, + OnShutdownSuccess: func() { + mu.Lock() + shutdown = true + mu.Unlock() + }, + }) + + require.NoError(t, err) + }() + + testCases := []struct { + Time time.Duration + ExpectedBody string + ExpectedStatusCode int + ExceptedErrsLen int + }{ + {Time: 100 * time.Millisecond, ExpectedBody: "example.com", ExpectedStatusCode: StatusOK, ExceptedErrsLen: 0}, + {Time: 500 * time.Millisecond, ExpectedBody: "", ExpectedStatusCode: 0, ExceptedErrsLen: 1}, + } + + for _, tc := range testCases { + time.Sleep(tc.Time) + + a := Get("http://example.com") + a.HostClient.Dial = func(addr string) (net.Conn, error) { return ln.Dial() } + code, body, errs := a.String() + + require.Equal(t, tc.ExpectedStatusCode, code) + require.Equal(t, tc.ExpectedBody, body) + require.Equal(t, tc.ExceptedErrsLen, len(errs)) + } + + mu.Lock() + require.True(t, shutdown) + mu.Unlock() } -// go test -run Test_App_Listen_Prefork -func Test_App_Listen_Prefork(t *testing.T) { +// go test -run Test_Listen_Prefork +func Test_Listen_Prefork(t *testing.T) { testPreforkMaster = true - app := New(Config{DisableStartupMessage: true, Prefork: true}) + app := New() - require.Nil(t, app.Listen(":99999")) + require.Nil(t, app.Listen(":99999", ListenConfig{DisableStartupMessage: true, EnablePrefork: true})) } -// go test -run Test_App_ListenTLS -func Test_App_ListenTLS(t *testing.T) { +// go test -run Test_Listen_TLS +func Test_Listen_TLS(t *testing.T) { app := New() // invalid port - require.False(t, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") == nil) - // missing perm/cert file - require.False(t, app.ListenTLS(":0", "", "./.github/testdata/ssl.key") == nil) + require.False(t, app.Listen(":99999", ListenConfig{ + CertFile: "./.github/testdata/ssl.pem", + CertKeyFile: "./.github/testdata/ssl.key", + }) == nil) go func() { time.Sleep(1000 * time.Millisecond) require.Nil(t, app.Shutdown()) }() - require.Nil(t, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) + require.Nil(t, app.Listen(":0", ListenConfig{ + CertFile: "./.github/testdata/ssl.pem", + CertKeyFile: "./.github/testdata/ssl.key", + })) + } -// go test -run Test_App_ListenTLS_Prefork -func Test_App_ListenTLS_Prefork(t *testing.T) { +// go test -run Test_Listen_TLS_Prefork +func Test_Listen_TLS_Prefork(t *testing.T) { testPreforkMaster = true - app := New(Config{DisableStartupMessage: true, Prefork: true}) + app := New() // invalid key file content - require.False(t, app.ListenTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.tmpl") == nil) + require.False(t, app.Listen(":0", ListenConfig{ + DisableStartupMessage: true, + EnablePrefork: true, + CertFile: "./.github/testdata/ssl.pem", + CertKeyFile: "./.github/testdata/template.tmpl", + }) == nil) + + go func() { + time.Sleep(1000 * time.Millisecond) + require.Nil(t, app.Shutdown()) + }() + + require.Nil(t, app.Listen(":99999", ListenConfig{ + DisableStartupMessage: true, + EnablePrefork: true, + CertFile: "./.github/testdata/ssl.pem", + CertKeyFile: "./.github/testdata/ssl.key", + })) - require.Nil(t, app.ListenTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key")) } -// go test -run Test_App_ListenMutualTLS -func Test_App_ListenMutualTLS(t *testing.T) { +// go test -run Test_Listen_MutualTLS +func Test_Listen_MutualTLS(t *testing.T) { app := New() // invalid port - require.False(t, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem") == nil) - // missing perm/cert file - require.False(t, app.ListenMutualTLS(":0", "", "./.github/testdata/ssl.key", "") == nil) + require.False(t, app.Listen(":99999", ListenConfig{ + CertFile: "./.github/testdata/ssl.pem", + CertKeyFile: "./.github/testdata/ssl.key", + CertClientFile: "./.github/testdata/ca-chain.cert.pem", + }) == nil) go func() { time.Sleep(1000 * time.Millisecond) require.Nil(t, app.Shutdown()) }() - require.Nil(t, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) + require.Nil(t, app.Listen(":0", ListenConfig{ + CertFile: "./.github/testdata/ssl.pem", + CertKeyFile: "./.github/testdata/ssl.key", + CertClientFile: "./.github/testdata/ca-chain.cert.pem", + })) + } -// go test -run Test_App_ListenMutualTLS_Prefork -func Test_App_ListenMutualTLS_Prefork(t *testing.T) { +// go test -run Test_Listen_MutualTLS_Prefork +func Test_Listen_MutualTLS_Prefork(t *testing.T) { testPreforkMaster = true - app := New(Config{DisableStartupMessage: true, Prefork: true}) + app := New() // invalid key file content - require.False(t, app.ListenMutualTLS(":0", "./.github/testdata/ssl.pem", "./.github/testdata/template.html", "") == nil) + require.False(t, app.Listen(":0", ListenConfig{ + DisableStartupMessage: true, + EnablePrefork: true, + CertFile: "./.github/testdata/ssl.pem", + CertKeyFile: "./.github/testdata/template.html", + CertClientFile: "./.github/testdata/ca-chain.cert.pem", + }) == nil) + + go func() { + time.Sleep(1000 * time.Millisecond) + require.Nil(t, app.Shutdown()) + }() + + require.Nil(t, app.Listen(":99999", ListenConfig{ + DisableStartupMessage: true, + EnablePrefork: true, + CertFile: "./.github/testdata/ssl.pem", + CertKeyFile: "./.github/testdata/ssl.key", + CertClientFile: "./.github/testdata/ca-chain.cert.pem", + })) - require.Nil(t, app.ListenMutualTLS(":99999", "./.github/testdata/ssl.pem", "./.github/testdata/ssl.key", "./.github/testdata/ca-chain.cert.pem")) } -// go test -run Test_App_Listener -func Test_App_Listener(t *testing.T) { +// go test -run Test_Listener +func Test_Listener(t *testing.T) { app := New() go func() { @@ -135,46 +237,117 @@ func Test_App_Listener_TLS_Listener(t *testing.T) { require.Nil(t, app.Listener(ln)) } -func captureOutput(f func()) string { - reader, writer, err := os.Pipe() - if err != nil { - panic(err) - } - stdout := os.Stdout - stderr := os.Stderr - defer func() { - os.Stdout = stdout - os.Stderr = stderr - log.SetOutput(os.Stderr) +// go test -run Test_Listen_TLSConfigFunc +func Test_Listen_TLSConfigFunc(t *testing.T) { + var callTLSConfig bool + app := New() + + go func() { + time.Sleep(1000 * time.Millisecond) + require.Nil(t, app.Shutdown()) }() - os.Stdout = writer - os.Stderr = writer - log.SetOutput(writer) - out := make(chan string) - wg := new(sync.WaitGroup) - wg.Add(1) + + require.Nil(t, app.Listen(":0", ListenConfig{ + DisableStartupMessage: true, + TLSConfigFunc: func(tlsConfig *tls.Config) { + callTLSConfig = true + }, + CertFile: "./.github/testdata/ssl.pem", + CertKeyFile: "./.github/testdata/ssl.key", + })) + + require.True(t, callTLSConfig) +} + +// go test -run Test_Listen_ListenerAddrFunc +func Test_Listen_ListenerAddrFunc(t *testing.T) { + var network string + app := New() + go func() { - var buf bytes.Buffer - wg.Done() - _, err := io.Copy(&buf, reader) - if err != nil { - panic(err) - } - out <- buf.String() + time.Sleep(1000 * time.Millisecond) + require.Nil(t, app.Shutdown()) }() - wg.Wait() - f() - err = writer.Close() - if err != nil { - panic(err) - } - return <-out + + require.Nil(t, app.Listen(":0", ListenConfig{ + DisableStartupMessage: true, + ListenerAddrFunc: func(addr net.Addr) { + network = addr.Network() + }, + CertFile: "./.github/testdata/ssl.pem", + CertKeyFile: "./.github/testdata/ssl.key", + })) + + require.Equal(t, "tcp", network) } -func Test_App_Master_Process_Show_Startup_Message(t *testing.T) { +// go test -run Test_Listen_BeforeServeFunc +func Test_Listen_BeforeServeFunc(t *testing.T) { + var handlers uint32 + app := New() + + go func() { + time.Sleep(1000 * time.Millisecond) + require.Nil(t, app.Shutdown()) + }() + + require.Equal(t, errors.New("test"), app.Listen(":0", ListenConfig{ + DisableStartupMessage: true, + BeforeServeFunc: func(fiber *App) error { + handlers = fiber.HandlersCount() + + return errors.New("test") + }, + })) + + require.Equal(t, uint32(0), handlers) +} + +// go test -run Test_Listen_ListenerNetwork +func Test_Listen_ListenerNetwork(t *testing.T) { + var network string + app := New() + + go func() { + time.Sleep(1000 * time.Millisecond) + require.Nil(t, app.Shutdown()) + }() + + require.Nil(t, app.Listen(":0", ListenConfig{ + DisableStartupMessage: true, + ListenerNetwork: NetworkTCP6, + ListenerAddrFunc: func(addr net.Addr) { + network = addr.String() + }, + })) + + require.True(t, strings.Contains(network, "[::]:")) + + go func() { + time.Sleep(1000 * time.Millisecond) + require.Nil(t, app.Shutdown()) + }() + + require.Nil(t, app.Listen(":0", ListenConfig{ + DisableStartupMessage: true, + ListenerNetwork: NetworkTCP4, + ListenerAddrFunc: func(addr net.Addr) { + network = addr.String() + }, + })) + + require.True(t, strings.Contains(network, "0.0.0.0:")) +} + +// go test -run Test_Listen_Master_Process_Show_Startup_Message +func Test_Listen_Master_Process_Show_Startup_Message(t *testing.T) { + cfg := ListenConfig{ + EnablePrefork: true, + } + startupMessage := captureOutput(func() { - New(Config{Prefork: true}). - startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) + New(). + startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10), cfg) }) fmt.Println(startupMessage) require.True(t, strings.Contains(startupMessage, "https://127.0.0.1:3000")) @@ -184,41 +357,41 @@ func Test_App_Master_Process_Show_Startup_Message(t *testing.T) { require.True(t, strings.Contains(startupMessage, "Prefork ........ Enabled")) } -func Test_App_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { - app := New(Config{Prefork: true, AppName: "Test App v1.0.1"}) - startupMessage := captureOutput(func() { - app.startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10)) - }) - fmt.Println(startupMessage) - require.Equal(t, "Test App v1.0.1", app.Config().AppName) - require.True(t, strings.Contains(startupMessage, app.Config().AppName)) -} +// go test -run Test_Listen_Master_Process_Show_Startup_MessageWithAppName +func Test_Listen_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { + cfg := ListenConfig{ + EnablePrefork: true, + } -func Test_App_Master_Process_Show_Startup_MessageWithAppNameNonAscii(t *testing.T) { - appName := "Serveur de vérification des données" - app := New(Config{Prefork: true, AppName: appName}) + app := New(Config{AppName: "Test App v3.0.0"}) startupMessage := captureOutput(func() { - app.startupMessage(":3000", false, "") + app.startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10), cfg) }) fmt.Println(startupMessage) - require.True(t, strings.Contains(startupMessage, "│ Serveur de vérification des données │")) + require.Equal(t, "Test App v3.0.0", app.Config().AppName) + require.True(t, strings.Contains(startupMessage, app.Config().AppName)) } -func Test_App_print_Route(t *testing.T) { - app := New(Config{EnablePrintRoutes: true}) +// go test -run Test_Listen_Print_Route +func Test_Listen_Print_Route(t *testing.T) { + app := New() app.Get("/", emptyHandler).Name("routeName") + printRoutesMessage := captureOutput(func() { app.printRoutesMessage() }) + fmt.Println(printRoutesMessage) + require.True(t, strings.Contains(printRoutesMessage, "GET")) require.True(t, strings.Contains(printRoutesMessage, "/")) require.True(t, strings.Contains(printRoutesMessage, "emptyHandler")) require.True(t, strings.Contains(printRoutesMessage, "routeName")) } -func Test_App_print_Route_with_group(t *testing.T) { - app := New(Config{EnablePrintRoutes: true}) +// go test -run Test_Listen_Print_Route_With_Group +func Test_Listen_Print_Route_With_Group(t *testing.T) { + app := New() app.Get("/", emptyHandler) v1 := app.Group("v1") @@ -230,6 +403,8 @@ func Test_App_print_Route_with_group(t *testing.T) { app.printRoutesMessage() }) + fmt.Println(printRoutesMessage) + require.True(t, strings.Contains(printRoutesMessage, "GET")) require.True(t, strings.Contains(printRoutesMessage, "/")) require.True(t, strings.Contains(printRoutesMessage, "emptyHandler")) @@ -240,6 +415,42 @@ func Test_App_print_Route_with_group(t *testing.T) { require.True(t, strings.Contains(printRoutesMessage, "/v1/test/fiber/*")) } +func captureOutput(f func()) string { + reader, writer, err := os.Pipe() + if err != nil { + panic(err) + } + stdout := os.Stdout + stderr := os.Stderr + defer func() { + os.Stdout = stdout + os.Stderr = stderr + log.SetOutput(os.Stderr) + }() + os.Stdout = writer + os.Stderr = writer + log.SetOutput(writer) + out := make(chan string) + wg := new(sync.WaitGroup) + wg.Add(1) + go func() { + var buf bytes.Buffer + wg.Done() + _, err := io.Copy(&buf, reader) + if err != nil { + panic(err) + } + out <- buf.String() + }() + wg.Wait() + f() + err = writer.Close() + if err != nil { + panic(err) + } + return <-out +} + func emptyHandler(_ Ctx) error { return nil } diff --git a/middleware/pprof/pprof_test.go b/middleware/pprof/pprof_test.go index b67663189d..eac8b3caec 100644 --- a/middleware/pprof/pprof_test.go +++ b/middleware/pprof/pprof_test.go @@ -11,7 +11,7 @@ import ( ) func Test_Non_Pprof_Path(t *testing.T) { - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New() app.Use(New()) @@ -29,7 +29,7 @@ func Test_Non_Pprof_Path(t *testing.T) { } func Test_Pprof_Index(t *testing.T) { - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New() app.Use(New()) @@ -48,7 +48,7 @@ func Test_Pprof_Index(t *testing.T) { } func Test_Pprof_Subs(t *testing.T) { - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New() app.Use(New()) @@ -75,7 +75,7 @@ func Test_Pprof_Subs(t *testing.T) { } func Test_Pprof_Other(t *testing.T) { - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New() app.Use(New()) diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index 05005a928b..b1debeee08 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -19,18 +19,21 @@ import ( func createProxyTestServer(handler fiber.Handler, t *testing.T) (*fiber.App, string) { t.Helper() - target := fiber.New(fiber.Config{DisableStartupMessage: true}) + target := fiber.New() target.Get("/", handler) ln, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0") require.NoError(t, err) + addr := ln.Addr().String() + go func() { - require.Nil(t, target.Listener(ln)) + require.Nil(t, target.Listener(ln, fiber.ListenConfig{ + DisableStartupMessage: true, + })) }() time.Sleep(2 * time.Second) - addr := ln.Addr().String() return target, addr } @@ -77,7 +80,7 @@ func Test_Proxy(t *testing.T) { require.NoError(t, err) require.Equal(t, fiber.StatusTeapot, resp.StatusCode) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New() app.Use(Balancer(Config{Servers: []string{addr}})) @@ -100,7 +103,7 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) { ln = tls.NewListener(ln, serverTLSConf) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New() app.Get("/tlsbalaner", func(c fiber.Ctx) error { return c.SendString("tls balancer") @@ -115,7 +118,11 @@ func Test_Proxy_Balancer_WithTlsConfig(t *testing.T) { TlsConfig: clientTLSConf, })) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, app.Listener(ln, fiber.ListenConfig{ + DisableStartupMessage: true, + })) + }() code, body, errs := fiber.Get("https://" + addr + "/tlsbalaner").TLSConfig(clientTLSConf).String() @@ -140,13 +147,17 @@ func Test_Proxy_Forward_WithTlsConfig_To_Http(t *testing.T) { proxyServerLn = tls.NewListener(proxyServerLn, proxyServerTLSConf) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New() proxyAddr := proxyServerLn.Addr().String() app.Use(Forward("http://" + targetAddr)) - go func() { require.Nil(t, app.Listener(proxyServerLn)) }() + go func() { + require.Nil(t, app.Listener(proxyServerLn, fiber.ListenConfig{ + DisableStartupMessage: true, + })) + }() code, body, errs := fiber.Get("https://" + proxyAddr). InsecureSkipVerify(). @@ -191,7 +202,7 @@ func Test_Proxy_Forward_WithTlsConfig(t *testing.T) { ln = tls.NewListener(ln, serverTLSConf) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New() app.Get("/tlsfwd", func(c fiber.Ctx) error { return c.SendString("tls forward") @@ -204,7 +215,11 @@ func Test_Proxy_Forward_WithTlsConfig(t *testing.T) { WithTlsConfig(clientTLSConf) app.Use(Forward("https://" + addr + "/tlsfwd")) - go func() { require.Nil(t, app.Listener(ln)) }() + go func() { + require.Nil(t, app.Listener(ln, fiber.ListenConfig{ + DisableStartupMessage: true, + })) + }() code, body, errs := fiber.Get("https://" + addr).TLSConfig(clientTLSConf).String() @@ -372,7 +387,7 @@ func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) { return c.SendString("hello world") }, t) - app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app := fiber.New() app.Get("/*", func(c fiber.Ctx) error { path := c.OriginalURL() url := strings.TrimPrefix(path, "/") diff --git a/prefork.go b/prefork.go index b3049abc60..71db86b8d7 100644 --- a/prefork.go +++ b/prefork.go @@ -28,7 +28,7 @@ func IsChild() bool { } // prefork manages child processes to make use of the OS REUSEPORT or REUSEADDR feature -func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) { +func (app *App) prefork(addr string, tlsConfig *tls.Config, cfg ListenConfig) (err error) { // 👶 child process 👶 if IsChild() { // use 1 cpu core per child process @@ -36,8 +36,8 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) var ln net.Listener // Linux will use SO_REUSEPORT and Windows falls back to SO_REUSEADDR // Only tcp4 or tcp6 is supported when preforking, both are not supported - if ln, err = reuseport.Listen(network, addr); err != nil { - if !app.config.DisableStartupMessage { + if ln, err = reuseport.Listen(cfg.ListenerNetwork, addr); err != nil { + if !cfg.DisableStartupMessage { time.Sleep(100 * time.Millisecond) // avoid colliding with startup message } return fmt.Errorf("prefork: %v", err) @@ -53,6 +53,10 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) // prepare the server for the start app.startupProcess() + if cfg.ListenerAddrFunc != nil { + cfg.ListenerAddrFunc(ln.Addr()) + } + // listen for incoming connections return app.server.Serve(ln) } @@ -94,6 +98,7 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) cmd.Env = append(os.Environ(), fmt.Sprintf("%s=%s", envPreforkChildKey, envPreforkChildVal), ) + if err = cmd.Start(); err != nil { return fmt.Errorf("failed to start a child prefork process, error: %v", err) } @@ -119,8 +124,13 @@ func (app *App) prefork(network, addr string, tlsConfig *tls.Config) (err error) } // Print startup message - if !app.config.DisableStartupMessage { - app.startupMessage(addr, tlsConfig != nil, ","+strings.Join(pids, ",")) + if !cfg.DisableStartupMessage { + app.startupMessage(addr, tlsConfig != nil, ","+strings.Join(pids, ","), cfg) + } + + // Print routes + if cfg.EnablePrintRoutes { + app.printRoutesMessage() } // return error if child crashes diff --git a/prefork_test.go b/prefork_test.go index f6c04b9b2a..a1a680c1bd 100644 --- a/prefork_test.go +++ b/prefork_test.go @@ -23,7 +23,7 @@ func Test_App_Prefork_Child_Process(t *testing.T) { app := New() - err := app.prefork(NetworkTCP4, "invalid", nil) + err := app.prefork("invalid", nil, listenConfigDefault()) require.False(t, err == nil) go func() { @@ -31,7 +31,7 @@ func Test_App_Prefork_Child_Process(t *testing.T) { require.Nil(t, app.Shutdown()) }() - require.Nil(t, app.prefork(NetworkTCP6, "[::1]:", nil)) + require.Nil(t, app.prefork("[::1]:", nil, ListenConfig{ListenerNetwork: NetworkTCP6})) // Create tls certificate cer, err := tls.LoadX509KeyPair("./.github/testdata/ssl.pem", "./.github/testdata/ssl.key") @@ -45,7 +45,7 @@ func Test_App_Prefork_Child_Process(t *testing.T) { require.Nil(t, app.Shutdown()) }() - require.Nil(t, app.prefork(NetworkTCP4, "127.0.0.1:", config)) + require.Nil(t, app.prefork("127.0.0.1:", config, listenConfigDefault())) } func Test_App_Prefork_Master_Process(t *testing.T) { @@ -59,12 +59,14 @@ func Test_App_Prefork_Master_Process(t *testing.T) { require.Nil(t, app.Shutdown()) }() - require.Nil(t, app.prefork(NetworkTCP4, ":3000", nil)) + require.Nil(t, app.prefork(":3000", nil, listenConfigDefault())) dummyChildCmd = "invalid" - err := app.prefork(NetworkTCP4, "127.0.0.1:", nil) + err := app.prefork("127.0.0.1:", nil, listenConfigDefault()) require.False(t, err == nil) + + dummyChildCmd = "go" } func Test_App_Prefork_Child_Process_Never_Show_Startup_Message(t *testing.T) { @@ -79,7 +81,7 @@ func Test_App_Prefork_Child_Process_Never_Show_Startup_Message(t *testing.T) { os.Stdout = w - New().startupProcess().startupMessage(":3000", false, "") + New().startupProcess().startupMessage(":3000", false, "", listenConfigDefault()) require.Nil(t, w.Close()) From f482b303b5c0deef8419db7a86281c6c07b96f15 Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Sun, 11 Sep 2022 15:39:39 +0430 Subject: [PATCH 089/141] remove redundant parentheses and update comments (#2082) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix unhandled errors * fix unhandled error in cache package test * omit variable type * omit variable type * rename variable because collide with the imported package name * handle file error on closing * fix unhandled in common_linux.go * fix unhandled errors in helpers_test.go * fix unhandled errors in listen_test.go * remove unused parameter in emptyHandler method * refactor path.go * unhandled error in hooks test * fix unhandled errors in app_test.go * fix unhandled errors in ctx_test.go * ✨ fix unhandled errors in helpers_test.go * revert app_test.go * remove redundant parentheses and update comments --- ctx.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ctx.go b/ctx.go index ed80c48ca9..966957e101 100644 --- a/ctx.go +++ b/ctx.go @@ -464,7 +464,7 @@ func (c *Ctx) Cookie(cookie *Cookie) { fasthttp.ReleaseCookie(fcookie) } -// Cookies is used for getting a cookie value by key. +// Cookies are used for getting a cookie value by key. // Defaults to the empty string "" if the cookie doesn't exist. // If a default value is given, it will return that value if the cookie doesn't exist. // The returned value is only valid within the handler. Do not store any references. @@ -933,7 +933,7 @@ func (c *Ctx) Params(key string, defaultValue ...string) string { return defaultString("", defaultValue) } -// Params is used to get all route parameters. +// AllParams Params is used to get all route parameters. // Using Params method to get params. func (c *Ctx) AllParams() map[string]string { params := make(map[string]string, len(c.route.Params)) @@ -1253,7 +1253,7 @@ func (c *Ctx) getLocationFromRoute(route Route, params Map) (string, error) { for key, val := range params { isSame := key == segment.ParamName || (!c.app.config.CaseSensitive && utils.EqualFold(key, segment.ParamName)) - isGreedy := (segment.IsGreedy && len(key) == 1 && isInCharset(key[0], greedyParameters)) + isGreedy := segment.IsGreedy && len(key) == 1 && isInCharset(key[0], greedyParameters) if isSame || isGreedy { _, err := buf.WriteString(utils.ToString(val)) if err != nil { From 8ec62a64cc8367372cd9eb1fc48eba380ee01bdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Sun, 11 Sep 2022 14:11:33 +0300 Subject: [PATCH 090/141] :broom: update: add methods configuration for cache middleware (#2081) * :broom: update: add methods configuration for cache middleware * :broom: update: add methods configuration for cache middleware --- middleware/cache/README.md | 7 ++++++ middleware/cache/cache.go | 11 ++++++-- middleware/cache/cache_test.go | 46 +++++++++++++++++++++++++++++++++- middleware/cache/config.go | 10 ++++++++ 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/middleware/cache/README.md b/middleware/cache/README.md index 10972e2f01..a5f3921a7a 100644 --- a/middleware/cache/README.md +++ b/middleware/cache/README.md @@ -125,6 +125,12 @@ type Config struct { // // Default: 0 MaxBytes uint + + // You can specify HTTP methods to cache. + // The middleware just caches the routes of its methods in this slice. + // + // Default: []string{fiber.MethodGet, fiber.MethodHead} + Methods []string } ``` @@ -144,5 +150,6 @@ var ConfigDefault = Config{ StoreResponseHeaders: false, Storage: nil, MaxBytes: 0, + Methods: []string{fiber.MethodGet, fiber.MethodHead}, } ``` diff --git a/middleware/cache/cache.go b/middleware/cache/cache.go index e761ff1731..a8485f450f 100644 --- a/middleware/cache/cache.go +++ b/middleware/cache/cache.go @@ -83,8 +83,15 @@ func New(config ...Config) fiber.Handler { // Return new handler return func(c *fiber.Ctx) error { - // Only cache GET and HEAD methods - if c.Method() != fiber.MethodGet && c.Method() != fiber.MethodHead { + // Only cache selected methods + var isExists bool + for _, method := range cfg.Methods { + if c.Method() == method { + isExists = true + } + } + + if !isExists { c.Set(cfg.CacheHeader, cacheUnreachable) return c.Next() } diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go index f991d24b0a..78aeab4f71 100644 --- a/middleware/cache/cache_test.go +++ b/middleware/cache/cache_test.go @@ -173,7 +173,7 @@ func Test_Cache_Invalid_Expiration(t *testing.T) { utils.AssertEqual(t, cachedBody, body) } -func Test_Cache_Invalid_Method(t *testing.T) { +func Test_Cache_Get(t *testing.T) { t.Parallel() app := fiber.New() @@ -213,6 +213,48 @@ func Test_Cache_Invalid_Method(t *testing.T) { utils.AssertEqual(t, "123", string(body)) } +func Test_Cache_Post(t *testing.T) { + t.Parallel() + + app := fiber.New() + + app.Use(New(Config{ + Methods: []string{fiber.MethodPost}, + })) + + app.Post("/", func(c *fiber.Ctx) error { + return c.SendString(c.Query("cache")) + }) + + app.Get("/get", func(c *fiber.Ctx) error { + return c.SendString(c.Query("cache")) + }) + + resp, err := app.Test(httptest.NewRequest("POST", "/?cache=123", nil)) + utils.AssertEqual(t, nil, err) + body, err := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "123", string(body)) + + resp, err = app.Test(httptest.NewRequest("POST", "/?cache=12345", nil)) + utils.AssertEqual(t, nil, err) + body, err = ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "123", string(body)) + + resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=123", nil)) + utils.AssertEqual(t, nil, err) + body, err = ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "123", string(body)) + + resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=12345", nil)) + utils.AssertEqual(t, nil, err) + body, err = ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "12345", string(body)) +} + func Test_Cache_NothingToCache(t *testing.T) { t.Parallel() @@ -428,10 +470,12 @@ func Test_Cache_WithHead(t *testing.T) { req := httptest.NewRequest("HEAD", "/", nil) resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) cachedReq := httptest.NewRequest("HEAD", "/", nil) cachedResp, err := app.Test(cachedReq) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, cachedResp.Header.Get("X-Cache")) body, err := ioutil.ReadAll(resp.Body) diff --git a/middleware/cache/config.go b/middleware/cache/config.go index 625d1c478b..12f81e2ae8 100644 --- a/middleware/cache/config.go +++ b/middleware/cache/config.go @@ -66,6 +66,12 @@ type Config struct { // // Default: 0 MaxBytes uint + + // You can specify HTTP methods to cache. + // The middleware just caches the routes of its methods in this slice. + // + // Default: []string{fiber.MethodGet, fiber.MethodHead} + Methods []string } // ConfigDefault is the default config @@ -81,6 +87,7 @@ var ConfigDefault = Config{ StoreResponseHeaders: false, Storage: nil, MaxBytes: 0, + Methods: []string{fiber.MethodGet, fiber.MethodHead}, } // Helper function to set default values @@ -114,5 +121,8 @@ func configDefault(config ...Config) Config { if cfg.KeyGenerator == nil { cfg.KeyGenerator = ConfigDefault.KeyGenerator } + if len(cfg.Methods) == 0 { + cfg.Methods = ConfigDefault.Methods + } return cfg } From 709c52301ca42708e8bd1a39f7c85b2f5467ecec Mon Sep 17 00:00:00 2001 From: Trim21 Date: Thu, 15 Sep 2022 13:19:25 +0800 Subject: [PATCH 091/141] fix `ctx.SendStream(io.Reader)` huge memory usage (#2091) * Update ctx.go * remove content-length from test --- ctx.go | 1 - ctx_test.go | 5 ----- 2 files changed, 6 deletions(-) diff --git a/ctx.go b/ctx.go index 966957e101..108db09c5e 100644 --- a/ctx.go +++ b/ctx.go @@ -1545,7 +1545,6 @@ func (c *Ctx) SendStream(stream io.Reader, size ...int) error { c.fasthttp.Response.SetBodyStream(stream, size[0]) } else { c.fasthttp.Response.SetBodyStream(stream, -1) - c.setCanonical(HeaderContentLength, strconv.Itoa(len(c.fasthttp.Response.Body()))) } return nil diff --git a/ctx_test.go b/ctx_test.go index caf037877d..8d12d4b552 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -3302,11 +3302,6 @@ func Test_Ctx_SendStream(t *testing.T) { c.SendStream(bufio.NewReader(bytes.NewReader([]byte("Hello bufio")))) utils.AssertEqual(t, "Hello bufio", string(c.Response().Body())) - - file, err := os.Open("./.github/index.html") - utils.AssertEqual(t, nil, err) - c.SendStream(bufio.NewReader(file)) - utils.AssertEqual(t, true, c.Response().Header.ContentLength() > 200) } // go test -run Test_Ctx_Set From 1a7f7ed8a9653fa990c907ad4c798d8a4c7f77c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=B6khan=20=C3=96zelo=C4=9Flu?= <33967642+gozeloglu@users.noreply.github.com> Date: Thu, 15 Sep 2022 10:53:07 +0300 Subject: [PATCH 092/141] =?UTF-8?q?=E2=9C=A8=20v3:=20Change=20startup=20me?= =?UTF-8?q?ssage=20(#2041)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add figlet text for Fiber startup message * Fiber figlet text is added to beginning of the startup message. Signed-off-by: Gökhan Özeloğlu * Refactor startup message * Table is removed. Logs are added. * Test cases refactored. Signed-off-by: Gökhan Özeloğlu * Add colorful log messages * Used custom color codes to make logs colorful. * Added a new test case. * Fixed broken test case after changes. * Added some color codes as a variable. * Handled Windows case with init() function. Color codes are set empty string. Signed-off-by: Gökhan Özeloğlu * Change color text logic * Color codes are taken from config. * Version is added to figlet text. * Reordered "server started on" message. * Test cases are refactored. Signed-off-by: Gökhan Özeloğlu * Add if condition for Windows * OS checked in tests for Windows machines. Signed-off-by: Gökhan Özeloğlu * some improvements * Fix startup message tests Signed-off-by: Gökhan Özeloğlu * colorize PIDs Signed-off-by: Gökhan Özeloğlu Co-authored-by: Muhammed Efe Çetin --- listen.go | 189 +++++++++++++------------------------------------ listen_test.go | 43 +++++++++-- 2 files changed, 85 insertions(+), 147 deletions(-) diff --git a/listen.go b/listen.go index d3ab216aae..1ecaa87318 100644 --- a/listen.go +++ b/listen.go @@ -24,6 +24,14 @@ import ( "github.com/mattn/go-isatty" ) +// Figlet text to show Fiber ASCII art on startup message +var figletFiberText = ` + _______ __ + / ____(_) /_ ___ _____ + / /_ / / __ \/ _ \/ ___/ + / __/ / / /_/ / __/ / +/_/ /_/_.___/\___/_/ %s` + // ListenConfig is a struct to customize startup of Fiber. // // TODO: Add timeout for graceful shutdown. @@ -279,52 +287,6 @@ func (app *App) startupMessage(addr string, tls bool, pids string, cfg ListenCon // Alias colors colors := app.config.ColorScheme - value := func(s string, width int) string { - pad := width - len(s) - str := "" - for i := 0; i < pad; i++ { - str += "." - } - if s == "Disabled" { - str += " " + s - } else { - str += fmt.Sprintf(" %s%s%s", colors.Cyan, s, colors.Black) - } - return str - } - - center := func(s string, width int) string { - pad := strconv.Itoa((width - len(s)) / 2) - str := fmt.Sprintf("%"+pad+"s", " ") - str += s - str += fmt.Sprintf("%"+pad+"s", " ") - if len(str) < width { - str += " " - } - return str - } - - centerValue := func(s string, width int) string { - pad := strconv.Itoa((width - len([]rune(s))) / 2) - str := fmt.Sprintf("%"+pad+"s", " ") - str += fmt.Sprintf("%s%s%s", colors.Cyan, s, colors.Black) - str += fmt.Sprintf("%"+pad+"s", " ") - if len([]rune(s))-10 < width && len([]rune(s))%2 == 0 { - // add an ending space if the length of str is even and str is not too long - str += " " - } - return str - } - - pad := func(s string, width int) (str string) { - toAdd := width - len(s) - str += s - for i := 0; i < toAdd; i++ { - str += " " - } - return - } - host, port := parseAddr(addr) if host == "" { if cfg.ListenerNetwork == NetworkTCP6 { @@ -349,41 +311,39 @@ func (app *App) startupMessage(addr string, tls bool, pids string, cfg ListenCon procs = "1" } - mainLogo := colors.Black + " ┌───────────────────────────────────────────────────┐\n" - if app.config.AppName != "" { - mainLogo += " │ " + centerValue(app.config.AppName, 49) + " │\n" + out := colorable.NewColorableStdout() + if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) { + out = colorable.NewNonColorable(os.Stdout) } - mainLogo += " │ " + centerValue("Fiber v"+Version, 49) + " │\n" + + _, _ = fmt.Fprintf(out, "%s\n", fmt.Sprintf(figletFiberText, colors.Red+"v"+Version+colors.Reset)) + _, _ = fmt.Fprintf(out, strings.Repeat("-", 50)+"\n") if host == "0.0.0.0" { - mainLogo += - " │ " + center(fmt.Sprintf("%s://127.0.0.1:%s", scheme, port), 49) + " │\n" + - " │ " + center(fmt.Sprintf("(bound on host 0.0.0.0 and port %s)", port), 49) + " │\n" + _, _ = fmt.Fprintf(out, + "%sINFO%s Server started on %s%s://127.0.0.1:%s%s (bound on host 0.0.0.0 and port %s)\n", + colors.Green, colors.Reset, colors.Blue, scheme, port, colors.Reset, port) } else { - mainLogo += - " │ " + center(fmt.Sprintf("%s://%s:%s", scheme, host, port), 49) + " │\n" + _, _ = fmt.Fprintf(out, + "%sINFO%s Server started on %s%s%s\n", + colors.Green, colors.Reset, colors.Blue, fmt.Sprintf("%s://%s:%s", scheme, host, port), colors.Reset) } - mainLogo += fmt.Sprintf( - " │ │\n"+ - " │ Handlers %s Processes %s │\n"+ - " │ Prefork .%s PID ....%s │\n"+ - " └───────────────────────────────────────────────────┘"+ - colors.Reset, - value(strconv.Itoa(int(app.handlersCount)), 14), value(procs, 12), - value(isPrefork, 14), value(strconv.Itoa(os.Getpid()), 14), - ) + if app.config.AppName != "" { + _, _ = fmt.Fprintf(out, "%sINFO%s Application name: %s%s%s\n", colors.Green, colors.Reset, colors.Blue, app.config.AppName, colors.Reset) + } + _, _ = fmt.Fprintf(out, + "%sINFO%s Total handlers count: %s%s%s\n", + colors.Green, colors.Reset, colors.Blue, strconv.Itoa(int(app.handlersCount)), colors.Reset) + if isPrefork == "Enabled" { + _, _ = fmt.Fprintf(out, "%sINFO%s Prefork: %s%s%s\n", colors.Green, colors.Reset, colors.Blue, isPrefork, colors.Reset) + } else { + _, _ = fmt.Fprintf(out, "%sINFO%s Prefork: %s%s%s\n", colors.Green, colors.Reset, colors.Red, isPrefork, colors.Reset) + } + _, _ = fmt.Fprintf(out, "%sINFO%s PID: %s%v%s\n", colors.Green, colors.Reset, colors.Blue, os.Getpid(), colors.Reset) + _, _ = fmt.Fprintf(out, "%sINFO%s Total process count: %s%s%s\n", colors.Green, colors.Reset, colors.Blue, procs, colors.Reset) - var childPidsLogo string if cfg.EnablePrefork { - var childPidsTemplate string - childPidsTemplate += "%s" - childPidsTemplate += " ┌───────────────────────────────────────────────────┐\n%s" - childPidsTemplate += " └───────────────────────────────────────────────────┘" - childPidsTemplate += "%s" - - newLine := " │ %s%s%s │" - // Turn the `pids` variable (in the form ",a,b,c,d,e,f,etc") into a slice of PIDs var pidSlice []string for _, v := range strings.Split(pids, ",") { @@ -392,77 +352,24 @@ func (app *App) startupMessage(addr string, tls bool, pids string, cfg ListenCon } } - var lines []string - thisLine := "Child PIDs ... " - var itemsOnThisLine []string - - addLine := func() { - lines = append(lines, - fmt.Sprintf( - newLine, - colors.Black, - thisLine+colors.Cyan+pad(strings.Join(itemsOnThisLine, ", "), 49-len(thisLine)), - colors.Black, - ), - ) - } - - for _, pid := range pidSlice { - if len(thisLine+strings.Join(append(itemsOnThisLine, pid), ", ")) > 49 { - addLine() - thisLine = "" - itemsOnThisLine = []string{pid} - } else { - itemsOnThisLine = append(itemsOnThisLine, pid) + _, _ = fmt.Fprintf(out, "%sINFO%s Child PIDs: %s", colors.Green, colors.Reset, colors.Blue) + totalPids := len(pidSlice) + rowTotalPidCount := 10 + for i := 0; i < totalPids; i += rowTotalPidCount { + start := i + end := i + rowTotalPidCount + if end > totalPids { + end = totalPids } + for n, pid := range pidSlice[start:end] { + _, _ = fmt.Fprintf(out, "%s", pid) + if n+1 != len(pidSlice[start:end]) { + _, _ = fmt.Fprintf(out, ", ") + } + } + _, _ = fmt.Fprintf(out, "\n%s", colors.Reset) } - - // Add left over items to their own line - if len(itemsOnThisLine) != 0 { - addLine() - } - - // Form logo - childPidsLogo = fmt.Sprintf(childPidsTemplate, - colors.Black, - strings.Join(lines, "\n")+"\n", - colors.Reset, - ) - } - - // Combine both the child PID logo and the main Fiber logo - - // Pad the shorter logo to the length of the longer one - splitMainLogo := strings.Split(mainLogo, "\n") - splitChildPidsLogo := strings.Split(childPidsLogo, "\n") - - mainLen := len(splitMainLogo) - childLen := len(splitChildPidsLogo) - - if mainLen > childLen { - diff := mainLen - childLen - for i := 0; i < diff; i++ { - splitChildPidsLogo = append(splitChildPidsLogo, "") - } - } else { - diff := childLen - mainLen - for i := 0; i < diff; i++ { - splitMainLogo = append(splitMainLogo, "") - } - } - - // Combine the two logos, line by line - output := "\n" - for i := range splitMainLogo { - output += colors.Black + splitMainLogo[i] + " " + splitChildPidsLogo[i] + "\n" } - - out := colorable.NewColorableStdout() - if os.Getenv("TERM") == "dumb" || os.Getenv("NO_COLOR") == "1" || (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) { - out = colorable.NewNonColorable(os.Stdout) - } - - _, _ = fmt.Fprintln(out, output) } // printRoutesMessage print all routes with method, path, name and handlers diff --git a/listen_test.go b/listen_test.go index 09ddc09630..de482e4331 100644 --- a/listen_test.go +++ b/listen_test.go @@ -349,12 +349,13 @@ func Test_Listen_Master_Process_Show_Startup_Message(t *testing.T) { New(). startupMessage(":3000", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 10), cfg) }) + colors := Colors{} fmt.Println(startupMessage) require.True(t, strings.Contains(startupMessage, "https://127.0.0.1:3000")) require.True(t, strings.Contains(startupMessage, "(bound on host 0.0.0.0 and port 3000)")) require.True(t, strings.Contains(startupMessage, "Child PIDs")) require.True(t, strings.Contains(startupMessage, "11111, 22222, 33333, 44444, 55555, 60000")) - require.True(t, strings.Contains(startupMessage, "Prefork ........ Enabled")) + require.True(t, strings.Contains(startupMessage, fmt.Sprintf("Prefork: %sEnabled%s", colors.Blue, colors.Reset))) } // go test -run Test_Listen_Master_Process_Show_Startup_MessageWithAppName @@ -372,17 +373,49 @@ func Test_Listen_Master_Process_Show_Startup_MessageWithAppName(t *testing.T) { require.True(t, strings.Contains(startupMessage, app.Config().AppName)) } +// go test -run Test_Listen_Master_Process_Show_Startup_MessageWithAppNameNonAscii +func Test_Listen_Master_Process_Show_Startup_MessageWithAppNameNonAscii(t *testing.T) { + cfg := ListenConfig{ + EnablePrefork: true, + } + + appName := "Serveur de vérification des données" + app := New(Config{AppName: appName}) + + startupMessage := captureOutput(func() { + app.startupMessage(":3000", false, "", cfg) + }) + fmt.Println(startupMessage) + require.True(t, strings.Contains(startupMessage, "Serveur de vérification des données")) +} + +// go test -run Test_Listen_Master_Process_Show_Startup_MessageWithDisabledPreforkAndCustomEndpoint +func Test_Listen_Master_Process_Show_Startup_MessageWithDisabledPreforkAndCustomEndpoint(t *testing.T) { + cfg := ListenConfig{ + EnablePrefork: false, + } + + appName := "Fiber Example Application" + app := New(Config{AppName: appName}) + startupMessage := captureOutput(func() { + app.startupMessage("server.com:8081", true, strings.Repeat(",11111,22222,33333,44444,55555,60000", 5), cfg) + }) + colors := Colors{} + fmt.Println(startupMessage) + require.True(t, strings.Contains(startupMessage, fmt.Sprintf("%sINFO%s", colors.Green, colors.Reset))) + require.True(t, strings.Contains(startupMessage, fmt.Sprintf("%s%s%s", colors.Blue, appName, colors.Reset))) + require.True(t, strings.Contains(startupMessage, fmt.Sprintf("%s%s%s", colors.Blue, "https://server.com:8081", colors.Reset))) + require.True(t, strings.Contains(startupMessage, fmt.Sprintf("Prefork: %sDisabled%s", colors.Red, colors.Reset))) +} + // go test -run Test_Listen_Print_Route func Test_Listen_Print_Route(t *testing.T) { app := New() app.Get("/", emptyHandler).Name("routeName") - printRoutesMessage := captureOutput(func() { app.printRoutesMessage() }) - fmt.Println(printRoutesMessage) - require.True(t, strings.Contains(printRoutesMessage, "GET")) require.True(t, strings.Contains(printRoutesMessage, "/")) require.True(t, strings.Contains(printRoutesMessage, "emptyHandler")) @@ -403,8 +436,6 @@ func Test_Listen_Print_Route_With_Group(t *testing.T) { app.printRoutesMessage() }) - fmt.Println(printRoutesMessage) - require.True(t, strings.Contains(printRoutesMessage, "GET")) require.True(t, strings.Contains(printRoutesMessage, "/")) require.True(t, strings.Contains(printRoutesMessage, "emptyHandler")) From 6e51f801fbb5e60b48f7fa58f202f171ceb8dc1d Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Fri, 16 Sep 2022 14:21:40 +0430 Subject: [PATCH 093/141] update code comment for helping IDEs (#2095) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix unhandled errors * fix unhandled error in cache package test * omit variable type * omit variable type * rename variable because collide with the imported package name * handle file error on closing * fix unhandled in common_linux.go * fix unhandled errors in helpers_test.go * fix unhandled errors in listen_test.go * remove unused parameter in emptyHandler method * refactor path.go * unhandled error in hooks test * fix unhandled errors in app_test.go * fix unhandled errors in ctx_test.go * ✨ fix unhandled errors in helpers_test.go * revert app_test.go * remove redundant parentheses and update comments * update code comment for helping ide * update code comment for helping ide --- app.go | 6 +++--- color.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app.go b/app.go index ee7b468bb6..033de56cdf 100644 --- a/app.go +++ b/app.go @@ -570,7 +570,7 @@ func (app *App) handleTrustedProxy(ipAddress string) { } } -// You can use SetTLSHandler to use ClientHelloInfo when using TLS with Listener. +// SetTLSHandler You can use SetTLSHandler to use ClientHelloInfo when using TLS with Listener. func (app *App) SetTLSHandler(tlsHandler *TLSHandler) { // Attach the tlsHandler to the config app.mutex.Lock() @@ -608,7 +608,7 @@ func (app *App) Mount(prefix string, fiber *App) Router { return app } -// Assign name to specific route. +// Name Assign name to specific route. func (app *App) Name(name string) Router { app.mutex.Lock() if strings.HasPrefix(app.latestRoute.path, app.latestGroup.Prefix) { @@ -625,7 +625,7 @@ func (app *App) Name(name string) Router { return app } -// Get route by name +// GetRoute Get route by name func (app *App) GetRoute(name string) Route { for _, routes := range app.stack { for _, route := range routes { diff --git a/color.go b/color.go index 0a47d81957..cbccd2ebee 100644 --- a/color.go +++ b/color.go @@ -52,7 +52,7 @@ type Colors struct { Reset string } -// Default color codes +// DefaultColors Default color codes var DefaultColors = Colors{ Black: "\u001b[90m", Red: "\u001b[91m", From e829caf808bd4d53ad39641617d971b7fe93c01b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Fri, 16 Sep 2022 15:22:55 +0200 Subject: [PATCH 094/141] correct ctx release for viewBindMap --- ctx.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ctx.go b/ctx.go index 108db09c5e..0bea4fae37 100644 --- a/ctx.go +++ b/ctx.go @@ -154,6 +154,7 @@ func (app *App) ReleaseCtx(c *Ctx) { c.fasthttp = nil if c.viewBindMap != nil { dictpool.ReleaseDict(c.viewBindMap) + c.viewBindMap = nil } app.pool.Put(c) } From 7c83e38757558e70dcc6c5d0a2a5db19683e2a81 Mon Sep 17 00:00:00 2001 From: Hakan Kutluay <77051856+hakankutluay@users.noreply.github.com> Date: Fri, 16 Sep 2022 17:03:02 +0300 Subject: [PATCH 095/141] =?UTF-8?q?=F0=9F=94=A5=20Update:=20add=20timeout?= =?UTF-8?q?=20context=20middleware=20(#2090)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔥 Feature: add timeoutcontext middleware * move timeoutconext to timeout package * remove timeoutcontext readme.md * replace timeout mware with timeout context mware * Update README.md * Update README.md * update timeout middleware readme * test curl commands fixed * rename sample code title on timeout middleware Co-authored-by: RW --- middleware/timeout/README.md | 81 +++++++++++++++++-- middleware/timeout/timeout.go | 47 ++++------- middleware/timeout/timeout_test.go | 125 ++++++++++++++++++----------- 3 files changed, 167 insertions(+), 86 deletions(-) diff --git a/middleware/timeout/README.md b/middleware/timeout/README.md index 4406d1ffac..4cc68d0710 100644 --- a/middleware/timeout/README.md +++ b/middleware/timeout/README.md @@ -1,5 +1,9 @@ # Timeout -Timeout middleware for [Fiber](https://github.com/gofiber/fiber) wraps a `fiber.Handler` with a timeout. If the handler takes longer than the given duration to return, the timeout error is set and forwarded to the centralized [ErrorHandler](https://docs.gofiber.io/error-handling). +Timeout middleware for Fiber. As a `fiber.Handler` wrapper, it creates a context with `context.WithTimeout` and pass it in `UserContext`. + +If the context passed executions (eg. DB ops, Http calls) takes longer than the given duration to return, the timeout error is set and forwarded to the centralized `ErrorHandler`. + +It has no race conditions, ready to use on production. ### Table of Contents - [Signatures](#signatures) @@ -8,7 +12,7 @@ Timeout middleware for [Fiber](https://github.com/gofiber/fiber) wraps a `fiber. ### Signatures ```go -func New(h fiber.Handler, t time.Duration) fiber.Handler +func New(handler fiber.Handler, timeout time.Duration, timeoutErrors ...error) fiber.Handler ``` ### Examples @@ -20,15 +24,76 @@ import ( ) ``` -After you initiate your Fiber app, you can use the following possibilities: +Sample timeout middleware usage ```go -handler := func(ctx *fiber.Ctx) error { - err := ctx.SendString("Hello, World 👋!") - if err != nil { - return err +func main() { + app := fiber.New() + h := func(c *fiber.Ctx) error { + sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") + if err := sleepWithContext(c.UserContext(), sleepTime); err != nil { + return fmt.Errorf("%w: execution error", err) + } + return nil + } + + app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second)) + _ = app.Listen(":3000") +} + +func sleepWithContext(ctx context.Context, d time.Duration) error { + timer := time.NewTimer(d) + + select { + case <-ctx.Done(): + if !timer.Stop() { + <-timer.C + } + return context.DeadlineExceeded + case <-timer.C: } return nil } +``` + +Test http 200 with curl: +```bash +curl --location -I --request GET 'http://localhost:3000/foo/1000' +``` + +Test http 408 with curl: +```bash +curl --location -I --request GET 'http://localhost:3000/foo/3000' +``` + + +When using with custom error: +```go +var ErrFooTimeOut = errors.New("foo context canceled") + +func main() { + app := fiber.New() + h := func(c *fiber.Ctx) error { + sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") + if err := sleepWithContextWithCustomError(c.UserContext(), sleepTime); err != nil { + return fmt.Errorf("%w: execution error", err) + } + return nil + } + + app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second), ErrFooTimeOut) + _ = app.Listen(":3000") +} -app.Get("/foo", timeout.New(handler, 5 * time.Second)) +func sleepWithContext(ctx context.Context, d time.Duration) error { + timer := time.NewTimer(d) + select { + case <-ctx.Done(): + if !timer.Stop() { + <-timer.C + } + return ErrFooTimeOut + case <-timer.C: + } + return nil +} ``` diff --git a/middleware/timeout/timeout.go b/middleware/timeout/timeout.go index 6cb980aa01..9f1fd21997 100644 --- a/middleware/timeout/timeout.go +++ b/middleware/timeout/timeout.go @@ -1,43 +1,30 @@ package timeout import ( - "fmt" - "sync" + "context" + "errors" "time" "github.com/gofiber/fiber/v2" ) -var once sync.Once - -// New wraps a handler and aborts the process of the handler if the timeout is reached -func New(handler fiber.Handler, timeout time.Duration) fiber.Handler { - once.Do(func() { - fmt.Println("[Warning] timeout contains data race issues, not ready for production!") - }) - - if timeout <= 0 { - return handler - } - - // logic is from fasthttp.TimeoutWithCodeHandler https://github.com/valyala/fasthttp/blob/master/server.go#L418 +// New implementation of timeout middleware. Set custom errors(context.DeadlineExceeded vs) for get fiber.ErrRequestTimeout response. +func New(h fiber.Handler, t time.Duration, tErrs ...error) fiber.Handler { return func(ctx *fiber.Ctx) error { - ch := make(chan struct{}, 1) - - go func() { - defer func() { - _ = recover() - }() - _ = handler(ctx) - ch <- struct{}{} - }() - - select { - case <-ch: - case <-time.After(timeout): - return fiber.ErrRequestTimeout + timeoutContext, cancel := context.WithTimeout(ctx.UserContext(), t) + defer cancel() + ctx.SetUserContext(timeoutContext) + if err := h(ctx); err != nil { + if errors.Is(err, context.DeadlineExceeded) { + return fiber.ErrRequestTimeout + } + for i := range tErrs { + if errors.Is(err, tErrs[i]) { + return fiber.ErrRequestTimeout + } + } + return err } - return nil } } diff --git a/middleware/timeout/timeout_test.go b/middleware/timeout/timeout_test.go index dc60e2eba5..225eabc152 100644 --- a/middleware/timeout/timeout_test.go +++ b/middleware/timeout/timeout_test.go @@ -1,55 +1,84 @@ package timeout -// // go test -run Test_Middleware_Timeout -// func Test_Middleware_Timeout(t *testing.T) { -// app := fiber.New(fiber.Config{DisableStartupMessage: true}) +import ( + "context" + "errors" + "fmt" + "net/http/httptest" + "testing" + "time" -// h := New(func(c *fiber.Ctx) error { -// sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") -// time.Sleep(sleepTime) -// return c.SendString("After " + c.Params("sleepTime") + "ms sleeping") -// }, 5*time.Millisecond) -// app.Get("/test/:sleepTime", h) + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" +) -// testTimeout := func(timeoutStr string) { -// resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) -// utils.AssertEqual(t, nil, err, "app.Test(req)") -// utils.AssertEqual(t, fiber.StatusRequestTimeout, resp.StatusCode, "Status code") +// go test -run Test_Timeout +func Test_Timeout(t *testing.T) { + // fiber instance + app := fiber.New() + h := New(func(c *fiber.Ctx) error { + sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") + if err := sleepWithContext(c.UserContext(), sleepTime, context.DeadlineExceeded); err != nil { + return fmt.Errorf("%w: l2 wrap", fmt.Errorf("%w: l1 wrap ", err)) + } + return nil + }, 100*time.Millisecond) + app.Get("/test/:sleepTime", h) + testTimeout := func(timeoutStr string) { + resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, fiber.StatusRequestTimeout, resp.StatusCode, "Status code") + } + testSucces := func(timeoutStr string) { + resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") + } + testTimeout("300") + testTimeout("500") + testSucces("50") + testSucces("30") +} -// body, err := ioutil.ReadAll(resp.Body) -// utils.AssertEqual(t, nil, err) -// utils.AssertEqual(t, "Request Timeout", string(body)) -// } -// testSucces := func(timeoutStr string) { -// resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) -// utils.AssertEqual(t, nil, err, "app.Test(req)") -// utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") +var ErrFooTimeOut = errors.New("foo context canceled") -// body, err := ioutil.ReadAll(resp.Body) -// utils.AssertEqual(t, nil, err) -// utils.AssertEqual(t, "After "+timeoutStr+"ms sleeping", string(body)) -// } +// go test -run Test_TimeoutWithCustomError +func Test_TimeoutWithCustomError(t *testing.T) { + // fiber instance + app := fiber.New() + h := New(func(c *fiber.Ctx) error { + sleepTime, _ := time.ParseDuration(c.Params("sleepTime") + "ms") + if err := sleepWithContext(c.UserContext(), sleepTime, ErrFooTimeOut); err != nil { + return fmt.Errorf("%w: execution error", err) + } + return nil + }, 100*time.Millisecond, ErrFooTimeOut) + app.Get("/test/:sleepTime", h) + testTimeout := func(timeoutStr string) { + resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, fiber.StatusRequestTimeout, resp.StatusCode, "Status code") + } + testSucces := func(timeoutStr string) { + resp, err := app.Test(httptest.NewRequest("GET", "/test/"+timeoutStr, nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") + } + testTimeout("300") + testTimeout("500") + testSucces("50") + testSucces("30") +} -// testTimeout("15") -// testSucces("2") -// testTimeout("30") -// testSucces("3") -// } - -// // go test -run -v Test_Timeout_Panic -// func Test_Timeout_Panic(t *testing.T) { -// app := fiber.New(fiber.Config{DisableStartupMessage: true}) - -// app.Get("/panic", recover.New(), New(func(c *fiber.Ctx) error { -// c.Set("dummy", "this should not be here") -// panic("panic in timeout handler") -// }, 5*time.Millisecond)) - -// resp, err := app.Test(httptest.NewRequest("GET", "/panic", nil)) -// utils.AssertEqual(t, nil, err, "app.Test(req)") -// utils.AssertEqual(t, fiber.StatusRequestTimeout, resp.StatusCode, "Status code") - -// body, err := ioutil.ReadAll(resp.Body) -// utils.AssertEqual(t, nil, err) -// utils.AssertEqual(t, "Request Timeout", string(body)) -// } +func sleepWithContext(ctx context.Context, d time.Duration, te error) error { + timer := time.NewTimer(d) + select { + case <-ctx.Done(): + if !timer.Stop() { + <-timer.C + } + return te + case <-timer.C: + } + return nil +} From 093882cfdf3948e2c250830e135f3076a79cdc4a Mon Sep 17 00:00:00 2001 From: Jinquan Wang <35188480+wangjq4214@users.noreply.github.com> Date: Sun, 18 Sep 2022 17:35:57 +0800 Subject: [PATCH 096/141] =?UTF-8?q?=E2=9C=A8=20v3=20(feature):=20new=20Rou?= =?UTF-8?q?te=20method=20(#2065)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🎨 perf: change name to add new feature * ✨ feat add route * 🎨 perf: rollback name * 🎨 perf: change file name * fix: fix errors caused by register * 🎨 perf: change filed to private and change comment * feat: interface is better * 🎨 perf: change type name * deeper tests Co-authored-by: Muhammed Efe Çetin --- app.go | 14 ++---- app_test.go | 64 +++++++++++++------------- group.go | 12 ++--- register.go | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++ router.go | 2 +- 5 files changed, 167 insertions(+), 53 deletions(-) create mode 100644 register.go diff --git a/app.go b/app.go index 861c321b68..708b16a1ce 100644 --- a/app.go +++ b/app.go @@ -748,17 +748,11 @@ func (app *App) Group(prefix string, handlers ...Handler) Router { // Route is used to define routes with a common prefix inside the common function. // Uses Group method to define new sub-router. -func (app *App) Route(prefix string, fn func(router Router), name ...string) Router { - // Create new group - group := app.Group(prefix) - if len(name) > 0 { - group.Name(name[0]) - } - - // Define routes - fn(group) +func (app *App) Route(path string) Register { + // Create new route + route := &Registering{app: app, path: path} - return group + return route } // Error makes it compatible with the `error` interface. diff --git a/app_test.go b/app_test.go index 1e14597346..b6a32605ed 100644 --- a/app_test.go +++ b/app_test.go @@ -1030,48 +1030,46 @@ func Test_App_Route(t *testing.T) { app := New() - grp := app.Route("/test", func(grp Router) { - grp.Get("/", dummyHandler) - grp.Get("/:demo?", dummyHandler) - grp.Connect("/CONNECT", dummyHandler) - grp.Put("/PUT", dummyHandler) - grp.Post("/POST", dummyHandler) - grp.Delete("/DELETE", dummyHandler) - grp.Head("/HEAD", dummyHandler) - grp.Patch("/PATCH", dummyHandler) - grp.Options("/OPTIONS", dummyHandler) - grp.Trace("/TRACE", dummyHandler) - grp.All("/ALL", dummyHandler) - grp.Use(dummyHandler) - grp.Use("/USE", dummyHandler) - }) + register := app.Route("/test"). + Get(dummyHandler). + Post(dummyHandler). + Put(dummyHandler). + Delete(dummyHandler). + Connect(dummyHandler). + Options(dummyHandler). + Trace(dummyHandler). + Patch(dummyHandler) testStatus200(t, app, "/test", MethodGet) - testStatus200(t, app, "/test/john", MethodGet) - testStatus200(t, app, "/test/CONNECT", MethodConnect) - testStatus200(t, app, "/test/PUT", MethodPut) - testStatus200(t, app, "/test/POST", MethodPost) - testStatus200(t, app, "/test/DELETE", MethodDelete) - testStatus200(t, app, "/test/HEAD", MethodHead) - testStatus200(t, app, "/test/PATCH", MethodPatch) - testStatus200(t, app, "/test/OPTIONS", MethodOptions) - testStatus200(t, app, "/test/TRACE", MethodTrace) - testStatus200(t, app, "/test/ALL", MethodPost) - testStatus200(t, app, "/test/oke", MethodGet) - testStatus200(t, app, "/test/USE/oke", MethodGet) + testStatus200(t, app, "/test", MethodHead) + testStatus200(t, app, "/test", MethodPost) + testStatus200(t, app, "/test", MethodPut) + testStatus200(t, app, "/test", MethodDelete) + testStatus200(t, app, "/test", MethodConnect) + testStatus200(t, app, "/test", MethodOptions) + testStatus200(t, app, "/test", MethodTrace) + testStatus200(t, app, "/test", MethodPatch) + + register.Route("/v1").Get(dummyHandler).Post(dummyHandler) + + resp, err := app.Test(httptest.NewRequest(MethodPost, "/test/v1", nil)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") - grp.Route("/v1", func(grp Router) { - grp.Post("/", dummyHandler) - grp.Get("/users", dummyHandler) - }) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1", nil)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") - resp, err := app.Test(httptest.NewRequest(MethodPost, "/test/v1/", nil)) + register.Route("/v1").Route("/v2").Route("/v3").Get(dummyHandler).Trace(dummyHandler) + + resp, err = app.Test(httptest.NewRequest(MethodTrace, "/test/v1/v2/v3", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") - resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1/UsErS", nil)) + resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/v1/v2/v3", nil)) require.NoError(t, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") + } func Test_App_Deep_Group(t *testing.T) { diff --git a/group.go b/group.go index 502041de45..1f09eca9dc 100644 --- a/group.go +++ b/group.go @@ -184,15 +184,9 @@ func (grp *Group) Group(prefix string, handlers ...Handler) Router { // Route is used to define routes with a common prefix inside the common function. // Uses Group method to define new sub-router. -func (grp *Group) Route(prefix string, fn func(router Router), name ...string) Router { +func (grp *Group) Route(path string) Register { // Create new group - group := grp.Group(prefix) - if len(name) > 0 { - group.Name(name[0]) - } - - // Define routes - fn(group) + register := &Registering{app: grp.app, path: getGroupPath(grp.Prefix, path)} - return group + return register } diff --git a/register.go b/register.go new file mode 100644 index 0000000000..9f0bf92d92 --- /dev/null +++ b/register.go @@ -0,0 +1,128 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package fiber + +// Register defines all router handle interface generate by Route(). +type Register interface { + All(handlers ...Handler) Register + Get(handlers ...Handler) Register + Head(handlers ...Handler) Register + Post(handlers ...Handler) Register + Put(handlers ...Handler) Register + Delete(handlers ...Handler) Register + Connect(handlers ...Handler) Register + Options(handlers ...Handler) Register + Trace(handlers ...Handler) Register + Patch(handlers ...Handler) Register + + Add(method string, handlers ...Handler) Register + + Static(root string, config ...Static) Register + + Route(path string) Register +} + +var _ (Register) = (*Registering)(nil) + +// Registering struct +type Registering struct { + app *App + + path string +} + +// All registers a middleware route that will match requests +// with the provided path which is stored in register struct. +// +// app.Route("/").All(func(c fiber.Ctx) error { +// return c.Next() +// }) +// app.Route("/api").All(func(c fiber.Ctx) error { +// return c.Next() +// }) +// app.Route("/api").All(handler, func(c fiber.Ctx) error { +// return c.Next() +// }) +// +// This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... +func (r *Registering) All(handlers ...Handler) Register { + r.app.register(methodUse, r.path, handlers...) + return r +} + +// Get registers a route for GET methods that requests a representation +// of the specified resource. Requests using GET should only retrieve data. +func (r *Registering) Get(handlers ...Handler) Register { + r.app.Add(MethodHead, r.path, handlers...).Add(MethodGet, r.path, handlers...) + return r +} + +// Head registers a route for HEAD methods that asks for a response identical +// to that of a GET request, but without the response body. +func (r *Registering) Head(handlers ...Handler) Register { + return r.Add(MethodHead, handlers...) +} + +// Post registers a route for POST methods that is used to submit an entity to the +// specified resource, often causing a change in state or side effects on the server. +func (r *Registering) Post(handlers ...Handler) Register { + return r.Add(MethodPost, handlers...) +} + +// Put registers a route for PUT methods that replaces all current representations +// of the target resource with the request payload. +func (r *Registering) Put(handlers ...Handler) Register { + return r.Add(MethodPut, handlers...) +} + +// Delete registers a route for DELETE methods that deletes the specified resource. +func (r *Registering) Delete(handlers ...Handler) Register { + return r.Add(MethodDelete, handlers...) +} + +// Connect registers a route for CONNECT methods that establishes a tunnel to the +// server identified by the target resource. +func (r *Registering) Connect(handlers ...Handler) Register { + return r.Add(MethodConnect, handlers...) +} + +// Options registers a route for OPTIONS methods that is used to describe the +// communication options for the target resource. +func (r *Registering) Options(handlers ...Handler) Register { + return r.Add(MethodOptions, handlers...) +} + +// Trace registers a route for TRACE methods that performs a message loop-back +// test along the r.Path to the target resource. +func (r *Registering) Trace(handlers ...Handler) Register { + return r.Add(MethodTrace, handlers...) +} + +// Patch registers a route for PATCH methods that is used to apply partial +// modifications to a resource. +func (r *Registering) Patch(handlers ...Handler) Register { + return r.Add(MethodPatch, handlers...) +} + +// Add allows you to specify a HTTP method to register a route +func (r *Registering) Add(method string, handlers ...Handler) Register { + r.app.register(method, r.path, handlers...) + return r +} + +// Static will create a file server serving static files +func (r *Registering) Static(root string, config ...Static) Register { + r.app.registerStatic(r.path, root, config...) + return r +} + +// Route returns a new Register instance whose route path takes +// the path in the current instance as its prefix. +func (r *Registering) Route(path string) Register { + // Create new group + route := &Registering{app: r.app, path: getGroupPath(r.path, path)} + + return route +} diff --git a/router.go b/router.go index 559635b399..614ff93748 100644 --- a/router.go +++ b/router.go @@ -36,7 +36,7 @@ type Router interface { Group(prefix string, handlers ...Handler) Router - Route(prefix string, fn func(router Router), name ...string) Router + Route(path string) Register Mount(prefix string, fiber *App) Router From 1d773c7f3c2d802d8a46b80b3eba8dae93a499df Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Sun, 18 Sep 2022 18:02:54 +0430 Subject: [PATCH 097/141] update code comments for helping IDEs and fix unhandled error in test (#2099) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix unhandled errors * fix unhandled error in cache package test * omit variable type * omit variable type * rename variable because collide with the imported package name * handle file error on closing * fix unhandled in common_linux.go * fix unhandled errors in helpers_test.go * fix unhandled errors in listen_test.go * remove unused parameter in emptyHandler method * refactor path.go * unhandled error in hooks test * fix unhandled errors in app_test.go * fix unhandled errors in ctx_test.go * ✨ fix unhandled errors in helpers_test.go * revert app_test.go * remove redundant parentheses and update comments * update code comment for helping ide * update code comment for helping ide * fix unhandled error in app_test.go * update code comments for helping IDEs --- app_test.go | 3 ++- ctx.go | 2 +- group.go | 2 +- hooks.go | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app_test.go b/app_test.go index aadb70d7d3..c5cc341c6d 100644 --- a/app_test.go +++ b/app_test.go @@ -1202,7 +1202,8 @@ func Benchmark_App_ETag(b *testing.B) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Send([]byte("Hello, World!")) + err := c.Send([]byte("Hello, World!")) + utils.AssertEqual(b, nil, err) for n := 0; n < b.N; n++ { setETag(c, false) } diff --git a/ctx.go b/ctx.go index 0bea4fae37..adfebefbad 100644 --- a/ctx.go +++ b/ctx.go @@ -1226,7 +1226,7 @@ func (c *Ctx) Redirect(location string, status ...int) error { return nil } -// Add vars to default view var map binding to template engine. +// Bind Add vars to default view var map binding to template engine. // Variables are read by the Render method and may be overwritten. func (c *Ctx) Bind(vars Map) error { // init viewBindMap - lazy map diff --git a/group.go b/group.go index 669aeab94d..ab74b80e92 100644 --- a/group.go +++ b/group.go @@ -48,7 +48,7 @@ func (grp *Group) Mount(prefix string, fiber *App) Router { return grp } -// Assign name to specific route. +// Name Assign name to specific route. func (grp *Group) Name(name string) Router { grp.app.mutex.Lock() if strings.HasPrefix(grp.Prefix, grp.app.latestGroup.Prefix) { diff --git a/hooks.go b/hooks.go index 0cae4a53a0..f614ef1318 100644 --- a/hooks.go +++ b/hooks.go @@ -1,6 +1,6 @@ package fiber -// Handlers define a function to create hooks for Fiber. +// OnRouteHandler Handlers define a function to create hooks for Fiber. type OnRouteHandler = func(Route) error type OnNameHandler = OnRouteHandler type OnGroupHandler = func(Group) error From 45229ed935f4acccf86b6f0b335e918310374948 Mon Sep 17 00:00:00 2001 From: naoki kuroda <68233204+nnnkkk7@users.noreply.github.com> Date: Mon, 19 Sep 2022 15:09:51 +0900 Subject: [PATCH 098/141] :white_check_mark: Upgrade go version to 1.19 in go.mod (#2103) upgrade go version to 1.19 in go.mod --- go.mod | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 30ed0e109b..d6d6fffb08 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,15 @@ module github.com/gofiber/fiber/v2 -go 1.16 +go 1.19 require ( github.com/valyala/fasthttp v1.40.0 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 ) + +require ( + github.com/andybalholm/brotli v1.0.4 // indirect + github.com/klauspost/compress v1.15.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect +) From 305bb072be55726e4a09129002e8b1f4344690b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Mon, 19 Sep 2022 09:33:58 +0300 Subject: [PATCH 099/141] :broom: tidy up: fix linter errors for tests (#2102) --- app_test.go | 5 +- ctx_test.go | 223 ++++++++++++++++++++--------- helpers_test.go | 10 +- middleware/envvar/envvar_test.go | 2 +- middleware/session/session_test.go | 21 +-- 5 files changed, 181 insertions(+), 80 deletions(-) diff --git a/app_test.go b/app_test.go index c5cc341c6d..9c12963868 100644 --- a/app_test.go +++ b/app_test.go @@ -1215,7 +1215,7 @@ func Benchmark_App_ETag_Weak(b *testing.B) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Send([]byte("Hello, World!")) + utils.AssertEqual(b, nil, c.Send([]byte("Hello, World!"))) for n := 0; n < b.N; n++ { setETag(c, true) } @@ -1440,7 +1440,8 @@ func Test_App_New_Test_Parallel(t *testing.T) { t.Run("Test_App_New_Test_Parallel_1", func(t *testing.T) { t.Parallel() app := New(Config{Immutable: true}) - app.Test(httptest.NewRequest("GET", "/", nil)) + _, err := app.Test(httptest.NewRequest("GET", "/", nil)) + utils.AssertEqual(t, nil, err) }) t.Run("Test_App_New_Test_Parallel_2", func(t *testing.T) { t.Parallel() diff --git a/ctx_test.go b/ctx_test.go index 8d12d4b552..2acaa1ce94 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -446,9 +446,11 @@ func Test_Ctx_ParamParser(t *testing.T) { utils.AssertEqual(t, uint(222), d.RoleID) return nil }) - app.Test(httptest.NewRequest(MethodGet, "/test1/111/role/222", nil)) - app.Test(httptest.NewRequest(MethodGet, "/test2/111/role/222", nil)) + _, err := app.Test(httptest.NewRequest(MethodGet, "/test1/111/role/222", nil)) + utils.AssertEqual(t, nil, err) + _, err = app.Test(httptest.NewRequest(MethodGet, "/test2/111/role/222", nil)) + utils.AssertEqual(t, nil, err) } // go test -run Test_Ctx_BodyParser_WithSetParserDecoder @@ -621,7 +623,7 @@ func Test_Ctx_UserContext(t *testing.T) { utils.AssertEqual(t, ctx, context.Background()) }) t.Run("ValueContext", func(t *testing.T) { - testKey := "Test Key" + testKey := struct{}{} testValue := "Test Value" ctx := context.WithValue(context.Background(), testKey, testValue) utils.AssertEqual(t, testValue, ctx.Value(testKey)) @@ -634,7 +636,7 @@ func Test_Ctx_SetUserContext(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - testKey := "Test Key" + testKey := struct{}{} testValue := "Test Value" ctx := context.WithValue(context.Background(), testKey, testValue) c.SetUserContext(ctx) @@ -643,7 +645,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) { - testKey := "foobar-key" + testKey := struct{}{} testValue := "foobar-value" app := New() @@ -802,9 +804,13 @@ func Benchmark_Ctx_Format(b *testing.B) { c.Request().Header.Set("Accept", "text/plain") b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.Format("Hello, World!") + err = c.Format("Hello, World!") } + + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, `Hello, World!`, string(c.Response().Body())) } @@ -816,9 +822,13 @@ func Benchmark_Ctx_Format_HTML(b *testing.B) { c.Request().Header.Set("Accept", "text/html") b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.Format("Hello, World!") + err = c.Format("Hello, World!") } + + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, "

    Hello, World!

    ", string(c.Response().Body())) } @@ -830,9 +840,13 @@ func Benchmark_Ctx_Format_JSON(b *testing.B) { c.Request().Header.Set("Accept", "application/json") b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.Format("Hello, World!") + err = c.Format("Hello, World!") } + + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, `"Hello, World!"`, string(c.Response().Body())) } @@ -844,9 +858,13 @@ func Benchmark_Ctx_Format_XML(b *testing.B) { c.Request().Header.Set("Accept", "application/xml") b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.Format("Hello, World!") + err = c.Format("Hello, World!") } + + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, `Hello, World!`, string(c.Response().Body())) } @@ -1752,9 +1770,13 @@ func Benchmark_Ctx_ParamsParse(b *testing.B) { } b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.ParamsParser(&res) + err = c.ParamsParser(&res) } + + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, "john", res.Param1) utils.AssertEqual(b, "doe", res.Param2) utils.AssertEqual(b, "is", res.Param3) @@ -2173,7 +2195,7 @@ func Test_Ctx_Download(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Download("ctx.go", "Awesome File!") + utils.AssertEqual(t, nil, c.Download("ctx.go", "Awesome File!")) f, err := os.Open("./ctx.go") utils.AssertEqual(t, nil, err) @@ -2184,7 +2206,7 @@ func Test_Ctx_Download(t *testing.T) { utils.AssertEqual(t, expect, c.Response().Body()) utils.AssertEqual(t, `attachment; filename="Awesome+File%21"`, string(c.Response().Header.Peek(HeaderContentDisposition))) - c.Download("ctx.go") + utils.AssertEqual(t, nil, c.Download("ctx.go")) utils.AssertEqual(t, `attachment; filename="ctx.go"`, string(c.Response().Header.Peek(HeaderContentDisposition))) } @@ -2319,10 +2341,11 @@ func Test_Ctx_JSON(t *testing.T) { utils.AssertEqual(t, true, c.JSON(complex(1, 1)) != nil) - c.JSON(Map{ // map has no order + err := c.JSON(Map{ // map has no order "Name": "Grame", "Age": 20, }) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, `{"Age":20,"Name":"Grame"}`, string(c.Response().Body())) utils.AssertEqual(t, "application/json", string(c.Response().Header.Peek("content-type"))) @@ -2370,17 +2393,19 @@ func Test_Ctx_JSONP(t *testing.T) { utils.AssertEqual(t, true, c.JSONP(complex(1, 1)) != nil) - c.JSONP(Map{ + err := c.JSONP(Map{ "Name": "Grame", "Age": 20, }) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, `callback({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) utils.AssertEqual(t, "application/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) - c.JSONP(Map{ + err = c.JSONP(Map{ "Name": "Grame", "Age": 20, }, "john") + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, `john({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) utils.AssertEqual(t, "application/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) } @@ -2424,11 +2449,11 @@ func Test_Ctx_XML(t *testing.T) { Ages []int `xml:"Ages"` } - c.XML(xmlResult{ + err := c.XML(xmlResult{ Names: []string{"Grame", "John"}, Ages: []int{1, 12, 20}, }) - + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, `GrameJohn11220`, string(c.Response().Body())) utils.AssertEqual(t, "application/xml", string(c.Response().Header.Peek("content-type"))) @@ -2547,11 +2572,13 @@ func Test_Ctx_Redirect(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Redirect("http://default.com") + err := c.Redirect("http://default.com") + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 302, c.Response().StatusCode()) utils.AssertEqual(t, "http://default.com", string(c.Response().Header.Peek(HeaderLocation))) - c.Redirect("http://example.com", 301) + err = c.Redirect("http://example.com", 301) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 301, c.Response().StatusCode()) utils.AssertEqual(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) } @@ -2566,9 +2593,10 @@ func Test_Ctx_RedirectToRouteWithParams(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.RedirectToRoute("user", Map{ + err := c.RedirectToRoute("user", Map{ "name": "fiber", }) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 302, c.Response().StatusCode()) utils.AssertEqual(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } @@ -2583,10 +2611,11 @@ func Test_Ctx_RedirectToRouteWithQueries(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.RedirectToRoute("user", Map{ + err := c.RedirectToRoute("user", Map{ "name": "fiber", "queries": map[string]string{"data[0][name]": "john", "data[0][age]": "10", "test": "doe"}, }) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 302, c.Response().StatusCode()) // analysis of query parameters with url parsing, since a map pass is always randomly ordered location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) @@ -2605,9 +2634,10 @@ func Test_Ctx_RedirectToRouteWithOptionalParams(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.RedirectToRoute("user", Map{ + err := c.RedirectToRoute("user", Map{ "name": "fiber", }) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 302, c.Response().StatusCode()) utils.AssertEqual(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } @@ -2622,7 +2652,8 @@ func Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.RedirectToRoute("user", Map{}) + err := c.RedirectToRoute("user", Map{}) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 302, c.Response().StatusCode()) utils.AssertEqual(t, "/user/", string(c.Response().Header.Peek(HeaderLocation))) } @@ -2637,9 +2668,10 @@ func Test_Ctx_RedirectToRouteWithGreedyParameters(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.RedirectToRoute("user", Map{ + err := c.RedirectToRoute("user", Map{ "+": "test/routes", }) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 302, c.Response().StatusCode()) utils.AssertEqual(t, "/user/test/routes", string(c.Response().Header.Peek(HeaderLocation))) } @@ -2653,7 +2685,8 @@ func Test_Ctx_RedirectBack(t *testing.T) { }).Name("home") c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.RedirectBack("/") + err := c.RedirectBack("/") + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 302, c.Response().StatusCode()) utils.AssertEqual(t, "/", string(c.Response().Header.Peek(HeaderLocation))) } @@ -2671,7 +2704,8 @@ func Test_Ctx_RedirectBackWithReferer(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) c.Request().Header.Set(HeaderReferer, "/back") - c.RedirectBack("/") + err := c.RedirectBack("/") + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 302, c.Response().StatusCode()) utils.AssertEqual(t, "/back", c.Get(HeaderReferer)) utils.AssertEqual(t, "/back", string(c.Response().Header.Peek(HeaderLocation))) @@ -2798,11 +2832,12 @@ func Test_Ctx_RenderWithBind(t *testing.T) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) - c.Bind(Map{ + err := c.Bind(Map{ "Title": "Hello, World!", }) + utils.AssertEqual(t, nil, err) defer app.ReleaseCtx(c) - err := c.Render("./.github/testdata/index.tmpl", Map{}) + err = c.Render("./.github/testdata/index.tmpl", Map{}) buf := bytebufferpool.Get() _, _ = buf.WriteString("overwrite") @@ -2822,14 +2857,15 @@ func Test_Ctx_RenderWithBindLocals(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) - c.Bind(Map{ + err := c.Bind(Map{ "Title": "Hello, World!", }) + utils.AssertEqual(t, nil, err) c.Locals("Summary", "Test") defer app.ReleaseCtx(c) - err := c.Render("./.github/testdata/template.tmpl", Map{}) + err = c.Render("./.github/testdata/template.tmpl", Map{}) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

    Hello, World! Test

    ", string(c.Response().Body())) @@ -2840,6 +2876,7 @@ func Test_Ctx_RenderWithLocalsAndBinding(t *testing.T) { t.Parallel() engine := &testTemplateEngine{} err := engine.Load() + utils.AssertEqual(t, nil, err) app := New(Config{ PassLocalsToViews: true, Views: engine, @@ -2860,15 +2897,17 @@ func Benchmark_Ctx_RenderWithLocalsAndBinding(b *testing.B) { engine := &testTemplateEngine{} err := engine.Load() utils.AssertEqual(b, nil, err) + utils.AssertEqual(b, nil, err) app := New(Config{ PassLocalsToViews: true, Views: engine, }) c := app.AcquireCtx(&fasthttp.RequestCtx{}) - c.Bind(Map{ + err = c.Bind(Map{ "Title": "Hello, World!", }) + utils.AssertEqual(b, nil, err) c.Locals("Summary", "Test") defer app.ReleaseCtx(c) @@ -2896,12 +2935,14 @@ func Benchmark_Ctx_RedirectToRoute(b *testing.B) { b.ReportAllocs() b.ResetTimer() + var err error for n := 0; n < b.N; n++ { - c.RedirectToRoute("user", Map{ + err = c.RedirectToRoute("user", Map{ "name": "fiber", }) } + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, 302, c.Response().StatusCode()) utils.AssertEqual(b, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) } @@ -2918,13 +2959,15 @@ func Benchmark_Ctx_RedirectToRouteWithQueries(b *testing.B) { b.ReportAllocs() b.ResetTimer() + var err error for n := 0; n < b.N; n++ { - c.RedirectToRoute("user", Map{ + err = c.RedirectToRoute("user", Map{ "name": "fiber", "queries": map[string]string{"a": "a", "b": "b"}, }) } + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, 302, c.Response().StatusCode()) // analysis of query parameters with url parsing, since a map pass is always randomly ordered location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) @@ -2966,9 +3009,10 @@ func Benchmark_Ctx_RenderBind(b *testing.B) { app.config.Views = engine c := app.AcquireCtx(&fasthttp.RequestCtx{}) - c.Bind(Map{ + err = c.Bind(Map{ "Title": "Hello, World!", }) + utils.AssertEqual(b, nil, err) defer app.ReleaseCtx(c) @@ -3066,7 +3110,7 @@ func (t *testTemplateEngine) Load() error { // go test -run Test_Ctx_Render_Engine func Test_Ctx_Render_Engine(t *testing.T) { engine := &testTemplateEngine{} - engine.Load() + utils.AssertEqual(t, nil, engine.Load()) app := New() app.config.Views = engine c := app.AcquireCtx(&fasthttp.RequestCtx{}) @@ -3081,7 +3125,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) { engine := &testTemplateEngine{} - engine.Load() + utils.AssertEqual(t, nil, engine.Load()) app := New(Config{ViewsLayout: "main.tmpl"}) app.config.Views = engine c := app.AcquireCtx(&fasthttp.RequestCtx{}) @@ -3121,9 +3165,15 @@ func Benchmark_Ctx_Get_Location_From_Route(b *testing.B) { app.Get("/user/:name", func(c *Ctx) error { return c.SendString(c.Params("name")) }).Name("User") + + var err error + var location string for n := 0; n < b.N; n++ { - c.getLocationFromRoute(app.GetRoute("User"), Map{"name": "fiber"}) + location, err = c.getLocationFromRoute(app.GetRoute("User"), Map{"name": "fiber"}) } + utils.AssertEqual(b, "/user/fiber", location) + utils.AssertEqual(b, nil, err) + } // go test -run Test_Ctx_Get_Location_From_Route_name @@ -3246,9 +3296,9 @@ func Test_Ctx_Send(t *testing.T) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Send([]byte("Hello, World")) - c.Send([]byte("Don't crash please")) - c.Send([]byte("1337")) + utils.AssertEqual(t, nil, c.Send([]byte("Hello, World"))) + utils.AssertEqual(t, nil, c.Send([]byte("Don't crash please"))) + utils.AssertEqual(t, nil, c.Send([]byte("1337"))) utils.AssertEqual(t, "1337", string(c.Response().Body())) } @@ -3260,9 +3310,12 @@ func Benchmark_Ctx_Send(b *testing.B) { byt := []byte("Hello, World!") b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.Send(byt) + err = c.Send(byt) } + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, "Hello, World!", string(c.Response().Body())) } @@ -3272,7 +3325,8 @@ func Test_Ctx_SendStatus(t *testing.T) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.SendStatus(415) + err := c.SendStatus(415) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 415, c.Response().StatusCode()) utils.AssertEqual(t, "Unsupported Media Type", string(c.Response().Body())) } @@ -3283,7 +3337,8 @@ func Test_Ctx_SendString(t *testing.T) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.SendString("Don't crash please") + err := c.SendString("Don't crash please") + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "Don't crash please", string(c.Response().Body())) } @@ -3294,13 +3349,16 @@ func Test_Ctx_SendStream(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.SendStream(bytes.NewReader([]byte("Don't crash please"))) + err := c.SendStream(bytes.NewReader([]byte("Don't crash please"))) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "Don't crash please", string(c.Response().Body())) - c.SendStream(bytes.NewReader([]byte("Don't crash please")), len([]byte("Don't crash please"))) + err = c.SendStream(bytes.NewReader([]byte("Don't crash please")), len([]byte("Don't crash please"))) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "Don't crash please", string(c.Response().Body())) - c.SendStream(bufio.NewReader(bytes.NewReader([]byte("Hello bufio")))) + err = c.SendStream(bufio.NewReader(bytes.NewReader([]byte("Hello bufio")))) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "Hello bufio", string(c.Response().Body())) } @@ -3356,7 +3414,8 @@ func Test_Ctx_Status(t *testing.T) { defer app.ReleaseCtx(c) c.Status(400) utils.AssertEqual(t, 400, c.Response().StatusCode()) - c.Status(415).Send([]byte("Hello, World")) + err := c.Status(415).Send([]byte("Hello, World")) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 415, c.Response().StatusCode()) utils.AssertEqual(t, "Hello, World", string(c.Response().Body())) } @@ -3436,8 +3495,10 @@ func Test_Ctx_Write(t *testing.T) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.Write([]byte("Hello, ")) - c.Write([]byte("World!")) + _, err := c.Write([]byte("Hello, ")) + utils.AssertEqual(t, nil, err) + _, err = c.Write([]byte("World!")) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "Hello, World!", string(c.Response().Body())) } @@ -3449,9 +3510,12 @@ func Benchmark_Ctx_Write(b *testing.B) { byt := []byte("Hello, World!") b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.Write(byt) + _, err = c.Write(byt) } + utils.AssertEqual(b, nil, err) } // go test -run Test_Ctx_Writef @@ -3461,7 +3525,8 @@ func Test_Ctx_Writef(t *testing.T) { c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) world := "World!" - c.Writef("Hello, %s", world) + _, err := c.Writef("Hello, %s", world) + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "Hello, World!", string(c.Response().Body())) } @@ -3473,9 +3538,12 @@ func Benchmark_Ctx_Writef(b *testing.B) { world := "World!" b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.Writef("Hello, %s", world) + _, err = c.Writef("Hello, %s", world) } + utils.AssertEqual(b, nil, err) } // go test -run Test_Ctx_WriteString @@ -3484,8 +3552,10 @@ func Test_Ctx_WriteString(t *testing.T) { app := New() c := app.AcquireCtx(&fasthttp.RequestCtx{}) defer app.ReleaseCtx(c) - c.WriteString("Hello, ") - c.WriteString("World!") + _, err := c.WriteString("Hello, ") + utils.AssertEqual(t, nil, err) + _, err = c.WriteString("World!") + utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "Hello, World!", string(c.Response().Body())) } @@ -3522,9 +3592,12 @@ func Benchmark_Ctx_SendString_B(b *testing.B) { body := "Hello, world!" b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.SendString(body) + err = c.SendString(body) } + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, []byte("Hello, world!"), c.Response().Body()) } @@ -4010,9 +4083,12 @@ func Benchmark_Ctx_QueryParser(b *testing.B) { q := new(Query) b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.QueryParser(q) + err = c.QueryParser(q) } + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, nil, c.QueryParser(q)) } @@ -4037,10 +4113,13 @@ func Benchmark_Ctx_parseQuery(b *testing.B) { b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.QueryParser(cq) + err = c.QueryParser(cq) } + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, nil, c.QueryParser(cq)) } @@ -4061,9 +4140,12 @@ func Benchmark_Ctx_QueryParser_Comma(b *testing.B) { q := new(Query) b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.QueryParser(q) + err = c.QueryParser(q) } + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, nil, c.QueryParser(q)) } @@ -4087,9 +4169,12 @@ func Benchmark_Ctx_ReqHeaderParser(b *testing.B) { q := new(ReqHeader) b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { - c.ReqHeaderParser(q) + err = c.ReqHeaderParser(q) } + utils.AssertEqual(b, nil, err) utils.AssertEqual(b, nil, c.ReqHeaderParser(q)) } @@ -4129,17 +4214,20 @@ func Benchmark_Ctx_BodyStreamWriter(b *testing.B) { user := []byte(`{"name":"john"}`) b.ReportAllocs() b.ResetTimer() + + var err error for n := 0; n < b.N; n++ { ctx.ResetBody() ctx.SetBodyStreamWriter(func(w *bufio.Writer) { for i := 0; i < 10; i++ { - w.Write(user) + _, err = w.Write(user) if err := w.Flush(); err != nil { return } } }) } + utils.AssertEqual(b, nil, err) } func Test_Ctx_String(t *testing.T) { @@ -4240,10 +4328,17 @@ func TestCtx_ParamsInt(t *testing.T) { return nil }) - app.Test(httptest.NewRequest(MethodGet, "/test/1111", nil)) - app.Test(httptest.NewRequest(MethodGet, "/testnoint/xd", nil)) - app.Test(httptest.NewRequest(MethodGet, "/testignoredefault/2222", nil)) - app.Test(httptest.NewRequest(MethodGet, "/testdefault/xd", nil)) + _, err := app.Test(httptest.NewRequest(MethodGet, "/test/1111", nil)) + utils.AssertEqual(t, nil, err) + + _, err = app.Test(httptest.NewRequest(MethodGet, "/testnoint/xd", nil)) + utils.AssertEqual(t, nil, err) + + _, err = app.Test(httptest.NewRequest(MethodGet, "/testignoredefault/2222", nil)) + utils.AssertEqual(t, nil, err) + + _, err = app.Test(httptest.NewRequest(MethodGet, "/testdefault/xd", nil)) + utils.AssertEqual(t, nil, err) } // go test -run Test_Ctx_GetRespHeader diff --git a/helpers_test.go b/helpers_test.go index 8afb71bb48..b56dcc5c51 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -248,11 +248,11 @@ func Test_Utils_IsNoCache(t *testing.T) { func Benchmark_Utils_IsNoCache(b *testing.B) { var ok bool for i := 0; i < b.N; i++ { - ok = isNoCache("public") - ok = isNoCache("no-cache") - ok = isNoCache("public, no-cache, max-age=30") - ok = isNoCache("public,no-cache") - ok = isNoCache("no-cache, public") + _ = isNoCache("public") + _ = isNoCache("no-cache") + _ = isNoCache("public, no-cache, max-age=30") + _ = isNoCache("public,no-cache") + _ = isNoCache("no-cache, public") ok = isNoCache("max-age=30, no-cache,public") } utils.AssertEqual(b, true, ok) diff --git a/middleware/envvar/envvar_test.go b/middleware/envvar/envvar_test.go index 6e90e99cba..56fc44fe1e 100644 --- a/middleware/envvar/envvar_test.go +++ b/middleware/envvar/envvar_test.go @@ -60,7 +60,7 @@ func TestEnvVarHandlerNotMatched(t *testing.T) { ExportVars: map[string]string{"testKey": ""}})) app.Get("/another-path", func(ctx *fiber.Ctx) error { - ctx.SendString("OK") + utils.AssertEqual(t, nil, ctx.SendString("OK")) return nil }) diff --git a/middleware/session/session_test.go b/middleware/session/session_test.go index 19bf121fa0..ff7ec15432 100644 --- a/middleware/session/session_test.go +++ b/middleware/session/session_test.go @@ -218,10 +218,10 @@ func Test_Session_Store_Reset(t *testing.T) { // set value & save sess.Set("hello", "world") ctx.Request().Header.SetCookie(store.sessionName, sess.ID()) - sess.Save() + utils.AssertEqual(t, nil, sess.Save()) // reset store - store.Reset() + utils.AssertEqual(t, nil, store.Reset()) // make sure the session is recreated sess, _ = store.Get(ctx) @@ -326,7 +326,7 @@ func Test_Session_Reset(t *testing.T) { sess, _ := store.Get(ctx) sess.Set("name", "fenny") - sess.Destroy() + utils.AssertEqual(t, nil, sess.Destroy()) name := sess.Get("name") utils.AssertEqual(t, nil, name) }) @@ -346,7 +346,7 @@ func Test_Session_Reset(t *testing.T) { // set value & save sess.Set("name", "fenny") - _ = sess.Save() + utils.AssertEqual(t, nil, sess.Save()) sess, _ = store.Get(ctx) err := sess.Destroy() @@ -381,7 +381,7 @@ func Test_Session_Cookie(t *testing.T) { // get session sess, _ := store.Get(ctx) - sess.Save() + utils.AssertEqual(t, nil, sess.Save()) // cookie should be set on Save ( even if empty data ) utils.AssertEqual(t, 84, len(ctx.Response().Header.PeekCookie(store.sessionName))) @@ -401,7 +401,7 @@ func Test_Session_Cookie_In_Response(t *testing.T) { sess, _ := store.Get(ctx) sess.Set("id", "1") utils.AssertEqual(t, true, sess.Fresh()) - sess.Save() + utils.AssertEqual(t, nil, sess.Save()) sess, _ = store.Get(ctx) sess.Set("name", "john") @@ -488,14 +488,17 @@ func Benchmark_Session(b *testing.B) { defer app.ReleaseCtx(c) c.Request().Header.SetCookie(store.sessionName, "12356789") + var err error b.Run("default", func(b *testing.B) { b.ReportAllocs() b.ResetTimer() for n := 0; n < b.N; n++ { sess, _ := store.Get(c) sess.Set("john", "doe") - _ = sess.Save() + err = sess.Save() } + + utils.AssertEqual(b, nil, err) }) b.Run("storage", func(b *testing.B) { @@ -507,7 +510,9 @@ func Benchmark_Session(b *testing.B) { for n := 0; n < b.N; n++ { sess, _ := store.Get(c) sess.Set("john", "doe") - _ = sess.Save() + err = sess.Save() } + + utils.AssertEqual(b, nil, err) }) } From 896b37183fdfe14830dcb40f12007d0d72d0975d Mon Sep 17 00:00:00 2001 From: kinggo <1963359402@qq.com> Date: Mon, 19 Sep 2022 16:58:04 +0800 Subject: [PATCH 100/141] :bug: fix: same struct parse param failed (#2101) fix: same struct parse param failed --- ctx.go | 46 +++++++++++++++++++++++++++++----------------- ctx_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 17 deletions(-) diff --git a/ctx.go b/ctx.go index adfebefbad..b5bc5f0153 100644 --- a/ctx.go +++ b/ctx.go @@ -46,6 +46,33 @@ const ( // userContextKey define the key name for storing context.Context in *fasthttp.RequestCtx const userContextKey = "__local_user_context__" +var ( + // decoderPoolMap helps to improve BodyParser's, QueryParser's and ReqHeaderParser's performance + decoderPoolMap = map[string]*sync.Pool{} + // tags is used to classify parser's pool + tags = []string{queryTag, bodyTag, reqHeaderTag, paramsTag} +) + +func init() { + for _, tag := range tags { + decoderPoolMap[tag] = &sync.Pool{New: func() interface{} { + return decoderBuilder(ParserConfig{ + IgnoreUnknownKeys: true, + ZeroEmpty: true, + }) + }} + } +} + +// SetParserDecoder allow globally change the option of form decoder, update decoderPool +func SetParserDecoder(parserConfig ParserConfig) { + for _, tag := range tags { + decoderPoolMap[tag] = &sync.Pool{New: func() interface{} { + return decoderBuilder(parserConfig) + }} + } +} + // Ctx represents the Context which hold the HTTP request and response. // It has methods for the request query string, parameters, body, HTTP headers and so on. type Ctx struct { @@ -311,21 +338,6 @@ func (c *Ctx) Body() []byte { return body } -// decoderPool helps to improve BodyParser's, QueryParser's and ReqHeaderParser's performance -var decoderPool = &sync.Pool{New: func() interface{} { - return decoderBuilder(ParserConfig{ - IgnoreUnknownKeys: true, - ZeroEmpty: true, - }) -}} - -// SetParserDecoder allow globally change the option of form decoder, update decoderPool -func SetParserDecoder(parserConfig ParserConfig) { - decoderPool = &sync.Pool{New: func() interface{} { - return decoderBuilder(parserConfig) - }} -} - func decoderBuilder(parserConfig ParserConfig) interface{} { decoder := schema.NewDecoder() decoder.IgnoreUnknownKeys(parserConfig.IgnoreUnknownKeys) @@ -1109,8 +1121,8 @@ func (c *Ctx) ReqHeaderParser(out interface{}) error { func (c *Ctx) parseToStruct(aliasTag string, out interface{}, data map[string][]string) error { // Get decoder from pool - schemaDecoder := decoderPool.Get().(*schema.Decoder) - defer decoderPool.Put(schemaDecoder) + schemaDecoder := decoderPoolMap[aliasTag].Get().(*schema.Decoder) + defer decoderPoolMap[aliasTag].Put(schemaDecoder) // Set alias tag schemaDecoder.SetAliasTag(aliasTag) diff --git a/ctx_test.go b/ctx_test.go index 2acaa1ce94..efbd7b1418 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -4441,3 +4441,52 @@ func Test_Ctx_IsFromLocal(t *testing.T) { utils.AssertEqual(t, false, c.IsFromLocal()) } } + +// go test -run Test_Ctx_RepeatParserWithSameStruct -v +func Test_Ctx_RepeatParserWithSameStruct(t *testing.T) { + t.Parallel() + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + + type Request struct { + QueryParam string `query:"query_param"` + HeaderParam string `reqHeader:"header_param"` + BodyParam string `json:"body_param" xml:"body_param" form:"body_param"` + } + + r := new(Request) + + c.Request().URI().SetQueryString("query_param=query_param") + utils.AssertEqual(t, nil, c.QueryParser(r)) + utils.AssertEqual(t, "query_param", r.QueryParam) + + c.Request().Header.Add("header_param", "header_param") + utils.AssertEqual(t, nil, c.ReqHeaderParser(r)) + utils.AssertEqual(t, "header_param", r.HeaderParam) + + var gzipJSON bytes.Buffer + w := gzip.NewWriter(&gzipJSON) + _, _ = w.Write([]byte(`{"body_param":"body_param"}`)) + _ = w.Close() + c.Request().Header.SetContentType(MIMEApplicationJSON) + c.Request().Header.Set(HeaderContentEncoding, "gzip") + c.Request().SetBody(gzipJSON.Bytes()) + c.Request().Header.SetContentLength(len(gzipJSON.Bytes())) + utils.AssertEqual(t, nil, c.BodyParser(r)) + utils.AssertEqual(t, "body_param", r.BodyParam) + c.Request().Header.Del(HeaderContentEncoding) + + testDecodeParser := func(contentType, body string) { + c.Request().Header.SetContentType(contentType) + c.Request().SetBody([]byte(body)) + c.Request().Header.SetContentLength(len(body)) + utils.AssertEqual(t, nil, c.BodyParser(r)) + utils.AssertEqual(t, "body_param", r.BodyParam) + } + + testDecodeParser(MIMEApplicationJSON, `{"body_param":"body_param"}`) + testDecodeParser(MIMEApplicationXML, `body_param`) + testDecodeParser(MIMEApplicationForm, "body_param=body_param") + testDecodeParser(MIMEMultipartForm+`;boundary="b"`, "--b\r\nContent-Disposition: form-data; name=\"body_param\"\r\n\r\nbody_param\r\n--b--") +} From b319e8d00ca188573d9489f7ba97b56fb69d0d61 Mon Sep 17 00:00:00 2001 From: naoki kuroda <68233204+nnnkkk7@users.noreply.github.com> Date: Wed, 21 Sep 2022 21:50:16 +0900 Subject: [PATCH 101/141] =?UTF-8?q?=E2=9C=8D=20Correct=20README=5Fja=20(#2?= =?UTF-8?q?108)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✍ correct reademe_ja --- .github/README_ja.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/README_ja.md b/.github/README_ja.md index 8243b29236..3775d8ae16 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -79,7 +79,7 @@

    -FIberは、Expressに触発されたWebフレームワークです。Go 最速のHTTPエンジンであるFasthttpで作られています。ゼロメモリアロケーションパフォーマンスを念頭に置いて設計されており、迅速な開発をサポートします。 +Fiberは、Expressに触発されたWebフレームワークです。Go 最速のHTTPエンジンであるFasthttpで作られています。ゼロメモリアロケーションパフォーマンスを念頭に置いて設計されており、迅速な開発をサポートします。

    @@ -114,7 +114,6 @@ func main() { Go がインストールされていることを確認してください ([ダウンロード](https://go.dev/dl/)). バージョン `1.14` またはそれ以上であることが必要です。 -Initialize your project by creating a folder and then running `go mod init github.com/your/repo` ([learn more](https://go.dev/blog/using-go-modules)) inside the folder. Then install Fiber with the [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) command: フォルダを作成し、フォルダ内で `go mod init github.com/your/repo` ([learn more](https://go.dev/blog/using-go-modules)) を実行してプロジェクトを初期化してください。その後、 Fiber を以下の [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) コマンドでインストールしてください。 ```bash @@ -127,14 +126,14 @@ go get -u github.com/gofiber/fiber/v2 - [静的ファイル](https://docs.gofiber.io/api/app#static)のサポート - 究極の[パフォーマンス](https://docs.gofiber.io/extra/benchmarks) - [低メモリ](https://docs.gofiber.io/extra/benchmarks)フットプリント -- Express [API エンドポイント](https://docs.gofiber.io/api/ctx) -- Middleware と[Next](https://docs.gofiber.io/api/ctx#next)のサポート +- [API エンドポイント](https://docs.gofiber.io/api/ctx) +- [Middleware](https://docs.gofiber.io/api/middleware) と[Next](https://docs.gofiber.io/api/ctx#next)のサポート - [迅速](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497)なサーバーサイドプログラミング - [Template engines](https://github.com/gofiber/template) - [WebSocket support](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- [15 ヶ国語](https://docs.gofiber.io/)で利用可能 +- [15 ヶ国語](https://docs.gofiber.io/)に翻訳 - [Fiber](https://docs.gofiber.io/)をもっと知る ## 💡 哲学 @@ -146,9 +145,10 @@ Fiber は人気の高い Web フレームワークである Expressjs に**イ わたしたちは Express の**手軽さ**と Go の**パフォーマンス**を組み合わせました。 もしも、Web アプリケーションを Express 等の Node.js フレームワークで実装した経験があれば、多くの方法や原理がとても**馴染み深い**でしょう。 -## ⚠️ 制限時効 -* Fiberはunsafeパッケージを使用しているため、最新のGoバージョンと互換性がない場合があります。Fiber 2.29.0 は、Go のバージョン 1.14 から 1.19 でテストされています。 -* Fiberはnet/httpインターフェースと互換性がありません。つまり、gqlgenやgo-swaggerなど、net/httpのエコシステムの一部であるプロジェクトを使用することができません。 +## ⚠️ 制限事項 + +- Fiber は unsafe パッケージを使用しているため、最新の Go バージョンと互換性がない場合があります。Fiber 2.29.0 は、Go のバージョン 1.14 から 1.19 でテストされています。 +- Fiber は net/http インターフェースと互換性がありません。つまり、gqlgen や go-swagger など、net/http のエコシステムの一部であるプロジェクトを使用することができません。 ## 👀 例 @@ -195,7 +195,7 @@ func main() { ``` -#### 📖 [**Route Naming**](https://docs.gofiber.io/api/app#name) +#### 📖 [**ルートの命名**](https://docs.gofiber.io/api/app#name) ```go func main() { @@ -285,11 +285,11 @@ func main() { 📖 [Engines](https://github.com/gofiber/template) 📖 [Render](https://docs.gofiber.io/api/ctx#render) -view engineが設定されていない時は、Fiberのデフォルトは[html/template](https://pkg.go.dev/html/template/) になります。 +view engine が設定されていない時は、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) などの別のエンジンを使用したい場合など、 -複数のview engineをサポートする [Template](https://github.com/gofiber/template) パッケージをご覧ください。 +複数の view engine をサポートする [Template](https://github.com/gofiber/template) パッケージをご覧ください。 ```go package main @@ -400,7 +400,7 @@ func main() { } ``` -`Origin`ヘッダーに任意のドメインを渡してCORSのチェックをします: +`Origin`ヘッダーに任意のドメインを渡して CORS のチェックをします: ```bash curl -H "Origin: http://example.com" --verbose http://localhost:3000 @@ -520,7 +520,7 @@ func main() { c.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) { fmt.Println("WRITER") var i int - + for { i++ msg := fmt.Sprintf("%d - the time is %v", i, time.Now()) @@ -610,7 +610,7 @@ func main() { ## 🕶️ Awesome List -For more articles, middlewares, examples or tools check our [awesome list](https://github.com/gofiber/awesome-fiber). +その他の記事、ミドルウェア、サンプル、ツールについては、私たちの[awesome list](https://github.com/gofiber/awesome-fiber)をご覧ください。 ## 👍 貢献する @@ -618,7 +618,7 @@ For more articles, middlewares, examples or tools check our [awesome list](https 1. [GitHub Star](https://github.com/gofiber/fiber/stargazers)をつけてください 。 2. [あなたの Twitter で](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber)プロジェクトについてツイートしてください。 -3. [Medium](https://medium.com/) 、 [Dev.to、](https://dev.to/)または個人のブログでレビューまたはチュートリアルを書いてください。 +3. [Medium](https://medium.com/) 、 [Dev.to](https://dev.to/)、または個人のブログでレビューやチュートリアルを書いてください。 4. [cup of coffee](https://buymeacoff.ee/fenny)の寄付でプロジェクトを支援しましょう。 ## ☕ サポーター From 72397a7c5be7291235261dcf5b079d7702277b65 Mon Sep 17 00:00:00 2001 From: naoki kuroda <68233204+nnnkkk7@users.noreply.github.com> Date: Wed, 21 Sep 2022 23:41:17 +0900 Subject: [PATCH 102/141] :pencil2: fix typo (#2110) :pencil2: fix typo --- router.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/router.go b/router.go index 69cc3c00da..46fb1168b4 100644 --- a/router.go +++ b/router.go @@ -16,7 +16,7 @@ import ( "github.com/valyala/fasthttp" ) -// Router defines all router handle interface includes app and group router. +// Router defines all router handle interface, including app and group router. type Router interface { Use(args ...interface{}) Router @@ -43,7 +43,7 @@ type Router interface { Name(name string) Router } -// Route is a struct that holds all metadata for each registered handler +// Route is a struct that holds all metadata for each registered handler. type Route struct { // Data for routing pos uint32 // Position in stack -> important for the sort of the matched routes From d461bf231e24ba053c08be259ce352db2bf10c83 Mon Sep 17 00:00:00 2001 From: dayu Date: Thu, 22 Sep 2022 17:30:19 +0800 Subject: [PATCH 103/141] Update README.md (#2114) some demo codes missing parenthesis, i added them to make that codes could be copied and run --- middleware/filesystem/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/middleware/filesystem/README.md b/middleware/filesystem/README.md index 52e0f8bbc8..e976abf4a7 100644 --- a/middleware/filesystem/README.md +++ b/middleware/filesystem/README.md @@ -123,7 +123,7 @@ func main() { app.Use("/assets", filesystem.New(filesystem.Config{ Root: pkger.Dir("/assets"), - }) + })) log.Fatal(app.Listen(":3000")) } @@ -148,7 +148,7 @@ func main() { app.Use("/assets", filesystem.New(filesystem.Config{ Root: packr.New("Assets Box", "/assets"), - }) + })) log.Fatal(app.Listen(":3000")) } @@ -173,7 +173,7 @@ func main() { app.Use("/assets", filesystem.New(filesystem.Config{ Root: rice.MustFindBox("assets").HTTPBox(), - }) + })) log.Fatal(app.Listen(":3000")) } @@ -198,7 +198,7 @@ func main() { app.Use("/assets", filesystem.New(filesystem.Config{ Root: myEmbeddedFiles.HTTP, - }) + })) log.Fatal(app.Listen(":3000")) } @@ -229,7 +229,7 @@ func main() { app.Use("/", filesystem.New(filesystem.Config{ Root: statikFS, - }) + })) log.Fatal(app.Listen(":3000")) } From 10d6f69a89390397aa5dd23f4f7352ea05638f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Fri, 23 Sep 2022 09:17:34 +0300 Subject: [PATCH 104/141] :sparkles: v3 (feature): new redirection methods (#2014) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :sparkles: v3 (feature): new redirection methods * add flash messages * withinput, parsing flash message * add tests * add benchmarks * gosec issues * fix tests * fix tests * fix performance issues * fix performance issues * optimization. * better names * fix tests * Update router.go * fix * fix old messaages with flash messages behavior, add new test case with req * complete some reviews * add pool for redirection. * use constant * update * ✨ v3 (feature): new redirection methods * fix tests, optimize cookie parsing (9 allocs -> 1 alloc) * test case for message includes comma * cleanup * optimization. * some improvements for the redirect feature * fix Benchmark_Redirect_Route_WithFlashMessages * some improvements for the redirect feature * Update redirect.go * improve message parsing & test case Co-authored-by: René Werner --- app.go | 5 +- client_test.go | 4 +- ctx.go | 58 +--- ctx_interface.go | 21 +- ctx_test.go | 179 ----------- error.go | 5 + middleware/expvar/expvar.go | 2 +- middleware/pprof/pprof.go | 2 +- middleware/redirect/redirect.go | 2 +- redirect.go | 295 ++++++++++++++++++ redirect_test.go | 528 ++++++++++++++++++++++++++++++++ router.go | 5 + 12 files changed, 861 insertions(+), 245 deletions(-) create mode 100644 redirect.go create mode 100644 redirect_test.go diff --git a/app.go b/app.go index 708b16a1ce..a4e24d1f8e 100644 --- a/app.go +++ b/app.go @@ -10,6 +10,8 @@ package fiber import ( "bufio" "bytes" + "encoding/json" + "encoding/xml" "errors" "fmt" "net" @@ -22,9 +24,6 @@ import ( "sync/atomic" "time" - "encoding/json" - "encoding/xml" - "github.com/gofiber/fiber/v3/utils" "github.com/valyala/fasthttp" ) diff --git a/client_test.go b/client_test.go index e3d78dca6d..670d8128b5 100644 --- a/client_test.go +++ b/client_test.go @@ -1078,9 +1078,9 @@ func Test_Client_Agent_MaxRedirectsCount(t *testing.T) { app.Get("/", func(c Ctx) error { if c.Request().URI().QueryArgs().Has("foo") { - return c.Redirect("/foo") + return c.Redirect().To("/foo") } - return c.Redirect("/") + return c.Redirect().To("/") }) app.Get("/foo", func(c Ctx) error { return c.SendString("redirect") diff --git a/ctx.go b/ctx.go index 5d6c57dfdb..1b7cb8461d 100644 --- a/ctx.go +++ b/ctx.go @@ -52,6 +52,8 @@ type DefaultCtx struct { matched bool // Non use route matched viewBindMap *dictpool.Dict // Default view map to bind template engine bind *Bind // Default bind reference + redirect *Redirect // Default redirect reference + redirectionMessages []string // Messages of the previous redirect } // TLSHandler object @@ -896,16 +898,17 @@ func (c *DefaultCtx) Range(size int) (rangeData Range, err error) { return } -// Redirect to the URL derived from the specified path, with specified status. +// Redirect returns the Redirect reference. +// Use Redirect().Status() to set custom redirection status code. // If status is not specified, status defaults to 302 Found. -func (c *DefaultCtx) Redirect(location string, status ...int) error { - c.setCanonical(HeaderLocation, location) - if len(status) > 0 { - c.Status(status[0]) - } else { - c.Status(StatusFound) +// You can use Redirect().To(), Redirect().Route() and Redirect().Back() for redirection. +func (c *DefaultCtx) Redirect() *Redirect { + if c.redirect == nil { + c.redirect = AcquireRedirect() + c.redirect.c = c } - return nil + + return c.redirect } // Add vars to default view var map binding to template engine. @@ -956,45 +959,6 @@ func (c *DefaultCtx) GetRouteURL(routeName string, params Map) (string, error) { return c.getLocationFromRoute(c.App().GetRoute(routeName), params) } -// RedirectToRoute to the Route registered in the app with appropriate parameters -// If status is not specified, status defaults to 302 Found. -// If you want to send queries to route, you must add "queries" key typed as map[string]string to params. -func (c *DefaultCtx) RedirectToRoute(routeName string, params Map, status ...int) error { - location, err := c.getLocationFromRoute(c.App().GetRoute(routeName), params) - if err != nil { - return err - } - - // Check queries - if queries, ok := params["queries"].(map[string]string); ok { - queryText := bytebufferpool.Get() - defer bytebufferpool.Put(queryText) - - i := 1 - for k, v := range queries { - _, _ = queryText.WriteString(k + "=" + v) - - if i != len(queries) { - _, _ = queryText.WriteString("&") - } - i++ - } - - return c.Redirect(location+"?"+queryText.String(), status...) - } - return c.Redirect(location, status...) -} - -// RedirectBack to the URL to referer -// If status is not specified, status defaults to 302 Found. -func (c *DefaultCtx) RedirectBack(fallback string, status ...int) error { - location := c.Get(HeaderReferer) - if location == "" { - location = fallback - } - return c.Redirect(location, status...) -} - // 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 { diff --git a/ctx_interface.go b/ctx_interface.go index 3adcea498f..a050356d03 100644 --- a/ctx_interface.go +++ b/ctx_interface.go @@ -229,9 +229,11 @@ type Ctx interface { // Range returns a struct containing the type and a slice of ranges. Range(size int) (rangeData Range, err error) - // Redirect to the URL derived from the specified path, with specified status. + // Redirect returns the Redirect reference. + // Use Redirect().Status() to set custom redirection status code. // If status is not specified, status defaults to 302 Found. - Redirect(location string, status ...int) error + // You can use Redirect().To(), Redirect().Route() and Redirect().Back() for redirection. + Redirect() *Redirect // Add vars to default view var map binding to template engine. // Variables are read by the Render method and may be overwritten. @@ -240,15 +242,6 @@ type Ctx interface { // GetRouteURL generates URLs to named routes, with parameters. URLs are relative, for example: "/user/1831" GetRouteURL(routeName string, params Map) (string, error) - // RedirectToRoute to the Route registered in the app with appropriate parameters - // If status is not specified, status defaults to 302 Found. - // If you want to send queries to route, you must add "queries" key typed as map[string]string to params. - RedirectToRoute(routeName string, params Map, status ...int) error - - // RedirectBack to the URL to referer - // If status is not specified, status defaults to 302 Found. - RedirectBack(fallback string, status ...int) error - // Render a template with data and sends a text/html response. // We support the following engines: https://github.com/gofiber/template Render(name string, bind Map, layouts ...string) error @@ -445,8 +438,14 @@ func (c *DefaultCtx) release() { c.route = nil c.fasthttp = nil c.bind = nil + c.redirectionMessages = c.redirectionMessages[:0] if c.viewBindMap != nil { dictpool.ReleaseDict(c.viewBindMap) + c.viewBindMap = nil + } + if c.redirect != nil { + ReleaseRedirect(c.redirect) + c.redirect = nil } } diff --git a/ctx_test.go b/ctx_test.go index 82e21d39bf..f0552a1106 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -16,7 +16,6 @@ import ( "io" "mime/multipart" "net/http/httptest" - "net/url" "os" "path/filepath" "strconv" @@ -2254,137 +2253,6 @@ func Test_Ctx_Next_Error(t *testing.T) { require.Equal(t, "Works", resp.Header.Get("X-Next-Result")) } -// go test -run Test_Ctx_Redirect -func Test_Ctx_Redirect(t *testing.T) { - t.Parallel() - app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) - - c.Redirect("http://default.com") - require.Equal(t, 302, c.Response().StatusCode()) - require.Equal(t, "http://default.com", string(c.Response().Header.Peek(HeaderLocation))) - - c.Redirect("http://example.com", 301) - require.Equal(t, 301, c.Response().StatusCode()) - require.Equal(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) -} - -// go test -run Test_Ctx_RedirectToRouteWithParams -func Test_Ctx_RedirectToRouteWithParams(t *testing.T) { - t.Parallel() - app := New() - app.Get("/user/:name", func(c Ctx) error { - return c.JSON(c.Params("name")) - }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}) - - c.RedirectToRoute("user", Map{ - "name": "fiber", - }) - require.Equal(t, 302, c.Response().StatusCode()) - require.Equal(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) -} - -// go test -run Test_Ctx_RedirectToRouteWithParams -func Test_Ctx_RedirectToRouteWithQueries(t *testing.T) { - t.Parallel() - app := New() - app.Get("/user/:name", func(c Ctx) error { - return c.JSON(c.Params("name")) - }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}) - - c.RedirectToRoute("user", Map{ - "name": "fiber", - "queries": map[string]string{"data[0][name]": "john", "data[0][age]": "10", "test": "doe"}, - }) - require.Equal(t, 302, c.Response().StatusCode()) - // analysis of query parameters with url parsing, since a map pass is always randomly ordered - location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) - require.NoError(t, err, "url.Parse(location)") - require.Equal(t, "/user/fiber", location.Path) - require.Equal(t, url.Values{"data[0][name]": []string{"john"}, "data[0][age]": []string{"10"}, "test": []string{"doe"}}, location.Query()) -} - -// go test -run Test_Ctx_RedirectToRouteWithOptionalParams -func Test_Ctx_RedirectToRouteWithOptionalParams(t *testing.T) { - t.Parallel() - app := New() - app.Get("/user/:name?", func(c Ctx) error { - return c.JSON(c.Params("name")) - }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}) - - c.RedirectToRoute("user", Map{ - "name": "fiber", - }) - require.Equal(t, 302, c.Response().StatusCode()) - require.Equal(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) -} - -// go test -run Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue -func Test_Ctx_RedirectToRouteWithOptionalParamsWithoutValue(t *testing.T) { - t.Parallel() - app := New() - app.Get("/user/:name?", func(c Ctx) error { - return c.JSON(c.Params("name")) - }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}) - - c.RedirectToRoute("user", Map{}) - require.Equal(t, 302, c.Response().StatusCode()) - require.Equal(t, "/user/", string(c.Response().Header.Peek(HeaderLocation))) -} - -// go test -run Test_Ctx_RedirectToRouteWithGreedyParameters -func Test_Ctx_RedirectToRouteWithGreedyParameters(t *testing.T) { - t.Parallel() - app := New() - app.Get("/user/+", func(c Ctx) error { - return c.JSON(c.Params("+")) - }).Name("user") - c := app.NewCtx(&fasthttp.RequestCtx{}) - - c.RedirectToRoute("user", Map{ - "+": "test/routes", - }) - require.Equal(t, 302, c.Response().StatusCode()) - require.Equal(t, "/user/test/routes", string(c.Response().Header.Peek(HeaderLocation))) -} - -// go test -run Test_Ctx_RedirectBack -func Test_Ctx_RedirectBack(t *testing.T) { - t.Parallel() - app := New() - app.Get("/", func(c Ctx) error { - return c.JSON("Home") - }).Name("home") - c := app.NewCtx(&fasthttp.RequestCtx{}) - - c.RedirectBack("/") - require.Equal(t, 302, c.Response().StatusCode()) - require.Equal(t, "/", string(c.Response().Header.Peek(HeaderLocation))) -} - -// go test -run Test_Ctx_RedirectBackWithReferer -func Test_Ctx_RedirectBackWithReferer(t *testing.T) { - t.Parallel() - app := New() - app.Get("/", func(c Ctx) error { - return c.JSON("Home") - }).Name("home") - app.Get("/back", func(c Ctx) error { - return c.JSON("Back") - }).Name("back") - c := app.NewCtx(&fasthttp.RequestCtx{}) - - c.Request().Header.Set(HeaderReferer, "/back") - c.RedirectBack("/") - require.Equal(t, 302, c.Response().StatusCode()) - require.Equal(t, "/back", c.Get(HeaderReferer)) - require.Equal(t, "/back", string(c.Response().Header.Peek(HeaderLocation))) -} - // go test -run Test_Ctx_Render func Test_Ctx_Render(t *testing.T) { t.Parallel() @@ -2597,53 +2465,6 @@ func Benchmark_Ctx_RenderWithLocalsAndBindVars(b *testing.B) { require.Equal(b, "

    Hello, World! Test

    ", string(c.Response().Body())) } -func Benchmark_Ctx_RedirectToRoute(b *testing.B) { - app := New() - app.Get("/user/:name", func(c Ctx) error { - return c.JSON(c.Params("name")) - }).Name("user") - - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) - - b.ReportAllocs() - b.ResetTimer() - - for n := 0; n < b.N; n++ { - c.RedirectToRoute("user", Map{ - "name": "fiber", - }) - } - - require.Equal(b, 302, c.Response().StatusCode()) - require.Equal(b, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) -} - -func Benchmark_Ctx_RedirectToRouteWithQueries(b *testing.B) { - app := New() - app.Get("/user/:name", func(c Ctx) error { - return c.JSON(c.Params("name")) - }).Name("user") - - c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) - - b.ReportAllocs() - b.ResetTimer() - - for n := 0; n < b.N; n++ { - c.RedirectToRoute("user", Map{ - "name": "fiber", - "queries": map[string]string{"a": "a", "b": "b"}, - }) - } - - require.Equal(b, 302, c.Response().StatusCode()) - // analysis of query parameters with url parsing, since a map pass is always randomly ordered - location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) - require.NoError(b, err, "url.Parse(location)") - require.Equal(b, "/user/fiber", location.Path) - require.Equal(b, url.Values{"a": []string{"a"}, "b": []string{"b"}}, location.Query()) -} - func Benchmark_Ctx_RenderLocals(b *testing.B) { engine := &testTemplateEngine{} err := engine.Load() diff --git a/error.go b/error.go index 6b08acef15..a688411bca 100644 --- a/error.go +++ b/error.go @@ -12,6 +12,11 @@ var ( ErrGracefulTimeout = stdErrors.New("shutdown: graceful timeout has been reached, exiting") ) +// Fiber redirection errors +var ( + ErrRedirectBackNoFallback = NewError(StatusInternalServerError, "Referer not found, you have to enter fallback URL for redirection.") +) + // Range errors var ( ErrRangeMalformed = stdErrors.New("range: malformed range header string") diff --git a/middleware/expvar/expvar.go b/middleware/expvar/expvar.go index 3b4861ec64..8ea09e1a77 100644 --- a/middleware/expvar/expvar.go +++ b/middleware/expvar/expvar.go @@ -29,6 +29,6 @@ func New(config ...Config) fiber.Handler { return nil } - return c.Redirect("/debug/vars", 302) + return c.Redirect().To("/debug/vars") } } diff --git a/middleware/pprof/pprof.go b/middleware/pprof/pprof.go index e3a5ea104d..afaab4407e 100644 --- a/middleware/pprof/pprof.go +++ b/middleware/pprof/pprof.go @@ -72,7 +72,7 @@ func New(config ...Config) fiber.Handler { path = "/debug/pprof/" } - return c.Redirect(path, fiber.StatusFound) + return c.Redirect().To(path) } return nil } diff --git a/middleware/redirect/redirect.go b/middleware/redirect/redirect.go index 8473df3a79..0dec5263a9 100644 --- a/middleware/redirect/redirect.go +++ b/middleware/redirect/redirect.go @@ -61,7 +61,7 @@ func New(config ...Config) fiber.Handler { for k, v := range cfg.rulesRegex { replacer := captureTokens(k, c.Path()) if replacer != nil { - return c.Redirect(replacer.Replace(v), cfg.StatusCode) + return c.Redirect().Status(cfg.StatusCode).To(replacer.Replace(v)) } } return c.Next() diff --git a/redirect.go b/redirect.go new file mode 100644 index 0000000000..25f01fde0d --- /dev/null +++ b/redirect.go @@ -0,0 +1,295 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 📝 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package fiber + +import ( + "strings" + "sync" + + "github.com/gofiber/fiber/v3/binder" + "github.com/gofiber/fiber/v3/utils" + "github.com/valyala/bytebufferpool" +) + +var ( + // Pool for redirection + redirectPool = sync.Pool{ + New: func() any { + return &Redirect{ + status: StatusFound, + oldInput: make(map[string]string, 0), + } + }, + } +) + +// Cookie name to send flash messages when to use redirection. +const ( + FlashCookieName = "fiber_flash" + OldInputDataPrefix = "old_input_data_" + CookieDataSeparator = "," + CookieDataAssigner = ":" +) + +// Redirect is a struct to use it with Ctx. +type Redirect struct { + c *DefaultCtx // Embed ctx + status int // Status code of redirection. Default: StatusFound + + messages []string // Flash messages + oldInput map[string]string // Old input data +} + +// A config to use with Redirect().Route() +// You can specify queries or route parameters. +// NOTE: We don't use net/url to parse parameters because of it has poor performance. You have to pass map. +type RedirectConfig struct { + Params Map // Route parameters + Queries map[string]string // Query map +} + +// AcquireRedirect return default Redirect reference from the redirect pool +func AcquireRedirect() *Redirect { + return redirectPool.Get().(*Redirect) +} + +// ReleaseRedirect returns c acquired via Redirect to redirect pool. +// +// It is forbidden accessing req and/or its' members after returning +// it to redirect pool. +func ReleaseRedirect(r *Redirect) { + r.release() + redirectPool.Put(r) +} + +func (r *Redirect) release() { + r.status = 302 + r.messages = r.messages[:0] + // reset map + for k := range r.oldInput { + delete(r.oldInput, k) + } + r.c = nil +} + +// Status sets the status code of redirection. +// If status is not specified, status defaults to 302 Found. +func (r *Redirect) Status(code int) *Redirect { + r.status = code + + return r +} + +// You can send flash messages by using With(). +// They will be sent as a cookie. +// You can get them by using: Redirect().Messages(), Redirect().Message() +// Note: You must use escape char before using ',' and ':' chars to avoid wrong parsing. +func (r *Redirect) With(key string, value string) *Redirect { + r.messages = append(r.messages, key+CookieDataAssigner+value) + + return r +} + +// You can send input data by using WithInput(). +// They will be sent as a cookie. +// This method can send form, multipart form, query data to redirected route. +// You can get them by using: Redirect().OldInputs(), Redirect().OldInput() +func (r *Redirect) WithInput() *Redirect { + // Get content-type + ctype := utils.ToLower(utils.UnsafeString(r.c.Context().Request.Header.ContentType())) + ctype = binder.FilterFlags(utils.ParseVendorSpecificContentType(ctype)) + + switch ctype { + case MIMEApplicationForm: + _ = r.c.Bind().Form(r.oldInput) + case MIMEMultipartForm: + _ = r.c.Bind().MultipartForm(r.oldInput) + default: + _ = r.c.Bind().Query(r.oldInput) + } + + return r +} + +// Get flash messages. +func (r *Redirect) Messages() map[string]string { + msgs := r.c.redirectionMessages + flashMessages := make(map[string]string, len(msgs)) + + for _, msg := range msgs { + k, v := parseMessage(msg) + + if !strings.HasPrefix(k, OldInputDataPrefix) { + flashMessages[k] = v + } + } + + return flashMessages +} + +// Get flash message by key. +func (r *Redirect) Message(key string) string { + msgs := r.c.redirectionMessages + + for _, msg := range msgs { + k, v := parseMessage(msg) + + if !strings.HasPrefix(k, OldInputDataPrefix) && k == key { + return v + } + } + return "" +} + +// Get old input data. +func (r *Redirect) OldInputs() map[string]string { + msgs := r.c.redirectionMessages + oldInputs := make(map[string]string, len(msgs)) + + for _, msg := range msgs { + k, v := parseMessage(msg) + + if strings.HasPrefix(k, OldInputDataPrefix) { + // remove "old_input_data_" part from key + oldInputs[k[len(OldInputDataPrefix):]] = v + } + } + return oldInputs +} + +// Get old input data by key. +func (r *Redirect) OldInput(key string) string { + msgs := r.c.redirectionMessages + + for _, msg := range msgs { + k, v := parseMessage(msg) + + if strings.HasPrefix(k, OldInputDataPrefix) && k[len(OldInputDataPrefix):] == key { + return v + } + } + return "" + +} + +// Redirect to the URL derived from the specified path, with specified status. +func (r *Redirect) To(location string) error { + r.c.setCanonical(HeaderLocation, location) + r.c.Status(r.status) + + return nil +} + +// Route redirects to the Route registered in the app with appropriate parameters. +// If you want to send queries or params to route, you should use config parameter. +func (r *Redirect) Route(name string, config ...RedirectConfig) error { + // Check config + cfg := RedirectConfig{} + if len(config) > 0 { + cfg = config[0] + } + + // Get location from route name + location, err := r.c.getLocationFromRoute(r.c.App().GetRoute(name), cfg.Params) + if err != nil { + return err + } + + // Flash messages + if len(r.messages) > 0 || len(r.oldInput) > 0 { + messageText := bytebufferpool.Get() + defer bytebufferpool.Put(messageText) + + // flash messages + for i, message := range r.messages { + _, _ = messageText.WriteString(message) + // when there are more messages or oldInput -> add a comma + if len(r.messages)-1 != i || (len(r.messages)-1 == i && len(r.oldInput) > 0) { + _, _ = messageText.WriteString(CookieDataSeparator) + } + } + r.messages = r.messages[:0] + + // old input data + i := 1 + for k, v := range r.oldInput { + _, _ = messageText.WriteString(OldInputDataPrefix + k + CookieDataAssigner + v) + if len(r.oldInput) != i { + _, _ = messageText.WriteString(CookieDataSeparator) + } + i++ + } + + r.c.Cookie(&Cookie{ + Name: FlashCookieName, + Value: r.c.app.getString(messageText.Bytes()), + SessionOnly: true, + }) + } + + // Check queries + if len(cfg.Queries) > 0 { + queryText := bytebufferpool.Get() + defer bytebufferpool.Put(queryText) + + i := 1 + for k, v := range cfg.Queries { + _, _ = queryText.WriteString(k + "=" + v) + + if i != len(cfg.Queries) { + _, _ = queryText.WriteString("&") + } + i++ + } + + return r.To(location + "?" + r.c.app.getString(queryText.Bytes())) + } + + return r.To(location) +} + +// Redirect back to the URL to referer. +func (r *Redirect) Back(fallback ...string) error { + location := r.c.Get(HeaderReferer) + if location == "" { + // Check fallback URL + if len(fallback) == 0 { + err := ErrRedirectBackNoFallback + r.c.Status(err.Code) + + return err + } + location = fallback[0] + } + + return r.To(location) +} + +// setFlash is a method to get flash messages before removing them +func (r *Redirect) setFlash() { + // parse flash messages + cookieValue := r.c.Cookies(FlashCookieName) + + var commaPos int + for { + commaPos = findNextNonEscapedCharsetPosition(cookieValue, []byte(CookieDataSeparator)) + if commaPos != -1 { + r.c.redirectionMessages = append(r.c.redirectionMessages, strings.Trim(cookieValue[:commaPos], " ")) + cookieValue = cookieValue[commaPos+1:] + } else { + r.c.redirectionMessages = append(r.c.redirectionMessages, strings.Trim(cookieValue, " ")) + break + } + } + + r.c.ClearCookie(FlashCookieName) +} + +func parseMessage(raw string) (key, value string) { + if i := findNextNonEscapedCharsetPosition(raw, []byte(CookieDataAssigner)); i != -1 { + return RemoveEscapeChar(raw[:i]), RemoveEscapeChar(raw[i+1:]) + } + return RemoveEscapeChar(raw), "" +} diff --git a/redirect_test.go b/redirect_test.go new file mode 100644 index 0000000000..f603c6fc43 --- /dev/null +++ b/redirect_test.go @@ -0,0 +1,528 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 📝 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package fiber + +import ( + "context" + "net" + "net/url" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/valyala/fasthttp" + "github.com/valyala/fasthttp/fasthttputil" +) + +// go test -run Test_Redirect_To +func Test_Redirect_To(t *testing.T) { + t.Parallel() + app := New() + c := app.NewCtx(&fasthttp.RequestCtx{}) + + c.Redirect().To("http://default.com") + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "http://default.com", string(c.Response().Header.Peek(HeaderLocation))) + + c.Redirect().Status(301).To("http://example.com") + require.Equal(t, 301, c.Response().StatusCode()) + require.Equal(t, "http://example.com", string(c.Response().Header.Peek(HeaderLocation))) +} + +// go test -run Test_Redirect_Route_WithParams +func Test_Redirect_Route_WithParams(t *testing.T) { + t.Parallel() + app := New() + app.Get("/user/:name", func(c Ctx) error { + return c.JSON(c.Params("name")) + }).Name("user") + c := app.NewCtx(&fasthttp.RequestCtx{}) + + c.Redirect().Route("user", RedirectConfig{ + Params: Map{ + "name": "fiber", + }, + }) + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) +} + +// go test -run Test_Redirect_Route_WithParams_WithQueries +func Test_Redirect_Route_WithParams_WithQueries(t *testing.T) { + t.Parallel() + app := New() + app.Get("/user/:name", func(c Ctx) error { + return c.JSON(c.Params("name")) + }).Name("user") + c := app.NewCtx(&fasthttp.RequestCtx{}) + + c.Redirect().Route("user", RedirectConfig{ + Params: Map{ + "name": "fiber", + }, + Queries: map[string]string{"data[0][name]": "john", "data[0][age]": "10", "test": "doe"}, + }) + require.Equal(t, 302, c.Response().StatusCode()) + // analysis of query parameters with url parsing, since a map pass is always randomly ordered + location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) + require.NoError(t, err, "url.Parse(location)") + require.Equal(t, "/user/fiber", location.Path) + require.Equal(t, url.Values{"data[0][name]": []string{"john"}, "data[0][age]": []string{"10"}, "test": []string{"doe"}}, location.Query()) +} + +// go test -run Test_Redirect_Route_WithOptionalParams +func Test_Redirect_Route_WithOptionalParams(t *testing.T) { + t.Parallel() + app := New() + app.Get("/user/:name?", func(c Ctx) error { + return c.JSON(c.Params("name")) + }).Name("user") + c := app.NewCtx(&fasthttp.RequestCtx{}) + + c.Redirect().Route("user", RedirectConfig{ + Params: Map{ + "name": "fiber", + }, + }) + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) +} + +// go test -run Test_Redirect_Route_WithOptionalParamsWithoutValue +func Test_Redirect_Route_WithOptionalParamsWithoutValue(t *testing.T) { + t.Parallel() + app := New() + app.Get("/user/:name?", func(c Ctx) error { + return c.JSON(c.Params("name")) + }).Name("user") + c := app.NewCtx(&fasthttp.RequestCtx{}) + + c.Redirect().Route("user") + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/user/", string(c.Response().Header.Peek(HeaderLocation))) +} + +// go test -run Test_Redirect_Route_WithGreedyParameters +func Test_Redirect_Route_WithGreedyParameters(t *testing.T) { + t.Parallel() + app := New() + app.Get("/user/+", func(c Ctx) error { + return c.JSON(c.Params("+")) + }).Name("user") + c := app.NewCtx(&fasthttp.RequestCtx{}) + + c.Redirect().Route("user", RedirectConfig{ + Params: Map{ + "+": "test/routes", + }, + }) + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/user/test/routes", string(c.Response().Header.Peek(HeaderLocation))) +} + +// go test -run Test_Redirect_Back +func Test_Redirect_Back(t *testing.T) { + t.Parallel() + app := New() + app.Get("/", func(c Ctx) error { + return c.JSON("Home") + }).Name("home") + c := app.NewCtx(&fasthttp.RequestCtx{}) + + c.Redirect().Back("/") + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/", string(c.Response().Header.Peek(HeaderLocation))) + + err := c.Redirect().Back() + require.Equal(t, 500, c.Response().StatusCode()) + require.ErrorAs(t, ErrRedirectBackNoFallback, &err) +} + +// go test -run Test_Redirect_Back_WithReferer +func Test_Redirect_Back_WithReferer(t *testing.T) { + t.Parallel() + app := New() + app.Get("/", func(c Ctx) error { + return c.JSON("Home") + }).Name("home") + app.Get("/back", func(c Ctx) error { + return c.JSON("Back") + }).Name("back") + c := app.NewCtx(&fasthttp.RequestCtx{}) + + c.Request().Header.Set(HeaderReferer, "/back") + c.Redirect().Back("/") + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/back", c.Get(HeaderReferer)) + require.Equal(t, "/back", string(c.Response().Header.Peek(HeaderLocation))) +} + +// go test -run Test_Redirect_Route_WithFlashMessages +func Test_Redirect_Route_WithFlashMessages(t *testing.T) { + t.Parallel() + + app := New() + app.Get("/user", func(c Ctx) error { + return c.SendString("user") + }).Name("user") + + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + + c.Redirect().With("success", "1").With("message", "test").Route("user") + + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/user", string(c.Response().Header.Peek(HeaderLocation))) + + equal := c.GetRespHeader(HeaderSetCookie) == "fiber_flash=success:1,message:test; path=/; SameSite=Lax" || c.GetRespHeader(HeaderSetCookie) == "fiber_flash=message:test,success:1; path=/; SameSite=Lax" + require.True(t, equal) + + c.Redirect().setFlash() + require.Equal(t, "fiber_flash=; expires=Tue, 10 Nov 2009 23:00:00 GMT", c.GetRespHeader(HeaderSetCookie)) +} + +// go test -run Test_Redirect_Route_WithOldInput +func Test_Redirect_Route_WithOldInput(t *testing.T) { + t.Parallel() + + app := New() + app.Get("/user", func(c Ctx) error { + return c.SendString("user") + }).Name("user") + + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + + c.Request().URI().SetQueryString("id=1&name=tom") + c.Redirect().With("success", "1").With("message", "test").WithInput().Route("user") + + require.Equal(t, 302, c.Response().StatusCode()) + require.Equal(t, "/user", string(c.Response().Header.Peek(HeaderLocation))) + + require.Contains(t, c.GetRespHeader(HeaderSetCookie), "fiber_flash=") + require.Contains(t, c.GetRespHeader(HeaderSetCookie), "success:1") + require.Contains(t, c.GetRespHeader(HeaderSetCookie), "message:test") + + require.Contains(t, c.GetRespHeader(HeaderSetCookie), ",old_input_data_id:1") + require.Contains(t, c.GetRespHeader(HeaderSetCookie), ",old_input_data_name:tom") + + c.Redirect().setFlash() + require.Equal(t, "fiber_flash=; expires=Tue, 10 Nov 2009 23:00:00 GMT", c.GetRespHeader(HeaderSetCookie)) +} + +// go test -run Test_Redirect_setFlash +func Test_Redirect_setFlash(t *testing.T) { + t.Parallel() + + app := New() + app.Get("/user", func(c Ctx) error { + return c.SendString("user") + }).Name("user") + + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + + c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") + + c.Redirect().setFlash() + + require.Equal(t, "fiber_flash=; expires=Tue, 10 Nov 2009 23:00:00 GMT", c.GetRespHeader(HeaderSetCookie)) + + require.Equal(t, "1", c.Redirect().Message("success")) + require.Equal(t, "test", c.Redirect().Message("message")) + require.Equal(t, map[string]string{"success": "1", "message": "test"}, c.Redirect().Messages()) + + require.Equal(t, "1", c.Redirect().OldInput("id")) + require.Equal(t, "tom", c.Redirect().OldInput("name")) + require.Equal(t, map[string]string{"id": "1", "name": "tom"}, c.Redirect().OldInputs()) +} + +// go test -run Test_Redirect_Request +func Test_Redirect_Request(t *testing.T) { + t.Parallel() + + app := New() + + app.Get("/", func(c Ctx) error { + return c.Redirect().With("key", "value").With("key2", "value2").With("co\\:m\\,ma", "Fi\\:ber\\, v3").Route("name") + }) + + app.Get("/with-inputs", func(c Ctx) error { + return c.Redirect().WithInput().With("key", "value").With("key2", "value2").Route("name") + }) + + app.Get("/just-inputs", func(c Ctx) error { + return c.Redirect().WithInput().Route("name") + }) + + app.Get("/redirected", func(c Ctx) error { + return c.JSON(Map{ + "messages": c.Redirect().Messages(), + "inputs": c.Redirect().OldInputs(), + }) + }).Name("name") + + // Start test server + ln := fasthttputil.NewInmemoryListener() + go func() { + ctx, cancel := context.WithTimeout(context.Background(), 250*time.Millisecond) + defer cancel() + + err := app.Listener(ln, ListenConfig{ + DisableStartupMessage: true, + GracefulContext: ctx, + }) + + require.NoError(t, err) + }() + + // Test cases + testCases := []struct { + URL string + CookieValue string + ExpectedBody string + ExpectedStatusCode int + ExceptedErrsLen int + }{ + { + URL: "/", + CookieValue: "key:value,key2:value2,co\\:m\\,ma:Fi\\:ber\\, v3", + ExpectedBody: `{"inputs":{},"messages":{"co:m,ma":"Fi:ber, v3","key":"value","key2":"value2"}}`, + ExpectedStatusCode: StatusOK, + ExceptedErrsLen: 0, + }, + { + URL: "/with-inputs?name=john&surname=doe", + CookieValue: "key:value,key2:value2,key:value,key2:value2,old_input_data_name:john,old_input_data_surname:doe", + ExpectedBody: `{"inputs":{"name":"john","surname":"doe"},"messages":{"key":"value","key2":"value2"}}`, + ExpectedStatusCode: StatusOK, + ExceptedErrsLen: 0, + }, + { + URL: "/just-inputs?name=john&surname=doe", + CookieValue: "old_input_data_name:john,old_input_data_surname:doe", + ExpectedBody: `{"inputs":{"name":"john","surname":"doe"},"messages":{}}`, + ExpectedStatusCode: StatusOK, + ExceptedErrsLen: 0, + }, + } + + for _, tc := range testCases { + a := Get("http://example.com" + tc.URL) + a.Cookie(FlashCookieName, tc.CookieValue) + a.MaxRedirectsCount(1) + a.HostClient.Dial = func(addr string) (net.Conn, error) { return ln.Dial() } + code, body, errs := a.String() + + require.Equal(t, tc.ExpectedStatusCode, code) + require.Equal(t, tc.ExpectedBody, body) + require.Equal(t, tc.ExceptedErrsLen, len(errs)) + } +} + +// go test -v -run=^$ -bench=Benchmark_Redirect_Route -benchmem -count=4 +func Benchmark_Redirect_Route(b *testing.B) { + app := New() + app.Get("/user/:name", func(c Ctx) error { + return c.JSON(c.Params("name")) + }).Name("user") + + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + c.Redirect().Route("user", RedirectConfig{ + Params: Map{ + "name": "fiber", + }, + }) + } + + require.Equal(b, 302, c.Response().StatusCode()) + require.Equal(b, "/user/fiber", string(c.Response().Header.Peek(HeaderLocation))) +} + +// go test -v -run=^$ -bench=Benchmark_Redirect_Route_WithQueries -benchmem -count=4 +func Benchmark_Redirect_Route_WithQueries(b *testing.B) { + app := New() + app.Get("/user/:name", func(c Ctx) error { + return c.JSON(c.Params("name")) + }).Name("user") + + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + c.Redirect().Route("user", RedirectConfig{ + Params: Map{ + "name": "fiber", + }, + Queries: map[string]string{"a": "a", "b": "b"}, + }) + } + + require.Equal(b, 302, c.Response().StatusCode()) + // analysis of query parameters with url parsing, since a map pass is always randomly ordered + location, err := url.Parse(string(c.Response().Header.Peek(HeaderLocation))) + require.NoError(b, err, "url.Parse(location)") + require.Equal(b, "/user/fiber", location.Path) + require.Equal(b, url.Values{"a": []string{"a"}, "b": []string{"b"}}, location.Query()) +} + +// go test -v -run=^$ -bench=Benchmark_Redirect_Route_WithFlashMessages -benchmem -count=4 +func Benchmark_Redirect_Route_WithFlashMessages(b *testing.B) { + app := New() + app.Get("/user", func(c Ctx) error { + return c.SendString("user") + }).Name("user") + + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + c.Redirect().With("success", "1").With("message", "test").Route("user") + } + + require.Equal(b, 302, c.Response().StatusCode()) + require.Equal(b, "/user", string(c.Response().Header.Peek(HeaderLocation))) + + equal := c.GetRespHeader(HeaderSetCookie) == "fiber_flash=success:1,message:test; path=/; SameSite=Lax" || c.GetRespHeader(HeaderSetCookie) == "fiber_flash=message:test,success:1; path=/; SameSite=Lax" + require.True(b, equal) + + c.Redirect().setFlash() + require.Equal(b, "fiber_flash=; expires=Tue, 10 Nov 2009 23:00:00 GMT", c.GetRespHeader(HeaderSetCookie)) +} + +// go test -v -run=^$ -bench=Benchmark_Redirect_setFlash -benchmem -count=4 +func Benchmark_Redirect_setFlash(b *testing.B) { + app := New() + app.Get("/user", func(c Ctx) error { + return c.SendString("user") + }).Name("user") + + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + + c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + c.Redirect().setFlash() + } + + require.Equal(b, "fiber_flash=; expires=Tue, 10 Nov 2009 23:00:00 GMT", c.GetRespHeader(HeaderSetCookie)) + + require.Equal(b, "1", c.Redirect().Message("success")) + require.Equal(b, "test", c.Redirect().Message("message")) + require.Equal(b, map[string]string{"success": "1", "message": "test"}, c.Redirect().Messages()) + + require.Equal(b, "1", c.Redirect().OldInput("id")) + require.Equal(b, "tom", c.Redirect().OldInput("name")) + require.Equal(b, map[string]string{"id": "1", "name": "tom"}, c.Redirect().OldInputs()) +} + +// go test -v -run=^$ -bench=Benchmark_Redirect_Messages -benchmem -count=4 +func Benchmark_Redirect_Messages(b *testing.B) { + app := New() + app.Get("/user", func(c Ctx) error { + return c.SendString("user") + }).Name("user") + + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + + c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") + c.Redirect().setFlash() + + var msgs map[string]string + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + msgs = c.Redirect().Messages() + } + + require.Equal(b, "fiber_flash=; expires=Tue, 10 Nov 2009 23:00:00 GMT", c.GetRespHeader(HeaderSetCookie)) + require.Equal(b, map[string]string{"success": "1", "message": "test"}, msgs) +} + +// go test -v -run=^$ -bench=Benchmark_Redirect_OldInputs -benchmem -count=4 +func Benchmark_Redirect_OldInputs(b *testing.B) { + app := New() + app.Get("/user", func(c Ctx) error { + return c.SendString("user") + }).Name("user") + + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + + c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") + c.Redirect().setFlash() + + var oldInputs map[string]string + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + oldInputs = c.Redirect().OldInputs() + } + + require.Equal(b, "fiber_flash=; expires=Tue, 10 Nov 2009 23:00:00 GMT", c.GetRespHeader(HeaderSetCookie)) + require.Equal(b, map[string]string{"id": "1", "name": "tom"}, oldInputs) +} + +// go test -v -run=^$ -bench=Benchmark_Redirect_Message -benchmem -count=4 +func Benchmark_Redirect_Message(b *testing.B) { + app := New() + app.Get("/user", func(c Ctx) error { + return c.SendString("user") + }).Name("user") + + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + + c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") + c.Redirect().setFlash() + + var msg string + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + msg = c.Redirect().Message("message") + } + + require.Equal(b, "fiber_flash=; expires=Tue, 10 Nov 2009 23:00:00 GMT", c.GetRespHeader(HeaderSetCookie)) + require.Equal(b, "test", msg) +} + +// go test -v -run=^$ -bench=Benchmark_Redirect_OldInput -benchmem -count=4 +func Benchmark_Redirect_OldInput(b *testing.B) { + app := New() + app.Get("/user", func(c Ctx) error { + return c.SendString("user") + }).Name("user") + + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) + + c.Request().Header.Set(HeaderCookie, "fiber_flash=success:1,message:test,old_input_data_name:tom,old_input_data_id:1") + c.Redirect().setFlash() + + var input string + + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + input = c.Redirect().OldInput("name") + } + + require.Equal(b, "fiber_flash=; expires=Tue, 10 Nov 2009 23:00:00 GMT", c.GetRespHeader(HeaderSetCookie)) + require.Equal(b, "tom", input) +} diff --git a/router.go b/router.go index 614ff93748..1d7851a63a 100644 --- a/router.go +++ b/router.go @@ -167,6 +167,11 @@ func (app *App) handler(rctx *fasthttp.RequestCtx) { return } + // check flash messages + if strings.Contains(utils.UnsafeString(c.Request().Header.RawHeaders()), FlashCookieName) { + c.Redirect().setFlash() + } + // Find match in stack _, err := app.next(c, app.newCtxFunc != nil) if err != nil { From ca6f25a89085326bce9b5b358a9029de24a1b4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Fri, 23 Sep 2022 21:19:44 +0300 Subject: [PATCH 105/141] :heavy_plus_sign: v3 (deps): move `utils` to `https://github.com/gofiber/utils` --- app.go | 2 +- bind.go | 2 +- binder/cookie.go | 2 +- binder/form.go | 2 +- binder/header.go | 2 +- binder/json.go | 2 +- binder/mapping.go | 2 +- binder/query.go | 2 +- binder/resp_header.go | 2 +- client.go | 2 +- ctx.go | 2 +- ctx_test.go | 2 +- go.mod | 1 + go.sum | 2 + helpers_test.go | 2 +- middleware/adaptor/adopter.go | 2 +- middleware/basicauth/basicauth.go | 2 +- middleware/basicauth/config.go | 2 +- middleware/cache/cache.go | 2 +- middleware/cache/cache_test.go | 2 +- middleware/cache/config.go | 2 +- middleware/csrf/config.go | 2 +- middleware/csrf/csrf_test.go | 2 +- middleware/proxy/proxy.go | 2 +- middleware/proxy/proxy_test.go | 2 +- middleware/requestid/config.go | 2 +- middleware/session/config.go | 2 +- middleware/session/session.go | 2 +- middleware/session/store.go | 2 +- redirect.go | 2 +- router.go | 2 +- router_test.go | 2 +- utils/README.md | 61 -------- utils/bytes.go | 21 --- utils/bytes_test.go | 68 --------- utils/byteseq.go | 18 --- utils/byteseq_test.go | 70 --------- utils/common.go | 155 ------------------- utils/common_test.go | 128 ---------------- utils/convert.go | 135 ----------------- utils/convert_test.go | 85 ----------- utils/http.go | 242 ------------------------------ utils/http_test.go | 142 ------------------ utils/json.go | 9 -- utils/json_test.go | 60 -------- utils/strings.go | 27 ---- utils/strings_test.go | 64 -------- utils/xml.go | 4 - utils/xml_test.go | 61 -------- 49 files changed, 33 insertions(+), 1380 deletions(-) delete mode 100644 utils/README.md delete mode 100644 utils/bytes.go delete mode 100644 utils/bytes_test.go delete mode 100644 utils/byteseq.go delete mode 100644 utils/byteseq_test.go delete mode 100644 utils/common.go delete mode 100644 utils/common_test.go delete mode 100644 utils/convert.go delete mode 100644 utils/convert_test.go delete mode 100644 utils/http.go delete mode 100644 utils/http_test.go delete mode 100644 utils/json.go delete mode 100644 utils/json_test.go delete mode 100644 utils/strings.go delete mode 100644 utils/strings_test.go delete mode 100644 utils/xml.go delete mode 100644 utils/xml_test.go diff --git a/app.go b/app.go index a4e24d1f8e..e1c0e511c6 100644 --- a/app.go +++ b/app.go @@ -24,7 +24,7 @@ import ( "sync/atomic" "time" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/fasthttp" ) diff --git a/bind.go b/bind.go index b390db2fda..147afa0b3a 100644 --- a/bind.go +++ b/bind.go @@ -2,7 +2,7 @@ package fiber import ( "github.com/gofiber/fiber/v3/binder" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" ) // An interface to register custom binders. diff --git a/binder/cookie.go b/binder/cookie.go index e761e4776c..f5550d7aef 100644 --- a/binder/cookie.go +++ b/binder/cookie.go @@ -4,7 +4,7 @@ import ( "reflect" "strings" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/fasthttp" ) diff --git a/binder/form.go b/binder/form.go index 24983ccdea..610ebed4ba 100644 --- a/binder/form.go +++ b/binder/form.go @@ -4,7 +4,7 @@ import ( "reflect" "strings" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/fasthttp" ) diff --git a/binder/header.go b/binder/header.go index 688a81136a..c94c21cb64 100644 --- a/binder/header.go +++ b/binder/header.go @@ -4,7 +4,7 @@ import ( "reflect" "strings" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/fasthttp" ) diff --git a/binder/json.go b/binder/json.go index 570a7f9b79..b3ae2e8965 100644 --- a/binder/json.go +++ b/binder/json.go @@ -1,7 +1,7 @@ package binder import ( - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" ) type jsonBinding struct{} diff --git a/binder/mapping.go b/binder/mapping.go index bec5634808..94af285f94 100644 --- a/binder/mapping.go +++ b/binder/mapping.go @@ -6,7 +6,7 @@ import ( "sync" "github.com/gofiber/fiber/v3/internal/schema" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/bytebufferpool" ) diff --git a/binder/query.go b/binder/query.go index ce62e09d0f..fe722c4147 100644 --- a/binder/query.go +++ b/binder/query.go @@ -4,7 +4,7 @@ import ( "reflect" "strings" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/fasthttp" ) diff --git a/binder/resp_header.go b/binder/resp_header.go index 2b31710d24..823109a6f0 100644 --- a/binder/resp_header.go +++ b/binder/resp_header.go @@ -4,7 +4,7 @@ import ( "reflect" "strings" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/fasthttp" ) diff --git a/client.go b/client.go index 682c6d1d9e..acc144458f 100644 --- a/client.go +++ b/client.go @@ -16,7 +16,7 @@ import ( "sync" "time" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/fasthttp" ) diff --git a/ctx.go b/ctx.go index 1b7cb8461d..65c7df67bf 100644 --- a/ctx.go +++ b/ctx.go @@ -21,7 +21,7 @@ import ( "text/template" "time" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/savsgio/dictpool" "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" diff --git a/ctx_test.go b/ctx_test.go index f0552a1106..ffca0015d3 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -25,7 +25,7 @@ import ( "time" "github.com/gofiber/fiber/v3/internal/storage/memory" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/stretchr/testify/require" "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" diff --git a/go.mod b/go.mod index 7bdc4a2efb..73eb28a2a4 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/gofiber/fiber/v3 go 1.18 require ( + github.com/gofiber/utils v1.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 diff --git a/go.sum b/go.sum index c72df004de..aa4b836c89 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gofiber/utils v1.0.0-beta.1 h1:7BntpaQqfHa6r78vf+5a5nnZc8tuPWv89PSqk/+WxIc= +github.com/gofiber/utils v1.0.0-beta.1/go.mod h1:RYennBgjLkuNtU+dxg7QgBEU8tmixFQHQ2GE1ioZlxw= 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.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= diff --git a/helpers_test.go b/helpers_test.go index 75c4afc541..56845f3895 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -10,7 +10,7 @@ import ( "testing" "time" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) diff --git a/middleware/adaptor/adopter.go b/middleware/adaptor/adopter.go index 5c34760892..0f37122cdd 100644 --- a/middleware/adaptor/adopter.go +++ b/middleware/adaptor/adopter.go @@ -10,7 +10,7 @@ import ( "net/http" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/fasthttp" "github.com/valyala/fasthttp/fasthttpadaptor" ) diff --git a/middleware/basicauth/basicauth.go b/middleware/basicauth/basicauth.go index 67a280a92c..d3349d8635 100644 --- a/middleware/basicauth/basicauth.go +++ b/middleware/basicauth/basicauth.go @@ -5,7 +5,7 @@ import ( "strings" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" ) // New creates a new middleware handler diff --git a/middleware/basicauth/config.go b/middleware/basicauth/config.go index 9a32b5ce17..6e076a3738 100644 --- a/middleware/basicauth/config.go +++ b/middleware/basicauth/config.go @@ -4,7 +4,7 @@ import ( "crypto/subtle" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" ) // Config defines the config for middleware. diff --git a/middleware/cache/cache.go b/middleware/cache/cache.go index c9253f0af8..15d7659af3 100644 --- a/middleware/cache/cache.go +++ b/middleware/cache/cache.go @@ -9,7 +9,7 @@ import ( "time" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" ) // timestampUpdatePeriod is the period which is used to check the cache expiration. diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go index 9dba9571ad..c2ba4fd9d5 100644 --- a/middleware/cache/cache_test.go +++ b/middleware/cache/cache_test.go @@ -16,7 +16,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/internal/storage/memory" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) diff --git a/middleware/cache/config.go b/middleware/cache/config.go index c41bad5087..e25d21ed12 100644 --- a/middleware/cache/config.go +++ b/middleware/cache/config.go @@ -4,7 +4,7 @@ import ( "time" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" ) // Config defines the config for middleware. diff --git a/middleware/csrf/config.go b/middleware/csrf/config.go index 994b91a6d9..c8c81129c0 100644 --- a/middleware/csrf/config.go +++ b/middleware/csrf/config.go @@ -6,7 +6,7 @@ import ( "time" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" ) // Config defines the config for middleware. diff --git a/middleware/csrf/csrf_test.go b/middleware/csrf/csrf_test.go index faf9255284..fd3e89a99b 100644 --- a/middleware/csrf/csrf_test.go +++ b/middleware/csrf/csrf_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index fe1e4114f3..3718fce2b0 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -7,7 +7,7 @@ import ( "strings" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/fasthttp" ) diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index b1debeee08..c928c9c3f5 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -12,7 +12,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/internal/tlstest" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/stretchr/testify/require" ) diff --git a/middleware/requestid/config.go b/middleware/requestid/config.go index 5c6930c30d..9909bee518 100644 --- a/middleware/requestid/config.go +++ b/middleware/requestid/config.go @@ -2,7 +2,7 @@ package requestid import ( "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" ) // Config defines the config for middleware. diff --git a/middleware/session/config.go b/middleware/session/config.go index 6e68df130c..0da7ea6bd1 100644 --- a/middleware/session/config.go +++ b/middleware/session/config.go @@ -5,7 +5,7 @@ import ( "time" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" ) // Config defines the config for middleware. diff --git a/middleware/session/session.go b/middleware/session/session.go index a45a506132..987838c66a 100644 --- a/middleware/session/session.go +++ b/middleware/session/session.go @@ -7,7 +7,7 @@ import ( "time" "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/fasthttp" ) diff --git a/middleware/session/store.go b/middleware/session/store.go index c85835e657..88db75c1f0 100644 --- a/middleware/session/store.go +++ b/middleware/session/store.go @@ -6,7 +6,7 @@ import ( "github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3/internal/storage/memory" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/fasthttp" ) diff --git a/redirect.go b/redirect.go index 25f01fde0d..e6ae673bfb 100644 --- a/redirect.go +++ b/redirect.go @@ -9,7 +9,7 @@ import ( "sync" "github.com/gofiber/fiber/v3/binder" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/bytebufferpool" ) diff --git a/router.go b/router.go index 1d7851a63a..84b187195c 100644 --- a/router.go +++ b/router.go @@ -12,7 +12,7 @@ import ( "sync/atomic" "time" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/valyala/fasthttp" ) diff --git a/router_test.go b/router_test.go index bd66430bea..e85ea801a2 100644 --- a/router_test.go +++ b/router_test.go @@ -16,7 +16,7 @@ import ( "strings" "testing" - "github.com/gofiber/fiber/v3/utils" + "github.com/gofiber/utils" "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" ) diff --git a/utils/README.md b/utils/README.md deleted file mode 100644 index 0030be7da6..0000000000 --- a/utils/README.md +++ /dev/null @@ -1,61 +0,0 @@ -A collection of common functions but with better performance, less allocations and no dependencies created for [Fiber](https://github.com/gofiber/fiber). - -```go -// go test -benchmem -run=^$ -bench=Benchmark_ -count=2 -goos: windows -goarch: amd64 -pkg: github.com/gofiber/fiber/v3/utils -cpu: AMD Ryzen 7 5800X 8-Core Processor -Benchmark_ToLowerBytes/fiber-16 51138252 22.61 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/fiber-16 52126545 22.63 ns/op 0 B/op 0 allocs/op -Benchmark_ToLowerBytes/default-16 16114736 72.76 ns/op 80 B/op 1 allocs/op -Benchmark_ToLowerBytes/default-16 16651540 73.85 ns/op 80 B/op 1 allocs/op -Benchmark_ToUpperBytes/fiber-16 52127224 22.62 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/fiber-16 54283167 22.86 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpperBytes/default-16 14060098 84.12 ns/op 80 B/op 1 allocs/op -Benchmark_ToUpperBytes/default-16 14183122 84.51 ns/op 80 B/op 1 allocs/op -Benchmark_EqualFoldBytes/fiber-16 29240264 41.22 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFoldBytes/fiber-16 28535826 40.84 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFoldBytes/default-16 7929867 150.2 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFoldBytes/default-16 7935478 149.7 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFold/fiber-16 35442768 34.25 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFold/fiber-16 35946870 34.96 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFold/default-16 8942130 133.5 ns/op 0 B/op 0 allocs/op -Benchmark_EqualFold/default-16 8977231 134.3 ns/op 0 B/op 0 allocs/op -Benchmark_UUID/fiber-16 30726213 40.57 ns/op 48 B/op 1 allocs/op -Benchmark_UUID/fiber-16 26539394 40.25 ns/op 48 B/op 1 allocs/op -Benchmark_UUID/default-16 4737199 247.5 ns/op 168 B/op 6 allocs/op -Benchmark_UUID/default-16 4603738 250.8 ns/op 168 B/op 6 allocs/op -Benchmark_ConvertToBytes/fiber-16 62450884 19.41 ns/op 0 B/op 0 allocs/op -Benchmark_ConvertToBytes/fiber-16 52123602 19.53 ns/op 0 B/op 0 allocs/op -Benchmark_UnsafeString/unsafe-16 1000000000 0.4496 ns/op 0 B/op 0 allocs/op -Benchmark_UnsafeString/unsafe-16 1000000000 0.4488 ns/op 0 B/op 0 allocs/op -Benchmark_UnsafeString/default-16 79925935 13.99 ns/op 16 B/op 1 allocs/op -Benchmark_UnsafeString/default-16 85637211 14.35 ns/op 16 B/op 1 allocs/op -Benchmark_UnsafeBytes/unsafe-16 540970148 2.214 ns/op 0 B/op 0 allocs/op -Benchmark_UnsafeBytes/unsafe-16 543356940 2.212 ns/op 0 B/op 0 allocs/op -Benchmark_UnsafeBytes/default-16 68896224 17.19 ns/op 16 B/op 1 allocs/op -Benchmark_UnsafeBytes/default-16 70560426 17.05 ns/op 16 B/op 1 allocs/op -Benchmark_ToString-16 29504036 39.57 ns/op 40 B/op 2 allocs/op -Benchmark_ToString-16 30738334 38.89 ns/op 40 B/op 2 allocs/op -Benchmark_GetMIME/fiber-16 28207086 41.84 ns/op 0 B/op 0 allocs/op -Benchmark_GetMIME/fiber-16 28165773 41.83 ns/op 0 B/op 0 allocs/op -Benchmark_GetMIME/default-16 12583132 94.04 ns/op 0 B/op 0 allocs/op -Benchmark_GetMIME/default-16 12829614 93.50 ns/op 0 B/op 0 allocs/op -Benchmark_ParseVendorSpecificContentType/vendorContentType-16 30267411 38.72 ns/op 16 B/op 1 allocs/op -Benchmark_ParseVendorSpecificContentType/vendorContentType-16 28543563 38.60 ns/op 16 B/op 1 allocs/op -Benchmark_ParseVendorSpecificContentType/defaultContentType-16 249869286 4.830 ns/op 0 B/op 0 allocs/op -Benchmark_ParseVendorSpecificContentType/defaultContentType-16 248999592 4.805 ns/op 0 B/op 0 allocs/op -Benchmark_StatusMessage/fiber-16 1000000000 0.6744 ns/op 0 B/op 0 allocs/op -Benchmark_StatusMessage/fiber-16 1000000000 0.6788 ns/op 0 B/op 0 allocs/op -Benchmark_StatusMessage/default-16 446818872 2.664 ns/op 0 B/op 0 allocs/op -Benchmark_StatusMessage/default-16 447009616 2.661 ns/op 0 B/op 0 allocs/op -Benchmark_ToUpper/fiber-16 20480331 56.50 ns/op 80 B/op 1 allocs/op -Benchmark_ToUpper/fiber-16 21541200 56.65 ns/op 80 B/op 1 allocs/op -Benchmark_ToUpper/default-16 8433409 141.2 ns/op 80 B/op 1 allocs/op -Benchmark_ToUpper/default-16 8473737 141.1 ns/op 80 B/op 1 allocs/op -Benchmark_ToLower/fiber-16 27248326 44.68 ns/op 80 B/op 1 allocs/op -Benchmark_ToLower/fiber-16 26918443 44.70 ns/op 80 B/op 1 allocs/op -Benchmark_ToLower/default-16 8447336 141.9 ns/op 80 B/op 1 allocs/op -Benchmark_ToLower/default-16 8423156 140.6 ns/op 80 B/op 1 allocs/op -``` diff --git a/utils/bytes.go b/utils/bytes.go deleted file mode 100644 index e5b548858c..0000000000 --- a/utils/bytes.go +++ /dev/null @@ -1,21 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -// ToLowerBytes converts ascii slice to lower-case in-place. -func ToLowerBytes(b []byte) []byte { - for i := 0; i < len(b); i++ { - b[i] = toLowerTable[b[i]] - } - return b -} - -// ToUpperBytes converts ascii slice to upper-case in-place. -func ToUpperBytes(b []byte) []byte { - for i := 0; i < len(b); i++ { - b[i] = toUpperTable[b[i]] - } - return b -} diff --git a/utils/bytes_test.go b/utils/bytes_test.go deleted file mode 100644 index 4dd1c5d137..0000000000 --- a/utils/bytes_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_ToLowerBytes(t *testing.T) { - t.Parallel() - - require.Equal(t, []byte("/my/name/is/:param/*"), ToLowerBytes([]byte("/MY/NAME/IS/:PARAM/*"))) - require.Equal(t, []byte("/my1/name/is/:param/*"), ToLowerBytes([]byte("/MY1/NAME/IS/:PARAM/*"))) - require.Equal(t, []byte("/my2/name/is/:param/*"), ToLowerBytes([]byte("/MY2/NAME/IS/:PARAM/*"))) - require.Equal(t, []byte("/my3/name/is/:param/*"), ToLowerBytes([]byte("/MY3/NAME/IS/:PARAM/*"))) - require.Equal(t, []byte("/my4/name/is/:param/*"), ToLowerBytes([]byte("/MY4/NAME/IS/:PARAM/*"))) -} - -func Benchmark_ToLowerBytes(b *testing.B) { - path := []byte(largeStr) - want := []byte(lowerStr) - var res []byte - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = ToLowerBytes(path) - } - require.Equal(b, bytes.Equal(want, res), true) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.ToLower(path) - } - require.Equal(b, bytes.Equal(want, res), true) - }) -} - -func Test_ToUpperBytes(t *testing.T) { - t.Parallel() - - require.Equal(t, []byte("/MY/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my/name/is/:param/*"))) - require.Equal(t, []byte("/MY1/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my1/name/is/:param/*"))) - require.Equal(t, []byte("/MY2/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my2/name/is/:param/*"))) - require.Equal(t, []byte("/MY3/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my3/name/is/:param/*"))) - require.Equal(t, []byte("/MY4/NAME/IS/:PARAM/*"), ToUpperBytes([]byte("/my4/name/is/:param/*"))) -} - -func Benchmark_ToUpperBytes(b *testing.B) { - path := []byte(largeStr) - want := []byte(upperStr) - var res []byte - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = ToUpperBytes(path) - } - require.Equal(b, want, res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.ToUpper(path) - } - require.Equal(b, want, res) - }) -} diff --git a/utils/byteseq.go b/utils/byteseq.go deleted file mode 100644 index 82932ca368..0000000000 --- a/utils/byteseq.go +++ /dev/null @@ -1,18 +0,0 @@ -package utils - -type byteSeq interface { - ~string | ~[]byte -} - -// EqualFold tests ascii strings or bytes for equality case-insensitively -func EqualFold[S byteSeq](b, s S) bool { - if len(b) != len(s) { - return false - } - for i := len(b) - 1; i >= 0; i-- { - if toUpperTable[b[i]] != toUpperTable[s[i]] { - return false - } - } - return true -} diff --git a/utils/byteseq_test.go b/utils/byteseq_test.go deleted file mode 100644 index 790329245a..0000000000 --- a/utils/byteseq_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package utils - -import ( - "bytes" - "strings" - "testing" - - "github.com/stretchr/testify/require" -) - -func Benchmark_EqualFoldBytes(b *testing.B) { - left := []byte(upperStr) - right := []byte(lowerStr) - var res bool - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = EqualFold(left, right) - } - require.True(b, res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = bytes.EqualFold(left, right) - } - require.True(b, res) - }) -} - -// go test -v -run=^$ -bench=Benchmark_EqualFold -benchmem -count=4 ./utils/ -func Benchmark_EqualFold(b *testing.B) { - var res bool - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = EqualFold(upperStr, lowerStr) - } - require.True(b, res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.EqualFold(upperStr, lowerStr) - } - require.True(b, res) - }) -} - -func Test_EqualFold(t *testing.T) { - t.Parallel() - testCases := []struct { - Expected bool - S1 string - S2 string - }{ - {Expected: true, S1: "/MY/NAME/IS/:PARAM/*", S2: "/my/name/is/:param/*"}, - {Expected: true, S1: "/MY/NAME/IS/:PARAM/*", S2: "/my/name/is/:param/*"}, - {Expected: true, S1: "/MY1/NAME/IS/:PARAM/*", S2: "/MY1/NAME/IS/:PARAM/*"}, - {Expected: false, S1: "/my2/name/is/:param/*", S2: "/my2/name"}, - {Expected: false, S1: "/dddddd", S2: "eeeeee"}, - {Expected: false, S1: "\na", S2: "*A"}, - {Expected: true, S1: "/MY3/NAME/IS/:PARAM/*", S2: "/my3/name/is/:param/*"}, - {Expected: true, S1: "/MY4/NAME/IS/:PARAM/*", S2: "/my4/nAME/IS/:param/*"}, - } - - for _, tc := range testCases { - res := EqualFold[string](tc.S1, tc.S2) - require.Equal(t, tc.Expected, res, "string") - - res = EqualFold[[]byte]([]byte(tc.S1), []byte(tc.S2)) - require.Equal(t, tc.Expected, res, "bytes") - } -} diff --git a/utils/common.go b/utils/common.go deleted file mode 100644 index 3fa4b05045..0000000000 --- a/utils/common.go +++ /dev/null @@ -1,155 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import ( - "bytes" - "crypto/rand" - "encoding/binary" - "encoding/hex" - "math" - "net" - "os" - "reflect" - "runtime" - "strconv" - "sync" - "sync/atomic" - "unicode" - - "github.com/google/uuid" -) - -const ( - toLowerTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" - toUpperTable = "\x00\x01\x02\x03\x04\x05\x06\a\b\t\n\v\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~\u007f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" -) - -// Copyright © 2014, Roger Peppe -// github.com/rogpeppe/fastuuid -// All rights reserved. - -var ( - uuidSeed [24]byte - uuidCounter uint64 - uuidSetup sync.Once - unitsSlice = []byte("kmgtp") -) - -// UUID generates an universally unique identifier (UUID) -func UUID() string { - // Setup seed & counter once - uuidSetup.Do(func() { - if _, err := rand.Read(uuidSeed[:]); err != nil { - return - } - uuidCounter = binary.LittleEndian.Uint64(uuidSeed[:8]) - }) - if atomic.LoadUint64(&uuidCounter) <= 0 { - return "00000000-0000-0000-0000-000000000000" - } - // first 8 bytes differ, taking a slice of the first 16 bytes - x := atomic.AddUint64(&uuidCounter, 1) - uuid := uuidSeed - binary.LittleEndian.PutUint64(uuid[:8], x) - uuid[6], uuid[9] = uuid[9], uuid[6] - - // RFC4122 v4 - uuid[6] = (uuid[6] & 0x0f) | 0x40 - uuid[8] = uuid[8]&0x3f | 0x80 - - // create UUID representation of the first 128 bits - b := make([]byte, 36) - hex.Encode(b[0:8], uuid[0:4]) - b[8] = '-' - hex.Encode(b[9:13], uuid[4:6]) - b[13] = '-' - hex.Encode(b[14:18], uuid[6:8]) - b[18] = '-' - hex.Encode(b[19:23], uuid[8:10]) - b[23] = '-' - hex.Encode(b[24:], uuid[10:16]) - - return UnsafeString(b) -} - -// UUIDv4 returns a Random (Version 4) UUID. -// The strength of the UUIDs is based on the strength of the crypto/rand package. -func UUIDv4() string { - token, err := uuid.NewRandom() - if err != nil { - return UUID() - } - return token.String() -} - -// FunctionName returns function name -func FunctionName(fn any) string { - t := reflect.ValueOf(fn).Type() - if t.Kind() == reflect.Func { - return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name() - } - return t.String() -} - -// GetArgument check if key is in arguments -func GetArgument(arg string) bool { - for i := range os.Args[1:] { - if os.Args[1:][i] == arg { - return true - } - } - return false -} - -// IncrementIPRange Find available next IP address -func IncrementIPRange(ip net.IP) { - for j := len(ip) - 1; j >= 0; j-- { - ip[j]++ - if ip[j] > 0 { - break - } - } -} - -// ConvertToBytes returns integer size of bytes from human-readable string, ex. 42kb, 42M -// Returns 0 if string is unrecognized -func ConvertToBytes(humanReadableString string) int { - strLen := len(humanReadableString) - if strLen == 0 { - return 0 - } - var unitPrefixPos, lastNumberPos int - // loop the string - for i := strLen - 1; i >= 0; i-- { - // check if the char is a number - if unicode.IsDigit(rune(humanReadableString[i])) { - lastNumberPos = i - break - } else if humanReadableString[i] != ' ' { - unitPrefixPos = i - } - } - - if lastNumberPos < 0 { - return 0 - } - // fetch the number part and parse it to float - size, err := strconv.ParseFloat(humanReadableString[:lastNumberPos+1], 64) - if err != nil { - return 0 - } - - // check the multiplier from the string and use it - if unitPrefixPos > 0 { - // convert multiplier char to lowercase and check if exists in units slice - index := bytes.IndexByte(unitsSlice, toLowerTable[humanReadableString[unitPrefixPos]]) - if index != -1 { - size *= math.Pow(1000, float64(index+1)) - } - } - - return int(size) -} diff --git a/utils/common_test.go b/utils/common_test.go deleted file mode 100644 index 4c427df5af..0000000000 --- a/utils/common_test.go +++ /dev/null @@ -1,128 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import ( - "crypto/rand" - "fmt" - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_FunctionName(t *testing.T) { - t.Parallel() - require.Equal(t, "github.com/gofiber/fiber/v3/utils.Test_UUID", FunctionName(Test_UUID)) - - require.Equal(t, "github.com/gofiber/fiber/v3/utils.Test_FunctionName.func1", FunctionName(func() {})) - - dummyint := 20 - require.Equal(t, "int", FunctionName(dummyint)) -} - -func Test_UUID(t *testing.T) { - t.Parallel() - res := UUID() - require.Equal(t, 36, len(res)) - require.True(t, res != "00000000-0000-0000-0000-000000000000") -} - -func Test_UUID_Concurrency(t *testing.T) { - t.Parallel() - iterations := 1000 - var res string - ch := make(chan string, iterations) - results := make(map[string]string) - for i := 0; i < iterations; i++ { - go func() { - ch <- UUID() - }() - } - for i := 0; i < iterations; i++ { - res = <-ch - results[res] = res - } - require.Equal(t, iterations, len(results)) -} - -func Test_UUIDv4(t *testing.T) { - t.Parallel() - res := UUIDv4() - require.Equal(t, 36, len(res)) - require.True(t, res != "00000000-0000-0000-0000-000000000000") -} - -func Test_UUIDv4_Concurrency(t *testing.T) { - t.Parallel() - iterations := 1000 - var res string - ch := make(chan string, iterations) - results := make(map[string]string) - for i := 0; i < iterations; i++ { - go func() { - ch <- UUIDv4() - }() - } - for i := 0; i < iterations; i++ { - res = <-ch - results[res] = res - } - require.Equal(t, iterations, len(results)) -} - -// go test -v -run=^$ -bench=Benchmark_UUID -benchmem -count=2 - -func Benchmark_UUID(b *testing.B) { - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = UUID() - } - require.Equal(b, 36, len(res)) - }) - b.Run("default", func(b *testing.B) { - rnd := make([]byte, 16) - _, _ = rand.Read(rnd) - for n := 0; n < b.N; n++ { - res = fmt.Sprintf("%x-%x-%x-%x-%x", rnd[0:4], rnd[4:6], rnd[6:8], rnd[8:10], rnd[10:]) - } - require.Equal(b, 36, len(res)) - }) -} - -func Test_ConvertToBytes(t *testing.T) { - t.Parallel() - require.Equal(t, 0, ConvertToBytes("")) - require.Equal(t, 42, ConvertToBytes("42")) - require.Equal(t, 42, ConvertToBytes("42b")) - require.Equal(t, 42, ConvertToBytes("42B")) - require.Equal(t, 42, ConvertToBytes("42 b")) - require.Equal(t, 42, ConvertToBytes("42 B")) - - require.Equal(t, 42*1000, ConvertToBytes("42k")) - require.Equal(t, 42*1000, ConvertToBytes("42K")) - require.Equal(t, 42*1000, ConvertToBytes("42kb")) - require.Equal(t, 42*1000, ConvertToBytes("42KB")) - require.Equal(t, 42*1000, ConvertToBytes("42 kb")) - require.Equal(t, 42*1000, ConvertToBytes("42 KB")) - - require.Equal(t, 42*1000000, ConvertToBytes("42M")) - require.Equal(t, int(42.5*1000000), ConvertToBytes("42.5MB")) - require.Equal(t, 42*1000000000, ConvertToBytes("42G")) - - require.Equal(t, 0, ConvertToBytes("string")) - require.Equal(t, 0, ConvertToBytes("MB")) -} - -// go test -v -run=^$ -bench=Benchmark_ConvertToBytes -benchmem -count=2 -func Benchmark_ConvertToBytes(b *testing.B) { - var res int - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = ConvertToBytes("42B") - } - require.Equal(b, 42, res) - }) -} diff --git a/utils/convert.go b/utils/convert.go deleted file mode 100644 index 9b456f1ef6..0000000000 --- a/utils/convert.go +++ /dev/null @@ -1,135 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import ( - "fmt" - "reflect" - "strconv" - "strings" - "time" - "unsafe" -) - -// #nosec G103 -// UnsafeString returns a string pointer without allocation -func UnsafeString(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) -} - -// #nosec G103 -// UnsafeBytes returns a byte pointer without allocation -func UnsafeBytes(s string) (bs []byte) { - sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) - bh := (*reflect.SliceHeader)(unsafe.Pointer(&bs)) - bh.Data = sh.Data - bh.Len = sh.Len - bh.Cap = sh.Len - return -} - -// CopyString copies a string to make it immutable -func CopyString(s string) string { - return string(UnsafeBytes(s)) -} - -// CopyBytes copies a slice to make it immutable -func CopyBytes(b []byte) []byte { - tmp := make([]byte, len(b)) - copy(tmp, b) - return tmp -} - -const ( - uByte = 1 << (10 * iota) - uKilobyte - uMegabyte - uGigabyte - uTerabyte - uPetabyte - uExabyte -) - -// ByteSize returns a human-readable byte string of the form 10M, 12.5K, and so forth. -// The unit that results in the smallest number greater than or equal to 1 is always chosen. -func ByteSize(bytes uint64) string { - unit := "" - value := float64(bytes) - switch { - case bytes >= uExabyte: - unit = "EB" - value /= uExabyte - case bytes >= uPetabyte: - unit = "PB" - value /= uPetabyte - case bytes >= uTerabyte: - unit = "TB" - value /= uTerabyte - case bytes >= uGigabyte: - unit = "GB" - value /= uGigabyte - case bytes >= uMegabyte: - unit = "MB" - value /= uMegabyte - case bytes >= uKilobyte: - unit = "KB" - value /= uKilobyte - case bytes >= uByte: - unit = "B" - default: - return "0B" - } - result := strconv.FormatFloat(value, 'f', 1, 64) - result = strings.TrimSuffix(result, ".0") - return result + unit -} - -// ToString Change arg to string -func ToString(arg any, timeFormat ...string) string { - var tmp = reflect.Indirect(reflect.ValueOf(arg)).Interface() - switch v := tmp.(type) { - case int: - return strconv.Itoa(v) - case int8: - return strconv.FormatInt(int64(v), 10) - case int16: - return strconv.FormatInt(int64(v), 10) - case int32: - return strconv.FormatInt(int64(v), 10) - case int64: - return strconv.FormatInt(v, 10) - case uint: - return strconv.Itoa(int(v)) - case uint8: - return strconv.FormatInt(int64(v), 10) - case uint16: - return strconv.FormatInt(int64(v), 10) - case uint32: - return strconv.FormatInt(int64(v), 10) - case uint64: - return strconv.FormatInt(int64(v), 10) - case string: - return v - case []byte: - return string(v) - case bool: - return strconv.FormatBool(v) - case float32: - return strconv.FormatFloat(float64(v), 'f', -1, 32) - case float64: - return strconv.FormatFloat(v, 'f', -1, 64) - case time.Time: - if len(timeFormat) > 0 { - return v.Format(timeFormat[0]) - } - return v.Format("2006-01-02 15:04:05") - case reflect.Value: - return ToString(v.Interface(), timeFormat...) - case fmt.Stringer: - return v.String() - default: - return "" - } -} diff --git a/utils/convert_test.go b/utils/convert_test.go deleted file mode 100644 index 722d53f18f..0000000000 --- a/utils/convert_test.go +++ /dev/null @@ -1,85 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_UnsafeString(t *testing.T) { - t.Parallel() - res := UnsafeString([]byte("Hello, World!")) - require.Equal(t, "Hello, World!", res) -} - -// go test -v -run=^$ -bench=UnsafeString -benchmem -count=2 - -func Benchmark_UnsafeString(b *testing.B) { - hello := []byte("Hello, World!") - var res string - b.Run("unsafe", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = UnsafeString(hello) - } - require.Equal(b, "Hello, World!", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = string(hello) - } - require.Equal(b, "Hello, World!", res) - }) -} - -func Test_UnsafeBytes(t *testing.T) { - t.Parallel() - res := UnsafeBytes("Hello, World!") - require.Equal(t, []byte("Hello, World!"), res) -} - -// go test -v -run=^$ -bench=UnsafeBytes -benchmem -count=4 - -func Benchmark_UnsafeBytes(b *testing.B) { - hello := "Hello, World!" - var res []byte - b.Run("unsafe", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = UnsafeBytes(hello) - } - require.Equal(b, []byte("Hello, World!"), res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = []byte(hello) - } - require.Equal(b, []byte("Hello, World!"), res) - }) -} - -func Test_CopyString(t *testing.T) { - t.Parallel() - res := CopyString("Hello, World!") - require.Equal(t, "Hello, World!", res) -} - -func Test_ToString(t *testing.T) { - t.Parallel() - res := ToString([]byte("Hello, World!")) - require.Equal(t, "Hello, World!", res) - res = ToString(true) - require.Equal(t, "true", res) - res = ToString(uint(100)) - require.Equal(t, "100", res) -} - -// go test -v -run=^$ -bench=ToString -benchmem -count=2 -func Benchmark_ToString(b *testing.B) { - hello := []byte("Hello, World!") - for n := 0; n < b.N; n++ { - ToString(hello) - } -} diff --git a/utils/http.go b/utils/http.go deleted file mode 100644 index 61eda1172a..0000000000 --- a/utils/http.go +++ /dev/null @@ -1,242 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import "strings" - -const MIMEOctetStream = "application/octet-stream" - -// GetMIME returns the content-type of a file extension -func GetMIME(extension string) (mime string) { - if len(extension) == 0 { - return mime - } - if extension[0] == '.' { - mime = mimeExtensions[extension[1:]] - } else { - mime = mimeExtensions[extension] - } - if len(mime) == 0 { - return MIMEOctetStream - } - return mime -} - -// ParseVendorSpecificContentType check if content type is vendor specific and -// if it is parsable to any known types. If its not vendor specific then returns -// the original content type. -func ParseVendorSpecificContentType(cType string) string { - plusIndex := strings.Index(cType, "+") - - if plusIndex == -1 { - return cType - } - - var parsableType string - if semiColonIndex := strings.Index(cType, ";"); semiColonIndex == -1 { - parsableType = cType[plusIndex+1:] - } else if plusIndex < semiColonIndex { - parsableType = cType[plusIndex+1 : semiColonIndex] - } else { - return cType[:semiColonIndex] - } - - slashIndex := strings.Index(cType, "/") - - if slashIndex == -1 { - return cType - } - - return cType[0:slashIndex+1] + parsableType -} - -// limits for HTTP statuscodes -const ( - statusMessageMin = 100 - statusMessageMax = 511 -) - -// StatusMessage returns the correct message for the provided HTTP statuscode -func StatusMessage(status int) string { - if status < statusMessageMin || status > statusMessageMax { - return "" - } - return statusMessage[status] -} - -// HTTP status codes were copied from net/http. -var statusMessage = []string{ - 100: "Continue", - 101: "Switching Protocols", - 102: "Processing", - 103: "Early Hints", - 200: "OK", - 201: "Created", - 202: "Accepted", - 203: "Non-Authoritative Information", - 204: "No Content", - 205: "Reset Content", - 206: "Partial Content", - 207: "Multi-Status", - 208: "Already Reported", - 226: "IM Used", - 300: "Multiple Choices", - 301: "Moved Permanently", - 302: "Found", - 303: "See Other", - 304: "Not Modified", - 305: "Use Proxy", - 306: "Switch Proxy", - 307: "Temporary Redirect", - 308: "Permanent Redirect", - 400: "Bad Request", - 401: "Unauthorized", - 402: "Payment Required", - 403: "Forbidden", - 404: "Not Found", - 405: "Method Not Allowed", - 406: "Not Acceptable", - 407: "Proxy Authentication Required", - 408: "Request Timeout", - 409: "Conflict", - 410: "Gone", - 411: "Length Required", - 412: "Precondition Failed", - 413: "Request Entity Too Large", - 414: "Request URI Too Long", - 415: "Unsupported Media Type", - 416: "Requested Range Not Satisfiable", - 417: "Expectation Failed", - 418: "I'm a teapot", - 421: "Misdirected Request", - 422: "Unprocessable Entity", - 423: "Locked", - 424: "Failed Dependency", - 426: "Upgrade Required", - 428: "Precondition Required", - 429: "Too Many Requests", - 431: "Request Header Fields Too Large", - 451: "Unavailable For Legal Reasons", - 500: "Internal Server Error", - 501: "Not Implemented", - 502: "Bad Gateway", - 503: "Service Unavailable", - 504: "Gateway Timeout", - 505: "HTTP Version Not Supported", - 506: "Variant Also Negotiates", - 507: "Insufficient Storage", - 508: "Loop Detected", - 510: "Not Extended", - 511: "Network Authentication Required", -} - -// MIME types were copied from https://github.com/nginx/nginx/blob/master/conf/mime.types -var mimeExtensions = map[string]string{ - "html": "text/html", - "htm": "text/html", - "shtml": "text/html", - "css": "text/css", - "gif": "image/gif", - "jpeg": "image/jpeg", - "jpg": "image/jpeg", - "xml": "application/xml", - "js": "application/javascript", - "atom": "application/atom+xml", - "rss": "application/rss+xml", - "mml": "text/mathml", - "txt": "text/plain", - "jad": "text/vnd.sun.j2me.app-descriptor", - "wml": "text/vnd.wap.wml", - "htc": "text/x-component", - "png": "image/png", - "svg": "image/svg+xml", - "svgz": "image/svg+xml", - "tif": "image/tiff", - "tiff": "image/tiff", - "wbmp": "image/vnd.wap.wbmp", - "webp": "image/webp", - "ico": "image/x-icon", - "jng": "image/x-jng", - "bmp": "image/x-ms-bmp", - "woff": "font/woff", - "woff2": "font/woff2", - "jar": "application/java-archive", - "war": "application/java-archive", - "ear": "application/java-archive", - "json": "application/json", - "hqx": "application/mac-binhex40", - "doc": "application/msword", - "pdf": "application/pdf", - "ps": "application/postscript", - "eps": "application/postscript", - "ai": "application/postscript", - "rtf": "application/rtf", - "m3u8": "application/vnd.apple.mpegurl", - "kml": "application/vnd.google-earth.kml+xml", - "kmz": "application/vnd.google-earth.kmz", - "xls": "application/vnd.ms-excel", - "eot": "application/vnd.ms-fontobject", - "ppt": "application/vnd.ms-powerpoint", - "odg": "application/vnd.oasis.opendocument.graphics", - "odp": "application/vnd.oasis.opendocument.presentation", - "ods": "application/vnd.oasis.opendocument.spreadsheet", - "odt": "application/vnd.oasis.opendocument.text", - "wmlc": "application/vnd.wap.wmlc", - "7z": "application/x-7z-compressed", - "cco": "application/x-cocoa", - "jardiff": "application/x-java-archive-diff", - "jnlp": "application/x-java-jnlp-file", - "run": "application/x-makeself", - "pl": "application/x-perl", - "pm": "application/x-perl", - "prc": "application/x-pilot", - "pdb": "application/x-pilot", - "rar": "application/x-rar-compressed", - "rpm": "application/x-redhat-package-manager", - "sea": "application/x-sea", - "swf": "application/x-shockwave-flash", - "sit": "application/x-stuffit", - "tcl": "application/x-tcl", - "tk": "application/x-tcl", - "der": "application/x-x509-ca-cert", - "pem": "application/x-x509-ca-cert", - "crt": "application/x-x509-ca-cert", - "xpi": "application/x-xpinstall", - "xhtml": "application/xhtml+xml", - "xspf": "application/xspf+xml", - "zip": "application/zip", - "bin": "application/octet-stream", - "exe": "application/octet-stream", - "dll": "application/octet-stream", - "deb": "application/octet-stream", - "dmg": "application/octet-stream", - "iso": "application/octet-stream", - "img": "application/octet-stream", - "msi": "application/octet-stream", - "msp": "application/octet-stream", - "msm": "application/octet-stream", - "mid": "audio/midi", - "midi": "audio/midi", - "kar": "audio/midi", - "mp3": "audio/mpeg", - "ogg": "audio/ogg", - "m4a": "audio/x-m4a", - "ra": "audio/x-realaudio", - "3gpp": "video/3gpp", - "3gp": "video/3gpp", - "ts": "video/mp2t", - "mp4": "video/mp4", - "mpeg": "video/mpeg", - "mpg": "video/mpeg", - "mov": "video/quicktime", - "webm": "video/webm", - "flv": "video/x-flv", - "m4v": "video/x-m4v", - "mng": "video/x-mng", - "asx": "video/x-ms-asf", - "asf": "video/x-ms-asf", - "wmv": "video/x-ms-wmv", - "avi": "video/x-msvideo", -} diff --git a/utils/http_test.go b/utils/http_test.go deleted file mode 100644 index e06e89057b..0000000000 --- a/utils/http_test.go +++ /dev/null @@ -1,142 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import ( - "mime" - "net/http" - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_GetMIME(t *testing.T) { - t.Parallel() - res := GetMIME(".json") - require.Equal(t, "application/json", res) - - res = GetMIME(".xml") - require.Equal(t, "application/xml", res) - - res = GetMIME("xml") - require.Equal(t, "application/xml", res) - - res = GetMIME("unknown") - require.Equal(t, MIMEOctetStream, res) - // empty case - res = GetMIME("") - require.Equal(t, "", res) -} - -// go test -v -run=^$ -bench=Benchmark_GetMIME -benchmem -count=2 -func Benchmark_GetMIME(b *testing.B) { - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = GetMIME(".xml") - res = GetMIME(".txt") - res = GetMIME(".png") - res = GetMIME(".exe") - res = GetMIME(".json") - } - require.Equal(b, "application/json", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = mime.TypeByExtension(".xml") - res = mime.TypeByExtension(".txt") - res = mime.TypeByExtension(".png") - res = mime.TypeByExtension(".exe") - res = mime.TypeByExtension(".json") - } - require.Equal(b, "application/json", res) - }) -} - -func Test_ParseVendorSpecificContentType(t *testing.T) { - t.Parallel() - - cType := ParseVendorSpecificContentType("application/json") - require.Equal(t, "application/json", cType) - - cType = ParseVendorSpecificContentType("multipart/form-data; boundary=dart-http-boundary-ZnVy.ICWq+7HOdsHqWxCFa8g3D.KAhy+Y0sYJ_lBADypu8po3_X") - require.Equal(t, "multipart/form-data", cType) - - cType = ParseVendorSpecificContentType("multipart/form-data") - require.Equal(t, "multipart/form-data", cType) - - cType = ParseVendorSpecificContentType("application/vnd.api+json; version=1") - require.Equal(t, "application/json", cType) - - cType = ParseVendorSpecificContentType("application/vnd.api+json") - require.Equal(t, "application/json", cType) - - cType = ParseVendorSpecificContentType("application/vnd.dummy+x-www-form-urlencoded") - require.Equal(t, "application/x-www-form-urlencoded", cType) - - cType = ParseVendorSpecificContentType("something invalid") - require.Equal(t, "something invalid", cType) -} - -func Benchmark_ParseVendorSpecificContentType(b *testing.B) { - var cType string - b.Run("vendorContentType", func(b *testing.B) { - for n := 0; n < b.N; n++ { - cType = ParseVendorSpecificContentType("application/vnd.api+json; version=1") - } - require.Equal(b, "application/json", cType) - }) - - b.Run("defaultContentType", func(b *testing.B) { - for n := 0; n < b.N; n++ { - cType = ParseVendorSpecificContentType("application/json") - } - require.Equal(b, "application/json", cType) - }) -} - -func Test_StatusMessage(t *testing.T) { - t.Parallel() - res := StatusMessage(204) - require.Equal(t, "No Content", res) - - res = StatusMessage(404) - require.Equal(t, "Not Found", res) - - res = StatusMessage(426) - require.Equal(t, "Upgrade Required", res) - - res = StatusMessage(511) - require.Equal(t, "Network Authentication Required", res) - - res = StatusMessage(1337) - require.Equal(t, "", res) - - res = StatusMessage(-1) - require.Equal(t, "", res) - - res = StatusMessage(0) - require.Equal(t, "", res) - - res = StatusMessage(600) - require.Equal(t, "", res) -} - -// go test -run=^$ -bench=Benchmark_StatusMessage -benchmem -count=2 -func Benchmark_StatusMessage(b *testing.B) { - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = StatusMessage(http.StatusNotExtended) - } - require.Equal(b, "Not Extended", res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = http.StatusText(http.StatusNotExtended) - } - require.Equal(b, "Not Extended", res) - }) -} diff --git a/utils/json.go b/utils/json.go deleted file mode 100644 index 361a7596b8..0000000000 --- a/utils/json.go +++ /dev/null @@ -1,9 +0,0 @@ -package utils - -// JSONMarshal returns the JSON encoding of v. -type JSONMarshal func(v any) ([]byte, error) - -// JSONUnmarshal parses the JSON-encoded data and stores the result -// in the value pointed to by v. If v is nil or not a pointer, -// Unmarshal returns an InvalidUnmarshalError. -type JSONUnmarshal func(data []byte, v any) error diff --git a/utils/json_test.go b/utils/json_test.go deleted file mode 100644 index 3c756aca24..0000000000 --- a/utils/json_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package utils - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/require" -) - -type sampleStructure struct { - ImportantString string `json:"important_string"` -} - -func Test_GolangJSONEncoder(t *testing.T) { - t.Parallel() - - var ( - ss = &sampleStructure{ - ImportantString: "Hello World", - } - importantString = `{"important_string":"Hello World"}` - jsonEncoder JSONMarshal = json.Marshal - ) - - raw, err := jsonEncoder(ss) - require.NoError(t, err) - - require.Equal(t, string(raw), importantString) -} - -func Test_DefaultJSONEncoder(t *testing.T) { - t.Parallel() - - var ( - ss = &sampleStructure{ - ImportantString: "Hello World", - } - importantString = `{"important_string":"Hello World"}` - jsonEncoder JSONMarshal = json.Marshal - ) - - raw, err := jsonEncoder(ss) - require.NoError(t, err) - - require.Equal(t, string(raw), importantString) -} - -func Test_DefaultJSONDecoder(t *testing.T) { - t.Parallel() - - var ( - ss sampleStructure - importantString = []byte(`{"important_string":"Hello World"}`) - jsonDecoder JSONUnmarshal = json.Unmarshal - ) - - err := jsonDecoder(importantString, &ss) - require.NoError(t, err) - require.Equal(t, "Hello World", ss.ImportantString) -} diff --git a/utils/strings.go b/utils/strings.go deleted file mode 100644 index 364c25f5c9..0000000000 --- a/utils/strings.go +++ /dev/null @@ -1,27 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -// ToLower converts ascii string to lower-case -func ToLower(b string) string { - res := make([]byte, len(b)) - copy(res, b) - for i := 0; i < len(res); i++ { - res[i] = toLowerTable[res[i]] - } - - return UnsafeString(res) -} - -// ToUpper converts ascii string to upper-case -func ToUpper(b string) string { - res := make([]byte, len(b)) - copy(res, b) - for i := 0; i < len(res); i++ { - res[i] = toUpperTable[res[i]] - } - - return UnsafeString(res) -} diff --git a/utils/strings_test.go b/utils/strings_test.go deleted file mode 100644 index 37abc96efb..0000000000 --- a/utils/strings_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 🤖 Github Repository: https://github.com/gofiber/fiber -// 📌 API Documentation: https://docs.gofiber.io - -package utils - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/require" -) - -func Test_ToUpper(t *testing.T) { - t.Parallel() - require.Equal(t, "/MY/NAME/IS/:PARAM/*", ToUpper("/my/name/is/:param/*")) -} - -const ( - largeStr = "/RePos/GoFiBer/FibEr/iSsues/187643/CoMmEnts/RePos/GoFiBer/FibEr/iSsues/CoMmEnts" - upperStr = "/REPOS/GOFIBER/FIBER/ISSUES/187643/COMMENTS/REPOS/GOFIBER/FIBER/ISSUES/COMMENTS" - lowerStr = "/repos/gofiber/fiber/issues/187643/comments/repos/gofiber/fiber/issues/comments" -) - -func Benchmark_ToUpper(b *testing.B) { - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = ToUpper(largeStr) - } - require.Equal(b, upperStr, res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.ToUpper(largeStr) - } - require.Equal(b, upperStr, res) - }) -} - -func Test_ToLower(t *testing.T) { - t.Parallel() - require.Equal(t, "/my/name/is/:param/*", ToLower("/MY/NAME/IS/:PARAM/*")) - require.Equal(t, "/my1/name/is/:param/*", ToLower("/MY1/NAME/IS/:PARAM/*")) - require.Equal(t, "/my2/name/is/:param/*", ToLower("/MY2/NAME/IS/:PARAM/*")) - require.Equal(t, "/my3/name/is/:param/*", ToLower("/MY3/NAME/IS/:PARAM/*")) - require.Equal(t, "/my4/name/is/:param/*", ToLower("/MY4/NAME/IS/:PARAM/*")) -} - -func Benchmark_ToLower(b *testing.B) { - var res string - b.Run("fiber", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = ToLower(largeStr) - } - require.Equal(b, lowerStr, res) - }) - b.Run("default", func(b *testing.B) { - for n := 0; n < b.N; n++ { - res = strings.ToLower(largeStr) - } - require.Equal(b, lowerStr, res) - }) -} diff --git a/utils/xml.go b/utils/xml.go deleted file mode 100644 index 9cc23512b0..0000000000 --- a/utils/xml.go +++ /dev/null @@ -1,4 +0,0 @@ -package utils - -// XMLMarshal returns the XML encoding of v. -type XMLMarshal func(v any) ([]byte, error) diff --git a/utils/xml_test.go b/utils/xml_test.go deleted file mode 100644 index e4699e68f5..0000000000 --- a/utils/xml_test.go +++ /dev/null @@ -1,61 +0,0 @@ -package utils - -import ( - "encoding/xml" - "testing" - - "github.com/stretchr/testify/require" -) - -type serversXMLStructure struct { - XMLName xml.Name `xml:"servers"` - Version string `xml:"version,attr"` - Servers []serverXMLStructure `xml:"server"` -} - -type serverXMLStructure struct { - XMLName xml.Name `xml:"server"` - Name string `xml:"name"` -} - -var xmlString = `fiber onefiber two` - -func Test_GolangXMLEncoder(t *testing.T) { - t.Parallel() - - var ( - ss = &serversXMLStructure{ - Version: "1", - Servers: []serverXMLStructure{ - {Name: "fiber one"}, - {Name: "fiber two"}, - }, - } - xmlEncoder XMLMarshal = xml.Marshal - ) - - raw, err := xmlEncoder(ss) - require.NoError(t, err) - - require.Equal(t, string(raw), xmlString) -} - -func Test_DefaultXMLEncoder(t *testing.T) { - t.Parallel() - - var ( - ss = &serversXMLStructure{ - Version: "1", - Servers: []serverXMLStructure{ - {Name: "fiber one"}, - {Name: "fiber two"}, - }, - } - xmlEncoder XMLMarshal = xml.Marshal - ) - - raw, err := xmlEncoder(ss) - require.NoError(t, err) - - require.Equal(t, string(raw), xmlString) -} From 14a5f61c2c01158e30e81ed64cd8b31f353200bf Mon Sep 17 00:00:00 2001 From: kinggo Date: Sun, 25 Sep 2022 14:32:03 +0800 Subject: [PATCH 106/141] :white_check_mark: test: fix Test_Ctx_ParamParser route param (#2119) test: fix Test_Ctx_ParamParser route param --- ctx_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ctx_test.go b/ctx_test.go index efbd7b1418..f1bcebfd17 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -431,7 +431,7 @@ func Test_Ctx_BodyParser(t *testing.T) { func Test_Ctx_ParamParser(t *testing.T) { t.Parallel() app := New() - app.Get("/test1/userId/role/:roleId", func(ctx *Ctx) error { + app.Get("/test1/:userId/role/:roleId", func(ctx *Ctx) error { type Demo struct { UserID uint `params:"userId"` RoleID uint `params:"roleId"` From 01cfc64f1c959a871e5d579286afe932a29c5036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Mon, 26 Sep 2022 13:14:53 +0200 Subject: [PATCH 107/141] prevent v3 from affecting v2 benchmarks --- .github/workflows/benchmark.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 3a8ee2bfef..9cc328eed5 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -16,7 +16,8 @@ jobs: uses: actions/cache@v3 with: path: ./cache - key: ${{ runner.os }}-benchmark + # TODO: reactivate it later -> when v3 is the stable one + key: ${{ runner.os }}-benchmark-v3 - name: Save Benchmark Results uses: rhysd/github-action-benchmark@v1 with: @@ -25,4 +26,5 @@ jobs: github-token: ${{ secrets.BENCHMARK_TOKEN }} fail-on-alert: true comment-on-alert: true - auto-push: true + # TODO: reactivate it later -> when v3 is the stable one + auto-push: false From d1778f381ab1ef17d93d1e57d8e771adbf747519 Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 26 Sep 2022 13:20:25 +0200 Subject: [PATCH 108/141] prepare release v2.38.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 033de56cdf..266f218eb7 100644 --- a/app.go +++ b/app.go @@ -30,7 +30,7 @@ import ( ) // Version of current fiber package -const Version = "2.37.1" +const Version = "2.38.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From cc1e9bf115a296924561a7cd041c5735f243237f Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 26 Sep 2022 13:21:16 +0200 Subject: [PATCH 109/141] prepare release v2.38.1 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 266f218eb7..93403b7d1f 100644 --- a/app.go +++ b/app.go @@ -30,7 +30,7 @@ import ( ) // Version of current fiber package -const Version = "2.38.0" +const Version = "2.38.1" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From ff348b5e92de1acd100d6042f59b3635211148f5 Mon Sep 17 00:00:00 2001 From: kinggo Date: Tue, 27 Sep 2022 15:02:38 +0800 Subject: [PATCH 110/141] :sparkles: feat: Add GetRoutes (#2112) feat: Add GetRoutes --- app.go | 18 ++++++++++++++++++ app_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/app.go b/app.go index 93403b7d1f..5ef8d840b1 100644 --- a/app.go +++ b/app.go @@ -638,6 +638,24 @@ func (app *App) GetRoute(name string) Route { return Route{} } +// GetRoutes Get all routes. When filterUseOption equal to true, it will filter the routes registered by the middleware. +func (app *App) GetRoutes(filterUseOption ...bool) []Route { + var rs []Route + var filterUse bool + if len(filterUseOption) != 0 { + filterUse = filterUseOption[0] + } + for _, routes := range app.stack { + for _, route := range routes { + if filterUse && route.use { + continue + } + rs = append(rs, *route) + } + } + return rs +} + // Use registers a middleware route that will match requests // with the provided prefix (which is optional and defaults to "/"). // diff --git a/app_test.go b/app_test.go index 9c12963868..ec9224dbc2 100644 --- a/app_test.go +++ b/app_test.go @@ -1647,3 +1647,33 @@ func Test_App_SetTLSHandler(t *testing.T) { utils.AssertEqual(t, "example.golang", c.ClientHelloInfo().ServerName) } + +func TestApp_GetRoutes(t *testing.T) { + app := New() + app.Use(func(c *Ctx) error { + return c.Next() + }) + handler := func(c *Ctx) error { + return c.SendStatus(StatusOK) + } + app.Delete("/delete", handler).Name("delete") + app.Post("/post", handler).Name("post") + routes := app.GetRoutes(false) + utils.AssertEqual(t, 11, len(routes)) + methodMap := map[string]string{"/delete": "delete", "/post": "post"} + for _, route := range routes { + name, ok := methodMap[route.Path] + if ok { + utils.AssertEqual(t, name, route.Name) + } + } + + routes = app.GetRoutes(true) + utils.AssertEqual(t, 2, len(routes)) + for _, route := range routes { + name, ok := methodMap[route.Path] + utils.AssertEqual(t, true, ok) + utils.AssertEqual(t, name, route.Name) + } + +} From 4108d73fe0303388a095a8616c1918a0ed4261e0 Mon Sep 17 00:00:00 2001 From: Fufu Date: Wed, 28 Sep 2022 13:55:28 +0800 Subject: [PATCH 111/141] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20typos=20(#2?= =?UTF-8?q?127)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: typos --- middleware/timeout/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/middleware/timeout/README.md b/middleware/timeout/README.md index 4cc68d0710..55ada503dd 100644 --- a/middleware/timeout/README.md +++ b/middleware/timeout/README.md @@ -42,7 +42,6 @@ func main() { func sleepWithContext(ctx context.Context, d time.Duration) error { timer := time.NewTimer(d) - select { case <-ctx.Done(): if !timer.Stop() { @@ -80,11 +79,11 @@ func main() { return nil } - app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second), ErrFooTimeOut) + app.Get("/foo/:sleepTime", timeout.New(h, 2*time.Second, ErrFooTimeOut)) _ = app.Listen(":3000") } -func sleepWithContext(ctx context.Context, d time.Duration) error { +func sleepWithContextWithCustomError(ctx context.Context, d time.Duration) error { timer := time.NewTimer(d) select { case <-ctx.Done(): From 66d5b195c5f2665d0249316f72ba7e1e49a5b508 Mon Sep 17 00:00:00 2001 From: Amir Hossein <77993374+Kamandlou@users.noreply.github.com> Date: Wed, 28 Sep 2022 15:56:20 +0330 Subject: [PATCH 112/141] fix unhandled errors and update code comments to help the IDEs (#2128) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix unhandled errors * fix unhandled error in cache package test * omit variable type * omit variable type * rename variable because collide with the imported package name * handle file error on closing * fix unhandled in common_linux.go * fix unhandled errors in helpers_test.go * fix unhandled errors in listen_test.go * remove unused parameter in emptyHandler method * refactor path.go * unhandled error in hooks test * fix unhandled errors in app_test.go * fix unhandled errors in ctx_test.go * ✨ fix unhandled errors in helpers_test.go * revert app_test.go * remove redundant parentheses and update comments * update code comment for helping ide * update code comment for helping ide * fix unhandled error in app_test.go * update code comments for helping IDEs * fix unhandled errors in app_test.go * update code comment for helping the IDEs --- app_test.go | 10 ++++++++-- error.go | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app_test.go b/app_test.go index ec9224dbc2..7aff77476c 100644 --- a/app_test.go +++ b/app_test.go @@ -1343,7 +1343,10 @@ func Test_App_ReadTimeout(t *testing.T) { conn, err := net.Dial(NetworkTCP4, "127.0.0.1:4004") utils.AssertEqual(t, nil, err) - defer conn.Close() + defer func(conn net.Conn) { + err := conn.Close() + utils.AssertEqual(t, nil, err) + }(conn) _, err = conn.Write([]byte("HEAD /read-timeout HTTP/1.1\r\n")) utils.AssertEqual(t, nil, err) @@ -1375,7 +1378,10 @@ func Test_App_BadRequest(t *testing.T) { time.Sleep(500 * time.Millisecond) conn, err := net.Dial(NetworkTCP4, "127.0.0.1:4005") utils.AssertEqual(t, nil, err) - defer conn.Close() + defer func(conn net.Conn) { + err := conn.Close() + utils.AssertEqual(t, nil, err) + }(conn) _, err = conn.Write([]byte("BadRequest\r\n")) utils.AssertEqual(t, nil, err) diff --git a/error.go b/error.go index 9d0d0de250..e520420a78 100644 --- a/error.go +++ b/error.go @@ -7,7 +7,7 @@ import ( ) type ( - // Conversion error exposes the internal schema.ConversionError for public use. + // ConversionError Conversion error exposes the internal schema.ConversionError for public use. ConversionError = schema.ConversionError // UnknownKeyError error exposes the internal schema.UnknownKeyError for public use. UnknownKeyError = schema.UnknownKeyError From 8e8ad950799808a826d31ce1f1cb4bfb377d6c3b Mon Sep 17 00:00:00 2001 From: kinggo Date: Wed, 28 Sep 2022 20:27:58 +0800 Subject: [PATCH 113/141] :sparkles: feat: support to set client in proxy mw (#2117) optimize: add WithClient --- internal/gopsutil/net/net_darwin.go | 3 +- middleware/proxy/README.md | 25 +++++++-- middleware/proxy/config.go | 9 +++- middleware/proxy/proxy.go | 76 ++++++++++++++++---------- middleware/proxy/proxy_test.go | 84 +++++++++++++++++++++++++++++ 5 files changed, 162 insertions(+), 35 deletions(-) diff --git a/internal/gopsutil/net/net_darwin.go b/internal/gopsutil/net/net_darwin.go index da294f6370..32d67c0d51 100644 --- a/internal/gopsutil/net/net_darwin.go +++ b/internal/gopsutil/net/net_darwin.go @@ -7,11 +7,12 @@ import ( "context" "errors" "fmt" - "github.com/gofiber/fiber/v2/internal/gopsutil/common" "os/exec" "regexp" "strconv" "strings" + + "github.com/gofiber/fiber/v2/internal/gopsutil/common" ) var ( diff --git a/middleware/proxy/README.md b/middleware/proxy/README.md index c3ceee9dfd..0c68b1d86a 100644 --- a/middleware/proxy/README.md +++ b/middleware/proxy/README.md @@ -13,8 +13,8 @@ Proxy middleware for [Fiber](https://github.com/gofiber/fiber) that allows you t ```go func Balancer(config Config) fiber.Handler -func Forward(addr string) fiber.Handler -func Do(c *fiber.Ctx, addr string) error +func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler +func Do(c *fiber.Ctx, addr string, clients ...*fasthttp.Client) error ``` ### Examples @@ -37,9 +37,21 @@ proxy.WithTlsConfig(&tls.Config{ InsecureSkipVerify: true, }) +// if you need to use global self-custom client, you should use proxy.WithClient. +proxy.WithClient(&fasthttp.Client{ + NoDefaultUserAgentHeader: true, + DisablePathNormalizing: true, +}) + // Forward to url app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif")) +// Forward to url with local custom client +app.Get("/gif", proxy.Forward("https://i.imgur.com/IWaBepg.gif", &fasthttp.Client{ + NoDefaultUserAgentHeader: true, + DisablePathNormalizing: true, +})) + // Make request within handler app.Get("/:id", func(c *fiber.Ctx) error { url := "https://i.imgur.com/"+c.Params("id")+".gif" @@ -120,8 +132,13 @@ type Config struct { // Per-connection buffer size for responses' writing. WriteBufferSize int - // tls config for the http client - TlsConfig *tls.Config + // tls config for the http client. + TlsConfig *tls.Config + + // Client is custom client when client config is complex. + // Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig + // will not be used if the client are set. + Client *fasthttp.LBClient } ``` diff --git a/middleware/proxy/config.go b/middleware/proxy/config.go index 80b6472514..be5e043e51 100644 --- a/middleware/proxy/config.go +++ b/middleware/proxy/config.go @@ -47,8 +47,13 @@ type Config struct { // Per-connection buffer size for responses' writing. WriteBufferSize int - // tls config for the http client + // tls config for the http client. TlsConfig *tls.Config + + // Client is custom client when client config is complex. + // Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig + // will not be used if the client are set. + Client *fasthttp.LBClient } // ConfigDefault is the default config @@ -75,7 +80,7 @@ func configDefault(config ...Config) Config { } // Set default values - if len(cfg.Servers) == 0 { + if len(cfg.Servers) == 0 && cfg.Client == nil { panic("Servers cannot be empty") } return cfg diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index bb96b03382..e9c3af823c 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -24,34 +24,39 @@ func Balancer(config Config) fiber.Handler { cfg := configDefault(config) // Load balanced client - var lbc fasthttp.LBClient - // Set timeout - lbc.Timeout = cfg.Timeout - - // Scheme must be provided, falls back to http - // TODO add https support - for _, server := range cfg.Servers { - if !strings.HasPrefix(server, "http") { - server = "http://" + server - } + var lbc = &fasthttp.LBClient{} + // Note that Servers, Timeout, WriteBufferSize, ReadBufferSize and TlsConfig + // will not be used if the client are set. + if config.Client == nil { + // Set timeout + lbc.Timeout = cfg.Timeout + // Scheme must be provided, falls back to http + for _, server := range cfg.Servers { + if !strings.HasPrefix(server, "http") { + server = "http://" + server + } - u, err := url.Parse(server) - if err != nil { - panic(err) - } + u, err := url.Parse(server) + if err != nil { + panic(err) + } - client := &fasthttp.HostClient{ - NoDefaultUserAgentHeader: true, - DisablePathNormalizing: true, - Addr: u.Host, + client := &fasthttp.HostClient{ + NoDefaultUserAgentHeader: true, + DisablePathNormalizing: true, + Addr: u.Host, - ReadBufferSize: config.ReadBufferSize, - WriteBufferSize: config.WriteBufferSize, + ReadBufferSize: config.ReadBufferSize, + WriteBufferSize: config.WriteBufferSize, - TLSConfig: config.TlsConfig, - } + TLSConfig: config.TlsConfig, + } - lbc.Clients = append(lbc.Clients, client) + lbc.Clients = append(lbc.Clients, client) + } + } else { + // Set custom client + lbc = config.Client } // Return new handler @@ -97,28 +102,43 @@ func Balancer(config Config) fiber.Handler { } } -var client = fasthttp.Client{ +var client = &fasthttp.Client{ NoDefaultUserAgentHeader: true, DisablePathNormalizing: true, } // WithTlsConfig update http client with a user specified tls.config // This function should be called before Do and Forward. +// Deprecated: use WithClient instead. func WithTlsConfig(tlsConfig *tls.Config) { client.TLSConfig = tlsConfig } +// WithClient sets the global proxy client. +// This function should be called before Do and Forward. +func WithClient(cli *fasthttp.Client) { + client = cli +} + // Forward performs the given http request and fills the given http response. // This method will return an fiber.Handler -func Forward(addr string) fiber.Handler { +func Forward(addr string, clients ...*fasthttp.Client) fiber.Handler { return func(c *fiber.Ctx) error { - return Do(c, addr) + return Do(c, addr, clients...) } } // Do performs the given http request and fills the given http response. // This method can be used within a fiber.Handler -func Do(c *fiber.Ctx, addr string) error { +func Do(c *fiber.Ctx, addr string, clients ...*fasthttp.Client) error { + var cli *fasthttp.Client + if len(clients) != 0 { + // Set local client + cli = clients[0] + } else { + // Set global client + cli = client + } req := c.Request() res := c.Response() originalURL := utils.CopyString(c.OriginalURL()) @@ -134,7 +154,7 @@ func Do(c *fiber.Ctx, addr string) error { } req.Header.Del(fiber.HeaderConnection) - if err := client.Do(req, res); err != nil { + if err := cli.Do(req, res); err != nil { return err } res.Header.Del(fiber.HeaderConnection) diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index 08160b2e86..47ec14a5fd 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -13,6 +13,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/tlstest" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/fasthttp" ) func createProxyTestServer(handler fiber.Handler, t *testing.T) (*fiber.App, string) { @@ -364,6 +365,7 @@ func Test_Proxy_Do_RestoreOriginalURL(t *testing.T) { utils.AssertEqual(t, nil, err2) } +// go test -race -run Test_Proxy_Do_HTTP_Prefix_URL func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) { t.Parallel() @@ -390,3 +392,85 @@ func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "hello world", string(s)) } + +// go test -race -run Test_Proxy_Forward_Global_Client +func Test_Proxy_Forward_Global_Client(t *testing.T) { + t.Parallel() + ln, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0") + utils.AssertEqual(t, nil, err) + WithClient(&fasthttp.Client{ + NoDefaultUserAgentHeader: true, + DisablePathNormalizing: true, + }) + app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app.Get("/test_global_client", func(c *fiber.Ctx) error { + return c.SendString("test_global_client") + }) + + addr := ln.Addr().String() + app.Use(Forward("http://" + addr + "/test_global_client")) + go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + + code, body, errs := fiber.Get("http://" + addr).String() + utils.AssertEqual(t, 0, len(errs)) + utils.AssertEqual(t, fiber.StatusOK, code) + utils.AssertEqual(t, "test_global_client", body) +} + +// go test -race -run Test_Proxy_Forward_Local_Client +func Test_Proxy_Forward_Local_Client(t *testing.T) { + t.Parallel() + ln, err := net.Listen(fiber.NetworkTCP4, "127.0.0.1:0") + utils.AssertEqual(t, nil, err) + app := fiber.New(fiber.Config{DisableStartupMessage: true}) + app.Get("/test_local_client", func(c *fiber.Ctx) error { + return c.SendString("test_local_client") + }) + + addr := ln.Addr().String() + app.Use(Forward("http://"+addr+"/test_local_client", &fasthttp.Client{ + NoDefaultUserAgentHeader: true, + DisablePathNormalizing: true, + Dial: func(addr string) (net.Conn, error) { + return fasthttp.Dial(addr) + }, + })) + go func() { utils.AssertEqual(t, nil, app.Listener(ln)) }() + + code, body, errs := fiber.Get("http://" + addr).String() + utils.AssertEqual(t, 0, len(errs)) + utils.AssertEqual(t, fiber.StatusOK, code) + utils.AssertEqual(t, "test_local_client", body) +} + +// go test -run Test_ProxyBalancer_Custom_Client +func Test_ProxyBalancer_Custom_Client(t *testing.T) { + t.Parallel() + + target, addr := createProxyTestServer( + func(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusTeapot) }, t, + ) + + resp, err := target.Test(httptest.NewRequest("GET", "/", nil), 2000) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) + + app := fiber.New(fiber.Config{DisableStartupMessage: true}) + + app.Use(Balancer(Config{Client: &fasthttp.LBClient{ + Clients: []fasthttp.BalancingClient{ + &fasthttp.HostClient{ + NoDefaultUserAgentHeader: true, + DisablePathNormalizing: true, + Addr: addr, + }, + }, + Timeout: time.Second, + }})) + + req := httptest.NewRequest("GET", "/", nil) + req.Host = addr + resp, err = app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusTeapot, resp.StatusCode) +} From 668b0c85f89bfbe46ea515b627577291e38d2bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Thu, 29 Sep 2022 09:28:52 +0300 Subject: [PATCH 114/141] :sparkles: v3 (enhancement): refactor filesystem middleware with `io/fs` (#2027) * :sparkles: v3 (feature): refactor filesystem middleware with `io/fs` * update docs * attachment support. * fix --- middleware/filesystem/README.md | 153 ++--------------------- middleware/filesystem/filesystem.go | 66 +++++++--- middleware/filesystem/filesystem_test.go | 35 ++++-- middleware/filesystem/utils.go | 24 +++- 4 files changed, 97 insertions(+), 181 deletions(-) diff --git a/middleware/filesystem/README.md b/middleware/filesystem/README.md index dea14fa43e..e7f3cbbb5e 100644 --- a/middleware/filesystem/README.md +++ b/middleware/filesystem/README.md @@ -12,11 +12,6 @@ Filesystem middleware for [Fiber](https://github.com/gofiber/fiber) that enables - [Examples](#examples) - [Config](#config) - [embed](#embed) - - [pkger](#pkger) - - [packr](#packr) - - [go.rice](#gorice) - - [fileb0x](#fileb0x) - - [statik](#statik) - [Config](#config-1) - [Default Config](#default-config) @@ -44,12 +39,12 @@ Then create a Fiber app with `app := fiber.New()`. ```go // Provide a minimal config app.Use(filesystem.New(filesystem.Config{ - Root: http.Dir("./assets"), + Root: os.DirFS("./assets"), })) // Or extend your config for customization app.Use(filesystem.New(filesystem.Config{ - Root: http.Dir("./assets"), + Root: os.DirFS("./assets"), Browse: true, Index: "index.html", NotFoundFile: "404.html", @@ -88,15 +83,14 @@ func main() { app := fiber.New() app.Use("/", filesystem.New(filesystem.Config{ - Root: http.FS(f), + Root: f, })) // Access file "image.png" under `static/` directory via URL: `http:///static/image.png`. // Without `PathPrefix`, you have to access it via URL: // `http:///static/static/image.png`. app.Use("/static", filesystem.New(filesystem.Config{ - Root: http.FS(embedDirStatic), - PathPrefix: "static", + Root: embedDirStatic, Browse: true, })) @@ -104,137 +98,6 @@ func main() { } ``` -### pkger - -[Pkger](https://github.com/markbates/pkger) can be used to embed files in a Golang excecutable. - -```go -package main - -import ( - "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/middleware/filesystem" - - "github.com/markbates/pkger" -) - -func main() { - app := fiber.New() - - app.Use("/assets", filesystem.New(filesystem.Config{ - Root: pkger.Dir("/assets"), - }) - - log.Fatal(app.Listen(":3000")) -} -``` - -### packr - -[Packr](https://github.com/gobuffalo/packr) can be used to embed files in a Golang excecutable. - -```go -package main - -import ( - "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/middleware/filesystem" - - "github.com/gobuffalo/packr/v2" -) - -func main() { - app := fiber.New() - - app.Use("/assets", filesystem.New(filesystem.Config{ - Root: packr.New("Assets Box", "/assets"), - }) - - log.Fatal(app.Listen(":3000")) -} -``` - -### go.rice - -https://github.com/GeertJohan/go.rice - -```go -package main - -import ( - "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/middleware/filesystem" - - "github.com/GeertJohan/go.rice" -) - -func main() { - app := fiber.New() - - app.Use("/assets", filesystem.New(filesystem.Config{ - Root: rice.MustFindBox("assets").HTTPBox(), - }) - - log.Fatal(app.Listen(":3000")) -} -``` - -### fileb0x - -[Fileb0x](https://github.com/UnnoTed/fileb0x) can be used to embed files in a Golang excecutable. - -```go -package main - -import ( - "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/middleware/filesystem" - - "/myEmbeddedFiles" -) - -func main() { - app := fiber.New() - - app.Use("/assets", filesystem.New(filesystem.Config{ - Root: myEmbeddedFiles.HTTP, - }) - - log.Fatal(app.Listen(":3000")) -} -``` - -### statik - -[Statik](https://github.com/rakyll/statik) can be used to embed files in a Golang excecutable. - -```go -package main - -import ( - "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/middleware/filesystem" - - "/statik" - fs "github.com/rakyll/statik/fs" -) - -func main() { - statik, err := fs.New() - if err != nil { - panic(err) - } - - app := fiber.New() - - app.Use("/", filesystem.New(filesystem.Config{ - Root: statikFS, - }) - - log.Fatal(app.Listen(":3000")) -} -``` - ## Config ```go @@ -249,14 +112,12 @@ type Config struct { // to a collection of files and directories. // // Required. Default: nil - Root http.FileSystem `json:"-"` + Root fs.FS `json:"-"` // PathPrefix defines a prefix to be added to a filepath when // reading a file from the FileSystem. // - // Use when using Go 1.16 embed.FS - // - // Optional. Default "" + // Optional. Default "." PathPrefix string `json:"path_prefix"` // Enable directory browsing. @@ -288,7 +149,7 @@ type Config struct { var ConfigDefault = Config{ Next: nil, Root: nil, - PathPrefix: "", + PathPrefix: ".", Browse: false, Index: "/index.html", MaxAge: 0, diff --git a/middleware/filesystem/filesystem.go b/middleware/filesystem/filesystem.go index ded63874ce..c8f08b636f 100644 --- a/middleware/filesystem/filesystem.go +++ b/middleware/filesystem/filesystem.go @@ -1,8 +1,10 @@ package filesystem import ( + "io/fs" "net/http" "os" + "path/filepath" "strconv" "strings" "sync" @@ -21,14 +23,12 @@ type Config struct { // to a collection of files and directories. // // Required. Default: nil - Root http.FileSystem `json:"-"` + Root fs.FS `json:"-"` // PathPrefix defines a prefix to be added to a filepath when // reading a file from the FileSystem. // - // Use when using Go 1.16 embed.FS - // - // Optional. Default "" + // Optional. Default "." PathPrefix string `json:"path_prefix"` // Enable directory browsing. @@ -41,6 +41,11 @@ type Config struct { // Optional. Default: "index.html" Index string `json:"index"` + // When set to true, enables direct download for files. + // + // Optional. Default: false. + Download bool `json:"download"` + // The value for the Cache-Control HTTP-header // that is set on the file response. MaxAge is defined in seconds. // @@ -57,7 +62,7 @@ type Config struct { var ConfigDefault = Config{ Next: nil, Root: nil, - PathPrefix: "", + PathPrefix: ".", Browse: false, Index: "/index.html", MaxAge: 0, @@ -76,6 +81,9 @@ func New(config ...Config) fiber.Handler { if cfg.Index == "" { cfg.Index = ConfigDefault.Index } + if cfg.PathPrefix == "" { + cfg.PathPrefix = ConfigDefault.PathPrefix + } if !strings.HasPrefix(cfg.Index, "/") { cfg.Index = "/" + cfg.Index } @@ -88,8 +96,13 @@ func New(config ...Config) fiber.Handler { panic("filesystem: Root cannot be nil") } - if cfg.PathPrefix != "" && !strings.HasPrefix(cfg.PathPrefix, "/") { - cfg.PathPrefix = "/" + cfg.PathPrefix + // PathPrefix configurations for io/fs compatibility. + if cfg.PathPrefix != "." && !strings.HasPrefix(cfg.PathPrefix, "/") { + cfg.PathPrefix = "./" + cfg.PathPrefix + } + + if cfg.NotFoundFile != "" { + cfg.NotFoundFile = filepath.Join(cfg.PathPrefix, filepath.Clean("/"+cfg.NotFoundFile)) } var once sync.Once @@ -120,23 +133,26 @@ func New(config ...Config) fiber.Handler { if !strings.HasPrefix(path, "/") { path = "/" + path } - // Add PathPrefix - if cfg.PathPrefix != "" { - // PathPrefix already has a "/" prefix - path = cfg.PathPrefix + path - } var ( - file http.File + file fs.File stat os.FileInfo ) + // Add PathPrefix + if cfg.PathPrefix != "" { + // PathPrefix already has a "/" prefix + path = filepath.Join(cfg.PathPrefix, filepath.Clean("/"+path)) + } + if len(path) > 1 { path = strings.TrimRight(path, "/") } - file, err = cfg.Root.Open(path) + + file, err = openFile(cfg.Root, path) + if err != nil && os.IsNotExist(err) && cfg.NotFoundFile != "" { - file, err = cfg.Root.Open(cfg.NotFoundFile) + file, err = openFile(cfg.Root, cfg.NotFoundFile) } if err != nil { @@ -153,7 +169,9 @@ func New(config ...Config) fiber.Handler { // Serve index if path is directory if stat.IsDir() { indexPath := strings.TrimRight(path, "/") + cfg.Index - index, err := cfg.Root.Open(indexPath) + indexPath = filepath.Join(cfg.PathPrefix, filepath.Clean("/"+indexPath)) + + index, err := openFile(cfg.Root, indexPath) if err == nil { indexStat, err := index.Stat() if err == nil { @@ -168,6 +186,7 @@ func New(config ...Config) fiber.Handler { if cfg.Browse { return dirList(c, file) } + return fiber.ErrForbidden } @@ -182,6 +201,11 @@ func New(config ...Config) fiber.Handler { c.Set(fiber.HeaderLastModified, modTime.UTC().Format(http.TimeFormat)) } + // Sets the response Content-Disposition header to attachment if the Download option is true and if it's a file + if cfg.Download && !stat.IsDir() { + c.Attachment() + } + if method == fiber.MethodGet { if cfg.MaxAge > 0 { c.Set(fiber.HeaderCacheControl, cacheControlStr) @@ -205,13 +229,15 @@ func New(config ...Config) fiber.Handler { } // SendFile ... -func SendFile(c fiber.Ctx, fs http.FileSystem, path string) (err error) { +func SendFile(c fiber.Ctx, filesystem fs.FS, path string) (err error) { var ( - file http.File + file fs.File stat os.FileInfo ) - file, err = fs.Open(path) + path = filepath.Join(".", filepath.Clean("/"+path)) + + file, err = openFile(filesystem, path) if err != nil { if os.IsNotExist(err) { return fiber.ErrNotFound @@ -226,7 +252,7 @@ func SendFile(c fiber.Ctx, fs http.FileSystem, path string) (err error) { // Serve index if path is directory if stat.IsDir() { indexPath := strings.TrimRight(path, "/") + ConfigDefault.Index - index, err := fs.Open(indexPath) + index, err := openFile(filesystem, indexPath) if err == nil { indexStat, err := index.Stat() if err == nil { diff --git a/middleware/filesystem/filesystem_test.go b/middleware/filesystem/filesystem_test.go index 215a9e6620..0e544d17fa 100644 --- a/middleware/filesystem/filesystem_test.go +++ b/middleware/filesystem/filesystem_test.go @@ -3,6 +3,7 @@ package filesystem import ( "net/http" "net/http/httptest" + "os" "testing" "github.com/gofiber/fiber/v3" @@ -14,11 +15,11 @@ func Test_FileSystem(t *testing.T) { app := fiber.New() app.Use("/test", New(Config{ - Root: http.Dir("../../.github/testdata/fs"), + Root: os.DirFS("../../.github/testdata/fs"), })) app.Use("/dir", New(Config{ - Root: http.Dir("../../.github/testdata/fs"), + Root: os.DirFS("../../.github/testdata/fs"), Browse: true, })) @@ -27,13 +28,13 @@ func Test_FileSystem(t *testing.T) { }) app.Use("/spatest", New(Config{ - Root: http.Dir("../../.github/testdata/fs"), + Root: os.DirFS("../../.github/testdata/fs"), Index: "index.html", NotFoundFile: "index.html", })) app.Use("/prefix", New(Config{ - Root: http.Dir("../../.github/testdata/fs"), + Root: os.DirFS("../../.github/testdata/fs"), PathPrefix: "img", })) @@ -133,7 +134,7 @@ func Test_FileSystem(t *testing.T) { func Test_FileSystem_Next(t *testing.T) { app := fiber.New() app.Use(New(Config{ - Root: http.Dir("../../.github/testdata/fs"), + Root: os.DirFS("../../.github/testdata/fs"), Next: func(_ fiber.Ctx) bool { return true }, @@ -144,11 +145,27 @@ func Test_FileSystem_Next(t *testing.T) { require.Equal(t, fiber.StatusNotFound, resp.StatusCode) } +// go test -run Test_FileSystem_Download +func Test_FileSystem_Download(t *testing.T) { + app := fiber.New() + app.Use(New(Config{ + Root: os.DirFS("../../.github/testdata/fs"), + Download: true, + })) + + resp, err := app.Test(httptest.NewRequest("GET", "/img/fiber.png", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + utils.AssertEqual(t, false, resp.Header.Get(fiber.HeaderContentLength) == "") + utils.AssertEqual(t, "image/png", resp.Header.Get(fiber.HeaderContentType)) + utils.AssertEqual(t, `attachment`, resp.Header.Get(fiber.HeaderContentDisposition)) +} + func Test_FileSystem_NonGetAndHead(t *testing.T) { app := fiber.New() app.Use("/test", New(Config{ - Root: http.Dir("../../.github/testdata/fs"), + Root: os.DirFS("../../.github/testdata/fs"), })) resp, err := app.Test(httptest.NewRequest(fiber.MethodPost, "/test", nil)) @@ -160,7 +177,7 @@ func Test_FileSystem_Head(t *testing.T) { app := fiber.New() app.Use("/test", New(Config{ - Root: http.Dir("../../.github/testdata/fs"), + Root: os.DirFS("../../.github/testdata/fs"), })) req, _ := http.NewRequest(fiber.MethodHead, "/test", nil) @@ -183,7 +200,7 @@ func Test_FileSystem_UsingParam(t *testing.T) { app := fiber.New() app.Use("/:path", func(c fiber.Ctx) error { - return SendFile(c, http.Dir("../../.github/testdata/fs"), c.Params("path")+".html") + return SendFile(c, os.DirFS("../../.github/testdata/fs"), c.Params("path")+".html") }) req, _ := http.NewRequest(fiber.MethodHead, "/index", nil) @@ -196,7 +213,7 @@ func Test_FileSystem_UsingParam_NonFile(t *testing.T) { app := fiber.New() app.Use("/:path", func(c fiber.Ctx) error { - return SendFile(c, http.Dir("../../.github/testdata/fs"), c.Params("path")+".html") + return SendFile(c, os.DirFS("../../.github/testdata/fs"), c.Params("path")+".html") }) req, _ := http.NewRequest(fiber.MethodHead, "/template", nil) diff --git a/middleware/filesystem/utils.go b/middleware/filesystem/utils.go index 7ec68cfde9..3561870a0a 100644 --- a/middleware/filesystem/utils.go +++ b/middleware/filesystem/utils.go @@ -3,9 +3,9 @@ package filesystem import ( "fmt" "html" - "net/http" - "os" + "io/fs" "path" + "path/filepath" "sort" "strings" @@ -20,17 +20,23 @@ func getFileExtension(path string) string { return path[n:] } -func dirList(c fiber.Ctx, f http.File) error { - fileinfos, err := f.Readdir(-1) +func dirList(c fiber.Ctx, f fs.File) error { + ff := f.(fs.ReadDirFile) + fileinfos, err := ff.ReadDir(-1) if err != nil { return err } - fm := make(map[string]os.FileInfo, len(fileinfos)) + fm := make(map[string]fs.FileInfo, len(fileinfos)) filenames := make([]string, 0, len(fileinfos)) for _, fi := range fileinfos { name := fi.Name() - fm[name] = fi + info, err := fi.Info() + if err != nil { + return err + } + + fm[name] = info filenames = append(filenames, name) } @@ -63,3 +69,9 @@ func dirList(c fiber.Ctx, f http.File) error { return nil } + +func openFile(fs fs.FS, name string) (fs.File, error) { + name = filepath.ToSlash(name) + + return fs.Open(name) +} From 7fb50f11f648a398c7d946476e9ae60f870d50de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Fri, 30 Sep 2022 18:34:38 +0300 Subject: [PATCH 115/141] :pencil2: v3 : replace utils.AssertEqual with require in filesystem mw tests --- middleware/filesystem/filesystem_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/middleware/filesystem/filesystem_test.go b/middleware/filesystem/filesystem_test.go index 0e544d17fa..dd2e8c0fbd 100644 --- a/middleware/filesystem/filesystem_test.go +++ b/middleware/filesystem/filesystem_test.go @@ -154,11 +154,11 @@ func Test_FileSystem_Download(t *testing.T) { })) resp, err := app.Test(httptest.NewRequest("GET", "/img/fiber.png", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, false, resp.Header.Get(fiber.HeaderContentLength) == "") - utils.AssertEqual(t, "image/png", resp.Header.Get(fiber.HeaderContentType)) - utils.AssertEqual(t, `attachment`, resp.Header.Get(fiber.HeaderContentDisposition)) + require.NoError(t, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + require.False(t, resp.Header.Get(fiber.HeaderContentLength) == "") + require.Equal(t, "image/png", resp.Header.Get(fiber.HeaderContentType)) + require.Equal(t, "attachment", resp.Header.Get(fiber.HeaderContentDisposition)) } func Test_FileSystem_NonGetAndHead(t *testing.T) { From e85a29abf3f5e859705fa85cc779de15bee25170 Mon Sep 17 00:00:00 2001 From: Ilhan Date: Sat, 1 Oct 2022 10:23:47 +0300 Subject: [PATCH 116/141] =?UTF-8?q?=F0=9F=93=9D=20docs:=20Update=20languag?= =?UTF-8?q?e=20count=20(#2131)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/README.md | 2 +- .github/README_ckb.md | 2 +- .github/README_de.md | 2 +- .github/README_es.md | 2 +- .github/README_fa.md | 2 +- .github/README_fr.md | 2 +- .github/README_id.md | 2 +- .github/README_it.md | 2 +- .github/README_ja.md | 2 +- .github/README_ko.md | 2 +- .github/README_nl.md | 2 +- .github/README_pt.md | 2 +- .github/README_ru.md | 2 +- .github/README_sa.md | 2 +- .github/README_tr.md | 2 +- .github/README_zh-CN.md | 2 +- .github/README_zh-TW.md | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/README.md b/.github/README.md index c04982642c..5d6db8fbd1 100644 --- a/.github/README.md +++ b/.github/README.md @@ -136,7 +136,7 @@ go get -u github.com/gofiber/fiber/v2 - [WebSocket support](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- Translated in [15 languages](https://docs.gofiber.io/) +- Translated in [18 languages](https://docs.gofiber.io/) - And much more, [explore Fiber](https://docs.gofiber.io/) ## 💡 Philosophy diff --git a/.github/README_ckb.md b/.github/README_ckb.md index 1f1bfd36d7..84d8ae91b6 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - پشتگیریی [WebSocket](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- وەرگێڕراوە بۆ [15 زمان](https://docs.gofiber.io/) +- وەرگێڕراوە بۆ [18 زمان](https://docs.gofiber.io/) - زیاتریش، [فایبەر بپشکنە](https://docs.gofiber.io/) ## 💡 فەلسەفە diff --git a/.github/README_de.md b/.github/README_de.md index 083fb17a20..1960b28e6e 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - [WebSocket support](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- Verfügbar in [15 Sprachen](https://docs.gofiber.io/) +- Verfügbar in [18 Sprachen](https://docs.gofiber.io/) - Und vieles mehr - [erkunde Fiber](https://docs.gofiber.io/) ## 💡 Philosophie diff --git a/.github/README_es.md b/.github/README_es.md index 7bfca8e1ed..976ceb6ece 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - [WebSocket support](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- Disponible en [15 idiomas](https://docs.gofiber.io/) +- Disponible en [18 idiomas](https://docs.gofiber.io/) - Y mucho más, [explora Fiber](https://docs.gofiber.io/) ## 💡 Filosofía diff --git a/.github/README_fa.md b/.github/README_fa.md index fb555ed4f1..8c2d615a82 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -161,7 +161,7 @@ go get -u github.com/gofiber/fiber/v2 - [پشتیبانی از وب سوکت](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - قابلیت [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- ترجمه در [15 زبان](https://docs.gofiber.io/) +- ترجمه در [18 زبان](https://docs.gofiber.io/) - و امکانات بیشتر, [دیدن در داکیومنت](https://docs.gofiber.io/)
    diff --git a/.github/README_fr.md b/.github/README_fr.md index b2fc37324b..217108adb9 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - [WebSocket support](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- Available in [15 languages](https://docs.gofiber.io/) +- Available in [18 languages](https://docs.gofiber.io/) - Et plus encore, [explorez Fiber](https://docs.gofiber.io/) ## 💡 Philosophie diff --git a/.github/README_id.md b/.github/README_id.md index 285644b8fa..4a8aa8e3b7 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - [Mendukung WebSocket](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- Tersedia dalam [15 bahasa](https://docs.gofiber.io/) +- Tersedia dalam [18 bahasa](https://docs.gofiber.io/) - Dan masih banyak lagi, [kunjungi Fiber](https://docs.gofiber.io/) ## 💡 Filosofi diff --git a/.github/README_it.md b/.github/README_it.md index b2e6307aea..81ab44a879 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - [Supporto WebSocket](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- Disponible in [15 lingue](https://docs.gofiber.io/) +- Disponible in [18 lingue](https://docs.gofiber.io/) - E molto altro ancora, [esplora Fiber](https://docs.gofiber.io/) ## 💡 Filosofia diff --git a/.github/README_ja.md b/.github/README_ja.md index 3775d8ae16..88b97c2b4f 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -133,7 +133,7 @@ go get -u github.com/gofiber/fiber/v2 - [WebSocket support](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- [15 ヶ国語](https://docs.gofiber.io/)に翻訳 +- [18 ヶ国語](https://docs.gofiber.io/)に翻訳 - [Fiber](https://docs.gofiber.io/)をもっと知る ## 💡 哲学 diff --git a/.github/README_ko.md b/.github/README_ko.md index 00cea2806e..291b5acefa 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - [WebSocket support](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- Available in [15 languages](https://docs.gofiber.io/) +- Available in [18 languages](https://docs.gofiber.io/) - 더 알고 싶다면, [Fiber 둘러보기](https://docs.gofiber.io/) ## 💡 철학 diff --git a/.github/README_nl.md b/.github/README_nl.md index 000a378797..3d2a73f175 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - [WebSocket ondersteuning](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/middleware/limiter) -- Vertaald in [15 talen](https://docs.gofiber.io/) +- Vertaald in [18 talen](https://docs.gofiber.io/) - En nog veel meer, [ontdek Fiber](https://docs.gofiber.io/) ## 💡 Filosofie diff --git a/.github/README_pt.md b/.github/README_pt.md index 0079eb632d..a120a1767e 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - [Suporte à WebSockets](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Limitador de requisições](https://docs.gofiber.io/api/middleware/limiter) -- Disponível em [15 línguas](https://docs.gofiber.io/) +- Disponível em [18 línguas](https://docs.gofiber.io/) - E muito mais, [explore o Fiber](https://docs.gofiber.io/) ## 💡 Filosofia diff --git a/.github/README_ru.md b/.github/README_ru.md index 12cd17cf2b..b314713cea 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - [Поддержка WebSocket](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- Документация доступна на [15 языках](https://docs.gofiber.io/) +- Документация доступна на [18 языках](https://docs.gofiber.io/) - И многое другое, [посетите наш Wiki](https://docs.gofiber.io/) ## 💡 Философия diff --git a/.github/README_sa.md b/.github/README_sa.md index b3655d8d0a..e8605edaef 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -145,7 +145,7 @@ go get -u github.com/gofiber/fiber/v2 - [WebSocket دعم](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- ترجم الى [15 لغة أخرى](https://docs.gofiber.io/) +- ترجم الى [18 لغة أخرى](https://docs.gofiber.io/) - وأكثر بكثير, [استكشف Fiber](https://docs.gofiber.io/) ## 💡 فلسفة diff --git a/.github/README_tr.md b/.github/README_tr.md index 7f53a7f7d9..4375426a26 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - [WebSocket desteği](https://github.com/gofiber/websocket) - [Server-Sent eventler](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- [15 dilde](https://docs.gofiber.io/) mevcut +- [18 dilde](https://docs.gofiber.io/) mevcut - Ve daha fazlası, [Fiber'ı keşfet](https://docs.gofiber.io/) ## 💡 Felsefe diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index b78811e868..5dfd510f14 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - [WebSocket 支持](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [频率限制](https://docs.gofiber.io/api/middleware/limiter) -- [15 种语言](https://docs.gofiber.io/) +- [18 种语言](https://docs.gofiber.io/) - 更多请[探索文档](https://docs.gofiber.io/) ## 💡 哲学 diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index a7f2dfe8ea..90e58b24be 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -132,7 +132,7 @@ go get -u github.com/gofiber/fiber/v2 - 支援[WebSocket](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - 支援[限速](https://docs.gofiber.io/api/middleware/limiter) -- 被翻譯成[15種語言](https://docs.gofiber.io/) +- 被翻譯成[18種語言](https://docs.gofiber.io/) - 豐富的[文件](https://docs.gofiber.io/) ## 💡 理念 From 2a3ee9ca418c6a8664a493b1c1a67fec6acba9f5 Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 5 Oct 2022 09:04:29 +0200 Subject: [PATCH 117/141] fix - agent.Struct fails to unmarshal response since 2.33.0 #2134 (#2137) --- client.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client.go b/client.go index deedc0f56c..4a520b7886 100644 --- a/client.go +++ b/client.go @@ -826,6 +826,10 @@ func (a *Agent) Struct(v interface{}) (code int, body []byte, errs []error) { return } + if a.jsonDecoder == nil { + a.jsonDecoder = json.Unmarshal + } + if err := a.jsonDecoder(body, v); err != nil { errs = append(errs, err) } From 902e30efb676dbc1012946701db96b170b4bb741 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Wed, 5 Oct 2022 09:10:20 +0200 Subject: [PATCH 118/141] utils: update mime extensions (#2133) --- utils/http.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/utils/http.go b/utils/http.go index 61eda1172a..d61560ce95 100644 --- a/utils/http.go +++ b/utils/http.go @@ -132,16 +132,17 @@ var statusMessage = []string{ 511: "Network Authentication Required", } -// MIME types were copied from https://github.com/nginx/nginx/blob/master/conf/mime.types +// MIME types were copied from https://github.com/nginx/nginx/blob/67d2a9541826ecd5db97d604f23460210fd3e517/conf/mime.types with the following updates: +// - Use "application/xml" instead of "text/xml" as recommended per https://datatracker.ietf.org/doc/html/rfc7303#section-4.1 var mimeExtensions = map[string]string{ "html": "text/html", "htm": "text/html", "shtml": "text/html", "css": "text/css", + "xml": "application/xml", "gif": "image/gif", "jpeg": "image/jpeg", "jpg": "image/jpeg", - "xml": "application/xml", "js": "application/javascript", "atom": "application/atom+xml", "rss": "application/rss+xml", @@ -150,6 +151,7 @@ var mimeExtensions = map[string]string{ "jad": "text/vnd.sun.j2me.app-descriptor", "wml": "text/vnd.wap.wml", "htc": "text/x-component", + "avif": "image/avif", "png": "image/png", "svg": "image/svg+xml", "svgz": "image/svg+xml", @@ -183,7 +185,11 @@ var mimeExtensions = map[string]string{ "odp": "application/vnd.oasis.opendocument.presentation", "ods": "application/vnd.oasis.opendocument.spreadsheet", "odt": "application/vnd.oasis.opendocument.text", + "pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation", + "xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "wmlc": "application/vnd.wap.wmlc", + "wasm": "application/wasm", "7z": "application/x-7z-compressed", "cco": "application/x-cocoa", "jardiff": "application/x-java-archive-diff", From 00ebb216d4d2b3b52211b4c8989e2ee0ada83e1d Mon Sep 17 00:00:00 2001 From: kinggo Date: Wed, 5 Oct 2022 21:46:14 +0800 Subject: [PATCH 119/141] :white_check_mark: test: add nil jsonDecoder test case (#2139) test: add nil jsonDecoder test case --- client_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/client_test.go b/client_test.go index 980ac1f955..d753054620 100644 --- a/client_test.go +++ b/client_test.go @@ -1106,6 +1106,24 @@ func Test_Client_Agent_Struct(t *testing.T) { utils.AssertEqual(t, 1, len(errs)) utils.AssertEqual(t, "unexpected end of JSON input", errs[0].Error()) }) + + t.Run("nil jsonDecoder", func(t *testing.T) { + a := AcquireAgent() + defer ReleaseAgent(a) + defer a.ConnectionClose() + request := a.Request() + request.Header.SetMethod("GET") + request.SetRequestURI("http://example.com") + err := a.Parse() + utils.AssertEqual(t, nil, err) + a.HostClient.Dial = func(addr string) (net.Conn, error) { return ln.Dial() } + var d data + code, body, errs := a.Struct(&d) + utils.AssertEqual(t, StatusOK, code) + utils.AssertEqual(t, `{"success":true}`, string(body)) + utils.AssertEqual(t, 0, len(errs)) + utils.AssertEqual(t, true, d.Success) + }) } func Test_Client_Agent_Parse(t *testing.T) { From 6a5fc64eddaa81a7fb65c94b8dcfd9a2caac2e78 Mon Sep 17 00:00:00 2001 From: Nathan Faucett Date: Wed, 5 Oct 2022 12:14:37 -0400 Subject: [PATCH 120/141] add CacheControl to Static config (#2140) * add CacheControl to Static config * change config name to ModifyResponse Co-authored-by: Nathan Faucett --- app.go | 5 +++++ app_test.go | 20 ++++++++++++++++++++ router.go | 5 +++++ 3 files changed, 30 insertions(+) diff --git a/app.go b/app.go index 5ef8d840b1..0ca6e7dc66 100644 --- a/app.go +++ b/app.go @@ -421,6 +421,11 @@ type Static struct { // Optional. Default value 0. MaxAge int `json:"max_age"` + // ModifyResponse defines a function that allows you to alter the response. + // + // Optional. Default: nil + ModifyResponse Handler + // Next defines a function to skip this middleware when returned true. // // Optional. Default: nil diff --git a/app_test.go b/app_test.go index 7aff77476c..6bbd8fcba4 100644 --- a/app_test.go +++ b/app_test.go @@ -767,6 +767,26 @@ func Test_App_Static_MaxAge(t *testing.T) { utils.AssertEqual(t, "public, max-age=100", resp.Header.Get(HeaderCacheControl), "CacheControl Control") } +// go test -run Test_App_Static_Custom_CacheControl +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") { + c.Response().Header.Set("Cache-Control", "no-cache, no-store, must-revalidate") + } + return nil + }}) + + resp, err := app.Test(httptest.NewRequest("GET", "/index.html", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, "no-cache, no-store, must-revalidate", resp.Header.Get(HeaderCacheControl), "CacheControl Control") + + normal_resp, normal_err := app.Test(httptest.NewRequest("GET", "/config.yml", nil)) + utils.AssertEqual(t, nil, normal_err, "app.Test(req)") + utils.AssertEqual(t, "", normal_resp.Header.Get(HeaderCacheControl), "CacheControl Control") +} + // go test -run Test_App_Static_Download func Test_App_Static_Download(t *testing.T) { app := New() diff --git a/router.go b/router.go index 46fb1168b4..ff51bb0d52 100644 --- a/router.go +++ b/router.go @@ -357,6 +357,7 @@ func (app *App) registerStatic(prefix, root string, config ...Static) Router { // Set config if provided var cacheControlValue string + var modifyResponse Handler if len(config) > 0 { maxAge := config[0].MaxAge if maxAge > 0 { @@ -369,6 +370,7 @@ func (app *App) registerStatic(prefix, root string, config ...Static) Router { if config[0].Index != "" { fs.IndexNames = []string{config[0].Index} } + modifyResponse = config[0].ModifyResponse } fileHandler := fs.NewRequestHandler() handler := func(c *Ctx) error { @@ -388,6 +390,9 @@ func (app *App) registerStatic(prefix, root string, config ...Static) Router { if len(cacheControlValue) > 0 { c.fasthttp.Response.Header.Set(HeaderCacheControl, cacheControlValue) } + if modifyResponse != nil { + return modifyResponse(c) + } return nil } // Reset response to default From 925d5d03dcf61e5d39b1fdbbbf94850c2065ddd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Thu, 6 Oct 2022 08:54:29 +0300 Subject: [PATCH 121/141] :memo: docs: add doc about usage of CSRF and EncryptCookie middlewares. (#2141) --- middleware/encryptcookie/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/middleware/encryptcookie/README.md b/middleware/encryptcookie/README.md index 2f9a8463ee..7f0f5edf3f 100644 --- a/middleware/encryptcookie/README.md +++ b/middleware/encryptcookie/README.md @@ -95,3 +95,19 @@ app.Use(encryptcookie.New(encryptcookie.Config{ Key: "secret-thirty-2-character-string", })) ``` + +## Usage of CSRF and Encryptcookie Middlewares with Custom Cookie Names +Normally, encryptcookie middleware skips `csrf_` cookies. However, it won't work when you use custom cookie names for CSRF. You should update `Except` config to avoid this problem. For example: + +```go +app.Use(encryptcookie.New(encryptcookie.Config{ + Key: "secret-thirty-2-character-string", + Except: []string{"csrf_1"}, // exclude CSRF cookie +})) + +app.Use(csrf.New(csrf.Config{ + KeyLookup: "form:test", + CookieName: "csrf_1", + CookieHTTPOnly: true, +})) +``` \ No newline at end of file From 23b817083bf14915de50f0b935dc574841576907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Mon, 10 Oct 2022 13:59:19 +0300 Subject: [PATCH 122/141] v3: fix benchmark results related to handler, next (#2130) --- ctx.go | 14 +++++++++-- router.go | 63 +++++++++++++++++++++++++++++++++++++++++++------- router_test.go | 6 ++--- 3 files changed, 70 insertions(+), 13 deletions(-) diff --git a/ctx.go b/ctx.go index 65c7df67bf..6545eb2709 100644 --- a/ctx.go +++ b/ctx.go @@ -728,7 +728,11 @@ func (c *DefaultCtx) Next() (err error) { err = c.route.Handlers[c.indexHandler](c) } else { // Continue handler stack - _, err = c.app.next(c, c.app.newCtxFunc != nil) + if c.app.newCtxFunc != nil { + _, err = c.app.nextCustom(c) + } else { + _, err = c.app.next(c) + } } return err } @@ -736,8 +740,14 @@ func (c *DefaultCtx) Next() (err error) { // RestartRouting instead of going to the next handler. This may be usefull after // changing the request path. Note that handlers might be executed again. func (c *DefaultCtx) RestartRouting() error { + var err error + c.indexRoute = -1 - _, err := c.app.next(c, c.app.newCtxFunc != nil) + if c.app.newCtxFunc != nil { + _, err = c.app.nextCustom(c) + } else { + _, err = c.app.next(c) + } return err } diff --git a/router.go b/router.go index 84b187195c..f47bd0c1df 100644 --- a/router.go +++ b/router.go @@ -96,7 +96,7 @@ func (r *Route) match(detectionPath, path string, params *[maxParams]string) (ma return false } -func (app *App) next(c CustomCtx, customCtx bool) (match bool, err error) { +func (app *App) nextCustom(c CustomCtx) (match bool, err error) { // Get stack length tree, ok := app.treeStack[c.getMethodINT()][c.getTreePath()] if !ok { @@ -136,22 +136,64 @@ func (app *App) next(c CustomCtx, customCtx bool) (match bool, err error) { // If c.Next() does not match, return 404 err = NewError(StatusNotFound, "Cannot "+c.Method()+" "+c.getPathOriginal()) - var isMethodExist bool - if customCtx { - isMethodExist = methodExistCustom(c) - } else { - isMethodExist = methodExist(c.(*DefaultCtx)) + // If no match, scan stack again if other methods match the request + // Moved from app.handler because middleware may break the route chain + if !c.getMatched() && methodExistCustom(c) { + err = ErrMethodNotAllowed } + return +} + +func (app *App) next(c *DefaultCtx) (match bool, err error) { + // Get stack length + tree, ok := app.treeStack[c.methodINT][c.treePath] + if !ok { + tree = app.treeStack[c.methodINT][""] + } + lenr := len(tree) - 1 + + // Loop over the route stack starting from previous index + for c.indexRoute < lenr { + // Increment route index + c.indexRoute++ + + // Get *Route + route := tree[c.indexRoute] + + // Check if it matches the request path + match = route.match(c.detectionPath, c.path, &c.values) + + // No match, next route + if !match { + continue + } + // Pass route reference and param values + c.route = route + + // Non use handler matched + if !c.matched && !route.use { + c.matched = true + } + + // Execute first handler of route + c.indexHandler = 0 + err = route.Handlers[0](c) + return match, err // Stop scanning the stack + } + + // If c.Next() does not match, return 404 + err = NewError(StatusNotFound, "Cannot "+c.method+" "+c.pathOriginal) // If no match, scan stack again if other methods match the request // Moved from app.handler because middleware may break the route chain - if !c.getMatched() && isMethodExist { + if !c.matched && methodExist(c) { err = ErrMethodNotAllowed } return } func (app *App) handler(rctx *fasthttp.RequestCtx) { + // Handler for default ctxs var c CustomCtx if app.newCtxFunc != nil { c = app.AcquireCtx().(CustomCtx) @@ -173,7 +215,12 @@ func (app *App) handler(rctx *fasthttp.RequestCtx) { } // Find match in stack - _, err := app.next(c, app.newCtxFunc != nil) + var err error + if app.newCtxFunc != nil { + _, err = app.nextCustom(c) + } else { + _, err = app.next(c.(*DefaultCtx)) + } if err != nil { if catch := c.App().ErrorHandler(c, err); catch != nil { _ = c.SendStatus(StatusInternalServerError) diff --git a/router_test.go b/router_test.go index e85ea801a2..9692fc9bae 100644 --- a/router_test.go +++ b/router_test.go @@ -596,7 +596,7 @@ func Benchmark_Router_Next(b *testing.B) { b.ResetTimer() for n := 0; n < b.N; n++ { c.indexRoute = -1 - res, err = app.next(c, false) + res, err = app.next(c) } require.NoError(b, err) require.True(b, res) @@ -772,10 +772,10 @@ func Benchmark_Router_Github_API(b *testing.B) { for n := 0; n < b.N; n++ { c.URI().SetPath(routesFixture.TestRoutes[i].Path) - ctx := app.AcquireCtx().(CustomCtx) + ctx := app.AcquireCtx().(*DefaultCtx) ctx.Reset(c) - match, err = app.next(ctx, false) + match, err = app.next(ctx) app.ReleaseCtx(ctx) } From 9a6002056c4c1e76a44f0c68a0d7c27c39e09507 Mon Sep 17 00:00:00 2001 From: Soham Sen Date: Mon, 10 Oct 2022 17:59:51 +0530 Subject: [PATCH 123/141] Switch to text/javascript as per RFC9239 (#2146) * Switch to text/javascript as per RFC9239 * Add deprecated flag to application/javascript --- ctx.go | 2 +- ctx_test.go | 4 ++-- helpers.go | 24 ++++++++++++++---------- utils/http.go | 3 ++- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/ctx.go b/ctx.go index b5bc5f0153..ac5b929395 100644 --- a/ctx.go +++ b/ctx.go @@ -813,7 +813,7 @@ func (c *Ctx) JSONP(data interface{}, callback ...string) error { result = cb + "(" + c.app.getString(raw) + ");" c.setCanonical(HeaderXContentTypeOptions, "nosniff") - c.fasthttp.Response.Header.SetContentType(MIMEApplicationJavaScriptCharsetUTF8) + c.fasthttp.Response.Header.SetContentType(MIMETextJavaScriptCharsetUTF8) return c.SendString(result) } diff --git a/ctx_test.go b/ctx_test.go index f1bcebfd17..925fbd1bc4 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2399,7 +2399,7 @@ func Test_Ctx_JSONP(t *testing.T) { }) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, `callback({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) - utils.AssertEqual(t, "application/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) + utils.AssertEqual(t, "text/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) err = c.JSONP(Map{ "Name": "Grame", @@ -2407,7 +2407,7 @@ func Test_Ctx_JSONP(t *testing.T) { }, "john") utils.AssertEqual(t, nil, err) utils.AssertEqual(t, `john({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) - utils.AssertEqual(t, "application/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) + utils.AssertEqual(t, "text/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) } // go test -v -run=^$ -bench=Benchmark_Ctx_JSONP -benchmem -count=4 diff --git a/helpers.go b/helpers.go index c3360ad032..c611b917f1 100644 --- a/helpers.go +++ b/helpers.go @@ -385,21 +385,25 @@ const ( // MIME types that are commonly used const ( - MIMETextXML = "text/xml" - MIMETextHTML = "text/html" - MIMETextPlain = "text/plain" - MIMEApplicationXML = "application/xml" - MIMEApplicationJSON = "application/json" + MIMETextXML = "text/xml" + MIMETextHTML = "text/html" + MIMETextPlain = "text/plain" + MIMETextJavaScript = "text/javascript" + MIMEApplicationXML = "application/xml" + MIMEApplicationJSON = "application/json" + // Deprecated: use MIMETextJavaScript instead MIMEApplicationJavaScript = "application/javascript" MIMEApplicationForm = "application/x-www-form-urlencoded" MIMEOctetStream = "application/octet-stream" MIMEMultipartForm = "multipart/form-data" - MIMETextXMLCharsetUTF8 = "text/xml; charset=utf-8" - MIMETextHTMLCharsetUTF8 = "text/html; charset=utf-8" - MIMETextPlainCharsetUTF8 = "text/plain; charset=utf-8" - MIMEApplicationXMLCharsetUTF8 = "application/xml; charset=utf-8" - MIMEApplicationJSONCharsetUTF8 = "application/json; charset=utf-8" + MIMETextXMLCharsetUTF8 = "text/xml; charset=utf-8" + MIMETextHTMLCharsetUTF8 = "text/html; charset=utf-8" + MIMETextPlainCharsetUTF8 = "text/plain; charset=utf-8" + MIMETextJavaScriptCharsetUTF8 = "text/javascript; charset=utf-8" + MIMEApplicationXMLCharsetUTF8 = "application/xml; charset=utf-8" + MIMEApplicationJSONCharsetUTF8 = "application/json; charset=utf-8" + // Deprecated: use MIMETextJavaScriptCharsetUTF8 instead MIMEApplicationJavaScriptCharsetUTF8 = "application/javascript; charset=utf-8" ) diff --git a/utils/http.go b/utils/http.go index d61560ce95..fc52402315 100644 --- a/utils/http.go +++ b/utils/http.go @@ -134,6 +134,7 @@ var statusMessage = []string{ // MIME types were copied from https://github.com/nginx/nginx/blob/67d2a9541826ecd5db97d604f23460210fd3e517/conf/mime.types with the following updates: // - Use "application/xml" instead of "text/xml" as recommended per https://datatracker.ietf.org/doc/html/rfc7303#section-4.1 +// - Use "text/javascript" instead of "application/javascript" as recommended per https://www.rfc-editor.org/rfc/rfc9239#name-text-javascript var mimeExtensions = map[string]string{ "html": "text/html", "htm": "text/html", @@ -143,7 +144,7 @@ var mimeExtensions = map[string]string{ "gif": "image/gif", "jpeg": "image/jpeg", "jpg": "image/jpeg", - "js": "application/javascript", + "js": "text/javascript", "atom": "application/atom+xml", "rss": "application/rss+xml", "mml": "text/mathml", From f3c7c424b770d0dc0a4a9fd2bdbff4046c1b199d Mon Sep 17 00:00:00 2001 From: Dio Rovelino <63448336+floxydio@users.noreply.github.com> Date: Tue, 11 Oct 2022 13:52:00 +0700 Subject: [PATCH 124/141] Update ReadmeID (#2150) --- .github/README_id.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README_id.md b/.github/README_id.md index 4a8aa8e3b7..5b7253e4d4 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -621,7 +621,7 @@ Apabila anda ingin mengucapkan **terima kasih** dan/atau mendukung pengembangan ## ☕ Pendukung -Fiber adalah proyek sumber terbuka yang beroperasi dalam donasi untuk membayar tagihan, seperti nama domain, _gitbook, netlify_ dan _serverless hosting_. Jika anda mau mendukung Fiber, anda dapat [**membeli kami kopi disini**](https://buymeacoff.ee/fenny). +Fiber adalah proyek sumber terbuka yang beroperasi dalam donasi untuk membayar tagihan, seperti nama domain, _gitbook, netlify_ dan _serverless hosting_. Jika anda mau mendukung Fiber, anda dapat [**membelikan kami kopi disini**](https://buymeacoff.ee/fenny). | | User | Donation | | :--------------------------------------------------------- | :----------------------------------------------- | :------- | From a023fc2947459131b88210e8329640a34b450a16 Mon Sep 17 00:00:00 2001 From: xbt573 <64583541+xbt573@users.noreply.github.com> Date: Mon, 17 Oct 2022 17:30:34 +0300 Subject: [PATCH 125/141] =?UTF-8?q?=F0=9F=90=9B=20Fix:=20Reset=20d.Data=20?= =?UTF-8?q?instead=20of=20deleting=20keys=20in=20it=20(#2156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🐛 Reset d.Data instead of deleting keys in it --- middleware/session/data.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/middleware/session/data.go b/middleware/session/data.go index 917179467b..7c8f793103 100644 --- a/middleware/session/data.go +++ b/middleware/session/data.go @@ -27,9 +27,7 @@ func acquireData() *data { func (d *data) Reset() { d.Lock() - for key := range d.Data { - delete(d.Data, key) - } + d.Data = make(map[string]interface{}) d.Unlock() } From d275091a2f8dfcea8f3d86a8c96703e94dc121bb Mon Sep 17 00:00:00 2001 From: kinggo Date: Mon, 17 Oct 2022 23:20:41 +0800 Subject: [PATCH 126/141] :bug: fix: solve data race in middleware/proxy's test (#2153) fix: solve data race in middleware/proxy's test --- middleware/proxy/proxy.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/middleware/proxy/proxy.go b/middleware/proxy/proxy.go index e9c3af823c..a468f41b8b 100644 --- a/middleware/proxy/proxy.go +++ b/middleware/proxy/proxy.go @@ -6,6 +6,7 @@ import ( "fmt" "net/url" "strings" + "sync" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/utils" @@ -107,6 +108,8 @@ var client = &fasthttp.Client{ DisablePathNormalizing: true, } +var lock sync.RWMutex + // WithTlsConfig update http client with a user specified tls.config // This function should be called before Do and Forward. // Deprecated: use WithClient instead. @@ -117,6 +120,8 @@ func WithTlsConfig(tlsConfig *tls.Config) { // WithClient sets the global proxy client. // This function should be called before Do and Forward. func WithClient(cli *fasthttp.Client) { + lock.Lock() + defer lock.Unlock() client = cli } @@ -137,7 +142,9 @@ func Do(c *fiber.Ctx, addr string, clients ...*fasthttp.Client) error { cli = clients[0] } else { // Set global client + lock.RLock() cli = client + lock.RUnlock() } req := c.Request() res := c.Response() From 2b7a632a1933341b5fc6419d61ced4e8ffc31ef5 Mon Sep 17 00:00:00 2001 From: kinggo Date: Mon, 17 Oct 2022 23:42:05 +0800 Subject: [PATCH 127/141] style: update logger's comment (#2157) --- middleware/logger/logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 776b050103..4aa7441a77 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -84,7 +84,7 @@ func New(config ...Config) fiber.Handler { var timestamp atomic.Value timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.TimeFormat)) - // Update date/time every 750 milliseconds in a separate go routine + // Update date/time every 500 milliseconds in a separate go routine if strings.Contains(cfg.Format, "${time}") { go func() { for { From 709b132dca182df884bbe75825c16e7d035a73d6 Mon Sep 17 00:00:00 2001 From: Supakorn Wongsawang <20299571+supakornbabe@users.noreply.github.com> Date: Tue, 18 Oct 2022 16:23:35 +0700 Subject: [PATCH 128/141] =?UTF-8?q?=F0=9F=A9=B9=20Fix:=20handle=20multiple?= =?UTF-8?q?=20X-Forwarded=20header=20(#2154)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: handle multiple XForwarded * fix: use strings.Index instead of strings.Split Co-authored-by: Supakorn Wongsawang --- ctx.go | 19 +++++++++++++++++-- ctx_test.go | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/ctx.go b/ctx.go index ac5b929395..a6959e6941 100644 --- a/ctx.go +++ b/ctx.go @@ -661,6 +661,10 @@ func (c *Ctx) GetRespHeaders() map[string]string { func (c *Ctx) Hostname() string { if c.IsProxyTrusted() { if host := c.Get(HeaderXForwardedHost); len(host) > 0 { + commaPos := strings.Index(host, ",") + if commaPos != -1 { + return host[:commaPos] + } return host } } @@ -1014,10 +1018,21 @@ func (c *Ctx) Protocol() string { if len(key) < 12 { return // X-Forwarded- } else if bytes.HasPrefix(key, []byte("X-Forwarded-")) { + v := c.app.getString(val) if bytes.Equal(key, []byte(HeaderXForwardedProto)) { - scheme = c.app.getString(val) + commaPos := strings.Index(v, ",") + if commaPos != -1 { + scheme = v[:commaPos] + } else { + scheme = v + } } else if bytes.Equal(key, []byte(HeaderXForwardedProtocol)) { - scheme = c.app.getString(val) + commaPos := strings.Index(v, ",") + if commaPos != -1 { + scheme = v[:commaPos] + } else { + scheme = v + } } else if bytes.Equal(key, []byte(HeaderXForwardedSsl)) && bytes.Equal(val, []byte("on")) { scheme = "https" } diff --git a/ctx_test.go b/ctx_test.go index 925fbd1bc4..9a60fd447a 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -1071,6 +1071,19 @@ func Test_Ctx_Hostname_TrustedProxy(t *testing.T) { } } +// go test -run Test_Ctx_Hostname_Trusted_Multiple +func Test_Ctx_Hostname_TrustedProxy_Multiple(t *testing.T) { + t.Parallel() + { + app := New(Config{EnableTrustedProxyCheck: true, TrustedProxies: []string{"0.0.0.0", "0.8.0.1"}}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + c.Request().SetRequestURI("http://google.com/test") + c.Request().Header.Set(HeaderXForwardedHost, "google1.com, google2.com") + utils.AssertEqual(t, "google1.com", c.Hostname()) + app.ReleaseCtx(c) + } +} + // go test -run Test_Ctx_Hostname_UntrustedProxyRange func Test_Ctx_Hostname_TrustedProxyRange(t *testing.T) { t.Parallel() @@ -1824,6 +1837,14 @@ func Test_Ctx_Protocol(t *testing.T) { utils.AssertEqual(t, "https", c.Protocol()) c.Request().Header.Reset() + c.Request().Header.Set(HeaderXForwardedProto, "https, http") + utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Reset() + + c.Request().Header.Set(HeaderXForwardedProtocol, "https, http") + utils.AssertEqual(t, "https", c.Protocol()) + c.Request().Header.Reset() + c.Request().Header.Set(HeaderXForwardedSsl, "on") utils.AssertEqual(t, "https", c.Protocol()) c.Request().Header.Reset() From 5316f0823db2a36326f379772a26ceca4d64565d Mon Sep 17 00:00:00 2001 From: sadfun <32015025+sadfun@users.noreply.github.com> Date: Tue, 18 Oct 2022 11:59:23 +0200 Subject: [PATCH 129/141] =?UTF-8?q?=F0=9F=9A=80=20Make=20IP=20validation?= =?UTF-8?q?=202x=20faster=20(#2158)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🚀 Make IP vallidation 2x faster * Add tests for IP validation * phrasing & linter fix * change test assert function That was a surprise that testing style in gofiber/utils and in gofiber/fiber/utils are different 😳 --- ctx.go | 128 ++++++++++++++++++++++++++--------------- ctx_test.go | 36 ++++++++++++ utils/ips.go | 141 ++++++++++++++++++++++++++++++++++++++++++++++ utils/ips_test.go | 85 ++++++++++++++++++++++++++++ 4 files changed, 344 insertions(+), 46 deletions(-) create mode 100644 utils/ips.go create mode 100644 utils/ips_test.go diff --git a/ctx.go b/ctx.go index a6959e6941..327b319386 100644 --- a/ctx.go +++ b/ctx.go @@ -688,72 +688,108 @@ func (c *Ctx) IP() string { return c.fasthttp.RemoteIP().String() } -// validateIPIfEnabled will return the input IP when validation is disabled. -// when validation is enabled, it will return an empty string if the input is not a valid IP. -func (c *Ctx) validateIPIfEnabled(ip string) string { - if c.app.config.EnableIPValidation && net.ParseIP(ip) == nil { - return "" - } - return ip -} - // extractIPsFromHeader will return a slice of IPs it found given a header name in the order they appear. // When IP validation is enabled, any invalid IPs will be omitted. -func (c *Ctx) extractIPsFromHeader(header string) (ipsFound []string) { +func (c *Ctx) extractIPsFromHeader(header string) []string { headerValue := c.Get(header) - // try to gather IPs in the input with minimal allocations to improve performance - ips := make([]string, bytes.Count([]byte(headerValue), []byte(","))+1) - var commaPos, i, validCount int + // 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 + } + + ipsFound := make([]string, 0, estimatedCount) + + i := 0 + j := -1 + +iploop: for { - commaPos = bytes.IndexByte([]byte(headerValue), ',') - if commaPos != -1 { - ips[i] = c.validateIPIfEnabled(utils.Trim(headerValue[:commaPos], ' ')) - if ips[i] != "" { - validCount++ - } - headerValue, i = headerValue[commaPos+1:], i+1 - } else { - ips[i] = c.validateIPIfEnabled(utils.Trim(headerValue, ' ')) - if ips[i] != "" { - validCount++ - } + v4 := false + v6 := false + + // Manually splitting string without allocating slice, working with parts directly + i, j = j+1, j+2 + + if j > len(headerValue) { break } - } - // filter out any invalid IP(s) that we found - if len(ips) == validCount { - ipsFound = ips - } else { - ipsFound = make([]string, validCount) - var validIndex int - for n := range ips { - if ips[n] != "" { - ipsFound[validIndex] = ips[n] - validIndex++ + for j < len(headerValue) && headerValue[j] != ',' { + if headerValue[j] == ':' { + v6 = true + } else if headerValue[j] == '.' { + v4 = true } + j++ } + + for i < j && headerValue[i] == ' ' { + i++ + } + + s := utils.TrimRight(headerValue[i:j], ' ') + + if c.app.config.EnableIPValidation { + // Skip validation if IP is clearly not IPv4/IPv6, otherwise validate without allocations + if (!v6 && !v4) || (v6 && !utils.IsIPv6(s)) || (v4 && !utils.IsIPv4(s)) { + continue iploop + } + } + + ipsFound = append(ipsFound, s) } - return + + return ipsFound } // extractIPFromHeader will attempt to pull the real client IP from the given header when IP validation is enabled. // currently, it will return the first valid IP address in header. // when IP validation is disabled, it will simply return the value of the header without any inspection. +// Implementation is almost the same as in extractIPsFromHeader, but without allocation of []string. func (c *Ctx) extractIPFromHeader(header string) string { if c.app.config.EnableIPValidation { - // extract all IPs from the header's value - ips := c.extractIPsFromHeader(header) - - // since X-Forwarded-For has no RFC, it's really up to the proxy to decide whether to append - // or prepend IPs to this list. For example, the AWS ALB will prepend but the F5 BIG-IP will append ;( - // for now lets just go with the first value in the list... - if len(ips) > 0 { - return ips[0] + headerValue := c.Get(header) + + i := 0 + j := -1 + + iploop: + for { + v4 := false + v6 := false + i, j = j+1, j+2 + + if j > len(headerValue) { + break + } + + for j < len(headerValue) && headerValue[j] != ',' { + if headerValue[j] == ':' { + v6 = true + } else if headerValue[j] == '.' { + v4 = true + } + j++ + } + + for i < j && headerValue[i] == ' ' { + i++ + } + + s := utils.TrimRight(headerValue[i:j], ' ') + + if c.app.config.EnableIPValidation { + if (!v6 && !v4) || (v6 && !utils.IsIPv6(s)) || (v4 && !utils.IsIPv4(s)) { + continue iploop + } + } + + return s } - // return the IP from the stack if we could not find any valid Ips return c.fasthttp.RemoteIP().String() } diff --git a/ctx_test.go b/ctx_test.go index 9a60fd447a..ad975de9cf 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -1265,6 +1265,10 @@ func Test_Ctx_IPs(t *testing.T) { c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.3, 127.0.0.1, 127.0.0.2") utils.AssertEqual(t, []string{"127.0.0.3", "127.0.0.1", "127.0.0.2"}, c.IPs()) + // ensure for IPv6 + c.Request().Header.Set(HeaderXForwardedFor, "9396:9549:b4f7:8ed0:4791:1330:8c06:e62d, invalid, 2345:0425:2CA1::0567:5673:23b5") + utils.AssertEqual(t, []string{"9396:9549:b4f7:8ed0:4791:1330:8c06:e62d", "invalid", "2345:0425:2CA1::0567:5673:23b5"}, c.IPs()) + // empty header c.Request().Header.Set(HeaderXForwardedFor, "") utils.AssertEqual(t, 0, len(c.IPs())) @@ -1298,6 +1302,10 @@ func Test_Ctx_IPs_With_IP_Validation(t *testing.T) { c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.3, 127.0.0.1, 127.0.0.2") utils.AssertEqual(t, []string{"127.0.0.3", "127.0.0.1", "127.0.0.2"}, c.IPs()) + // ensure for IPv6 + c.Request().Header.Set(HeaderXForwardedFor, "f037:825e:eadb:1b7b:1667:6f0a:5356:f604, invalid, 9396:9549:b4f7:8ed0:4791:1330:8c06:e62d") + utils.AssertEqual(t, []string{"f037:825e:eadb:1b7b:1667:6f0a:5356:f604", "9396:9549:b4f7:8ed0:4791:1330:8c06:e62d"}, c.IPs()) + // empty header c.Request().Header.Set(HeaderXForwardedFor, "") utils.AssertEqual(t, 0, len(c.IPs())) @@ -1322,6 +1330,20 @@ func Benchmark_Ctx_IPs(b *testing.B) { utils.AssertEqual(b, []string{"127.0.0.1", "invalid", "127.0.0.1"}, res) } +func Benchmark_Ctx_IPs_v6(b *testing.B) { + app := New() + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Request().Header.Set(HeaderXForwardedFor, "f037:825e:eadb:1b7b:1667:6f0a:5356:f604, invalid, 2345:0425:2CA1::0567:5673:23b5") + var res []string + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + res = c.IPs() + } + utils.AssertEqual(b, []string{"f037:825e:eadb:1b7b:1667:6f0a:5356:f604", "invalid", "2345:0425:2CA1::0567:5673:23b5"}, res) +} + func Benchmark_Ctx_IPs_With_IP_Validation(b *testing.B) { app := New(Config{EnableIPValidation: true}) c := app.AcquireCtx(&fasthttp.RequestCtx{}) @@ -1336,6 +1358,20 @@ func Benchmark_Ctx_IPs_With_IP_Validation(b *testing.B) { utils.AssertEqual(b, []string{"127.0.0.1", "127.0.0.1"}, res) } +func Benchmark_Ctx_IPs_v6_With_IP_Validation(b *testing.B) { + app := New(Config{EnableIPValidation: true}) + c := app.AcquireCtx(&fasthttp.RequestCtx{}) + defer app.ReleaseCtx(c) + c.Request().Header.Set(HeaderXForwardedFor, "2345:0425:2CA1:0000:0000:0567:5673:23b5, invalid, 2345:0425:2CA1::0567:5673:23b5") + var res []string + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + res = c.IPs() + } + utils.AssertEqual(b, []string{"2345:0425:2CA1:0000:0000:0567:5673:23b5", "2345:0425:2CA1::0567:5673:23b5"}, res) +} + func Benchmark_Ctx_IP_With_ProxyHeader(b *testing.B) { app := New(Config{ProxyHeader: HeaderXForwardedFor}) c := app.AcquireCtx(&fasthttp.RequestCtx{}) diff --git a/utils/ips.go b/utils/ips.go new file mode 100644 index 0000000000..6d2446470a --- /dev/null +++ b/utils/ips.go @@ -0,0 +1,141 @@ +package utils + +import "net" + +// IsIPv4 works the same way as net.ParseIP, +// but without check for IPv6 case and without returning net.IP slice, whereby IsIPv4 makes no allocations. +func IsIPv4(s string) bool { + for i := 0; i < net.IPv4len; i++ { + if len(s) == 0 { + return false + } + + if i > 0 { + if s[0] != '.' { + return false + } + s = s[1:] + } + + n, ci := 0, 0 + + for ci = 0; ci < len(s) && '0' <= s[ci] && s[ci] <= '9'; ci++ { + n = n*10 + int(s[ci]-'0') + if n >= 0xFF { + return false + } + } + + if ci == 0 || n > 0xFF || (ci > 1 && s[0] == '0') { + return false + } + + s = s[ci:] + } + + return len(s) == 0 +} + +// IsIPv6 works the same way as net.ParseIP, +// but without check for IPv4 case and without returning net.IP slice, whereby IsIPv6 makes no allocations. +func IsIPv6(s string) bool { + ellipsis := -1 // position of ellipsis in ip + + // Might have leading ellipsis + if len(s) >= 2 && s[0] == ':' && s[1] == ':' { + ellipsis = 0 + s = s[2:] + // Might be only ellipsis + if len(s) == 0 { + return true + } + } + + // Loop, parsing hex numbers followed by colon. + i := 0 + for i < net.IPv6len { + // Hex number. + n, ci := 0, 0 + + for ci = 0; ci < len(s); ci++ { + if '0' <= s[ci] && s[ci] <= '9' { + n *= 16 + n += int(s[ci] - '0') + } else if 'a' <= s[ci] && s[ci] <= 'f' { + n *= 16 + n += int(s[ci]-'a') + 10 + } else if 'A' <= s[ci] && s[ci] <= 'F' { + n *= 16 + n += int(s[ci]-'A') + 10 + } else { + break + } + if n > 0xFFFF { + return false + } + } + if ci == 0 || n > 0xFFFF { + return false + } + + if ci < len(s) && s[ci] == '.' { + if ellipsis < 0 && i != net.IPv6len-net.IPv4len { + return false + } + if i+net.IPv4len > net.IPv6len { + return false + } + + if !IsIPv4(s) { + return false + } + + s = "" + i += net.IPv4len + break + } + + // Save this 16-bit chunk. + i += 2 + + // Stop at end of string. + s = s[ci:] + if len(s) == 0 { + break + } + + // Otherwise must be followed by colon and more. + if s[0] != ':' || len(s) == 1 { + return false + } + s = s[1:] + + // Look for ellipsis. + if s[0] == ':' { + if ellipsis >= 0 { // already have one + return false + } + ellipsis = i + s = s[1:] + if len(s) == 0 { // can be at end + break + } + } + } + + // Must have used entire string. + if len(s) != 0 { + return false + } + + // If didn't parse enough, expand ellipsis. + if i < net.IPv6len { + if ellipsis < 0 { + return false + } + } else if ellipsis >= 0 { + // Ellipsis must represent at least one 0 group. + return false + } + return true +} diff --git a/utils/ips_test.go b/utils/ips_test.go new file mode 100644 index 0000000000..49c258e604 --- /dev/null +++ b/utils/ips_test.go @@ -0,0 +1,85 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package utils + +import ( + "net" + "testing" +) + +func Test_IsIPv4(t *testing.T) { + t.Parallel() + + AssertEqual(t, true, IsIPv4("174.23.33.100")) + AssertEqual(t, true, IsIPv4("127.0.0.1")) + AssertEqual(t, true, IsIPv4("0.0.0.0")) + + AssertEqual(t, false, IsIPv4(".0.0.0")) + AssertEqual(t, false, IsIPv4("0.0.0.")) + AssertEqual(t, false, IsIPv4("0.0.0")) + AssertEqual(t, false, IsIPv4(".0.0.0.")) + AssertEqual(t, false, IsIPv4("0.0.0.0.0")) + AssertEqual(t, false, IsIPv4("0")) + AssertEqual(t, false, IsIPv4("")) + AssertEqual(t, false, IsIPv4("2345:0425:2CA1::0567:5673:23b5")) + AssertEqual(t, false, IsIPv4("invalid")) +} + +// go test -v -run=^$ -bench=UnsafeString -benchmem -count=2 + +func Benchmark_IsIPv4(b *testing.B) { + ip := "174.23.33.100" + var res bool + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = IsIPv4(ip) + } + AssertEqual(b, true, res) + }) + + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = net.ParseIP(ip) != nil + } + AssertEqual(b, true, res) + }) +} + +func Test_IsIPv6(t *testing.T) { + t.Parallel() + + AssertEqual(t, true, IsIPv6("9396:9549:b4f7:8ed0:4791:1330:8c06:e62d")) + AssertEqual(t, true, IsIPv6("2345:0425:2CA1::0567:5673:23b5")) + AssertEqual(t, true, IsIPv6("2001:1:2:3:4:5:6:7")) + + AssertEqual(t, false, IsIPv6("1.1.1.1")) + AssertEqual(t, false, IsIPv6("2001:1:2:3:4:5:6:")) + AssertEqual(t, false, IsIPv6(":1:2:3:4:5:6:")) + AssertEqual(t, false, IsIPv6("1:2:3:4:5:6:")) + AssertEqual(t, false, IsIPv6("")) + AssertEqual(t, false, IsIPv6("invalid")) +} + +// go test -v -run=^$ -bench=UnsafeString -benchmem -count=2 + +func Benchmark_IsIPv6(b *testing.B) { + ip := "9396:9549:b4f7:8ed0:4791:1330:8c06:e62d" + var res bool + + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = IsIPv6(ip) + } + AssertEqual(b, true, res) + }) + + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = net.ParseIP(ip) != nil + } + AssertEqual(b, true, res) + }) +} From ce2d087394dc74dbe45e6c9f9bb263ae6f105a26 Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 19 Oct 2022 16:22:42 +0200 Subject: [PATCH 130/141] Improve memory storage (#2162) * improve memory storage code and performance * improve memory storage code and performance * improve memory storage code and performance * improve memory storage code and performance * improve memory storage code and performance * improve memory storage code and performance --- internal/memory/memory.go | 49 ++++---- internal/memory/memory_test.go | 2 +- internal/storage/memory/config.go | 33 ++++++ internal/storage/memory/memory.go | 42 +++++-- internal/storage/memory/memory_test.go | 153 +++++++++++++++++++++++++ middleware/limiter/limiter_fixed.go | 12 +- middleware/limiter/limiter_sliding.go | 11 +- utils/time.go | 32 ++++++ utils/time_test.go | 45 ++++++++ 9 files changed, 326 insertions(+), 53 deletions(-) create mode 100644 internal/storage/memory/config.go create mode 100644 internal/storage/memory/memory_test.go create mode 100644 utils/time.go create mode 100644 utils/time_test.go diff --git a/internal/memory/memory.go b/internal/memory/memory.go index dd8111a361..0c37aa9c2e 100644 --- a/internal/memory/memory.go +++ b/internal/memory/memory.go @@ -1,15 +1,18 @@ +// Package memory Is a slight copy of the memory storage, but far from the storage interface it can not only work with bytes +// but directly store any kind of data without having to encode it each time, which gives a huge speed advantage package memory import ( "sync" "sync/atomic" "time" + + "github.com/gofiber/fiber/v2/utils" ) type Storage struct { sync.RWMutex data map[string]item // data - ts uint32 // timestamp } type item struct { @@ -21,10 +24,9 @@ type item struct { func New() *Storage { store := &Storage{ data: make(map[string]item), - ts: uint32(time.Now().Unix()), } + utils.StartTimeStampUpdater() go store.gc(1 * time.Second) - go store.updater(1 * time.Second) return store } @@ -33,7 +35,7 @@ func (s *Storage) Get(key string) interface{} { s.RLock() v, ok := s.data[key] s.RUnlock() - if !ok || v.e != 0 && v.e <= atomic.LoadUint32(&s.ts) { + if !ok || v.e != 0 && v.e <= atomic.LoadUint32(&utils.Timestamp) { return nil } return v.v @@ -43,7 +45,7 @@ func (s *Storage) Get(key string) interface{} { func (s *Storage) Set(key string, val interface{}, ttl time.Duration) { var exp uint32 if ttl > 0 { - exp = uint32(ttl.Seconds()) + atomic.LoadUint32(&s.ts) + exp = uint32(ttl.Seconds()) + atomic.LoadUint32(&utils.Timestamp) } s.Lock() s.data[key] = item{exp, val} @@ -64,28 +66,27 @@ func (s *Storage) Reset() { s.Unlock() } -func (s *Storage) updater(sleep time.Duration) { - for { - time.Sleep(sleep) - atomic.StoreUint32(&s.ts, uint32(time.Now().Unix())) - } -} func (s *Storage) gc(sleep time.Duration) { - expired := []string{} + ticker := time.NewTicker(sleep) + defer ticker.Stop() + var expired []string + for { - time.Sleep(sleep) - expired = expired[:0] - s.RLock() - for key, v := range s.data { - if v.e != 0 && v.e <= atomic.LoadUint32(&s.ts) { - expired = append(expired, key) + select { + case <-ticker.C: + expired = expired[:0] + s.RLock() + for key, v := range s.data { + if v.e != 0 && v.e <= atomic.LoadUint32(&utils.Timestamp) { + expired = append(expired, key) + } } + s.RUnlock() + s.Lock() + for i := range expired { + delete(s.data, expired[i]) + } + s.Unlock() } - s.RUnlock() - s.Lock() - for i := range expired { - delete(s.data, expired[i]) - } - s.Unlock() } } diff --git a/internal/memory/memory_test.go b/internal/memory/memory_test.go index a592bb7cba..12bcf3884c 100644 --- a/internal/memory/memory_test.go +++ b/internal/memory/memory_test.go @@ -58,7 +58,7 @@ func Benchmark_Memory(b *testing.B) { for i := 0; i < keyLength; i++ { keys[i] = utils.UUID() } - value := []string{"some", "random", "value"} + value := []byte("joe") ttl := 2 * time.Second b.Run("fiber_memory", func(b *testing.B) { diff --git a/internal/storage/memory/config.go b/internal/storage/memory/config.go new file mode 100644 index 0000000000..07d13edb5b --- /dev/null +++ b/internal/storage/memory/config.go @@ -0,0 +1,33 @@ +package memory + +import "time" + +// Config defines the config for storage. +type Config struct { + // Time before deleting expired keys + // + // Default is 10 * time.Second + GCInterval time.Duration +} + +// ConfigDefault is the default config +var ConfigDefault = Config{ + GCInterval: 10 * time.Second, +} + +// configDefault is a helper function to set default values +func configDefault(config ...Config) Config { + // Return default config if nothing provided + if len(config) < 1 { + return ConfigDefault + } + + // Override default config + cfg := config[0] + + // Set default values + if int(cfg.GCInterval.Seconds()) <= 0 { + cfg.GCInterval = ConfigDefault.GCInterval + } + return cfg +} diff --git a/internal/storage/memory/memory.go b/internal/storage/memory/memory.go index 048fb3a325..ff43c30533 100644 --- a/internal/storage/memory/memory.go +++ b/internal/storage/memory/memory.go @@ -1,8 +1,13 @@ +// Package memory Is a copy of the storage memory from the external storage packet as a purpose to test the behavior +// in the unittests when using a storages from these packets package memory import ( "sync" + "sync/atomic" "time" + + "github.com/gofiber/fiber/v2/utils" ) // Storage interface that is implemented by storage providers @@ -14,21 +19,25 @@ type Storage struct { } type entry struct { + data []byte // max value is 4294967295 -> Sun Feb 07 2106 06:28:15 GMT+0000 expiry uint32 - data []byte } // New creates a new memory storage -func New() *Storage { +func New(config ...Config) *Storage { + // Set default config + cfg := configDefault(config...) + // Create storage store := &Storage{ db: make(map[string]entry), - gcInterval: 10 * time.Second, + gcInterval: cfg.GCInterval, done: make(chan struct{}), } // Start garbage collector + utils.StartTimeStampUpdater() go store.gc() return store @@ -42,7 +51,7 @@ func (s *Storage) Get(key string) ([]byte, error) { s.mux.RLock() v, ok := s.db[key] s.mux.RUnlock() - if !ok || v.expiry != 0 && v.expiry <= uint32(time.Now().Unix()) { + if !ok || v.expiry != 0 && v.expiry <= atomic.LoadUint32(&utils.Timestamp) { return nil, nil } @@ -58,11 +67,11 @@ func (s *Storage) Set(key string, val []byte, exp time.Duration) error { var expire uint32 if exp != 0 { - expire = uint32(time.Now().Add(exp).Unix()) + expire = uint32(exp.Seconds()) + atomic.LoadUint32(&utils.Timestamp) } s.mux.Lock() - s.db[key] = entry{expire, val} + s.db[key] = entry{val, expire} s.mux.Unlock() return nil } @@ -96,20 +105,31 @@ func (s *Storage) Close() error { func (s *Storage) gc() { ticker := time.NewTicker(s.gcInterval) defer ticker.Stop() + var expired []string for { select { case <-s.done: return - case t := <-ticker.C: - now := uint32(t.Unix()) - s.mux.Lock() + case <-ticker.C: + expired = expired[:0] + s.mux.RLock() for id, v := range s.db { - if v.expiry != 0 && v.expiry < now { - delete(s.db, id) + if v.expiry != 0 && v.expiry < atomic.LoadUint32(&utils.Timestamp) { + expired = append(expired, id) } } + s.mux.RUnlock() + s.mux.Lock() + for i := range expired { + delete(s.db, expired[i]) + } s.mux.Unlock() } } } + +// Return database client +func (s *Storage) Conn() map[string]entry { + return s.db +} diff --git a/internal/storage/memory/memory_test.go b/internal/storage/memory/memory_test.go new file mode 100644 index 0000000000..1fc527f2cd --- /dev/null +++ b/internal/storage/memory/memory_test.go @@ -0,0 +1,153 @@ +package memory + +import ( + "testing" + "time" + + "github.com/gofiber/fiber/v2/utils" +) + +var testStore = New() + +func Test_Storage_Memory_Set(t *testing.T) { + var ( + key = "john" + val = []byte("doe") + ) + + err := testStore.Set(key, val, 0) + utils.AssertEqual(t, nil, err) +} + +func Test_Storage_Memory_Set_Override(t *testing.T) { + var ( + key = "john" + val = []byte("doe") + ) + + err := testStore.Set(key, val, 0) + utils.AssertEqual(t, nil, err) + + err = testStore.Set(key, val, 0) + utils.AssertEqual(t, nil, err) +} + +func Test_Storage_Memory_Get(t *testing.T) { + var ( + key = "john" + val = []byte("doe") + ) + + err := testStore.Set(key, val, 0) + utils.AssertEqual(t, nil, err) + + result, err := testStore.Get(key) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, val, result) +} + +func Test_Storage_Memory_Set_Expiration(t *testing.T) { + var ( + key = "john" + val = []byte("doe") + exp = 1 * time.Second + ) + + err := testStore.Set(key, val, exp) + utils.AssertEqual(t, nil, err) + + time.Sleep(1100 * time.Millisecond) +} + +func Test_Storage_Memory_Get_Expired(t *testing.T) { + var ( + key = "john" + ) + + result, err := testStore.Get(key) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, true, len(result) == 0) +} + +func Test_Storage_Memory_Get_NotExist(t *testing.T) { + + result, err := testStore.Get("notexist") + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, true, len(result) == 0) +} + +func Test_Storage_Memory_Delete(t *testing.T) { + var ( + key = "john" + val = []byte("doe") + ) + + err := testStore.Set(key, val, 0) + utils.AssertEqual(t, nil, err) + + err = testStore.Delete(key) + utils.AssertEqual(t, nil, err) + + result, err := testStore.Get(key) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, true, len(result) == 0) +} + +func Test_Storage_Memory_Reset(t *testing.T) { + var ( + val = []byte("doe") + ) + + err := testStore.Set("john1", val, 0) + utils.AssertEqual(t, nil, err) + + err = testStore.Set("john2", val, 0) + utils.AssertEqual(t, nil, err) + + err = testStore.Reset() + utils.AssertEqual(t, nil, err) + + result, err := testStore.Get("john1") + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, true, len(result) == 0) + + result, err = testStore.Get("john2") + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, true, len(result) == 0) +} + +func Test_Storage_Memory_Close(t *testing.T) { + utils.AssertEqual(t, nil, testStore.Close()) +} + +func Test_Storage_Memory_Conn(t *testing.T) { + utils.AssertEqual(t, true, testStore.Conn() != nil) +} + +// go test -v -run=^$ -bench=Benchmark_Storage_Memory -benchmem -count=4 +func Benchmark_Storage_Memory(b *testing.B) { + keyLength := 1000 + keys := make([]string, keyLength) + for i := 0; i < keyLength; i++ { + keys[i] = utils.UUID() + } + value := []byte("joe") + + ttl := 2 * time.Second + b.Run("fiber_memory", func(b *testing.B) { + d := New() + b.ReportAllocs() + b.ResetTimer() + for n := 0; n < b.N; n++ { + for _, key := range keys { + d.Set(key, value, ttl) + } + for _, key := range keys { + _, _ = d.Get(key) + } + for _, key := range keys { + d.Delete(key) + } + } + }) +} diff --git a/middleware/limiter/limiter_fixed.go b/middleware/limiter/limiter_fixed.go index ed1f23d523..b6b6d35939 100644 --- a/middleware/limiter/limiter_fixed.go +++ b/middleware/limiter/limiter_fixed.go @@ -4,9 +4,9 @@ import ( "strconv" "sync" "sync/atomic" - "time" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" ) type FixedWindow struct{} @@ -17,7 +17,6 @@ func (FixedWindow) New(cfg Config) fiber.Handler { // Limiter variables mux = &sync.RWMutex{} max = strconv.Itoa(cfg.Max) - timestamp = uint64(time.Now().Unix()) expiration = uint64(cfg.Expiration.Seconds()) ) @@ -25,12 +24,7 @@ func (FixedWindow) New(cfg Config) fiber.Handler { manager := newManager(cfg.Storage) // Update timestamp every second - go func() { - for { - atomic.StoreUint64(×tamp, uint64(time.Now().Unix())) - time.Sleep(1 * time.Second) - } - }() + utils.StartTimeStampUpdater() // Return new handler return func(c *fiber.Ctx) error { @@ -49,7 +43,7 @@ func (FixedWindow) New(cfg Config) fiber.Handler { e := manager.get(key) // Get timestamp - ts := atomic.LoadUint64(×tamp) + ts := uint64(atomic.LoadUint32(&utils.Timestamp)) // Set expiration if entry does not exist if e.exp == 0 { diff --git a/middleware/limiter/limiter_sliding.go b/middleware/limiter/limiter_sliding.go index 9369998d29..7f49863d7a 100644 --- a/middleware/limiter/limiter_sliding.go +++ b/middleware/limiter/limiter_sliding.go @@ -7,6 +7,7 @@ import ( "time" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" ) type SlidingWindow struct{} @@ -17,7 +18,6 @@ func (SlidingWindow) New(cfg Config) fiber.Handler { // Limiter variables mux = &sync.RWMutex{} max = strconv.Itoa(cfg.Max) - timestamp = uint64(time.Now().Unix()) expiration = uint64(cfg.Expiration.Seconds()) ) @@ -25,12 +25,7 @@ func (SlidingWindow) New(cfg Config) fiber.Handler { manager := newManager(cfg.Storage) // Update timestamp every second - go func() { - for { - atomic.StoreUint64(×tamp, uint64(time.Now().Unix())) - time.Sleep(1 * time.Second) - } - }() + utils.StartTimeStampUpdater() // Return new handler return func(c *fiber.Ctx) error { @@ -49,7 +44,7 @@ func (SlidingWindow) New(cfg Config) fiber.Handler { e := manager.get(key) // Get timestamp - ts := atomic.LoadUint64(×tamp) + ts := uint64(atomic.LoadUint32(&utils.Timestamp)) // Set expiration if entry does not exist if e.exp == 0 { diff --git a/utils/time.go b/utils/time.go new file mode 100644 index 0000000000..8ea13c2262 --- /dev/null +++ b/utils/time.go @@ -0,0 +1,32 @@ +package utils + +import ( + "sync" + "sync/atomic" + "time" +) + +var ( + timestampTimer sync.Once + // Timestamp please start the timer function before you use this value + // please load the value with atomic `atomic.LoadUint32(&utils.Timestamp)` + Timestamp uint32 +) + +// StartTimeStampUpdater starts a concurrent function which stores the timestamp to an atomic value per second, +// which is much better for performance than determining it at runtime each time +func StartTimeStampUpdater() { + timestampTimer.Do(func() { + // set initial value + atomic.StoreUint32(&Timestamp, uint32(time.Now().Unix())) + go func(sleep time.Duration) { + ticker := time.NewTicker(sleep) + defer ticker.Stop() + + for t := range ticker.C { + // update timestamp + atomic.StoreUint32(&Timestamp, uint32(t.Unix())) + } + }(1 * time.Second) // duration + }) +} diff --git a/utils/time_test.go b/utils/time_test.go new file mode 100644 index 0000000000..75c13b1dd8 --- /dev/null +++ b/utils/time_test.go @@ -0,0 +1,45 @@ +package utils + +import ( + "sync/atomic" + "testing" + "time" +) + +func checkTimeStamp(t testing.TB, expectedCurrent, actualCurrent uint32) { + // test with some buffer in front and back of the expectedCurrent time -> because of the timing on the work machine + AssertEqual(t, true, actualCurrent >= expectedCurrent-1 || actualCurrent <= expectedCurrent+1) +} + +func Test_TimeStampUpdater(t *testing.T) { + t.Parallel() + + StartTimeStampUpdater() + + now := uint32(time.Now().Unix()) + checkTimeStamp(t, now, atomic.LoadUint32(&Timestamp)) + // one second later + time.Sleep(1 * time.Second) + checkTimeStamp(t, now+1, atomic.LoadUint32(&Timestamp)) + // two seconds later + time.Sleep(1 * time.Second) + checkTimeStamp(t, now+2, atomic.LoadUint32(&Timestamp)) +} + +func Benchmark_CalculateTimestamp(b *testing.B) { + StartTimeStampUpdater() + + var res uint32 + b.Run("fiber", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = atomic.LoadUint32(&Timestamp) + } + checkTimeStamp(b, uint32(time.Now().Unix()), res) + }) + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + res = uint32(time.Now().Unix()) + } + checkTimeStamp(b, uint32(time.Now().Unix()), res) + }) +} From 917c937c7663eeeb7c424a7f25c6cc5696568f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Roberto=20Vasconcelos?= Date: Thu, 20 Oct 2022 15:42:06 -0300 Subject: [PATCH 131/141] [Bug] Fix query string parameter pass to fiber context (#2164) * Remove dump to keep querystring param when test * Remove unused import --- app.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app.go b/app.go index 0ca6e7dc66..5ae0318055 100644 --- a/app.go +++ b/app.go @@ -9,7 +9,6 @@ package fiber import ( "bufio" - "bytes" "errors" "fmt" "net" @@ -883,11 +882,6 @@ func (app *App) Test(req *http.Request, msTimeout ...int) (resp *http.Response, return nil, err } - // adding back the query from URL, since dump cleans it - dumps := bytes.Split(dump, []byte(" ")) - dumps[1] = []byte(req.URL.String()) - dump = bytes.Join(dumps, []byte(" ")) - // Create test connection conn := new(testConn) From 5fb93fdff665ae1a81a5a6bd11707cda10d1b9ea Mon Sep 17 00:00:00 2001 From: kinggo Date: Fri, 21 Oct 2022 14:30:56 +0800 Subject: [PATCH 132/141] :bug: fix: multi-byte AppName displays confusion (#2148) fix: solve wrong print width --- go.mod | 6 +- go.sum | 11 +- internal/colorable/LICENSE | 21 - internal/colorable/colorable_appengine.go | 38 - internal/colorable/colorable_others.go | 38 - internal/colorable/colorable_windows.go | 1043 --------------------- internal/colorable/noncolorable.go | 55 -- internal/isatty/LICENSE | 9 - internal/isatty/isatty_bsd.go | 19 - internal/isatty/isatty_others.go | 16 - internal/isatty/isatty_plan9.go | 23 - internal/isatty/isatty_solaris.go | 22 - internal/isatty/isatty_tcgets.go | 19 - internal/isatty/isatty_windows.go | 126 --- listen.go | 9 +- middleware/logger/logger.go | 4 +- 16 files changed, 22 insertions(+), 1437 deletions(-) delete mode 100644 internal/colorable/LICENSE delete mode 100644 internal/colorable/colorable_appengine.go delete mode 100644 internal/colorable/colorable_others.go delete mode 100644 internal/colorable/colorable_windows.go delete mode 100644 internal/colorable/noncolorable.go delete mode 100644 internal/isatty/LICENSE delete mode 100644 internal/isatty/isatty_bsd.go delete mode 100644 internal/isatty/isatty_others.go delete mode 100644 internal/isatty/isatty_plan9.go delete mode 100644 internal/isatty/isatty_solaris.go delete mode 100644 internal/isatty/isatty_tcgets.go delete mode 100644 internal/isatty/isatty_windows.go diff --git a/go.mod b/go.mod index d6d6fffb08..c726a553f2 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,17 @@ module github.com/gofiber/fiber/v2 go 1.19 require ( + github.com/mattn/go-colorable v0.1.13 + github.com/mattn/go-isatty v0.0.16 + github.com/mattn/go-runewidth v0.0.14 github.com/valyala/fasthttp v1.40.0 - golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab ) require ( github.com/andybalholm/brotli v1.0.4 // indirect github.com/klauspost/compress v1.15.0 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect ) diff --git a/go.sum b/go.sum index b96abe70e2..46a41e53ee 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,14 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U= github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +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-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc= @@ -15,8 +23,9 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/internal/colorable/LICENSE b/internal/colorable/LICENSE deleted file mode 100644 index 91b5cef30e..0000000000 --- a/internal/colorable/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Yasuhiro Matsumoto - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/internal/colorable/colorable_appengine.go b/internal/colorable/colorable_appengine.go deleted file mode 100644 index 17811c0228..0000000000 --- a/internal/colorable/colorable_appengine.go +++ /dev/null @@ -1,38 +0,0 @@ -//go:build appengine -// +build appengine - -package colorable - -import ( - "io" - "os" - - _ "github.com/gofiber/fiber/v2/internal/isatty" -) - -// NewColorable returns new instance of Writer which handles escape sequence. -func NewColorable(file *os.File) io.Writer { - if file == nil { - panic("nil passed instead of *os.File to NewColorable()") - } - - return file -} - -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. -func NewColorableStdout() io.Writer { - return os.Stdout -} - -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. -func NewColorableStderr() io.Writer { - return os.Stderr -} - -// EnableColorsStdout enable colors if possible. -func EnableColorsStdout(enabled *bool) func() { - if enabled != nil { - *enabled = true - } - return func() {} -} diff --git a/internal/colorable/colorable_others.go b/internal/colorable/colorable_others.go deleted file mode 100644 index 22965c60df..0000000000 --- a/internal/colorable/colorable_others.go +++ /dev/null @@ -1,38 +0,0 @@ -//go:build !windows && !appengine -// +build !windows,!appengine - -package colorable - -import ( - "io" - "os" - - _ "github.com/gofiber/fiber/v2/internal/isatty" -) - -// NewColorable returns new instance of Writer which handles escape sequence. -func NewColorable(file *os.File) io.Writer { - if file == nil { - panic("nil passed instead of *os.File to NewColorable()") - } - - return file -} - -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. -func NewColorableStdout() io.Writer { - return os.Stdout -} - -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. -func NewColorableStderr() io.Writer { - return os.Stderr -} - -// EnableColorsStdout enable colors if possible. -func EnableColorsStdout(enabled *bool) func() { - if enabled != nil { - *enabled = true - } - return func() {} -} diff --git a/internal/colorable/colorable_windows.go b/internal/colorable/colorable_windows.go deleted file mode 100644 index de139e5a7e..0000000000 --- a/internal/colorable/colorable_windows.go +++ /dev/null @@ -1,1043 +0,0 @@ -//go:build windows && !appengine -// +build windows,!appengine - -package colorable - -import ( - "bytes" - "io" - "math" - "os" - "strconv" - "strings" - "sync" - "syscall" - "unsafe" - - "github.com/gofiber/fiber/v2/internal/isatty" -) - -const ( - foregroundBlue = 0x1 - foregroundGreen = 0x2 - foregroundRed = 0x4 - foregroundIntensity = 0x8 - foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) - backgroundBlue = 0x10 - backgroundGreen = 0x20 - backgroundRed = 0x40 - backgroundIntensity = 0x80 - backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) - commonLvbUnderscore = 0x8000 - - cENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4 -) - -const ( - genericRead = 0x80000000 - genericWrite = 0x40000000 -) - -const ( - consoleTextmodeBuffer = 0x1 -) - -type wchar uint16 -type short int16 -type dword uint32 -type word uint16 - -type coord struct { - x short - y short -} - -type smallRect struct { - left short - top short - right short - bottom short -} - -type consoleScreenBufferInfo struct { - size coord - cursorPosition coord - attributes word - window smallRect - maximumWindowSize coord -} - -type consoleCursorInfo struct { - size dword - visible int32 -} - -var ( - kernel32 = syscall.NewLazyDLL("kernel32.dll") - procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") - procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") - procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") - procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") - procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") - procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") - procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") - procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW") - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") - procSetConsoleMode = kernel32.NewProc("SetConsoleMode") - procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") -) - -// Writer provides colorable Writer to the console -type Writer struct { - out io.Writer - handle syscall.Handle - althandle syscall.Handle - oldattr word - oldpos coord - rest bytes.Buffer - mutex sync.Mutex -} - -// NewColorable returns new instance of Writer which handles escape sequence from File. -func NewColorable(file *os.File) io.Writer { - if file == nil { - panic("nil passed instead of *os.File to NewColorable()") - } - - if isatty.IsTerminal(file.Fd()) { - var mode uint32 - if r, _, _ := procGetConsoleMode.Call(file.Fd(), uintptr(unsafe.Pointer(&mode))); r != 0 && mode&cENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 { - return file - } - var csbi consoleScreenBufferInfo - handle := syscall.Handle(file.Fd()) - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} - } - return file -} - -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. -func NewColorableStdout() io.Writer { - return NewColorable(os.Stdout) -} - -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. -func NewColorableStderr() io.Writer { - return NewColorable(os.Stderr) -} - -var color256 = map[int]int{ - 0: 0x000000, - 1: 0x800000, - 2: 0x008000, - 3: 0x808000, - 4: 0x000080, - 5: 0x800080, - 6: 0x008080, - 7: 0xc0c0c0, - 8: 0x808080, - 9: 0xff0000, - 10: 0x00ff00, - 11: 0xffff00, - 12: 0x0000ff, - 13: 0xff00ff, - 14: 0x00ffff, - 15: 0xffffff, - 16: 0x000000, - 17: 0x00005f, - 18: 0x000087, - 19: 0x0000af, - 20: 0x0000d7, - 21: 0x0000ff, - 22: 0x005f00, - 23: 0x005f5f, - 24: 0x005f87, - 25: 0x005faf, - 26: 0x005fd7, - 27: 0x005fff, - 28: 0x008700, - 29: 0x00875f, - 30: 0x008787, - 31: 0x0087af, - 32: 0x0087d7, - 33: 0x0087ff, - 34: 0x00af00, - 35: 0x00af5f, - 36: 0x00af87, - 37: 0x00afaf, - 38: 0x00afd7, - 39: 0x00afff, - 40: 0x00d700, - 41: 0x00d75f, - 42: 0x00d787, - 43: 0x00d7af, - 44: 0x00d7d7, - 45: 0x00d7ff, - 46: 0x00ff00, - 47: 0x00ff5f, - 48: 0x00ff87, - 49: 0x00ffaf, - 50: 0x00ffd7, - 51: 0x00ffff, - 52: 0x5f0000, - 53: 0x5f005f, - 54: 0x5f0087, - 55: 0x5f00af, - 56: 0x5f00d7, - 57: 0x5f00ff, - 58: 0x5f5f00, - 59: 0x5f5f5f, - 60: 0x5f5f87, - 61: 0x5f5faf, - 62: 0x5f5fd7, - 63: 0x5f5fff, - 64: 0x5f8700, - 65: 0x5f875f, - 66: 0x5f8787, - 67: 0x5f87af, - 68: 0x5f87d7, - 69: 0x5f87ff, - 70: 0x5faf00, - 71: 0x5faf5f, - 72: 0x5faf87, - 73: 0x5fafaf, - 74: 0x5fafd7, - 75: 0x5fafff, - 76: 0x5fd700, - 77: 0x5fd75f, - 78: 0x5fd787, - 79: 0x5fd7af, - 80: 0x5fd7d7, - 81: 0x5fd7ff, - 82: 0x5fff00, - 83: 0x5fff5f, - 84: 0x5fff87, - 85: 0x5fffaf, - 86: 0x5fffd7, - 87: 0x5fffff, - 88: 0x870000, - 89: 0x87005f, - 90: 0x870087, - 91: 0x8700af, - 92: 0x8700d7, - 93: 0x8700ff, - 94: 0x875f00, - 95: 0x875f5f, - 96: 0x875f87, - 97: 0x875faf, - 98: 0x875fd7, - 99: 0x875fff, - 100: 0x878700, - 101: 0x87875f, - 102: 0x878787, - 103: 0x8787af, - 104: 0x8787d7, - 105: 0x8787ff, - 106: 0x87af00, - 107: 0x87af5f, - 108: 0x87af87, - 109: 0x87afaf, - 110: 0x87afd7, - 111: 0x87afff, - 112: 0x87d700, - 113: 0x87d75f, - 114: 0x87d787, - 115: 0x87d7af, - 116: 0x87d7d7, - 117: 0x87d7ff, - 118: 0x87ff00, - 119: 0x87ff5f, - 120: 0x87ff87, - 121: 0x87ffaf, - 122: 0x87ffd7, - 123: 0x87ffff, - 124: 0xaf0000, - 125: 0xaf005f, - 126: 0xaf0087, - 127: 0xaf00af, - 128: 0xaf00d7, - 129: 0xaf00ff, - 130: 0xaf5f00, - 131: 0xaf5f5f, - 132: 0xaf5f87, - 133: 0xaf5faf, - 134: 0xaf5fd7, - 135: 0xaf5fff, - 136: 0xaf8700, - 137: 0xaf875f, - 138: 0xaf8787, - 139: 0xaf87af, - 140: 0xaf87d7, - 141: 0xaf87ff, - 142: 0xafaf00, - 143: 0xafaf5f, - 144: 0xafaf87, - 145: 0xafafaf, - 146: 0xafafd7, - 147: 0xafafff, - 148: 0xafd700, - 149: 0xafd75f, - 150: 0xafd787, - 151: 0xafd7af, - 152: 0xafd7d7, - 153: 0xafd7ff, - 154: 0xafff00, - 155: 0xafff5f, - 156: 0xafff87, - 157: 0xafffaf, - 158: 0xafffd7, - 159: 0xafffff, - 160: 0xd70000, - 161: 0xd7005f, - 162: 0xd70087, - 163: 0xd700af, - 164: 0xd700d7, - 165: 0xd700ff, - 166: 0xd75f00, - 167: 0xd75f5f, - 168: 0xd75f87, - 169: 0xd75faf, - 170: 0xd75fd7, - 171: 0xd75fff, - 172: 0xd78700, - 173: 0xd7875f, - 174: 0xd78787, - 175: 0xd787af, - 176: 0xd787d7, - 177: 0xd787ff, - 178: 0xd7af00, - 179: 0xd7af5f, - 180: 0xd7af87, - 181: 0xd7afaf, - 182: 0xd7afd7, - 183: 0xd7afff, - 184: 0xd7d700, - 185: 0xd7d75f, - 186: 0xd7d787, - 187: 0xd7d7af, - 188: 0xd7d7d7, - 189: 0xd7d7ff, - 190: 0xd7ff00, - 191: 0xd7ff5f, - 192: 0xd7ff87, - 193: 0xd7ffaf, - 194: 0xd7ffd7, - 195: 0xd7ffff, - 196: 0xff0000, - 197: 0xff005f, - 198: 0xff0087, - 199: 0xff00af, - 200: 0xff00d7, - 201: 0xff00ff, - 202: 0xff5f00, - 203: 0xff5f5f, - 204: 0xff5f87, - 205: 0xff5faf, - 206: 0xff5fd7, - 207: 0xff5fff, - 208: 0xff8700, - 209: 0xff875f, - 210: 0xff8787, - 211: 0xff87af, - 212: 0xff87d7, - 213: 0xff87ff, - 214: 0xffaf00, - 215: 0xffaf5f, - 216: 0xffaf87, - 217: 0xffafaf, - 218: 0xffafd7, - 219: 0xffafff, - 220: 0xffd700, - 221: 0xffd75f, - 222: 0xffd787, - 223: 0xffd7af, - 224: 0xffd7d7, - 225: 0xffd7ff, - 226: 0xffff00, - 227: 0xffff5f, - 228: 0xffff87, - 229: 0xffffaf, - 230: 0xffffd7, - 231: 0xffffff, - 232: 0x080808, - 233: 0x121212, - 234: 0x1c1c1c, - 235: 0x262626, - 236: 0x303030, - 237: 0x3a3a3a, - 238: 0x444444, - 239: 0x4e4e4e, - 240: 0x585858, - 241: 0x626262, - 242: 0x6c6c6c, - 243: 0x767676, - 244: 0x808080, - 245: 0x8a8a8a, - 246: 0x949494, - 247: 0x9e9e9e, - 248: 0xa8a8a8, - 249: 0xb2b2b2, - 250: 0xbcbcbc, - 251: 0xc6c6c6, - 252: 0xd0d0d0, - 253: 0xdadada, - 254: 0xe4e4e4, - 255: 0xeeeeee, -} - -// `\033]0;TITLESTR\007` -func doTitleSequence(er *bytes.Reader) error { - var c byte - var err error - - c, err = er.ReadByte() - if err != nil { - return err - } - if c != '0' && c != '2' { - return nil - } - c, err = er.ReadByte() - if err != nil { - return err - } - if c != ';' { - return nil - } - title := make([]byte, 0, 80) - for { - c, err = er.ReadByte() - if err != nil { - return err - } - if c == 0x07 || c == '\n' { - break - } - title = append(title, c) - } - if len(title) > 0 { - title8, err := syscall.UTF16PtrFromString(string(title)) - if err == nil { - procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8))) - } - } - return nil -} - -// returns Atoi(s) unless s == "" in which case it returns def -func atoiWithDefault(s string, def int) (int, error) { - if s == "" { - return def, nil - } - return strconv.Atoi(s) -} - -// Write writes data on console -func (w *Writer) Write(data []byte) (n int, err error) { - w.mutex.Lock() - defer w.mutex.Unlock() - var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) - - handle := w.handle - - var er *bytes.Reader - if w.rest.Len() > 0 { - var rest bytes.Buffer - w.rest.WriteTo(&rest) - w.rest.Reset() - rest.Write(data) - er = bytes.NewReader(rest.Bytes()) - } else { - er = bytes.NewReader(data) - } - var bw [1]byte -loop: - for { - c1, err := er.ReadByte() - if err != nil { - break loop - } - if c1 != 0x1b { - bw[0] = c1 - w.out.Write(bw[:]) - continue - } - c2, err := er.ReadByte() - if err != nil { - break loop - } - - switch c2 { - case '>': - continue - case ']': - w.rest.WriteByte(c1) - w.rest.WriteByte(c2) - er.WriteTo(&w.rest) - if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 { - break loop - } - er = bytes.NewReader(w.rest.Bytes()[2:]) - err := doTitleSequence(er) - if err != nil { - break loop - } - w.rest.Reset() - continue - // https://github.com/mattn/go-colorable/issues/27 - case '7': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - w.oldpos = csbi.cursorPosition - continue - case '8': - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) - continue - case 0x5b: - // execute part after switch - default: - continue - } - - w.rest.WriteByte(c1) - w.rest.WriteByte(c2) - er.WriteTo(&w.rest) - - var buf bytes.Buffer - var m byte - for i, c := range w.rest.Bytes()[2:] { - if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { - m = c - er = bytes.NewReader(w.rest.Bytes()[2+i+1:]) - w.rest.Reset() - break - } - buf.Write([]byte(string(c))) - } - if m == 0 { - break loop - } - - switch m { - case 'A': - n, err = atoiWithDefault(buf.String(), 1) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.y -= short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'B': - n, err = atoiWithDefault(buf.String(), 1) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.y += short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'C': - n, err = atoiWithDefault(buf.String(), 1) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x += short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'D': - n, err = atoiWithDefault(buf.String(), 1) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x -= short(n) - if csbi.cursorPosition.x < 0 { - csbi.cursorPosition.x = 0 - } - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'E': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x = 0 - csbi.cursorPosition.y += short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'F': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x = 0 - csbi.cursorPosition.y -= short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'G': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - if n < 1 { - n = 1 - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x = short(n - 1) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'H', 'f': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - if buf.Len() > 0 { - token := strings.Split(buf.String(), ";") - switch len(token) { - case 1: - n1, err := strconv.Atoi(token[0]) - if err != nil { - continue - } - csbi.cursorPosition.y = short(n1 - 1) - case 2: - n1, err := strconv.Atoi(token[0]) - if err != nil { - continue - } - n2, err := strconv.Atoi(token[1]) - if err != nil { - continue - } - csbi.cursorPosition.x = short(n2 - 1) - csbi.cursorPosition.y = short(n1 - 1) - } - } else { - csbi.cursorPosition.y = 0 - } - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'J': - n := 0 - if buf.Len() > 0 { - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - } - var count, written dword - var cursor coord - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - switch n { - case 0: - cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} - count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) - case 1: - cursor = coord{x: csbi.window.left, y: csbi.window.top} - count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x) - case 2: - cursor = coord{x: csbi.window.left, y: csbi.window.top} - count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) - } - procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - case 'K': - n := 0 - if buf.Len() > 0 { - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - var cursor coord - var count, written dword - switch n { - case 0: - cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} - count = dword(csbi.size.x - csbi.cursorPosition.x) - case 1: - cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} - count = dword(csbi.size.x - csbi.cursorPosition.x) - case 2: - cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} - count = dword(csbi.size.x) - } - procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - case 'X': - n := 0 - if buf.Len() > 0 { - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - var cursor coord - var written dword - cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} - procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - case 'm': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - attr := csbi.attributes - cs := buf.String() - if cs == "" { - procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr)) - continue - } - token := strings.Split(cs, ";") - for i := 0; i < len(token); i++ { - ns := token[i] - if n, err = strconv.Atoi(ns); err == nil { - switch { - case n == 0 || n == 100: - attr = w.oldattr - case n == 4: - attr |= commonLvbUnderscore - case (1 <= n && n <= 3) || n == 5: - attr |= foregroundIntensity - case n == 7 || n == 27: - attr = - (attr &^ (foregroundMask | backgroundMask)) | - ((attr & foregroundMask) << 4) | - ((attr & backgroundMask) >> 4) - case n == 22: - attr &^= foregroundIntensity - case n == 24: - attr &^= commonLvbUnderscore - case 30 <= n && n <= 37: - attr &= backgroundMask - if (n-30)&1 != 0 { - attr |= foregroundRed - } - if (n-30)&2 != 0 { - attr |= foregroundGreen - } - if (n-30)&4 != 0 { - attr |= foregroundBlue - } - case n == 38: // set foreground color. - if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { - if n256, err := strconv.Atoi(token[i+2]); err == nil { - if n256foreAttr == nil { - n256setup() - } - attr &= backgroundMask - attr |= n256foreAttr[n256] - i += 2 - } - } else if len(token) == 5 && token[i+1] == "2" { - var r, g, b int - r, _ = strconv.Atoi(token[i+2]) - g, _ = strconv.Atoi(token[i+3]) - b, _ = strconv.Atoi(token[i+4]) - i += 4 - if r > 127 { - attr |= foregroundRed - } - if g > 127 { - attr |= foregroundGreen - } - if b > 127 { - attr |= foregroundBlue - } - } else { - attr = attr & (w.oldattr & backgroundMask) - } - case n == 39: // reset foreground color. - attr &= backgroundMask - attr |= w.oldattr & foregroundMask - case 40 <= n && n <= 47: - attr &= foregroundMask - if (n-40)&1 != 0 { - attr |= backgroundRed - } - if (n-40)&2 != 0 { - attr |= backgroundGreen - } - if (n-40)&4 != 0 { - attr |= backgroundBlue - } - case n == 48: // set background color. - if i < len(token)-2 && token[i+1] == "5" { - if n256, err := strconv.Atoi(token[i+2]); err == nil { - if n256backAttr == nil { - n256setup() - } - attr &= foregroundMask - attr |= n256backAttr[n256] - i += 2 - } - } else if len(token) == 5 && token[i+1] == "2" { - var r, g, b int - r, _ = strconv.Atoi(token[i+2]) - g, _ = strconv.Atoi(token[i+3]) - b, _ = strconv.Atoi(token[i+4]) - i += 4 - if r > 127 { - attr |= backgroundRed - } - if g > 127 { - attr |= backgroundGreen - } - if b > 127 { - attr |= backgroundBlue - } - } else { - attr = attr & (w.oldattr & foregroundMask) - } - case n == 49: // reset foreground color. - attr &= foregroundMask - attr |= w.oldattr & backgroundMask - case 90 <= n && n <= 97: - attr = (attr & backgroundMask) - attr |= foregroundIntensity - if (n-90)&1 != 0 { - attr |= foregroundRed - } - if (n-90)&2 != 0 { - attr |= foregroundGreen - } - if (n-90)&4 != 0 { - attr |= foregroundBlue - } - case 100 <= n && n <= 107: - attr = (attr & foregroundMask) - attr |= backgroundIntensity - if (n-100)&1 != 0 { - attr |= backgroundRed - } - if (n-100)&2 != 0 { - attr |= backgroundGreen - } - if (n-100)&4 != 0 { - attr |= backgroundBlue - } - } - procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) - } - } - case 'h': - var ci consoleCursorInfo - cs := buf.String() - if cs == "5>" { - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - ci.visible = 0 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - } else if cs == "?25" { - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - ci.visible = 1 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - } else if cs == "?1049" { - if w.althandle == 0 { - h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0) - w.althandle = syscall.Handle(h) - if w.althandle != 0 { - handle = w.althandle - } - } - } - case 'l': - var ci consoleCursorInfo - cs := buf.String() - if cs == "5>" { - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - ci.visible = 1 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - } else if cs == "?25" { - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - ci.visible = 0 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - } else if cs == "?1049" { - if w.althandle != 0 { - syscall.CloseHandle(w.althandle) - w.althandle = 0 - handle = w.handle - } - } - case 's': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - w.oldpos = csbi.cursorPosition - case 'u': - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) - } - } - - return len(data), nil -} - -type consoleColor struct { - rgb int - red bool - green bool - blue bool - intensity bool -} - -func (c consoleColor) foregroundAttr() (attr word) { - if c.red { - attr |= foregroundRed - } - if c.green { - attr |= foregroundGreen - } - if c.blue { - attr |= foregroundBlue - } - if c.intensity { - attr |= foregroundIntensity - } - return -} - -func (c consoleColor) backgroundAttr() (attr word) { - if c.red { - attr |= backgroundRed - } - if c.green { - attr |= backgroundGreen - } - if c.blue { - attr |= backgroundBlue - } - if c.intensity { - attr |= backgroundIntensity - } - return -} - -var color16 = []consoleColor{ - {0x000000, false, false, false, false}, - {0x000080, false, false, true, false}, - {0x008000, false, true, false, false}, - {0x008080, false, true, true, false}, - {0x800000, true, false, false, false}, - {0x800080, true, false, true, false}, - {0x808000, true, true, false, false}, - {0xc0c0c0, true, true, true, false}, - {0x808080, false, false, false, true}, - {0x0000ff, false, false, true, true}, - {0x00ff00, false, true, false, true}, - {0x00ffff, false, true, true, true}, - {0xff0000, true, false, false, true}, - {0xff00ff, true, false, true, true}, - {0xffff00, true, true, false, true}, - {0xffffff, true, true, true, true}, -} - -type hsv struct { - h, s, v float32 -} - -func (a hsv) dist(b hsv) float32 { - dh := a.h - b.h - switch { - case dh > 0.5: - dh = 1 - dh - case dh < -0.5: - dh = -1 - dh - } - ds := a.s - b.s - dv := a.v - b.v - return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) -} - -func toHSV(rgb int) hsv { - r, g, b := float32((rgb&0xFF0000)>>16)/256.0, - float32((rgb&0x00FF00)>>8)/256.0, - float32(rgb&0x0000FF)/256.0 - min, max := minmax3f(r, g, b) - h := max - min - if h > 0 { - if max == r { - h = (g - b) / h - if h < 0 { - h += 6 - } - } else if max == g { - h = 2 + (b-r)/h - } else { - h = 4 + (r-g)/h - } - } - h /= 6.0 - s := max - min - if max != 0 { - s /= max - } - v := max - return hsv{h: h, s: s, v: v} -} - -type hsvTable []hsv - -func toHSVTable(rgbTable []consoleColor) hsvTable { - t := make(hsvTable, len(rgbTable)) - for i, c := range rgbTable { - t[i] = toHSV(c.rgb) - } - return t -} - -func (t hsvTable) find(rgb int) consoleColor { - hsv := toHSV(rgb) - n := 7 - l := float32(5.0) - for i, p := range t { - d := hsv.dist(p) - if d < l { - l, n = d, i - } - } - return color16[n] -} - -func minmax3f(a, b, c float32) (min, max float32) { - if a < b { - if b < c { - return a, c - } else if a < c { - return a, b - } else { - return c, b - } - } else { - if a < c { - return b, c - } else if b < c { - return b, a - } else { - return c, a - } - } -} - -var n256foreAttr []word -var n256backAttr []word - -func n256setup() { - n256foreAttr = make([]word, 256) - n256backAttr = make([]word, 256) - t := toHSVTable(color16) - for i, rgb := range color256 { - c := t.find(rgb) - n256foreAttr[i] = c.foregroundAttr() - n256backAttr[i] = c.backgroundAttr() - } -} - -// EnableColorsStdout enable colors if possible. -func EnableColorsStdout(enabled *bool) func() { - var mode uint32 - h := os.Stdout.Fd() - if r, _, _ := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&mode))); r != 0 { - if r, _, _ = procSetConsoleMode.Call(h, uintptr(mode|cENABLE_VIRTUAL_TERMINAL_PROCESSING)); r != 0 { - if enabled != nil { - *enabled = true - } - return func() { - procSetConsoleMode.Call(h, uintptr(mode)) - } - } - } - if enabled != nil { - *enabled = true - } - return func() {} -} diff --git a/internal/colorable/noncolorable.go b/internal/colorable/noncolorable.go deleted file mode 100644 index 95f2c6be25..0000000000 --- a/internal/colorable/noncolorable.go +++ /dev/null @@ -1,55 +0,0 @@ -package colorable - -import ( - "bytes" - "io" -) - -// NonColorable holds writer but removes escape sequence. -type NonColorable struct { - out io.Writer -} - -// NewNonColorable returns new instance of Writer which removes escape sequence from Writer. -func NewNonColorable(w io.Writer) io.Writer { - return &NonColorable{out: w} -} - -// Write writes data on console -func (w *NonColorable) Write(data []byte) (n int, err error) { - er := bytes.NewReader(data) - var bw [1]byte -loop: - for { - c1, err := er.ReadByte() - if err != nil { - break loop - } - if c1 != 0x1b { - bw[0] = c1 - w.out.Write(bw[:]) - continue - } - c2, err := er.ReadByte() - if err != nil { - break loop - } - if c2 != 0x5b { - continue - } - - var buf bytes.Buffer - for { - c, err := er.ReadByte() - if err != nil { - break loop - } - if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { - break - } - buf.Write([]byte(string(c))) - } - } - - return len(data), nil -} diff --git a/internal/isatty/LICENSE b/internal/isatty/LICENSE deleted file mode 100644 index 65dc692b6b..0000000000 --- a/internal/isatty/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -Copyright (c) Yasuhiro MATSUMOTO - -MIT License (Expat) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/internal/isatty/isatty_bsd.go b/internal/isatty/isatty_bsd.go deleted file mode 100644 index 39bbcf00f0..0000000000 --- a/internal/isatty/isatty_bsd.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build (darwin || freebsd || openbsd || netbsd || dragonfly) && !appengine -// +build darwin freebsd openbsd netbsd dragonfly -// +build !appengine - -package isatty - -import "golang.org/x/sys/unix" - -// IsTerminal return true if the file descriptor is terminal. -func IsTerminal(fd uintptr) bool { - _, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA) - return err == nil -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/internal/isatty/isatty_others.go b/internal/isatty/isatty_others.go deleted file mode 100644 index 31503226f6..0000000000 --- a/internal/isatty/isatty_others.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build appengine || js || nacl || wasm -// +build appengine js nacl wasm - -package isatty - -// IsTerminal returns true if the file descriptor is terminal which -// is always false on js and appengine classic which is a sandboxed PaaS. -func IsTerminal(fd uintptr) bool { - return false -} - -// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/internal/isatty/isatty_plan9.go b/internal/isatty/isatty_plan9.go deleted file mode 100644 index bae7f9bb3d..0000000000 --- a/internal/isatty/isatty_plan9.go +++ /dev/null @@ -1,23 +0,0 @@ -//go:build plan9 -// +build plan9 - -package isatty - -import ( - "syscall" -) - -// IsTerminal returns true if the given file descriptor is a terminal. -func IsTerminal(fd uintptr) bool { - path, err := syscall.Fd2path(int(fd)) - if err != nil { - return false - } - return path == "/dev/cons" || path == "/mnt/term/dev/cons" -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/internal/isatty/isatty_solaris.go b/internal/isatty/isatty_solaris.go deleted file mode 100644 index 69e5ab3cdc..0000000000 --- a/internal/isatty/isatty_solaris.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build solaris && !appengine -// +build solaris,!appengine - -package isatty - -import ( - "golang.org/x/sys/unix" -) - -// IsTerminal returns true if the given file descriptor is a terminal. -// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c -func IsTerminal(fd uintptr) bool { - var termio unix.Termio - err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio) - return err == nil -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/internal/isatty/isatty_tcgets.go b/internal/isatty/isatty_tcgets.go deleted file mode 100644 index 67787657fb..0000000000 --- a/internal/isatty/isatty_tcgets.go +++ /dev/null @@ -1,19 +0,0 @@ -//go:build (linux || aix || zos) && !appengine -// +build linux aix zos -// +build !appengine - -package isatty - -import "golang.org/x/sys/unix" - -// IsTerminal return true if the file descriptor is terminal. -func IsTerminal(fd uintptr) bool { - _, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) - return err == nil -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/internal/isatty/isatty_windows.go b/internal/isatty/isatty_windows.go deleted file mode 100644 index 367adab997..0000000000 --- a/internal/isatty/isatty_windows.go +++ /dev/null @@ -1,126 +0,0 @@ -//go:build windows && !appengine -// +build windows,!appengine - -package isatty - -import ( - "errors" - "strings" - "syscall" - "unicode/utf16" - "unsafe" -) - -const ( - objectNameInfo uintptr = 1 - fileNameInfo = 2 - fileTypePipe = 3 -) - -var ( - kernel32 = syscall.NewLazyDLL("kernel32.dll") - ntdll = syscall.NewLazyDLL("ntdll.dll") - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") - procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") - procGetFileType = kernel32.NewProc("GetFileType") - procNtQueryObject = ntdll.NewProc("NtQueryObject") -) - -func init() { - // Check if GetFileInformationByHandleEx is available. - if procGetFileInformationByHandleEx.Find() != nil { - procGetFileInformationByHandleEx = nil - } -} - -// IsTerminal return true if the file descriptor is terminal. -func IsTerminal(fd uintptr) bool { - var st uint32 - r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) - return r != 0 && e == 0 -} - -// Check pipe name is used for cygwin/msys2 pty. -// Cygwin/MSYS2 PTY has a name like: -// -// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master -func isCygwinPipeName(name string) bool { - token := strings.Split(name, "-") - if len(token) < 5 { - return false - } - - if token[0] != `\msys` && - token[0] != `\cygwin` && - token[0] != `\Device\NamedPipe\msys` && - token[0] != `\Device\NamedPipe\cygwin` { - return false - } - - if token[1] == "" { - return false - } - - if !strings.HasPrefix(token[2], "pty") { - return false - } - - if token[3] != `from` && token[3] != `to` { - return false - } - - if token[4] != "master" { - return false - } - - return true -} - -// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler -// since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion -// guys are using Windows XP, this is a workaround for those guys, it will also work on system from -// Windows vista to 10 -// see https://stackoverflow.com/a/18792477 for details -func getFileNameByHandle(fd uintptr) (string, error) { - if procNtQueryObject == nil { - return "", errors.New("ntdll.dll: NtQueryObject not supported") - } - - var buf [4 + syscall.MAX_PATH]uint16 - var result int - r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5, - fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0) - if r != 0 { - return "", e - } - return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil -} - -// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 -// terminal. -func IsCygwinTerminal(fd uintptr) bool { - if procGetFileInformationByHandleEx == nil { - name, err := getFileNameByHandle(fd) - if err != nil { - return false - } - return isCygwinPipeName(name) - } - - // Cygwin/msys's pty is a pipe. - ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) - if ft != fileTypePipe || e != 0 { - return false - } - - var buf [2 + syscall.MAX_PATH]uint16 - r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), - 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)), - uintptr(len(buf)*2), 0, 0) - if r == 0 || e != 0 { - return false - } - - l := *(*uint32)(unsafe.Pointer(&buf)) - return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2]))) -} diff --git a/listen.go b/listen.go index c82afc3a20..d23d426780 100644 --- a/listen.go +++ b/listen.go @@ -20,8 +20,9 @@ import ( "strings" "text/tabwriter" - "github.com/gofiber/fiber/v2/internal/colorable" - "github.com/gofiber/fiber/v2/internal/isatty" + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" + "github.com/mattn/go-runewidth" ) // Listener can be used to pass a custom listener. @@ -238,11 +239,11 @@ func (app *App) startupMessage(addr string, tls bool, pids string) { } centerValue := func(s string, width int) string { - pad := strconv.Itoa((width - len([]rune(s))) / 2) + pad := strconv.Itoa((width - runewidth.StringWidth(s)) / 2) str := fmt.Sprintf("%"+pad+"s", " ") str += fmt.Sprintf("%s%s%s", colors.Cyan, s, colors.Black) str += fmt.Sprintf("%"+pad+"s", " ") - if len([]rune(s))-10 < width && len([]rune(s))%2 == 0 { + if runewidth.StringWidth(s)-10 < width && runewidth.StringWidth(s)%2 == 0 { // add an ending space if the length of str is even and str is not too long str += " " } diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 4aa7441a77..566b685d22 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -12,9 +12,9 @@ import ( "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/bytebufferpool" - "github.com/gofiber/fiber/v2/internal/colorable" "github.com/gofiber/fiber/v2/internal/fasttemplate" - "github.com/gofiber/fiber/v2/internal/isatty" + "github.com/mattn/go-colorable" + "github.com/mattn/go-isatty" "github.com/valyala/fasthttp" ) From c187c6a2f5d5e71bfc9ac799f7f8f1ab187aa8a4 Mon Sep 17 00:00:00 2001 From: marcmartin13 <87841930+marcmartin13@users.noreply.github.com> Date: Fri, 21 Oct 2022 16:28:31 +0800 Subject: [PATCH 133/141] =?UTF-8?q?=F0=9F=9A=80=20[Feature]:=20Cache-Contr?= =?UTF-8?q?ol:=20no-cache=20(#2159)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added noCache field Check if the request header Cache-Control contains no-cache * Update cache.go * Update config.go * Update cache.go * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 * patch-1 --- middleware/cache/README.md | 4 + middleware/cache/cache.go | 19 +++- middleware/cache/cache_test.go | 154 +++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 1 deletion(-) diff --git a/middleware/cache/README.md b/middleware/cache/README.md index a5f3921a7a..373446486b 100644 --- a/middleware/cache/README.md +++ b/middleware/cache/README.md @@ -2,6 +2,10 @@ Cache middleware for [Fiber](https://github.com/gofiber/fiber) designed to intercept responses and cache them. This middleware will cache the `Body`, `Content-Type` and `StatusCode` using the `c.Path()` (or a string returned by the Key function) as unique identifier. Special thanks to [@codemicro](https://github.com/codemicro/fiber-cache) for creating this middleware for Fiber core! +Request Directives +The no-cache request directive will return the up-to-date response but still caches it. You will always get a "miss" cache status. +The no-store request directive will refrain from caching. You will always get the up-to-date response. + ## Table of Contents - [Cache Middleware](#cache-middleware) diff --git a/middleware/cache/cache.go b/middleware/cache/cache.go index a8485f450f..f6db49e2cf 100644 --- a/middleware/cache/cache.go +++ b/middleware/cache/cache.go @@ -4,6 +4,7 @@ package cache import ( "strconv" + "strings" "sync" "sync/atomic" "time" @@ -27,6 +28,12 @@ const ( cacheMiss = "miss" ) +// directives +const ( + noCache = "no-cache" + noStore = "no-store" +) + var ignoreHeaders = map[string]interface{}{ "Connection": nil, "Keep-Alive": nil, @@ -83,6 +90,11 @@ func New(config ...Config) fiber.Handler { // Return new handler return func(c *fiber.Ctx) error { + // Refrain from caching + if hasRequestDirective(c, noStore) { + return c.Next() + } + // Only cache selected methods var isExists bool for _, method := range cfg.Methods { @@ -116,7 +128,7 @@ func New(config ...Config) fiber.Handler { _, size := heap.remove(e.heapidx) storedBytes -= size } - } else if e.exp != 0 { + } else if e.exp != 0 && !hasRequestDirective(c, noCache) { // Separate body value to avoid msgp serialization // We can store raw bytes with Storage 👍 if cfg.Storage != nil { @@ -235,3 +247,8 @@ func New(config ...Config) fiber.Handler { return nil } } + +// Check if request has directive +func hasRequestDirective(c *fiber.Ctx, directive string) bool { + return strings.Contains(c.Get(fiber.HeaderCacheControl), directive) +} diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go index 78aeab4f71..3216a7c48a 100644 --- a/middleware/cache/cache_test.go +++ b/middleware/cache/cache_test.go @@ -15,6 +15,7 @@ import ( "time" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/etag" "github.com/gofiber/fiber/v2/internal/storage/memory" "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" @@ -107,6 +108,159 @@ func Test_Cache(t *testing.T) { utils.AssertEqual(t, cachedBody, body) } +// go test -run Test_Cache_WithNoCacheRequestDirective +func Test_Cache_WithNoCacheRequestDirective(t *testing.T) { + t.Parallel() + + app := fiber.New() + app.Use(New()) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString(c.Query("id", "1")) + }) + + // Request id = 1 + req := httptest.NewRequest("GET", "/", nil) + resp, err := app.Test(req) + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) + utils.AssertEqual(t, []byte("1"), body) + // Response cached, entry id = 1 + + // Request id = 2 without Cache-Control: no-cache + cachedReq := httptest.NewRequest("GET", "/?id=2", nil) + cachedResp, err := app.Test(cachedReq) + defer cachedResp.Body.Close() + cachedBody, _ := ioutil.ReadAll(cachedResp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, cacheHit, cachedResp.Header.Get("X-Cache")) + utils.AssertEqual(t, []byte("1"), cachedBody) + // Response not cached, returns cached response, entry id = 1 + + // Request id = 2 with Cache-Control: no-cache + noCacheReq := httptest.NewRequest("GET", "/?id=2", nil) + noCacheReq.Header.Set(fiber.HeaderCacheControl, noCache) + noCacheResp, err := app.Test(noCacheReq) + defer noCacheResp.Body.Close() + noCacheBody, _ := ioutil.ReadAll(noCacheResp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, cacheMiss, noCacheResp.Header.Get("X-Cache")) + utils.AssertEqual(t, []byte("2"), noCacheBody) + // Response cached, returns updated response, entry = 2 + + /* Check Test_Cache_WithETagAndNoCacheRequestDirective */ + // Request id = 2 with Cache-Control: no-cache again + noCacheReq1 := httptest.NewRequest("GET", "/?id=2", nil) + noCacheReq1.Header.Set(fiber.HeaderCacheControl, noCache) + noCacheResp1, err := app.Test(noCacheReq1) + defer noCacheResp1.Body.Close() + noCacheBody1, _ := ioutil.ReadAll(noCacheResp1.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, cacheMiss, noCacheResp1.Header.Get("X-Cache")) + utils.AssertEqual(t, []byte("2"), noCacheBody1) + // Response cached, returns updated response, entry = 2 + + // Request id = 1 without Cache-Control: no-cache + cachedReq1 := httptest.NewRequest("GET", "/", nil) + cachedResp1, err := app.Test(cachedReq1) + defer cachedResp1.Body.Close() + cachedBody1, _ := ioutil.ReadAll(cachedResp1.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, cacheHit, cachedResp1.Header.Get("X-Cache")) + utils.AssertEqual(t, []byte("2"), cachedBody1) + // Response not cached, returns cached response, entry id = 2 +} + +// go test -run Test_Cache_WithETagAndNoCacheRequestDirective +func Test_Cache_WithETagAndNoCacheRequestDirective(t *testing.T) { + t.Parallel() + + app := fiber.New() + app.Use( + etag.New(), + New(), + ) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString(c.Query("id", "1")) + }) + + // Request id = 1 + req := httptest.NewRequest("GET", "/", nil) + resp, err := app.Test(req) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + // Response cached, entry id = 1 + + // If response status 200 + etagToken := resp.Header.Get("Etag") + + // Request id = 2 with ETag but without Cache-Control: no-cache + cachedReq := httptest.NewRequest("GET", "/?id=2", nil) + cachedReq.Header.Set(fiber.HeaderIfNoneMatch, etagToken) + cachedResp, err := app.Test(cachedReq) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, cacheHit, cachedResp.Header.Get("X-Cache")) + utils.AssertEqual(t, fiber.StatusNotModified, cachedResp.StatusCode) + // 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.Header.Set(fiber.HeaderCacheControl, noCache) + noCacheReq.Header.Set(fiber.HeaderIfNoneMatch, etagToken) + noCacheResp, err := app.Test(noCacheReq) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, cacheMiss, noCacheResp.Header.Get("X-Cache")) + utils.AssertEqual(t, fiber.StatusOK, noCacheResp.StatusCode) + // Response cached, returns updated response, entry id = 2 + + // If response status 200 + etagToken = noCacheResp.Header.Get("Etag") + + // Request id = 2 with ETag and Cache-Control: no-cache again + noCacheReq1 := httptest.NewRequest("GET", "/?id=2", nil) + noCacheReq1.Header.Set(fiber.HeaderCacheControl, noCache) + noCacheReq1.Header.Set(fiber.HeaderIfNoneMatch, etagToken) + noCacheResp1, err := app.Test(noCacheReq1) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, cacheMiss, noCacheResp1.Header.Get("X-Cache")) + utils.AssertEqual(t, fiber.StatusNotModified, noCacheResp1.StatusCode) + // 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) + cachedResp1, err := app.Test(cachedReq1) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, cacheHit, cachedResp1.Header.Get("X-Cache")) + utils.AssertEqual(t, fiber.StatusOK, cachedResp1.StatusCode) + // Response not cached, returns cached response, entry id = 2 +} + +// go test -run Test_Cache_WithNoStoreRequestDirective +func Test_Cache_WithNoStoreRequestDirective(t *testing.T) { + t.Parallel() + + app := fiber.New() + app.Use(New()) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString(c.Query("id", "1")) + }) + + // Request id = 2 + noStoreReq := httptest.NewRequest("GET", "/?id=2", nil) + noStoreReq.Header.Set(fiber.HeaderCacheControl, noStore) + noStoreResp, err := app.Test(noStoreReq) + defer noStoreResp.Body.Close() + noStoreBody, _ := ioutil.ReadAll(noStoreResp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, []byte("2"), noStoreBody) + // Response not cached, returns updated response +} + func Test_Cache_WithSeveralRequests(t *testing.T) { t.Parallel() From 5b1885a44c059b567e2fab9104df04ec9e0065e3 Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 23 Oct 2022 10:01:35 +0200 Subject: [PATCH 134/141] prepare release 2.39.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 5ae0318055..ace88eb8b7 100644 --- a/app.go +++ b/app.go @@ -29,7 +29,7 @@ import ( ) // Version of current fiber package -const Version = "2.38.1" +const Version = "2.39.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From 156b81c768ec3e9e8b4cfdf6a37040fa8daa0e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Tue, 25 Oct 2022 08:51:44 +0300 Subject: [PATCH 135/141] :bug: bug: improve mounting behavior (#2120) * :bug: bug: fix mounting doesn't work if when to declare it before routes * :bug: bug: fix mounting doesn't work if when to declare it before routes * :bug: bug: fix mounting doesn't work if when to declare it before routes * :bug: bug: fix mounting doesn't work if when to declare it before routes * :bug: bug: fix mounting doesn't work if when to declare it before routes * add onMount hooks, mountPath like express.js, better behavior for onName, onRoute, onGroup, onGroupName hooks on mounted apps * add comment * use once * fix views when both app and sub-app have view engine, better behavior for mount path * fix tests * fix tests * make some tasks * make some tasks --- .github/testdata2/bruh.tmpl | 1 + app.go | 56 ++---- app_test.go | 161 --------------- ctx.go | 12 +- ctx_test.go | 59 +----- group.go | 30 --- hooks.go | 44 +++++ hooks_test.go | 59 ++++++ internal/template/html/html.go | 9 - mount.go | 146 ++++++++++++++ mount_test.go | 351 +++++++++++++++++++++++++++++++++ router.go | 23 ++- 12 files changed, 649 insertions(+), 302 deletions(-) create mode 100644 .github/testdata2/bruh.tmpl create mode 100644 mount.go create mode 100644 mount_test.go diff --git a/.github/testdata2/bruh.tmpl b/.github/testdata2/bruh.tmpl new file mode 100644 index 0000000000..28d75b49e7 --- /dev/null +++ b/.github/testdata2/bruh.tmpl @@ -0,0 +1 @@ +

    I'm Bruh

    \ No newline at end of file diff --git a/app.go b/app.go index ace88eb8b7..f725424ee6 100644 --- a/app.go +++ b/app.go @@ -18,7 +18,6 @@ import ( "strconv" "strings" "sync" - "sync/atomic" "time" "encoding/json" @@ -106,8 +105,6 @@ type App struct { getBytes func(s string) (b []byte) // Converts byte slice to a string getString func(b []byte) string - // Mounted and main apps - appList map[string]*App // Hooks hooks *Hooks // Latest route & group @@ -115,6 +112,8 @@ type App struct { latestGroup *Group // TLS handler tlsHandler *TLSHandler + // Mount fields + mountFields *mountFields } // Config is a struct holding the server settings. @@ -485,7 +484,6 @@ func New(config ...Config) *App { config: Config{}, getBytes: utils.UnsafeBytes, getString: utils.UnsafeString, - appList: make(map[string]*App), latestRoute: &Route{}, latestGroup: &Group{}, } @@ -493,6 +491,9 @@ func New(config ...Config) *App { // Define hooks app.hooks = newHooks(app) + // Define mountFields + app.mountFields = newMountFields(app) + // Override config if provided if len(config) > 0 { app.config = config[0] @@ -549,9 +550,6 @@ func New(config ...Config) *App { // Override colors app.config.ColorScheme = defaultColors(app.config.ColorScheme) - // Init appList - app.appList[""] = app - // Init app app.init() @@ -582,36 +580,6 @@ func (app *App) SetTLSHandler(tlsHandler *TLSHandler) { app.mutex.Unlock() } -// Mount attaches another app instance as a sub-router along a routing path. -// It's very useful to split up a large API as many independent routers and -// compose them as a single service using Mount. The fiber's error handler and -// any of the fiber's sub apps are added to the application's error handlers -// to be invoked on errors that happen within the prefix route. -func (app *App) Mount(prefix string, fiber *App) Router { - stack := fiber.Stack() - prefix = strings.TrimRight(prefix, "/") - if prefix == "" { - prefix = "/" - } - - for m := range stack { - for r := range stack[m] { - route := app.copyRoute(stack[m][r]) - app.addRoute(route.Method, app.addPrefixToRoute(prefix, route)) - } - } - - // Support for configs of mounted-apps and sub-mounted-apps - for mountedPrefixes, subApp := range fiber.appList { - app.appList[prefix+mountedPrefixes] = subApp - subApp.init() - } - - atomic.AddUint32(&app.handlersCount, fiber.handlersCount) - - return app -} - // Name Assign name to specific route. func (app *App) Name(name string) Router { app.mutex.Lock() @@ -991,7 +959,7 @@ func (app *App) ErrorHandler(ctx *Ctx, err error) error { mountedPrefixParts int ) - for prefix, subApp := range app.appList { + for prefix, subApp := range app.mountFields.appList { if prefix != "" && strings.HasPrefix(ctx.path, prefix) { parts := len(strings.Split(prefix, "/")) if mountedPrefixParts <= parts { @@ -1041,7 +1009,17 @@ func (app *App) startupProcess() *App { } app.mutex.Lock() + defer app.mutex.Unlock() + + // add routes of sub-apps + app.mountFields.subAppsRoutesAdded.Do(func() { + app.appendSubAppLists(app.mountFields.appList) + app.addSubAppsRoutes(app.mountFields.appList) + app.generateAppListKeys() + }) + + // build route tree stack app.buildTree() - app.mutex.Unlock() + return app } diff --git a/app_test.go b/app_test.go index 6bbd8fcba4..acfa8f9b3d 100644 --- a/app_test.go +++ b/app_test.go @@ -246,44 +246,6 @@ func Test_App_ErrorHandler_RouteStack(t *testing.T) { utils.AssertEqual(t, "1: USE error", string(body)) } -func Test_App_ErrorHandler_GroupMount(t *testing.T) { - micro := New(Config{ - ErrorHandler: func(c *Ctx, err error) error { - utils.AssertEqual(t, "0: GET error", err.Error()) - return c.Status(500).SendString("1: custom error") - }, - }) - micro.Get("/doe", func(c *Ctx) error { - return errors.New("0: GET error") - }) - - app := New() - v1 := app.Group("/v1") - v1.Mount("/john", micro) - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) - testErrorResponse(t, err, resp, "1: custom error") -} - -func Test_App_ErrorHandler_GroupMountRootLevel(t *testing.T) { - micro := New(Config{ - ErrorHandler: func(c *Ctx, err error) error { - utils.AssertEqual(t, "0: GET error", err.Error()) - return c.Status(500).SendString("1: custom error") - }, - }) - micro.Get("/john/doe", func(c *Ctx) error { - return errors.New("0: GET error") - }) - - app := New() - v1 := app.Group("/v1") - v1.Mount("/", micro) - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) - testErrorResponse(t, err, resp, "1: custom error") -} - func Test_App_Nested_Params(t *testing.T) { app := New() @@ -307,22 +269,6 @@ func Test_App_Nested_Params(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode, "Status code") } -// go test -run Test_App_Mount -func Test_App_Mount(t *testing.T) { - micro := New() - micro.Get("/doe", func(c *Ctx) error { - return c.SendStatus(StatusOK) - }) - - app := New() - app.Mount("/john", micro) - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, uint32(2), app.handlersCount) -} - func Test_App_Use_Params(t *testing.T) { app := New() @@ -1046,23 +992,6 @@ func Test_App_Group_Invalid(t *testing.T) { New().Group("/").Use(1) } -// go test -run Test_App_Group_Mount -func Test_App_Group_Mount(t *testing.T) { - micro := New() - micro.Get("/doe", func(c *Ctx) error { - return c.SendStatus(StatusOK) - }) - - app := New() - v1 := app.Group("/v1") - v1.Mount("/john", micro) - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - utils.AssertEqual(t, uint32(2), app.handlersCount) -} - func Test_App_Group(t *testing.T) { dummyHandler := testEmptyHandler @@ -1538,96 +1467,6 @@ func Test_App_DisablePreParseMultipartForm(t *testing.T) { utils.AssertEqual(t, testString, string(body)) } -func Test_App_UseMountedErrorHandler(t *testing.T) { - app := New() - - fiber := New(Config{ - ErrorHandler: func(ctx *Ctx, err error) error { - return ctx.Status(500).SendString("hi, i'm a custom error") - }, - }) - fiber.Get("/", func(c *Ctx) error { - return errors.New("something happened") - }) - - app.Mount("/api", fiber) - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) - testErrorResponse(t, err, resp, "hi, i'm a custom error") -} - -func Test_App_UseMountedErrorHandlerRootLevel(t *testing.T) { - app := New() - - fiber := New(Config{ - ErrorHandler: func(ctx *Ctx, err error) error { - return ctx.Status(500).SendString("hi, i'm a custom error") - }, - }) - fiber.Get("/api", func(c *Ctx) error { - return errors.New("something happened") - }) - - app.Mount("/", fiber) - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) - testErrorResponse(t, err, resp, "hi, i'm a custom error") -} - -func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { - app := New() - - tsf := func(ctx *Ctx, err error) error { - return ctx.Status(200).SendString("hi, i'm a custom sub sub fiber error") - } - tripleSubFiber := New(Config{ - ErrorHandler: tsf, - }) - tripleSubFiber.Get("/", func(c *Ctx) error { - return errors.New("something happened") - }) - - sf := func(ctx *Ctx, err error) error { - return ctx.Status(200).SendString("hi, i'm a custom sub fiber error") - } - subfiber := New(Config{ - ErrorHandler: sf, - }) - subfiber.Get("/", func(c *Ctx) error { - return errors.New("something happened") - }) - subfiber.Mount("/third", tripleSubFiber) - - f := func(ctx *Ctx, err error) error { - return ctx.Status(200).SendString("hi, i'm a custom error") - } - fiber := New(Config{ - ErrorHandler: f, - }) - fiber.Get("/", func(c *Ctx) error { - return errors.New("something happened") - }) - fiber.Mount("/sub", subfiber) - - app.Mount("/api", fiber) - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub", nil)) - utils.AssertEqual(t, nil, err, "/api/sub req") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - - b, err := ioutil.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") - utils.AssertEqual(t, "hi, i'm a custom sub fiber error", string(b), "Response body") - - resp2, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub/third", nil)) - utils.AssertEqual(t, nil, err, "/api/sub/third req") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - - b, err = ioutil.ReadAll(resp2.Body) - utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") - utils.AssertEqual(t, "hi, i'm a custom sub sub fiber error", string(b), "Third fiber Response body") -} - func Test_App_Test_no_timeout_infinitely(t *testing.T) { var err error c := make(chan int) diff --git a/ctx.go b/ctx.go index 327b319386..0fa466e5c3 100644 --- a/ctx.go +++ b/ctx.go @@ -1384,11 +1384,13 @@ func (c *Ctx) Render(name string, bind interface{}, layouts ...string) error { buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) - // Pass-locals-to-views & bind + // Pass-locals-to-views, bind, appListKeys c.renderExtensions(bind) - rendered := false - for prefix, app := range c.app.appList { + var rendered bool + for i := len(c.app.mountFields.appListKeys) - 1; i >= 0; i-- { + prefix := c.app.mountFields.appListKeys[i] + app := c.app.mountFields.appList[prefix] if prefix == "" || strings.Contains(c.OriginalURL(), prefix) { if len(layouts) == 0 && app.config.ViewsLayout != "" { layouts = []string{ @@ -1454,6 +1456,10 @@ func (c *Ctx) renderExtensions(bind interface{}) { }) } } + + if len(c.app.mountFields.appListKeys) == 0 { + c.app.generateAppListKeys() + } } // Route returns the matched Route struct. diff --git a/ctx_test.go b/ctx_test.go index ad975de9cf..f1ba1f6cde 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -32,7 +32,6 @@ import ( "github.com/gofiber/fiber/v2/internal/bytebufferpool" "github.com/gofiber/fiber/v2/internal/storage/memory" - "github.com/gofiber/fiber/v2/internal/template/html" "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" ) @@ -2792,58 +2791,6 @@ func Test_Ctx_Render(t *testing.T) { utils.AssertEqual(t, false, err == nil) } -// go test -run Test_Ctx_Render_Mount -func Test_Ctx_Render_Mount(t *testing.T) { - t.Parallel() - - sub := New(Config{ - Views: html.New("./.github/testdata/template", ".gohtml"), - }) - - sub.Get("/:name", func(ctx *Ctx) error { - return ctx.Render("hello_world", Map{ - "Name": ctx.Params("name"), - }) - }) - - app := New() - app.Mount("/hello", sub) - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/a", nil)) - utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") - utils.AssertEqual(t, nil, err, "app.Test(req)") - - body, err := ioutil.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

    Hello a!

    ", string(body)) -} - -func Test_Ctx_Render_MountGroup(t *testing.T) { - t.Parallel() - - micro := New(Config{ - Views: html.New("./.github/testdata/template", ".gohtml"), - }) - - micro.Get("/doe", func(c *Ctx) error { - return c.Render("hello_world", Map{ - "Name": "doe", - }) - }) - - app := New() - v1 := app.Group("/v1") - v1.Mount("/john", micro) - - resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) - utils.AssertEqual(t, nil, err, "app.Test(req)") - utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - - body, err := ioutil.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err) - utils.AssertEqual(t, "

    Hello doe!

    ", string(body)) -} - func Test_Ctx_RenderWithoutLocals(t *testing.T) { t.Parallel() app := New(Config{ @@ -3149,6 +3096,7 @@ func Test_Ctx_RestartRoutingWithChangedPathAndCatchAll(t *testing.T) { type testTemplateEngine struct { templates *template.Template + path string } func (t *testTemplateEngine) Render(w io.Writer, name string, bind interface{}, layout ...string) error { @@ -3160,7 +3108,10 @@ func (t *testTemplateEngine) Render(w io.Writer, name string, bind interface{}, } func (t *testTemplateEngine) Load() error { - t.templates = template.Must(template.ParseGlob("./.github/testdata/*.tmpl")) + if t.path == "" { + t.path = "testdata" + } + t.templates = template.Must(template.ParseGlob("./.github/" + t.path + "/*.tmpl")) return nil } diff --git a/group.go b/group.go index ab74b80e92..a59eaed8de 100644 --- a/group.go +++ b/group.go @@ -8,7 +8,6 @@ import ( "fmt" "reflect" "strings" - "sync/atomic" ) // Group struct @@ -19,35 +18,6 @@ type Group struct { Prefix string } -// Mount attaches another app instance as a sub-router along a routing path. -// It's very useful to split up a large API as many independent routers and -// compose them as a single service using Mount. -func (grp *Group) Mount(prefix string, fiber *App) Router { - stack := fiber.Stack() - groupPath := getGroupPath(grp.Prefix, prefix) - groupPath = strings.TrimRight(groupPath, "/") - if groupPath == "" { - groupPath = "/" - } - - for m := range stack { - for r := range stack[m] { - route := grp.app.copyRoute(stack[m][r]) - grp.app.addRoute(route.Method, grp.app.addPrefixToRoute(groupPath, route)) - } - } - - // Support for configs of mounted-apps and sub-mounted-apps - for mountedPrefixes, subApp := range fiber.appList { - grp.app.appList[groupPath+mountedPrefixes] = subApp - subApp.init() - } - - atomic.AddUint32(&grp.app.handlersCount, fiber.handlersCount) - - return grp -} - // Name Assign name to specific route. func (grp *Group) Name(name string) Router { grp.app.mutex.Lock() diff --git a/hooks.go b/hooks.go index f614ef1318..b2766a8cab 100644 --- a/hooks.go +++ b/hooks.go @@ -8,6 +8,7 @@ type OnGroupNameHandler = OnGroupHandler type OnListenHandler = func() error type OnShutdownHandler = OnListenHandler type OnForkHandler = func(int) error +type OnMountHandler = func(*App) error // Hooks is a struct to use it with App. type Hooks struct { @@ -22,6 +23,7 @@ type Hooks struct { onListen []OnListenHandler onShutdown []OnShutdownHandler onFork []OnForkHandler + onMount []OnMountHandler } func newHooks(app *App) *Hooks { @@ -34,6 +36,7 @@ func newHooks(app *App) *Hooks { onListen: make([]OnListenHandler, 0), onShutdown: make([]OnShutdownHandler, 0), onFork: make([]OnForkHandler, 0), + onMount: make([]OnMountHandler, 0), } } @@ -94,7 +97,22 @@ func (h *Hooks) OnFork(handler ...OnForkHandler) { h.app.mutex.Unlock() } +// OnMount is a hook to execute user function after mounting process. +// The mount event is fired when sub-app is mounted on a parent app. The parent app is passed as a parameter. +// It works for app and group mounting. +func (h *Hooks) OnMount(handler ...OnMountHandler) { + h.app.mutex.Lock() + h.onMount = append(h.onMount, handler...) + h.app.mutex.Unlock() +} + func (h *Hooks) executeOnRouteHooks(route Route) error { + // Check mounting + if h.app.mountFields.mountPath != "" { + route.path = h.app.mountFields.mountPath + route.path + route.Path = route.path + } + for _, v := range h.onRoute { if err := v(route); err != nil { return err @@ -105,6 +123,12 @@ func (h *Hooks) executeOnRouteHooks(route Route) error { } func (h *Hooks) executeOnNameHooks(route Route) error { + // Check mounting + if h.app.mountFields.mountPath != "" { + route.path = h.app.mountFields.mountPath + route.path + route.Path = route.path + } + for _, v := range h.onName { if err := v(route); err != nil { return err @@ -115,6 +139,11 @@ func (h *Hooks) executeOnNameHooks(route Route) error { } func (h *Hooks) executeOnGroupHooks(group Group) error { + // Check mounting + if h.app.mountFields.mountPath != "" { + group.Prefix = h.app.mountFields.mountPath + group.Prefix + } + for _, v := range h.onGroup { if err := v(group); err != nil { return err @@ -125,6 +154,11 @@ func (h *Hooks) executeOnGroupHooks(group Group) error { } func (h *Hooks) executeOnGroupNameHooks(group Group) error { + // Check mounting + if h.app.mountFields.mountPath != "" { + group.Prefix = h.app.mountFields.mountPath + group.Prefix + } + for _, v := range h.onGroupName { if err := v(group); err != nil { return err @@ -155,3 +189,13 @@ func (h *Hooks) executeOnForkHooks(pid int) { _ = v(pid) } } + +func (h *Hooks) executeOnMountHooks(app *App) error { + for _, v := range h.onMount { + if err := v(app); err != nil { + return err + } + } + + return nil +} diff --git a/hooks_test.go b/hooks_test.go index 5ed9d82145..e39d269cfb 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -33,6 +33,29 @@ func Test_Hook_OnRoute(t *testing.T) { app.Mount("/sub", subApp) } +func Test_Hook_OnRoute_Mount(t *testing.T) { + t.Parallel() + + app := New() + subApp := New() + app.Mount("/sub", subApp) + + subApp.Hooks().OnRoute(func(r Route) error { + utils.AssertEqual(t, "/sub/test", r.Path) + + return nil + }) + + app.Hooks().OnRoute(func(r Route) error { + utils.AssertEqual(t, "/", r.Path) + + return nil + }) + + app.Get("/", testSimpleHandler).Name("x") + subApp.Get("/test", testSimpleHandler) +} + func Test_Hook_OnName(t *testing.T) { t.Parallel() @@ -96,6 +119,24 @@ func Test_Hook_OnGroup(t *testing.T) { utils.AssertEqual(t, "/x/x/a", buf.String()) } +func Test_Hook_OnGroup_Mount(t *testing.T) { + t.Parallel() + + app := New() + micro := New() + micro.Mount("/john", app) + + app.Hooks().OnGroup(func(g Group) error { + utils.AssertEqual(t, "/john/v1", g.Prefix) + return nil + }) + + v1 := app.Group("/v1") + v1.Get("/doe", func(c *Ctx) error { + return c.SendStatus(StatusOK) + }) +} + func Test_Hook_OnGroupName(t *testing.T) { t.Parallel() @@ -200,3 +241,21 @@ func Test_Hook_OnHook(t *testing.T) { utils.AssertEqual(t, nil, app.prefork(NetworkTCP4, ":3000", nil)) } + +func Test_Hook_OnMount(t *testing.T) { + t.Parallel() + + app := New() + app.Get("/", testSimpleHandler).Name("x") + + subApp := New() + subApp.Get("/test", testSimpleHandler) + + subApp.Hooks().OnMount(func(parent *App) error { + utils.AssertEqual(t, parent.mountFields.mountPath, "") + + return nil + }) + + app.Mount("/sub", subApp) +} diff --git a/internal/template/html/html.go b/internal/template/html/html.go index da9d8c5211..71f6f02cb5 100644 --- a/internal/template/html/html.go +++ b/internal/template/html/html.go @@ -183,15 +183,6 @@ func (e *Engine) Load() error { // Render will execute the template name along with the given values. func (e *Engine) Render(out io.Writer, template string, binding interface{}, layout ...string) error { - if !e.loaded || e.reload { - if e.reload { - e.loaded = false - } - if err := e.Load(); err != nil { - return err - } - } - tmpl := e.Templates.Lookup(template) if tmpl == nil { return fmt.Errorf("render: template %s does not exist", template) diff --git a/mount.go b/mount.go new file mode 100644 index 0000000000..f7df3a97e6 --- /dev/null +++ b/mount.go @@ -0,0 +1,146 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package fiber + +import ( + "sort" + "strings" + "sync" + "sync/atomic" +) + +// Put fields related to mounting. +type mountFields struct { + // Mounted and main apps + appList map[string]*App + // Ordered keys of apps (sorted by key length for Render) + appListKeys []string + // check added routes of sub-apps + subAppsRoutesAdded sync.Once + // Prefix of app if it was mounted + mountPath string +} + +// Create empty mountFields instance +func newMountFields(app *App) *mountFields { + return &mountFields{ + appList: map[string]*App{"": app}, + appListKeys: make([]string, 0), + } +} + +// Mount attaches another app instance as a sub-router along a routing path. +// It's very useful to split up a large API as many independent routers and +// compose them as a single service using Mount. The fiber's error handler and +// any of the fiber's sub apps are added to the application's error handlers +// to be invoked on errors that happen within the prefix route. +func (app *App) Mount(prefix string, fiber *App) Router { + prefix = strings.TrimRight(prefix, "/") + if prefix == "" { + prefix = "/" + } + + // Support for configs of mounted-apps and sub-mounted-apps + for mountedPrefixes, subApp := range fiber.mountFields.appList { + subApp.mountFields.mountPath = prefix + mountedPrefixes + app.mountFields.appList[prefix+mountedPrefixes] = subApp + } + + // Execute onMount hooks + if err := fiber.hooks.executeOnMountHooks(app); err != nil { + panic(err) + } + + return app +} + +// Mount attaches another app instance as a sub-router along a routing path. +// It's very useful to split up a large API as many independent routers and +// compose them as a single service using Mount. +func (grp *Group) Mount(prefix string, fiber *App) Router { + groupPath := getGroupPath(grp.Prefix, prefix) + groupPath = strings.TrimRight(groupPath, "/") + if groupPath == "" { + groupPath = "/" + } + + // Support for configs of mounted-apps and sub-mounted-apps + for mountedPrefixes, subApp := range fiber.mountFields.appList { + subApp.mountFields.mountPath = groupPath + mountedPrefixes + grp.app.mountFields.appList[groupPath+mountedPrefixes] = subApp + } + + // Execute onMount hooks + if err := fiber.hooks.executeOnMountHooks(grp.app); err != nil { + panic(err) + } + + return grp +} + +// The MountPath property contains one or more path patterns on which a sub-app was mounted. +func (app *App) MountPath() string { + return app.mountFields.mountPath +} + +// generateAppListKeys generates app list keys for Render, should work after appendSubAppLists +func (app *App) generateAppListKeys() { + for key := range app.mountFields.appList { + app.mountFields.appListKeys = append(app.mountFields.appListKeys, key) + } + + sort.Slice(app.mountFields.appListKeys, func(i, j int) bool { + return len(app.mountFields.appListKeys[i]) < len(app.mountFields.appListKeys[j]) + }) +} + +// appendSubAppLists supports nested for sub apps +func (app *App) appendSubAppLists(appList map[string]*App, parent ...string) { + for prefix, subApp := range appList { + // skip real app + if prefix == "" { + continue + } + + if len(parent) > 0 { + prefix = parent[0] + prefix + } + + if _, ok := app.mountFields.appList[prefix]; !ok { + app.mountFields.appList[prefix] = subApp + } + + // The first element of appList is always the app itself. If there are no other sub apps, we should skip appending nested apps. + if len(subApp.mountFields.appList) > 1 { + app.appendSubAppLists(subApp.mountFields.appList, prefix) + } + + } +} + +// addSubAppsRoutes adds routes of sub apps nestedly when to start the server +func (app *App) addSubAppsRoutes(appList map[string]*App, parent ...string) { + for prefix, subApp := range appList { + // skip real app + if prefix == "" { + continue + } + + if len(parent) > 0 { + prefix = parent[0] + prefix + } + + // add routes + stack := subApp.stack + for m := range stack { + for r := range stack[m] { + route := app.copyRoute(stack[m][r]) + app.addRoute(route.Method, app.addPrefixToRoute(prefix, route), true) + } + } + + atomic.AddUint32(&app.handlersCount, subApp.handlersCount) + } +} diff --git a/mount_test.go b/mount_test.go new file mode 100644 index 0000000000..1fa9135914 --- /dev/null +++ b/mount_test.go @@ -0,0 +1,351 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package fiber + +import ( + "errors" + "io/ioutil" + "net/http/httptest" + "testing" + + "github.com/gofiber/fiber/v2/internal/template/html" + "github.com/gofiber/fiber/v2/utils" +) + +// go test -run Test_App_Mount +func Test_App_Mount(t *testing.T) { + micro := New() + micro.Get("/doe", func(c *Ctx) error { + return c.SendStatus(StatusOK) + }) + + app := New() + app.Mount("/john", micro) + resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + utils.AssertEqual(t, uint32(2), app.handlersCount) +} + +// go test -run Test_App_Mount_Nested +func Test_App_Mount_Nested(t *testing.T) { + app := New() + one := New() + two := New() + three := New() + + two.Mount("/three", three) + app.Mount("/one", one) + one.Mount("/two", two) + + one.Get("/doe", func(c *Ctx) error { + return c.SendStatus(StatusOK) + }) + + two.Get("/nested", func(c *Ctx) error { + return c.SendStatus(StatusOK) + }) + + three.Get("/test", func(c *Ctx) error { + return c.SendStatus(StatusOK) + }) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/one/doe", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + + resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/nested", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + + resp, err = app.Test(httptest.NewRequest(MethodGet, "/one/two/three/test", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + + utils.AssertEqual(t, uint32(6), app.handlersCount) +} + +// go test -run Test_App_MountPath +func Test_App_MountPath(t *testing.T) { + app := New() + one := New() + two := New() + three := New() + + two.Mount("/three", three) + one.Mount("/two", two) + app.Mount("/one", one) + + utils.AssertEqual(t, "/one", one.MountPath()) + utils.AssertEqual(t, "/one/two", two.MountPath()) + utils.AssertEqual(t, "/one/two/three", three.MountPath()) + utils.AssertEqual(t, "", app.MountPath()) +} + +func Test_App_ErrorHandler_GroupMount(t *testing.T) { + micro := New(Config{ + ErrorHandler: func(c *Ctx, err error) error { + utils.AssertEqual(t, "0: GET error", err.Error()) + return c.Status(500).SendString("1: custom error") + }, + }) + micro.Get("/doe", func(c *Ctx) error { + return errors.New("0: GET error") + }) + + app := New() + v1 := app.Group("/v1") + v1.Mount("/john", micro) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) + testErrorResponse(t, err, resp, "1: custom error") +} + +func Test_App_ErrorHandler_GroupMountRootLevel(t *testing.T) { + micro := New(Config{ + ErrorHandler: func(c *Ctx, err error) error { + utils.AssertEqual(t, "0: GET error", err.Error()) + return c.Status(500).SendString("1: custom error") + }, + }) + micro.Get("/john/doe", func(c *Ctx) error { + return errors.New("0: GET error") + }) + + app := New() + v1 := app.Group("/v1") + v1.Mount("/", micro) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) + testErrorResponse(t, err, resp, "1: custom error") +} + +// go test -run Test_App_Group_Mount +func Test_App_Group_Mount(t *testing.T) { + micro := New() + micro.Get("/doe", func(c *Ctx) error { + return c.SendStatus(StatusOK) + }) + + app := New() + v1 := app.Group("/v1") + v1.Mount("/john", micro) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + utils.AssertEqual(t, uint32(2), app.handlersCount) +} + +func Test_App_UseMountedErrorHandler(t *testing.T) { + app := New() + + fiber := New(Config{ + ErrorHandler: func(ctx *Ctx, err error) error { + return ctx.Status(500).SendString("hi, i'm a custom error") + }, + }) + fiber.Get("/", func(c *Ctx) error { + return errors.New("something happened") + }) + + app.Mount("/api", fiber) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) + testErrorResponse(t, err, resp, "hi, i'm a custom error") +} + +func Test_App_UseMountedErrorHandlerRootLevel(t *testing.T) { + app := New() + + fiber := New(Config{ + ErrorHandler: func(ctx *Ctx, err error) error { + return ctx.Status(500).SendString("hi, i'm a custom error") + }, + }) + fiber.Get("/api", func(c *Ctx) error { + return errors.New("something happened") + }) + + app.Mount("/", fiber) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) + testErrorResponse(t, err, resp, "hi, i'm a custom error") +} + +func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { + app := New() + + tsf := func(ctx *Ctx, err error) error { + return ctx.Status(200).SendString("hi, i'm a custom sub sub fiber error") + } + tripleSubFiber := New(Config{ + ErrorHandler: tsf, + }) + tripleSubFiber.Get("/", func(c *Ctx) error { + return errors.New("something happened") + }) + + sf := func(ctx *Ctx, err error) error { + return ctx.Status(200).SendString("hi, i'm a custom sub fiber error") + } + subfiber := New(Config{ + ErrorHandler: sf, + }) + subfiber.Get("/", func(c *Ctx) error { + return errors.New("something happened") + }) + subfiber.Mount("/third", tripleSubFiber) + + f := func(ctx *Ctx, err error) error { + return ctx.Status(200).SendString("hi, i'm a custom error") + } + fiber := New(Config{ + ErrorHandler: f, + }) + fiber.Get("/", func(c *Ctx) error { + return errors.New("something happened") + }) + fiber.Mount("/sub", subfiber) + + app.Mount("/api", fiber) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub", nil)) + utils.AssertEqual(t, nil, err, "/api/sub req") + utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + + b, err := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") + utils.AssertEqual(t, "hi, i'm a custom sub fiber error", string(b), "Response body") + + resp2, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub/third", nil)) + utils.AssertEqual(t, nil, err, "/api/sub/third req") + utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + + b, err = ioutil.ReadAll(resp2.Body) + utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") + utils.AssertEqual(t, "hi, i'm a custom sub sub fiber error", string(b), "Third fiber Response body") +} + +// go test -run Test_Ctx_Render_Mount +func Test_Ctx_Render_Mount(t *testing.T) { + t.Parallel() + + sub := New(Config{ + Views: html.New("./.github/testdata/template", ".gohtml"), + }) + + sub.Get("/:name", func(ctx *Ctx) error { + return ctx.Render("hello_world", Map{ + "Name": ctx.Params("name"), + }) + }) + + app := New() + app.Mount("/hello", sub) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/a", nil)) + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + utils.AssertEqual(t, nil, err, "app.Test(req)") + + body, err := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "

    Hello a!

    ", string(body)) +} + +// go test -run Test_Ctx_Render_Mount_ParentOrSubHasViews +func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { + t.Parallel() + + engine := &testTemplateEngine{} + err := engine.Load() + utils.AssertEqual(t, nil, err) + + engine2 := &testTemplateEngine{path: "testdata2"} + err = engine2.Load() + utils.AssertEqual(t, nil, err) + + sub := New(Config{ + Views: html.New("./.github/testdata/template", ".gohtml"), + }) + + sub2 := New(Config{ + Views: engine2, + }) + + app := New(Config{ + Views: engine, + }) + + app.Get("/test", func(c *Ctx) error { + return c.Render("index.tmpl", Map{ + "Title": "Hello, World!", + }) + }) + + sub.Get("/world/:name", func(c *Ctx) error { + return c.Render("hello_world", Map{ + "Name": c.Params("name"), + }) + }) + + sub2.Get("/moment", func(c *Ctx) error { + return c.Render("bruh.tmpl", Map{}) + }) + + sub.Mount("/bruh", sub2) + app.Mount("/hello", sub) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/world/a", nil)) + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + utils.AssertEqual(t, nil, err, "app.Test(req)") + + body, err := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "

    Hello a!

    ", string(body)) + + resp, err = app.Test(httptest.NewRequest(MethodGet, "/test", nil)) + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + utils.AssertEqual(t, nil, err, "app.Test(req)") + + body, err = ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "

    Hello, World!

    ", string(body)) + + resp, err = app.Test(httptest.NewRequest(MethodGet, "/hello/bruh/moment", nil)) + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + utils.AssertEqual(t, nil, err, "app.Test(req)") + + body, err = ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "

    I'm Bruh

    ", string(body)) + +} + +func Test_Ctx_Render_MountGroup(t *testing.T) { + t.Parallel() + + micro := New(Config{ + Views: html.New("./.github/testdata/template", ".gohtml"), + }) + + micro.Get("/doe", func(c *Ctx) error { + return c.Render("hello_world", Map{ + "Name": "doe", + }) + }) + + app := New() + v1 := app.Group("/v1") + v1.Mount("/john", micro) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + + body, err := ioutil.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "

    Hello doe!

    ", string(body)) +} diff --git a/router.go b/router.go index ff51bb0d52..642149d52a 100644 --- a/router.go +++ b/router.go @@ -423,7 +423,13 @@ func (app *App) registerStatic(prefix, root string, config ...Static) Router { return app } -func (app *App) addRoute(method string, route *Route) { +func (app *App) addRoute(method string, route *Route, isMounted ...bool) { + // Check mounted routes + var mounted bool + if len(isMounted) > 0 { + mounted = isMounted[0] + } + // Get unique HTTP method identifier m := methodInt(method) @@ -441,12 +447,15 @@ func (app *App) addRoute(method string, route *Route) { app.routesRefreshed = true } - app.mutex.Lock() - app.latestRoute = route - if err := app.hooks.executeOnRouteHooks(*route); err != nil { - panic(err) + // Execute onRoute hooks & change latestRoute if not adding mounted route + if !mounted { + app.mutex.Lock() + app.latestRoute = route + if err := app.hooks.executeOnRouteHooks(*route); err != nil { + panic(err) + } + app.mutex.Unlock() } - app.mutex.Unlock() } // buildTree build the prefix tree from the previously registered routes @@ -454,6 +463,7 @@ func (app *App) buildTree() *App { if !app.routesRefreshed { return app } + // loop all the methods and stacks and create the prefix tree for m := range intMethod { tsMap := make(map[string][]*Route) @@ -467,6 +477,7 @@ func (app *App) buildTree() *App { } app.treeStack[m] = tsMap } + // loop the methods and tree stacks and add global stack and sort everything for m := range intMethod { tsMap := app.treeStack[m] From 4d584c9c9bd5c7119bb39c41c03750ddeb757ed9 Mon Sep 17 00:00:00 2001 From: marcmartin13 <87841930+marcmartin13@users.noreply.github.com> Date: Tue, 25 Oct 2022 13:52:28 +0800 Subject: [PATCH 136/141] =?UTF-8?q?=F0=9F=93=9D=20Cache=20readme=20and=20d?= =?UTF-8?q?ocs=20update=20(#2169)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update README.md * update no-store request directive added cache status unreachable * Update cache.go --- middleware/cache/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/middleware/cache/README.md b/middleware/cache/README.md index 373446486b..638d3d525d 100644 --- a/middleware/cache/README.md +++ b/middleware/cache/README.md @@ -2,9 +2,9 @@ Cache middleware for [Fiber](https://github.com/gofiber/fiber) designed to intercept responses and cache them. This middleware will cache the `Body`, `Content-Type` and `StatusCode` using the `c.Path()` (or a string returned by the Key function) as unique identifier. Special thanks to [@codemicro](https://github.com/codemicro/fiber-cache) for creating this middleware for Fiber core! -Request Directives -The no-cache request directive will return the up-to-date response but still caches it. You will always get a "miss" cache status. -The no-store request directive will refrain from caching. You will always get the up-to-date response. +Request Directives
    +`Cache-Control: no-cache` will return the up-to-date response but still caches it. You will always get a `miss` cache status.
    +`Cache-Control: no-store` will refrain from caching. You will always get the up-to-date response. ## Table of Contents From eb692727c51f1d9072b524a6beb79210fdd2ede5 Mon Sep 17 00:00:00 2001 From: Diego Parisi Date: Tue, 25 Oct 2022 18:05:57 +0200 Subject: [PATCH 137/141] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20Fix:=20comment=20t?= =?UTF-8?q?ypo=20(#2173)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just a typo I think there's a typo "unmashal" for "unmarshal" --- middleware/session/store.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/middleware/session/store.go b/middleware/session/store.go index c1af980a17..cc8a80a0cf 100644 --- a/middleware/session/store.go +++ b/middleware/session/store.go @@ -66,7 +66,7 @@ func (s *Store) Get(c *fiber.Ctx) (*Session, error) { // Fetch existing data if loadData { raw, err := s.Storage.Get(id) - // Unmashal if we found data + // Unmarshal if we found data if raw != nil && err == nil { mux.Lock() defer mux.Unlock() From 691d2e6ad5c565eea26676a04064cf99fd515b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Wed, 26 Oct 2022 12:51:50 +0300 Subject: [PATCH 138/141] :wastebasket: deprecate: go 1.14 & go 1.15 support deprecation (#2172) * :wastebasket: deprecate: go 1.14 & go 1.15 support deprecation https://github.com/valyala/fasthttp/pull/1379 * fix tests --- .github/README.md | 2 +- .github/README_de.md | 2 +- .github/README_es.md | 2 +- .github/README_fa.md | 2 +- .github/README_fr.md | 2 +- .github/README_he.md | 2 +- .github/README_id.md | 2 +- .github/README_it.md | 2 +- .github/README_ja.md | 2 +- .github/README_ko.md | 2 +- .github/README_nl.md | 2 +- .github/README_pt.md | 2 +- .github/README_ru.md | 2 +- .github/README_sa.md | 2 +- .github/README_tr.md | 2 +- .github/README_zh-CN.md | 2 +- .github/README_zh-TW.md | 2 +- .github/workflows/test.yml | 2 +- app_test.go | 45 ++++++------- client.go | 3 +- client_test.go | 4 +- ctx.go | 3 +- ctx_test.go | 23 +++---- internal/gopsutil/common/common.go | 5 +- internal/gopsutil/load/load_linux.go | 6 +- internal/gopsutil/net/net_linux.go | 7 +- internal/gopsutil/process/process_linux.go | 17 +++-- internal/msgp/file_port.go | 4 +- internal/template/utils/utils.go | 6 +- listen.go | 3 +- middleware/basicauth/basicauth_test.go | 4 +- middleware/cache/cache_test.go | 78 +++++++++++----------- middleware/compress/compress_test.go | 17 ++--- middleware/envvar/envvar_test.go | 12 ++-- middleware/etag/etag_test.go | 8 +-- middleware/expvar/expvar_test.go | 8 +-- middleware/favicon/favicon.go | 7 +- middleware/limiter/limiter_test.go | 6 +- middleware/logger/logger_test.go | 4 +- middleware/monitor/monitor_test.go | 14 ++-- middleware/pprof/pprof_test.go | 6 +- middleware/proxy/proxy_test.go | 14 ++-- mount_test.go | 16 ++--- prefork_test.go | 4 +- router_test.go | 47 ++++++------- 45 files changed, 201 insertions(+), 206 deletions(-) diff --git a/.github/README.md b/.github/README.md index 5d6db8fbd1..45c10be6c4 100644 --- a/.github/README.md +++ b/.github/README.md @@ -148,7 +148,7 @@ Fiber is **inspired** by Express, the most popular web framework on the Internet We **listen** to our users in [issues](https://github.com/gofiber/fiber/issues), Discord [channel](https://gofiber.io/discord) _and all over the Internet_ to create a **fast**, **flexible** and **friendly** Go web framework for **any** task, **deadline** and developer **skill**! Just like Express does in the JavaScript world. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Examples diff --git a/.github/README_de.md b/.github/README_de.md index 1960b28e6e..6fa4a2b1d0 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -142,7 +142,7 @@ Neue Gopher, welche von [Node.js](https://nodejs.org/en/about/) zu [Go](https:// Fiber ist **inspiriert** von Express.js, dem beliebtesten Web-Framework im Internet. Wir haben die **Leichtigkeit** von Express und die **Rohleistung** von Go kombiniert. Wenn du jemals eine Webanwendung mit Node.js implementiert hast (_mit Express.js oder ähnlichem_), werden dir viele Methoden und Prinzipien **sehr vertraut** vorkommen. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Beispiele diff --git a/.github/README_es.md b/.github/README_es.md index 976ceb6ece..cf524f6287 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -142,7 +142,7 @@ Los nuevos gophers que hacen el cambio de [Node.js](https://nodejs.org/en/about/ Fiber está **inspirado** en Expressjs, el framework web más popular en Internet. Combinamos la **facilidad** de Express y **el rendimiento bruto** de Go. Si alguna vez ha implementado una aplicación web en Node.js ( _utilizando Express.js o similar_ ), muchos métodos y principios le parecerán **muy comunes** . ## ⚠️ Limitantes -* Debido a que Fiber utiliza unsafe, la biblioteca no siempre será compatible con la última versión de Go. Fiber 2.29.0 ha sido probado con las versiones de Go 1.14 a 1.19. +* Debido a que Fiber utiliza unsafe, la biblioteca no siempre será compatible con la última versión de Go. Fiber 2.40.0 ha sido probado con las versiones de Go 1.16 a 1.19. * Fiber no es compatible con interfaces net/http. Esto significa que no lo podrá usar en proyectos como qglgen, go-swagger, u otros que son parte del ecosistema net/http. ## 👀 Ejemplos diff --git a/.github/README_fa.md b/.github/README_fa.md index 8c2d615a82..30afaa5f83 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -182,7 +182,7 @@ Fiber از Express الهام گرفته, که محبوب ترین فری

    ## ⚠️ محدودیت ها -* به دلیل استفاده ناامن از Fiber, ممکن است کتابخانه همیشه با آخرین نسخه Go سازگار نباشد. Fiber 2.29.0 با زبان گو نسخه ۱.۱۴ تا ۱.۱۷ تست شده است. +* به دلیل استفاده ناامن از Fiber, ممکن است کتابخانه همیشه با آخرین نسخه Go سازگار نباشد. Fiber 2.40.0 با زبان گو نسخه 1.16 تا 1.19 تست شده است. * فریمورک Fiber با پکیج net/http سازگار نیست. این بدان معناست شما نمی توانید از پکیج های مانند go-swagger, gqlgen یا سایر پروژه هایی که بخشی از اکوسیستم net/http هستند استفاده کنید.
    diff --git a/.github/README_fr.md b/.github/README_fr.md index 217108adb9..f0ad7525b0 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -142,7 +142,7 @@ Les nouveaux gophers qui passent de [Node.js](https://nodejs.org/en/about/) à [ Fiber est **inspiré** par Express, le framework web le plus populaire d'Internet. Nous avons combiné la **facilité** d'Express, et la **performance brute** de Go. Si vous avez déja développé une application web en Node.js (_en utilisant Express ou équivalent_), alors de nombreuses méthodes et principes vous sembleront **familiers**. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Exemples diff --git a/.github/README_he.md b/.github/README_he.md index 7b19957e0e..aacf2777e7 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -187,7 +187,7 @@ Fiber נוצרה **בהשראת** Express, ה-web framework הפופולרית
    ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 דוגמאות diff --git a/.github/README_id.md b/.github/README_id.md index 5b7253e4d4..b16b32d6c8 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -145,7 +145,7 @@ Kami **mendengarkan** para pengguna di [GitHub Issues](https://github.com/gofibe ## ⚠️ Limitasi -* Karena penggunaan Fiber yang tidak aman, perpustakaan mungkin tidak selalu kompatibel dengan versi Go terbaru. Fiber 2.29.0 telah diuji dengan Go versi 1.14 hingga 1.19. +* Karena penggunaan Fiber yang tidak aman, perpustakaan mungkin tidak selalu kompatibel dengan versi Go terbaru. Fiber 2.40.0 telah diuji dengan Go versi 1.16 hingga 1.19. * Fiber tidak kompatibel dengan antarmuka net/http. Ini berarti kamu tidak akan dapat menggunakan proyek seperti gqlgen, go-swagger, atau lainnya yang merupakan bagian dari ekosistem net/http. ## 👀 Contoh diff --git a/.github/README_it.md b/.github/README_it.md index 81ab44a879..5651c3ebee 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -143,7 +143,7 @@ Fiber è **ispirato** da Express, il web framework più popolare su internet. Ab ## ⚠️ Limitazioni -* Dato che Fiber utilizza unsafe, la libreria non sempre potrebbe essere compatibile con l'ultima versione di Go. Fiber 2.29.0 è stato testato con la versioni 1.14 alla 1.19 di Go. +* Dato che Fiber utilizza unsafe, la libreria non sempre potrebbe essere compatibile con l'ultima versione di Go. Fiber 2.40.0 è stato testato con la versioni 1.16 alla 1.19 di Go. * Fiber non è compatibile con le interfacce net/http. Questo significa che non è possibile utilizzare progetti come qglgen, go-swagger, o altri che fanno parte dell'ecosistema net/http. ## 👀 Esempi diff --git a/.github/README_ja.md b/.github/README_ja.md index 88b97c2b4f..f15e5247dd 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -147,7 +147,7 @@ Fiber は人気の高い Web フレームワークである Expressjs に**イ ## ⚠️ 制限事項 -- Fiber は unsafe パッケージを使用しているため、最新の Go バージョンと互換性がない場合があります。Fiber 2.29.0 は、Go のバージョン 1.14 から 1.19 でテストされています。 +- Fiber は unsafe パッケージを使用しているため、最新の Go バージョンと互換性がない場合があります。Fiber 2.40.0 は、Go のバージョン 1.16 から 1.19 でテストされています。 - Fiber は net/http インターフェースと互換性がありません。つまり、gqlgen や go-swagger など、net/http のエコシステムの一部であるプロジェクトを使用することができません。 ## 👀 例 diff --git a/.github/README_ko.md b/.github/README_ko.md index 291b5acefa..6c14afc5e0 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -144,7 +144,7 @@ Fiber는 인터넷에서 가장 인기있는 웹 프레임워크인 Express에 우리는 **어떤한** 작업, **마감일정**, 개발자의 **기술**이던간에 **빠르고**, **유연하고**, **익숙한** Go 웹 프레임워크를 만들기 위해 사용자들의 [이슈들](https://github.com/gofiber/fiber/issues)을(그리고 모든 인터넷을 통해) **듣고 있습니다**! Express가 자바스크립트 세계에서 하는 것 처럼요. ## ⚠️ 한계점 -* Fiber는 unsafe 패키지를 사용하기 때문에 최신 Go버전과 호환되지 않을 수 있습니다.Fiber 2.29.0은 Go 버전 1.14에서 1.19로 테스트되고 있습니다. +* Fiber는 unsafe 패키지를 사용하기 때문에 최신 Go버전과 호환되지 않을 수 있습니다.Fiber 2.40.0은 Go 버전 1.16에서 1.19로 테스트되고 있습니다. * Fiber는 net/http 인터페이스와 호환되지 않습니다.즉, gqlgen이나 go-swagger 등 net/http 생태계의 일부인 프로젝트를 사용할 수 없습니다. ## 👀 예제 diff --git a/.github/README_nl.md b/.github/README_nl.md index 3d2a73f175..2bf40e931f 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -144,7 +144,7 @@ Fiber is **geïnspireerd** door Express, het populairste webframework op interne We **luisteren** naar onze gebruikers in [issues](https://github.com/gofiber/fiber/issues) (_en overal op het internet_) om een **snelle**, **flexibele** en **vriendelijk** Go web framework te maken voor **elke** taak, **deadline** en ontwikkelaar **vaardigheid**! Net zoals Express dat doet in de JavaScript-wereld. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Voorbeelden diff --git a/.github/README_pt.md b/.github/README_pt.md index a120a1767e..f4bfaa0412 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -142,7 +142,7 @@ Os novos gophers que mudaram do [Node.js](https://nodejs.org/en/about/) para o [ O Fiber é **inspirado** no Express, o framework web mais popular da Internet. Combinamos a **facilidade** do Express e com o **desempenho bruto** do Go. Se você já implementou um aplicativo web com Node.js ( _usando Express.js ou similar_ ), então muitos métodos e princípios parecerão **muito familiares** para você. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Exemplos diff --git a/.github/README_ru.md b/.github/README_ru.md index b314713cea..05063c44f1 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -144,7 +144,7 @@ Fiber **вдохновлен** Express, самым популярным веб Мы **прислушиваемся** к нашим пользователям в [issues](https://github.com/gofiber/fiber/issues), Discord [канале](https://gofiber.io/discord) _и в остальном Интернете_, чтобы создать **быстрый**, **гибкий** и **дружелюбный** веб фреймворк на Go для **любых** задач, **дедлайнов** и **уровней** разработчиков! Как это делает Express в мире JavaScript. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 Примеры diff --git a/.github/README_sa.md b/.github/README_sa.md index e8605edaef..50a0b63257 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -158,7 +158,7 @@ Fiber هو **مستوحى** من Express, إطار الويب الأكثر شع ** و تطوير **مهارات**! فقط مثل Express تفعل لـ JavaScript عالم. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.29.0 has been tested with Go versions 1.14 to 1.19. +* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.16 to 1.19. * Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. ## 👀 أمثلة diff --git a/.github/README_tr.md b/.github/README_tr.md index 4375426a26..f775218c0f 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -143,7 +143,7 @@ Fiber, internet üzerinde en popüler web framework'ü olan Express'ten **esinle ## ⚠️ Sınırlamalar -- Fiber unsafe kullanımı sebebiyle Go'nun son sürümüyle her zaman uyumlu olmayabilir. Fiber 2.29.0, Go 1.14 ile 1.19 sürümleriyle test edildi. +- Fiber unsafe kullanımı sebebiyle Go'nun son sürümüyle her zaman uyumlu olmayabilir. Fiber 2.40.0, Go 1.14 ile 1.19 sürümleriyle test edildi. - Fiber net/http arabirimiyle uyumlu değildir. Yani gqlgen veya go-swagger gibi net/http ekosisteminin parçası olan projeleri kullanamazsınız. ## 👀 Örnekler diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index 5dfd510f14..7697ae6ee0 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -144,7 +144,7 @@ go get -u github.com/gofiber/fiber/v2 我们会**倾听**用户在[issues](https://github.com/gofiber/fiber/issues)和 Discord [channel](https://gofiber.io/discord)和在互联网上的所有诉求,为了创建一个能让有着任何技术栈的开发者都能在deadline前完成任务的**迅速**,**灵活**以及**友好**的`Go web`框架,就像`Express`在`JavaScript`世界中一样。 ## ⚠️ 限制 -* 由于 Fiber 使用了 unsafe 特性,导致其可能与最新的 Go 版本不兼容。Fiber 2.29.0 已经在 Go 1.14 到 1.19 上测试过。 +* 由于 Fiber 使用了 unsafe 特性,导致其可能与最新的 Go 版本不兼容。Fiber 2.40.0 已经在 Go 1.16 到 1.19 上测试过。 * Fiber 与 net/http 接口不兼容。也就是说你无法使用 gqlen,go-swagger 或者任何其他属于 net/http 生态的项目。 ## 👀 示例 diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index 90e58b24be..61d012e230 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -144,7 +144,7 @@ Fiber **受到** 網路上最流行的 Web 框架 ExpressJS**啟發**,結合 E 有什麼問題請發[issues](https://github.com/gofiber/fiber/issues)或加入 Discord [channel](https://gofiber.io/discord)討論,我們想要創造**快速**、**彈性**、**友善**的社群給**任何人**使用!就像 Express 那樣。 ## 限制 -* 由於 Fiber 使用了 unsafe,該庫可能並不總是與最新的 Go 版本兼容。 Fiber 2.29.0 已經用 Go 版本 1.14 到 1.19 進行了測試。 +* 由於 Fiber 使用了 unsafe,該庫可能並不總是與最新的 Go 版本兼容。 Fiber 2.40.0 已經用 Go 版本 1.16 到 1.19 進行了測試。 * Fiber 與 net/http 接口不兼容。 這意味著您將無法使用 gqlgen、go-swagger 或任何其他屬於 net/http 生態系統的項目。 ## 👀 範例 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 000f7717ef..c83f89e8c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: Build: strategy: matrix: - go-version: [1.14.x, 1.16.x, 1.19.x] + go-version: [1.16.x, 1.17.x, 1.19.x] platform: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: diff --git a/app_test.go b/app_test.go index acfa8f9b3d..2bef6a4796 100644 --- a/app_test.go +++ b/app_test.go @@ -10,7 +10,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "mime/multipart" "net" "net/http" @@ -44,7 +43,7 @@ func testErrorResponse(t *testing.T, err error, resp *http.Response, expectedBod utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 500, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, expectedBodyError, string(body), "Response body") } @@ -162,7 +161,7 @@ func Test_App_Errors(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 500, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "hi, i'm an error", string(body)) @@ -187,7 +186,7 @@ func Test_App_ErrorHandler_Custom(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "hi, i'm an custom error", string(body)) } @@ -216,7 +215,7 @@ func Test_App_ErrorHandler_HandlerStack(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 500, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "1: USE error", string(body)) } @@ -241,7 +240,7 @@ func Test_App_ErrorHandler_RouteStack(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 500, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "1: USE error", string(body)) } @@ -328,7 +327,7 @@ func Test_App_Use_UnescapedPath(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") // check the param result utils.AssertEqual(t, "اختبار", app.getString(body)) @@ -363,7 +362,7 @@ func Test_App_Use_CaseSensitive(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") // check the detected path result utils.AssertEqual(t, "/AbC", app.getString(body)) @@ -534,7 +533,7 @@ func Test_App_Order(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) } @@ -656,7 +655,7 @@ func Test_App_Static_Index_Default(t *testing.T) { utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!")) @@ -666,7 +665,7 @@ func Test_App_Static_Index_Default(t *testing.T) { utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") utils.AssertEqual(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "Cannot GET /not-found", string(body)) } @@ -683,7 +682,7 @@ func Test_App_Static_Direct(t *testing.T) { utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!")) @@ -694,7 +693,7 @@ func Test_App_Static_Direct(t *testing.T) { utils.AssertEqual(t, MIMEApplicationJSON, resp.Header.Get("Content-Type")) utils.AssertEqual(t, "", resp.Header.Get(HeaderCacheControl), "CacheControl Control") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, strings.Contains(string(body), "testRoutes")) } @@ -791,7 +790,7 @@ func Test_App_Static_Wildcard(t *testing.T) { utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, strings.Contains(string(body), "Test file")) } @@ -816,7 +815,7 @@ func Test_App_Static_Prefix_Wildcard(t *testing.T) { utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, strings.Contains(string(body), "Test file")) } @@ -919,7 +918,7 @@ func Test_App_Static_Next(t *testing.T) { utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") utils.AssertEqual(t, MIMETextPlainCharsetUTF8, resp.Header.Get(HeaderContentType)) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, strings.Contains(string(body), "You've skipped app.Static")) }) @@ -933,7 +932,7 @@ func Test_App_Static_Next(t *testing.T) { utils.AssertEqual(t, false, resp.Header.Get(HeaderContentLength) == "") utils.AssertEqual(t, MIMETextHTMLCharsetUTF8, resp.Header.Get(HeaderContentType)) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!")) }) @@ -964,7 +963,7 @@ func Test_App_Mixed_Routes_WithSameLen(t *testing.T) { utils.AssertEqual(t, "TestValue", resp.Header.Get("TestHeader")) utils.AssertEqual(t, "text/html", resp.Header.Get(HeaderContentType)) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "FOO_BAR", string(body)) @@ -977,7 +976,7 @@ func Test_App_Mixed_Routes_WithSameLen(t *testing.T) { utils.AssertEqual(t, "TestValue", resp.Header.Get("TestHeader")) utils.AssertEqual(t, "text/html; charset=utf-8", resp.Header.Get(HeaderContentType)) - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, strings.Contains(string(body), "Hello, World!"), "Response: "+string(body)) utils.AssertEqual(t, true, strings.HasPrefix(string(body), ""), "Response: "+string(body)) @@ -1415,8 +1414,8 @@ func Test_App_ReadBodyStream(t *testing.T) { testString := "this is a test" resp, err := app.Test(httptest.NewRequest("POST", "/", bytes.NewBufferString(testString))) utils.AssertEqual(t, nil, err, "app.Test(req)") - body, err := ioutil.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "ioutil.ReadAll(resp.Body)") + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err, "io.ReadAll(resp.Body)") utils.AssertEqual(t, fmt.Sprintf("true %s", testString), string(body)) } @@ -1461,8 +1460,8 @@ func Test_App_DisablePreParseMultipartForm(t *testing.T) { req.Header.Set("Content-Type", w.FormDataContentType()) resp, err := app.Test(req) utils.AssertEqual(t, nil, err, "app.Test(req)") - body, err := ioutil.ReadAll(resp.Body) - utils.AssertEqual(t, nil, err, "ioutil.ReadAll(resp.Body)") + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err, "io.ReadAll(resp.Body)") utils.AssertEqual(t, testString, string(body)) } diff --git a/client.go b/client.go index 4a520b7886..02816db228 100644 --- a/client.go +++ b/client.go @@ -7,7 +7,6 @@ import ( "encoding/xml" "fmt" "io" - "io/ioutil" "mime/multipart" "net" "os" @@ -542,7 +541,7 @@ func (a *Agent) FileData(formFiles ...*FormFile) *Agent { // SendFile reads file and appends it to multipart form request. func (a *Agent) SendFile(filename string, fieldname ...string) *Agent { - content, err := ioutil.ReadFile(filepath.Clean(filename)) + content, err := os.ReadFile(filepath.Clean(filename)) if err != nil { a.errs = append(a.errs, err) return a diff --git a/client_test.go b/client_test.go index d753054620..c8e93c5e85 100644 --- a/client_test.go +++ b/client_test.go @@ -8,9 +8,9 @@ import ( "errors" "fmt" "io" - "io/ioutil" "mime/multipart" "net" + "os" "path/filepath" "regexp" "strings" @@ -811,7 +811,7 @@ func checkFormFile(t *testing.T, fh *multipart.FileHeader, filename string) { basename := filepath.Base(filename) utils.AssertEqual(t, fh.Filename, basename) - b1, err := ioutil.ReadFile(filename) + b1, err := os.ReadFile(filename) utils.AssertEqual(t, nil, err) b2 := make([]byte, fh.Size) diff --git a/ctx.go b/ctx.go index 0fa466e5c3..595399992a 100644 --- a/ctx.go +++ b/ctx.go @@ -13,7 +13,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "mime/multipart" "net" "net/http" @@ -1489,7 +1488,7 @@ func (c *Ctx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, s return err } - content, err := ioutil.ReadAll(file) + content, err := io.ReadAll(file) if err != nil { return err } diff --git a/ctx_test.go b/ctx_test.go index f1ba1f6cde..5d27c34b9e 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -17,7 +17,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "mime/multipart" "net/http/httptest" "net/url" @@ -670,7 +669,7 @@ func Test_Ctx_UserContext_Multiple_Requests(t *testing.T) { utils.AssertEqual(t, nil, err, "Unexpected error from response") utils.AssertEqual(t, StatusOK, resp.StatusCode, "context.Context returned from c.UserContext() is reused") - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "Unexpected error from reading response body") utils.AssertEqual(t, fmt.Sprintf("resp_%d_returned", i), string(b), "response text incorrect") }) @@ -1129,7 +1128,7 @@ func Test_Ctx_PortInHandler(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "0", string(body)) } @@ -1527,7 +1526,7 @@ func Test_Ctx_ClientHelloInfo(t *testing.T) { // Test without TLS handler resp, _ := app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) - body, _ := ioutil.ReadAll(resp.Body) + body, _ := io.ReadAll(resp.Body) utils.AssertEqual(t, []byte("ClientHelloInfo is nil"), body) // Test with TLS Handler @@ -1543,17 +1542,17 @@ func Test_Ctx_ClientHelloInfo(t *testing.T) { // Test ServerName resp, _ = app.Test(httptest.NewRequest(MethodGet, "/ServerName", nil)) - body, _ = ioutil.ReadAll(resp.Body) + body, _ = io.ReadAll(resp.Body) utils.AssertEqual(t, []byte("example.golang"), body) // Test SignatureSchemes resp, _ = app.Test(httptest.NewRequest(MethodGet, "/SignatureSchemes", nil)) - body, _ = ioutil.ReadAll(resp.Body) + body, _ = io.ReadAll(resp.Body) utils.AssertEqual(t, "["+strconv.Itoa(PSSWithSHA256)+"]", string(body)) // Test SupportedVersions resp, _ = app.Test(httptest.NewRequest(MethodGet, "/SupportedVersions", nil)) - body, _ = ioutil.ReadAll(resp.Body) + body, _ = io.ReadAll(resp.Body) utils.AssertEqual(t, "["+strconv.Itoa(VersionTLS13)+"]", string(body)) } @@ -2106,14 +2105,14 @@ func Test_Ctx_SaveFile(t *testing.T) { fh, err := c.FormFile("file") utils.AssertEqual(t, nil, err) - tempFile, err := ioutil.TempFile(os.TempDir(), "test-") + tempFile, err := os.CreateTemp(os.TempDir(), "test-") utils.AssertEqual(t, nil, err) defer os.Remove(tempFile.Name()) err = c.SaveFile(fh, tempFile.Name()) utils.AssertEqual(t, nil, err) - bs, err := ioutil.ReadFile(tempFile.Name()) + bs, err := os.ReadFile(tempFile.Name()) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "hello world", string(bs)) return nil @@ -2257,7 +2256,7 @@ func Test_Ctx_Download(t *testing.T) { utils.AssertEqual(t, nil, err) defer f.Close() - expect, err := ioutil.ReadAll(f) + expect, err := io.ReadAll(f) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, expect, c.Response().Body()) utils.AssertEqual(t, `attachment; filename="Awesome+File%21"`, string(c.Response().Header.Peek(HeaderContentDisposition))) @@ -2275,7 +2274,7 @@ func Test_Ctx_SendFile(t *testing.T) { f, err := os.Open("./ctx.go") utils.AssertEqual(t, nil, err) defer f.Close() - expectFileContent, err := ioutil.ReadAll(f) + expectFileContent, err := io.ReadAll(f) utils.AssertEqual(t, nil, err) // fetch file info for the not modified test case fI, err := os.Stat("./ctx.go") @@ -3278,7 +3277,7 @@ func Test_Ctx_Render_Engine_Error(t *testing.T) { func Test_Ctx_Render_Go_Template(t *testing.T) { t.Parallel() - file, err := ioutil.TempFile(os.TempDir(), "fiber") + file, err := os.CreateTemp(os.TempDir(), "fiber") utils.AssertEqual(t, nil, err) defer os.Remove(file.Name()) diff --git a/internal/gopsutil/common/common.go b/internal/gopsutil/common/common.go index 5c7adace0e..a02fce4b1a 100644 --- a/internal/gopsutil/common/common.go +++ b/internal/gopsutil/common/common.go @@ -12,7 +12,6 @@ import ( "context" "errors" "fmt" - "io/ioutil" "log" "net/url" "os" @@ -84,7 +83,7 @@ func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) { fpath += "_" + i.Suffix } if PathExists(fpath) { - return ioutil.ReadFile(fpath) + return os.ReadFile(fpath) } return []byte{}, fmt.Errorf("could not find testdata: %s", fpath) } @@ -97,7 +96,7 @@ var ErrNotImplementedError = errors.New("not implemented yet") // ReadFile reads contents from a file func ReadFile(filename string) (string, error) { - content, err := ioutil.ReadFile(filename) + content, err := os.ReadFile(filename) if err != nil { return "", err diff --git a/internal/gopsutil/load/load_linux.go b/internal/gopsutil/load/load_linux.go index 8cdd84243d..422e20075f 100644 --- a/internal/gopsutil/load/load_linux.go +++ b/internal/gopsutil/load/load_linux.go @@ -5,7 +5,7 @@ package load import ( "context" - "io/ioutil" + "os" "strconv" "strings" "syscall" @@ -76,7 +76,7 @@ func Misc() (*MiscStat, error) { func MiscWithContext(ctx context.Context) (*MiscStat, error) { filename := common.HostProc("stat") - out, err := ioutil.ReadFile(filename) + out, err := os.ReadFile(filename) if err != nil { return nil, err } @@ -126,7 +126,7 @@ func getProcsTotal() (int64, error) { func readLoadAvgFromFile() ([]string, error) { loadavgFilename := common.HostProc("loadavg") - line, err := ioutil.ReadFile(loadavgFilename) + line, err := os.ReadFile(loadavgFilename) if err != nil { return nil, err } diff --git a/internal/gopsutil/net/net_linux.go b/internal/gopsutil/net/net_linux.go index 7c7d3bd90c..de8a3a7e80 100644 --- a/internal/gopsutil/net/net_linux.go +++ b/internal/gopsutil/net/net_linux.go @@ -10,7 +10,6 @@ import ( "errors" "fmt" "io" - "io/ioutil" "net" "os" "strconv" @@ -637,7 +636,7 @@ func (p *process) getUids() ([]int32, error) { func (p *process) fillFromStatus() error { pid := p.Pid statPath := common.HostProc(strconv.Itoa(int(pid)), "status") - contents, err := ioutil.ReadFile(statPath) + contents, err := os.ReadFile(statPath) if err != nil { return err } @@ -759,7 +758,7 @@ func processInet(file string, kind netConnectionKindType, inodes map[string][]in // This minimizes duplicates in the returned connections // For more info: // https://github.com/shirou/gopsutil/pull/361 - contents, err := ioutil.ReadFile(file) + contents, err := os.ReadFile(file) if err != nil { return nil, err } @@ -820,7 +819,7 @@ func processUnix(file string, kind netConnectionKindType, inodes map[string][]in // This minimizes duplicates in the returned connections // For more info: // https://github.com/shirou/gopsutil/pull/361 - contents, err := ioutil.ReadFile(file) + contents, err := os.ReadFile(file) if err != nil { return nil, err } diff --git a/internal/gopsutil/process/process_linux.go b/internal/gopsutil/process/process_linux.go index 73ce975b5e..05d3e7ab26 100644 --- a/internal/gopsutil/process/process_linux.go +++ b/internal/gopsutil/process/process_linux.go @@ -9,7 +9,6 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" "math" "os" "path/filepath" @@ -190,7 +189,7 @@ func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) { // see https://github.com/shirou/gopsutil/issues/596#issuecomment-432707831 for implementation details pid := p.Pid statPath := common.HostProc(strconv.Itoa(int(pid)), "stat") - contents, err := ioutil.ReadFile(statPath) + contents, err := os.ReadFile(statPath) if err != nil { return false, err } @@ -566,7 +565,7 @@ func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]M ret = make([]MemoryMapsStat, 1) } smapsPath := common.HostProc(strconv.Itoa(int(pid)), "smaps") - contents, err := ioutil.ReadFile(smapsPath) + contents, err := os.ReadFile(smapsPath) if err != nil { return nil, err } @@ -830,7 +829,7 @@ func (p *Process) fillFromExeWithContext(ctx context.Context) (string, error) { func (p *Process) fillFromCmdlineWithContext(ctx context.Context) (string, error) { pid := p.Pid cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline") - cmdline, err := ioutil.ReadFile(cmdPath) + cmdline, err := os.ReadFile(cmdPath) if err != nil { return "", err } @@ -847,7 +846,7 @@ func (p *Process) fillFromCmdlineWithContext(ctx context.Context) (string, error func (p *Process) fillSliceFromCmdlineWithContext(ctx context.Context) ([]string, error) { pid := p.Pid cmdPath := common.HostProc(strconv.Itoa(int(pid)), "cmdline") - cmdline, err := ioutil.ReadFile(cmdPath) + cmdline, err := os.ReadFile(cmdPath) if err != nil { return nil, err } @@ -870,7 +869,7 @@ func (p *Process) fillSliceFromCmdlineWithContext(ctx context.Context) ([]string func (p *Process) fillFromIOWithContext(ctx context.Context) (*IOCountersStat, error) { pid := p.Pid ioPath := common.HostProc(strconv.Itoa(int(pid)), "io") - ioline, err := ioutil.ReadFile(ioPath) + ioline, err := os.ReadFile(ioPath) if err != nil { return nil, err } @@ -909,7 +908,7 @@ func (p *Process) fillFromIOWithContext(ctx context.Context) (*IOCountersStat, e func (p *Process) fillFromStatmWithContext(ctx context.Context) (*MemoryInfoStat, *MemoryInfoExStat, error) { pid := p.Pid memPath := common.HostProc(strconv.Itoa(int(pid)), "statm") - contents, err := ioutil.ReadFile(memPath) + contents, err := os.ReadFile(memPath) if err != nil { return nil, nil, err } @@ -961,7 +960,7 @@ func (p *Process) fillFromStatmWithContext(ctx context.Context) (*MemoryInfoStat func (p *Process) fillFromStatusWithContext(ctx context.Context) error { pid := p.Pid statPath := common.HostProc(strconv.Itoa(int(pid)), "status") - contents, err := ioutil.ReadFile(statPath) + contents, err := os.ReadFile(statPath) if err != nil { return err } @@ -1147,7 +1146,7 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (ui statPath = common.HostProc(strconv.Itoa(int(pid)), "task", strconv.Itoa(int(tid)), "stat") } - contents, err := ioutil.ReadFile(statPath) + contents, err := os.ReadFile(statPath) if err != nil { return 0, 0, nil, 0, 0, 0, nil, err } diff --git a/internal/msgp/file_port.go b/internal/msgp/file_port.go index 7280a8b943..5f44b2f581 100644 --- a/internal/msgp/file_port.go +++ b/internal/msgp/file_port.go @@ -4,7 +4,7 @@ package msgp import ( - "io/ioutil" + "io" "os" ) @@ -21,7 +21,7 @@ func ReadFile(dst Unmarshaler, file *os.File) error { return u.DecodeMsg(NewReader(file)) } - data, err := ioutil.ReadAll(file) + data, err := io.ReadAll(file) if err != nil { return err } diff --git a/internal/template/utils/utils.go b/internal/template/utils/utils.go index a19e6d6e24..0ae8f22fd8 100644 --- a/internal/template/utils/utils.go +++ b/internal/template/utils/utils.go @@ -1,7 +1,7 @@ package utils import ( - "io/ioutil" + "io" "net/http" "os" pathpkg "path" @@ -30,9 +30,9 @@ func ReadFile(path string, fs http.FileSystem) ([]byte, error) { return nil, err } defer file.Close() - return ioutil.ReadAll(file) + return io.ReadAll(file) } - return ioutil.ReadFile(path) + return os.ReadFile(path) } // readDirNames reads the directory named by dirname and returns diff --git a/listen.go b/listen.go index d23d426780..ddbf8c81fc 100644 --- a/listen.go +++ b/listen.go @@ -9,7 +9,6 @@ import ( "crypto/x509" "errors" "fmt" - "io/ioutil" "net" "os" "path/filepath" @@ -154,7 +153,7 @@ func (app *App) ListenMutualTLS(addr, certFile, keyFile, clientCertFile string) return fmt.Errorf("tls: cannot load TLS key pair from certFile=%q and keyFile=%q: %s", certFile, keyFile, err) } - clientCACert, err := ioutil.ReadFile(filepath.Clean(clientCertFile)) + clientCACert, err := os.ReadFile(filepath.Clean(clientCertFile)) if err != nil { return err } diff --git a/middleware/basicauth/basicauth_test.go b/middleware/basicauth/basicauth_test.go index 8bfdd21df8..9046059e8d 100644 --- a/middleware/basicauth/basicauth_test.go +++ b/middleware/basicauth/basicauth_test.go @@ -2,7 +2,7 @@ package basicauth import ( "fmt" - "io/ioutil" + "io" "net/http/httptest" "testing" @@ -82,7 +82,7 @@ func Test_Middleware_BasicAuth(t *testing.T) { resp, err := app.Test(req) utils.AssertEqual(t, nil, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, tt.statusCode, resp.StatusCode) diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go index 3216a7c48a..e261023820 100644 --- a/middleware/cache/cache_test.go +++ b/middleware/cache/cache_test.go @@ -6,17 +6,17 @@ import ( "bytes" "fmt" "io" - "io/ioutil" "math" "net/http" "net/http/httptest" + "os" "strconv" "testing" "time" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/middleware/etag" "github.com/gofiber/fiber/v2/internal/storage/memory" + "github.com/gofiber/fiber/v2/middleware/etag" "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" ) @@ -55,7 +55,7 @@ func Test_Cache_Expired(t *testing.T) { resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) utils.AssertEqual(t, nil, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) // Sleep until the cache is expired @@ -63,7 +63,7 @@ func Test_Cache_Expired(t *testing.T) { respCached, err := app.Test(httptest.NewRequest("GET", "/", nil)) utils.AssertEqual(t, nil, err) - bodyCached, err := ioutil.ReadAll(respCached.Body) + bodyCached, err := io.ReadAll(respCached.Body) utils.AssertEqual(t, nil, err) if bytes.Equal(body, bodyCached) { @@ -73,7 +73,7 @@ func Test_Cache_Expired(t *testing.T) { // Next response should be also cached respCachedNextRound, err := app.Test(httptest.NewRequest("GET", "/", nil)) utils.AssertEqual(t, nil, err) - bodyCachedNextRound, err := ioutil.ReadAll(respCachedNextRound.Body) + bodyCachedNextRound, err := io.ReadAll(respCachedNextRound.Body) utils.AssertEqual(t, nil, err) if !bytes.Equal(bodyCachedNextRound, bodyCached) { @@ -100,9 +100,9 @@ func Test_Cache(t *testing.T) { cachedResp, err := app.Test(cachedReq) utils.AssertEqual(t, nil, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) - cachedBody, err := ioutil.ReadAll(cachedResp.Body) + cachedBody, err := io.ReadAll(cachedResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cachedBody, body) @@ -123,7 +123,7 @@ func Test_Cache_WithNoCacheRequestDirective(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) resp, err := app.Test(req) defer resp.Body.Close() - body, _ := ioutil.ReadAll(resp.Body) + body, _ := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, resp.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("1"), body) @@ -133,7 +133,7 @@ func Test_Cache_WithNoCacheRequestDirective(t *testing.T) { cachedReq := httptest.NewRequest("GET", "/?id=2", nil) cachedResp, err := app.Test(cachedReq) defer cachedResp.Body.Close() - cachedBody, _ := ioutil.ReadAll(cachedResp.Body) + cachedBody, _ := io.ReadAll(cachedResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, cachedResp.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("1"), cachedBody) @@ -144,7 +144,7 @@ func Test_Cache_WithNoCacheRequestDirective(t *testing.T) { noCacheReq.Header.Set(fiber.HeaderCacheControl, noCache) noCacheResp, err := app.Test(noCacheReq) defer noCacheResp.Body.Close() - noCacheBody, _ := ioutil.ReadAll(noCacheResp.Body) + noCacheBody, _ := io.ReadAll(noCacheResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, noCacheResp.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("2"), noCacheBody) @@ -156,7 +156,7 @@ func Test_Cache_WithNoCacheRequestDirective(t *testing.T) { noCacheReq1.Header.Set(fiber.HeaderCacheControl, noCache) noCacheResp1, err := app.Test(noCacheReq1) defer noCacheResp1.Body.Close() - noCacheBody1, _ := ioutil.ReadAll(noCacheResp1.Body) + noCacheBody1, _ := io.ReadAll(noCacheResp1.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheMiss, noCacheResp1.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("2"), noCacheBody1) @@ -166,7 +166,7 @@ func Test_Cache_WithNoCacheRequestDirective(t *testing.T) { cachedReq1 := httptest.NewRequest("GET", "/", nil) cachedResp1, err := app.Test(cachedReq1) defer cachedResp1.Body.Close() - cachedBody1, _ := ioutil.ReadAll(cachedResp1.Body) + cachedBody1, _ := io.ReadAll(cachedResp1.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, cachedResp1.Header.Get("X-Cache")) utils.AssertEqual(t, []byte("2"), cachedBody1) @@ -255,7 +255,7 @@ func Test_Cache_WithNoStoreRequestDirective(t *testing.T) { noStoreReq.Header.Set(fiber.HeaderCacheControl, noStore) noStoreResp, err := app.Test(noStoreReq) defer noStoreResp.Body.Close() - noStoreBody, _ := ioutil.ReadAll(noStoreResp.Body) + noStoreBody, _ := io.ReadAll(noStoreResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, []byte("2"), noStoreBody) // Response not cached, returns updated response @@ -286,7 +286,7 @@ func Test_Cache_WithSeveralRequests(t *testing.T) { utils.AssertEqual(t, nil, err) }(rsp.Body) - idFromServ, err := ioutil.ReadAll(rsp.Body) + idFromServ, err := io.ReadAll(rsp.Body) utils.AssertEqual(t, nil, err) a, err := strconv.Atoi(string(idFromServ)) @@ -319,9 +319,9 @@ func Test_Cache_Invalid_Expiration(t *testing.T) { cachedResp, err := app.Test(cachedReq) utils.AssertEqual(t, nil, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) - cachedBody, err := ioutil.ReadAll(cachedResp.Body) + cachedBody, err := io.ReadAll(cachedResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cachedBody, body) @@ -344,25 +344,25 @@ func Test_Cache_Get(t *testing.T) { resp, err := app.Test(httptest.NewRequest("POST", "/?cache=123", nil)) utils.AssertEqual(t, nil, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) resp, err = app.Test(httptest.NewRequest("POST", "/?cache=12345", nil)) utils.AssertEqual(t, nil, err) - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "12345", string(body)) resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=123", nil)) utils.AssertEqual(t, nil, err) - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=12345", nil)) utils.AssertEqual(t, nil, err) - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) } @@ -386,25 +386,25 @@ func Test_Cache_Post(t *testing.T) { resp, err := app.Test(httptest.NewRequest("POST", "/?cache=123", nil)) utils.AssertEqual(t, nil, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) resp, err = app.Test(httptest.NewRequest("POST", "/?cache=12345", nil)) utils.AssertEqual(t, nil, err) - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=123", nil)) utils.AssertEqual(t, nil, err) - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(body)) resp, err = app.Test(httptest.NewRequest("GET", "/get?cache=12345", nil)) utils.AssertEqual(t, nil, err) - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "12345", string(body)) } @@ -422,14 +422,14 @@ func Test_Cache_NothingToCache(t *testing.T) { resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) utils.AssertEqual(t, nil, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) time.Sleep(500 * time.Millisecond) respCached, err := app.Test(httptest.NewRequest("GET", "/", nil)) utils.AssertEqual(t, nil, err) - bodyCached, err := ioutil.ReadAll(respCached.Body) + bodyCached, err := io.ReadAll(respCached.Body) utils.AssertEqual(t, nil, err) if bytes.Equal(body, bodyCached) { @@ -459,12 +459,12 @@ func Test_Cache_CustomNext(t *testing.T) { resp, err := app.Test(httptest.NewRequest("GET", "/", nil)) utils.AssertEqual(t, nil, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) respCached, err := app.Test(httptest.NewRequest("GET", "/", nil)) utils.AssertEqual(t, nil, err) - bodyCached, err := ioutil.ReadAll(respCached.Body) + bodyCached, err := io.ReadAll(respCached.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, bytes.Equal(body, bodyCached)) utils.AssertEqual(t, true, respCached.Header.Get(fiber.HeaderCacheControl) != "") @@ -526,9 +526,9 @@ func Test_CustomExpiration(t *testing.T) { cachedResp, err := app.Test(httptest.NewRequest("GET", "/", nil)) utils.AssertEqual(t, nil, err) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) - cachedBody, err := ioutil.ReadAll(cachedResp.Body) + cachedBody, err := io.ReadAll(cachedResp.Body) utils.AssertEqual(t, nil, err) if bytes.Equal(body, cachedBody) { @@ -538,7 +538,7 @@ func Test_CustomExpiration(t *testing.T) { // Next response should be cached cachedRespNextRound, err := app.Test(httptest.NewRequest("GET", "/", nil)) utils.AssertEqual(t, nil, err) - cachedBodyNextRound, err := ioutil.ReadAll(cachedRespNextRound.Body) + cachedBodyNextRound, err := io.ReadAll(cachedRespNextRound.Body) utils.AssertEqual(t, nil, err) if !bytes.Equal(cachedBodyNextRound, cachedBody) { @@ -632,9 +632,9 @@ func Test_Cache_WithHead(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cacheHit, cachedResp.Header.Get("X-Cache")) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) - cachedBody, err := ioutil.ReadAll(cachedResp.Body) + cachedBody, err := io.ReadAll(cachedResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, cachedBody, body) @@ -651,28 +651,28 @@ func Test_Cache_WithHeadThenGet(t *testing.T) { headResp, err := app.Test(httptest.NewRequest("HEAD", "/?cache=123", nil)) utils.AssertEqual(t, nil, err) - headBody, err := ioutil.ReadAll(headResp.Body) + headBody, err := io.ReadAll(headResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "", string(headBody)) utils.AssertEqual(t, cacheMiss, headResp.Header.Get("X-Cache")) headResp, err = app.Test(httptest.NewRequest("HEAD", "/?cache=123", nil)) utils.AssertEqual(t, nil, err) - headBody, err = ioutil.ReadAll(headResp.Body) + headBody, err = io.ReadAll(headResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "", string(headBody)) utils.AssertEqual(t, cacheHit, headResp.Header.Get("X-Cache")) getResp, err := app.Test(httptest.NewRequest("GET", "/?cache=123", nil)) utils.AssertEqual(t, nil, err) - getBody, err := ioutil.ReadAll(getResp.Body) + getBody, err := io.ReadAll(getResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(getBody)) utils.AssertEqual(t, cacheMiss, getResp.Header.Get("X-Cache")) getResp, err = app.Test(httptest.NewRequest("GET", "/?cache=123", nil)) utils.AssertEqual(t, nil, err) - getBody, err = ioutil.ReadAll(getResp.Body) + getBody, err = io.ReadAll(getResp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "123", string(getBody)) utils.AssertEqual(t, cacheHit, getResp.Header.Get("X-Cache")) @@ -785,7 +785,7 @@ func Benchmark_Cache(b *testing.B) { app.Use(New()) app.Get("/demo", func(c *fiber.Ctx) error { - data, _ := ioutil.ReadFile("../../.github/README.md") + data, _ := os.ReadFile("../../.github/README.md") return c.Status(fiber.StatusTeapot).Send(data) }) @@ -815,7 +815,7 @@ func Benchmark_Cache_Storage(b *testing.B) { })) app.Get("/demo", func(c *fiber.Ctx) error { - data, _ := ioutil.ReadFile("../../.github/README.md") + data, _ := os.ReadFile("../../.github/README.md") return c.Status(fiber.StatusTeapot).Send(data) }) diff --git a/middleware/compress/compress_test.go b/middleware/compress/compress_test.go index 75110f3f66..e2ad5acc4f 100644 --- a/middleware/compress/compress_test.go +++ b/middleware/compress/compress_test.go @@ -3,8 +3,9 @@ package compress import ( "errors" "fmt" - "io/ioutil" + "io" "net/http/httptest" + "os" "testing" "github.com/gofiber/fiber/v2" @@ -14,7 +15,7 @@ import ( var filedata []byte func init() { - dat, err := ioutil.ReadFile("../../.github/README.md") + dat, err := os.ReadFile("../../.github/README.md") if err != nil { panic(err) } @@ -41,7 +42,7 @@ func Test_Compress_Gzip(t *testing.T) { utils.AssertEqual(t, "gzip", resp.Header.Get(fiber.HeaderContentEncoding)) // Validate that the file size has shrunk - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, len(body) < len(filedata)) } @@ -69,7 +70,7 @@ func Test_Compress_Different_Level(t *testing.T) { utils.AssertEqual(t, "gzip", resp.Header.Get(fiber.HeaderContentEncoding)) // Validate that the file size has shrunk - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, len(body) < len(filedata)) }) @@ -94,7 +95,7 @@ func Test_Compress_Deflate(t *testing.T) { utils.AssertEqual(t, "deflate", resp.Header.Get(fiber.HeaderContentEncoding)) // Validate that the file size has shrunk - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, len(body) < len(filedata)) } @@ -117,7 +118,7 @@ func Test_Compress_Brotli(t *testing.T) { utils.AssertEqual(t, "br", resp.Header.Get(fiber.HeaderContentEncoding)) // Validate that the file size has shrunk - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, len(body) < len(filedata)) } @@ -140,7 +141,7 @@ func Test_Compress_Disabled(t *testing.T) { utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderContentEncoding)) // Validate the file size is not shrunk - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, len(body) == len(filedata)) } @@ -162,7 +163,7 @@ func Test_Compress_Next_Error(t *testing.T) { utils.AssertEqual(t, 500, resp.StatusCode, "Status code") utils.AssertEqual(t, "", resp.Header.Get(fiber.HeaderContentEncoding)) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "next error", string(body)) } diff --git a/middleware/envvar/envvar_test.go b/middleware/envvar/envvar_test.go index 56fc44fe1e..34d8a05074 100644 --- a/middleware/envvar/envvar_test.go +++ b/middleware/envvar/envvar_test.go @@ -2,7 +2,7 @@ package envvar import ( "encoding/json" - "io/ioutil" + "io" "net/http" "os" "testing" @@ -48,7 +48,7 @@ func TestEnvVarHandler(t *testing.T) { resp, err := app.Test(req) utils.AssertEqual(t, nil, err) - respBody, err := ioutil.ReadAll(resp.Body) + respBody, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, expectedEnvVarResponse, respBody) @@ -68,7 +68,7 @@ func TestEnvVarHandlerNotMatched(t *testing.T) { resp, err := app.Test(req) utils.AssertEqual(t, nil, err) - respBody, err := ioutil.ReadAll(resp.Body) + respBody, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, []byte("OK"), respBody) @@ -85,7 +85,7 @@ func TestEnvVarHandlerDefaultConfig(t *testing.T) { resp, err := app.Test(req) utils.AssertEqual(t, nil, err) - respBody, err := ioutil.ReadAll(resp.Body) + respBody, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) var envVars EnvVar @@ -118,7 +118,7 @@ func TestEnvVarHandlerSpecialValue(t *testing.T) { resp, err := app.Test(req) utils.AssertEqual(t, nil, err) - respBody, err := ioutil.ReadAll(resp.Body) + respBody, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) var envVars EnvVar @@ -130,7 +130,7 @@ func TestEnvVarHandlerSpecialValue(t *testing.T) { resp, err = app.Test(req) utils.AssertEqual(t, nil, err) - respBody, err = ioutil.ReadAll(resp.Body) + respBody, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) var envVarsExport EnvVar diff --git a/middleware/etag/etag_test.go b/middleware/etag/etag_test.go index c5765fc8ba..3b01cc89fe 100644 --- a/middleware/etag/etag_test.go +++ b/middleware/etag/etag_test.go @@ -2,7 +2,7 @@ package etag import ( "bytes" - "io/ioutil" + "io" "net/http/httptest" "testing" @@ -114,7 +114,7 @@ func testETagNewEtag(t *testing.T, headerIfNoneMatch, matched bool) { if matched { utils.AssertEqual(t, fiber.StatusNotModified, resp.StatusCode) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 0, len(b)) } @@ -164,7 +164,7 @@ func testETagWeakEtag(t *testing.T, headerIfNoneMatch, matched bool) { if matched { utils.AssertEqual(t, fiber.StatusNotModified, resp.StatusCode) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 0, len(b)) } @@ -218,7 +218,7 @@ func testETagCustomEtag(t *testing.T, headerIfNoneMatch, matched bool) { if matched { utils.AssertEqual(t, fiber.StatusNotModified, resp.StatusCode) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 0, len(b)) } diff --git a/middleware/expvar/expvar_test.go b/middleware/expvar/expvar_test.go index c202c2cba0..7240c93a62 100644 --- a/middleware/expvar/expvar_test.go +++ b/middleware/expvar/expvar_test.go @@ -2,7 +2,7 @@ package expvar import ( "bytes" - "io/ioutil" + "io" "net/http/httptest" "testing" @@ -23,7 +23,7 @@ func Test_Non_Expvar_Path(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "escaped", string(b)) } @@ -42,7 +42,7 @@ func Test_Expvar_Index(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode) utils.AssertEqual(t, fiber.MIMEApplicationJSONCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, bytes.Contains(b, []byte("cmdline"))) utils.AssertEqual(t, true, bytes.Contains(b, []byte("memstat"))) @@ -62,7 +62,7 @@ func Test_Expvar_Filter(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode) utils.AssertEqual(t, fiber.MIMEApplicationJSONCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, bytes.Contains(b, []byte("cmdline"))) utils.AssertEqual(t, false, bytes.Contains(b, []byte("memstat"))) diff --git a/middleware/favicon/favicon.go b/middleware/favicon/favicon.go index 5f7bcbfbb0..66b688093c 100644 --- a/middleware/favicon/favicon.go +++ b/middleware/favicon/favicon.go @@ -1,8 +1,9 @@ package favicon import ( - "io/ioutil" + "io" "net/http" + "os" "strconv" "github.com/gofiber/fiber/v2" @@ -80,10 +81,10 @@ func New(config ...Config) fiber.Handler { if err != nil { panic(err) } - if icon, err = ioutil.ReadAll(f); err != nil { + if icon, err = io.ReadAll(f); err != nil { panic(err) } - } else if icon, err = ioutil.ReadFile(cfg.File); err != nil { + } else if icon, err = os.ReadFile(cfg.File); err != nil { panic(err) } diff --git a/middleware/limiter/limiter_test.go b/middleware/limiter/limiter_test.go index 2a3e3d6fa0..acf0f0026b 100644 --- a/middleware/limiter/limiter_test.go +++ b/middleware/limiter/limiter_test.go @@ -1,7 +1,7 @@ package limiter import ( - "io/ioutil" + "io" "net/http" "net/http/httptest" "sync" @@ -37,7 +37,7 @@ func Test_Limiter_Concurrency_Store(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "Hello tester!", string(body)) } @@ -82,7 +82,7 @@ func Test_Limiter_Concurrency(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "Hello tester!", string(body)) } diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 95f053a008..43522fa300 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -3,7 +3,7 @@ package logger import ( "errors" "fmt" - "io/ioutil" + "io" "net/http" "net/http/httptest" "os" @@ -268,7 +268,7 @@ func Benchmark_Logger(b *testing.B) { app.Use(New(Config{ Format: "${bytesReceived} ${bytesSent} ${status}", - Output: ioutil.Discard, + Output: io.Discard, })) app.Get("/", func(c *fiber.Ctx) error { return c.SendString("Hello, World!") diff --git a/middleware/monitor/monitor_test.go b/middleware/monitor/monitor_test.go index 7b73e295ed..661bed8373 100644 --- a/middleware/monitor/monitor_test.go +++ b/middleware/monitor/monitor_test.go @@ -3,7 +3,7 @@ package monitor import ( "bytes" "fmt" - "io/ioutil" + "io" "net/http/httptest" "testing" "time" @@ -38,7 +38,7 @@ func Test_Monitor_Html(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode) utils.AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) - buf, err := ioutil.ReadAll(resp.Body) + buf, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, bytes.Contains(buf, []byte(""+defaultTitle+""))) timeoutLine := fmt.Sprintf("setTimeout(fetchJSON, %d)", @@ -54,7 +54,7 @@ func Test_Monitor_Html(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode) utils.AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) - buf, err = ioutil.ReadAll(resp.Body) + buf, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, bytes.Contains(buf, []byte(""+conf.Title+""))) timeoutLine = fmt.Sprintf("setTimeout(fetchJSON, %d)", @@ -74,7 +74,7 @@ func Test_Monitor_Html_CustomCodes(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode) utils.AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) - buf, err := ioutil.ReadAll(resp.Body) + buf, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, bytes.Contains(buf, []byte(""+defaultTitle+""))) timeoutLine := fmt.Sprintf("setTimeout(fetchJSON, %d)", @@ -94,7 +94,7 @@ func Test_Monitor_Html_CustomCodes(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode) utils.AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) - buf, err = ioutil.ReadAll(resp.Body) + buf, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, bytes.Contains(buf, []byte(""+conf.Title+""))) utils.AssertEqual(t, true, bytes.Contains(buf, []byte("https://cdnjs.com/libraries/Chart.js"))) @@ -121,7 +121,7 @@ func Test_Monitor_JSON(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode) utils.AssertEqual(t, fiber.MIMEApplicationJSON, resp.Header.Get(fiber.HeaderContentType)) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, bytes.Contains(b, []byte("pid"))) utils.AssertEqual(t, true, bytes.Contains(b, []byte("os"))) @@ -189,7 +189,7 @@ func Test_Monitor_APIOnly(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode) utils.AssertEqual(t, fiber.MIMEApplicationJSON, resp.Header.Get(fiber.HeaderContentType)) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, bytes.Contains(b, []byte("pid"))) utils.AssertEqual(t, true, bytes.Contains(b, []byte("os"))) diff --git a/middleware/pprof/pprof_test.go b/middleware/pprof/pprof_test.go index 5779bb8eb2..ed0a41b5b1 100644 --- a/middleware/pprof/pprof_test.go +++ b/middleware/pprof/pprof_test.go @@ -2,7 +2,7 @@ package pprof import ( "bytes" - "io/ioutil" + "io" "net/http/httptest" "testing" @@ -23,7 +23,7 @@ func Test_Non_Pprof_Path(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 200, resp.StatusCode) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "escaped", string(b)) } @@ -42,7 +42,7 @@ func Test_Pprof_Index(t *testing.T) { utils.AssertEqual(t, 200, resp.StatusCode) utils.AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, true, bytes.Contains(b, []byte("/debug/pprof/"))) } diff --git a/middleware/proxy/proxy_test.go b/middleware/proxy/proxy_test.go index 47ec14a5fd..595a46ff66 100644 --- a/middleware/proxy/proxy_test.go +++ b/middleware/proxy/proxy_test.go @@ -2,7 +2,7 @@ package proxy import ( "crypto/tls" - "io/ioutil" + "io" "net" "net/http" "net/http/httptest" @@ -174,7 +174,7 @@ func Test_Proxy_Forward(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "forwarded", string(b)) } @@ -234,7 +234,7 @@ func Test_Proxy_Modify_Response(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "modified response", string(b)) } @@ -261,7 +261,7 @@ func Test_Proxy_Modify_Request(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "modified request", string(b)) } @@ -285,7 +285,7 @@ func Test_Proxy_Timeout_Slow_Server(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "fiber is awesome", string(b)) } @@ -309,7 +309,7 @@ func Test_Proxy_With_Timeout(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, fiber.StatusInternalServerError, resp.StatusCode) - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "timeout", string(b)) } @@ -388,7 +388,7 @@ func Test_Proxy_Do_HTTP_Prefix_URL(t *testing.T) { resp, err := app.Test(httptest.NewRequest(http.MethodGet, "/http://"+addr, nil)) utils.AssertEqual(t, nil, err) - s, err := ioutil.ReadAll(resp.Body) + s, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "hello world", string(s)) } diff --git a/mount_test.go b/mount_test.go index 1fa9135914..deee3ad4c5 100644 --- a/mount_test.go +++ b/mount_test.go @@ -6,7 +6,7 @@ package fiber import ( "errors" - "io/ioutil" + "io" "net/http/httptest" "testing" @@ -216,7 +216,7 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { utils.AssertEqual(t, nil, err, "/api/sub req") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - b, err := ioutil.ReadAll(resp.Body) + b, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") utils.AssertEqual(t, "hi, i'm a custom sub fiber error", string(b), "Response body") @@ -224,7 +224,7 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { utils.AssertEqual(t, nil, err, "/api/sub/third req") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - b, err = ioutil.ReadAll(resp2.Body) + b, err = io.ReadAll(resp2.Body) utils.AssertEqual(t, nil, err, "iotuil.ReadAll()") utils.AssertEqual(t, "hi, i'm a custom sub sub fiber error", string(b), "Third fiber Response body") } @@ -250,7 +250,7 @@ func Test_Ctx_Render_Mount(t *testing.T) { utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, nil, err, "app.Test(req)") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

    Hello a!

    ", string(body)) } @@ -302,7 +302,7 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, nil, err, "app.Test(req)") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

    Hello a!

    ", string(body)) @@ -310,7 +310,7 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, nil, err, "app.Test(req)") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

    Hello, World!

    ", string(body)) @@ -318,7 +318,7 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") utils.AssertEqual(t, nil, err, "app.Test(req)") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

    I'm Bruh

    ", string(body)) @@ -345,7 +345,7 @@ func Test_Ctx_Render_MountGroup(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, "

    Hello doe!

    ", string(body)) } diff --git a/prefork_test.go b/prefork_test.go index 22dcfa6f68..e61780f2cd 100644 --- a/prefork_test.go +++ b/prefork_test.go @@ -6,7 +6,7 @@ package fiber import ( "crypto/tls" - "io/ioutil" + "io" "os" "testing" "time" @@ -83,7 +83,7 @@ func Test_App_Prefork_Child_Process_Never_Show_Startup_Message(t *testing.T) { utils.AssertEqual(t, nil, w.Close()) - out, err := ioutil.ReadAll(r) + out, err := io.ReadAll(r) utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 0, len(out)) } diff --git a/router_test.go b/router_test.go index d8a8c323f4..eceae1fd5b 100644 --- a/router_test.go +++ b/router_test.go @@ -10,8 +10,9 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "net/http/httptest" + "os" "strings" "testing" @@ -22,7 +23,7 @@ import ( var routesFixture = routeJSON{} func init() { - dat, err := ioutil.ReadFile("./.github/testdata/testRoutes.json") + dat, err := os.ReadFile("./.github/testdata/testRoutes.json") if err != nil { panic(err) } @@ -42,7 +43,7 @@ func Test_Route_Match_SameLength(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, ":param", app.getString(body)) @@ -51,7 +52,7 @@ func Test_Route_Match_SameLength(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "test", app.getString(body)) } @@ -67,7 +68,7 @@ func Test_Route_Match_Star(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "*", app.getString(body)) @@ -76,7 +77,7 @@ func Test_Route_Match_Star(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "test", app.getString(body)) @@ -113,7 +114,7 @@ func Test_Route_Match_Root(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "root", app.getString(body)) } @@ -131,7 +132,7 @@ func Test_Route_Match_Parser(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "bar", app.getString(body)) @@ -140,7 +141,7 @@ func Test_Route_Match_Parser(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "test", app.getString(body)) } @@ -156,7 +157,7 @@ func Test_Route_Match_Middleware(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "*", app.getString(body)) @@ -165,7 +166,7 @@ func Test_Route_Match_Middleware(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "bar/fasel", app.getString(body)) } @@ -181,7 +182,7 @@ func Test_Route_Match_UnescapedPath(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "test", app.getString(body)) // without special chars @@ -217,7 +218,7 @@ func Test_Route_Match_WithEscapeChar(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "static", app.getString(body)) @@ -226,7 +227,7 @@ func Test_Route_Match_WithEscapeChar(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "group", app.getString(body)) @@ -235,7 +236,7 @@ func Test_Route_Match_WithEscapeChar(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "awesome", app.getString(body)) } @@ -251,7 +252,7 @@ func Test_Route_Match_Middleware_HasPrefix(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "middleware", app.getString(body)) } @@ -267,7 +268,7 @@ func Test_Route_Match_Middleware_Root(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, "middleware", app.getString(body)) } @@ -339,7 +340,7 @@ func Test_Route_Static_Root(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, true, strings.Contains(app.getString(body), "color")) @@ -354,7 +355,7 @@ func Test_Route_Static_Root(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, true, strings.Contains(app.getString(body), "color")) } @@ -378,7 +379,7 @@ func Test_Route_Static_HasPrefix(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, true, strings.Contains(app.getString(body), "color")) @@ -399,7 +400,7 @@ func Test_Route_Static_HasPrefix(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, true, strings.Contains(app.getString(body), "color")) @@ -418,7 +419,7 @@ func Test_Route_Static_HasPrefix(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, true, strings.Contains(app.getString(body), "color")) @@ -437,7 +438,7 @@ func Test_Route_Static_HasPrefix(t *testing.T) { utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, 200, resp.StatusCode, "Status code") - body, err = ioutil.ReadAll(resp.Body) + body, err = io.ReadAll(resp.Body) utils.AssertEqual(t, nil, err, "app.Test(req)") utils.AssertEqual(t, true, strings.Contains(app.getString(body), "color")) } From fd5045edf92eea296651b44e2ff836b915c5ae32 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Oct 2022 12:16:49 +0200 Subject: [PATCH 139/141] Bump github.com/valyala/fasthttp from 1.40.0 to 1.41.0 (#2171) Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.40.0 to 1.41.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.40.0...v1.41.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index c726a553f2..d9239e4919 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,13 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 - github.com/valyala/fasthttp v1.40.0 + github.com/valyala/fasthttp v1.41.0 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab ) require ( github.com/andybalholm/brotli v1.0.4 // indirect - github.com/klauspost/compress v1.15.0 // indirect + github.com/klauspost/compress v1.15.9 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect diff --git a/go.sum b/go.sum index 46a41e53ee..b84535bd1f 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U= -github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 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= @@ -12,18 +12,17 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc= -github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= +github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY= +github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From ff4602980a2a88b4a543641714716e7a348b3007 Mon Sep 17 00:00:00 2001 From: pj Date: Thu, 27 Oct 2022 16:51:08 +1100 Subject: [PATCH 140/141] - update Locals function to accept interface{} key (#2144) - update Locals function to accept interface{} key (Requires new version of fasthttp) Co-authored-by: rocketlaunchr-cto --- ctx.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctx.go b/ctx.go index 595399992a..dcbe712526 100644 --- a/ctx.go +++ b/ctx.go @@ -887,9 +887,9 @@ func (c *Ctx) Links(link ...string) { bytebufferpool.Put(bb) } -// Locals makes it possible to pass interface{} values under string keys scoped to the request +// Locals makes it possible to pass interface{} values under keys scoped to the request // and therefore available to all following routes that match the request. -func (c *Ctx) Locals(key string, value ...interface{}) (val interface{}) { +func (c *Ctx) Locals(key interface{}, value ...interface{}) (val interface{}) { if len(value) == 0 { return c.fasthttp.UserValue(key) } From 543e8bb7ea36ff97c5f5ab6b64a58f594c1228d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Fri, 28 Oct 2022 18:19:02 +0300 Subject: [PATCH 141/141] v3: fix tests --- bind_test.go | 2 +- ctx_test.go | 34 +++++----------------------------- helpers_test.go | 2 +- middleware/cache/cache_test.go | 11 +++++++---- 4 files changed, 14 insertions(+), 35 deletions(-) diff --git a/bind_test.go b/bind_test.go index 923c83822d..51101859f4 100644 --- a/bind_test.go +++ b/bind_test.go @@ -1552,7 +1552,7 @@ func Test_Bind_RepeatParserWithSameStruct(t *testing.T) { type Request struct { QueryParam string `query:"query_param"` - HeaderParam string `reqHeader:"header_param"` + HeaderParam string `header:"header_param"` BodyParam string `json:"body_param" xml:"body_param" form:"body_param"` } diff --git a/ctx_test.go b/ctx_test.go index bdc1651bda..3ab3bdb19a 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -1596,27 +1596,8 @@ func Test_Ctx_Protocol(t *testing.T) { require.Equal(t, "HTTP/1.1", c.Protocol()) - c.Request().Header.Set(HeaderXForwardedProtocol, "https") - require.Equal(t, "https", c.Protocol()) - c.Request().Header.Reset() - - c.Request().Header.Set(HeaderXForwardedProto, "https, http") - require.Equal(t, "https", c.Protocol()) - c.Request().Header.Reset() - - c.Request().Header.Set(HeaderXForwardedProtocol, "https, http") - require.Equal(t, "https", c.Protocol()) - c.Request().Header.Reset() - - c.Request().Header.Set(HeaderXForwardedSsl, "on") - require.Equal(t, "https", c.Protocol()) - c.Request().Header.Reset() - - c.Request().Header.Set(HeaderXUrlScheme, "https") - require.Equal(t, "https", c.Protocol()) - c.Request().Header.Reset() - - require.Equal(t, "http", c.Protocol()) + c.Request().Header.SetProtocol("HTTP/2") + require.Equal(t, "HTTP/2", c.Protocol()) } // go test -v -run=^$ -bench=Benchmark_Ctx_Protocol -benchmem -count=4 @@ -2218,7 +2199,7 @@ func Test_Ctx_JSONP(t *testing.T) { }) require.NoError(t, err) require.Equal(t, `callback({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) - require.Equal(t, "application/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) + require.Equal(t, "text/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) err = c.JSONP(Map{ "Name": "Grame", @@ -2226,7 +2207,7 @@ func Test_Ctx_JSONP(t *testing.T) { }, "john") require.NoError(t, err) require.Equal(t, `john({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) - require.Equal(t, "application/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) + require.Equal(t, "text/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) } // go test -v -run=^$ -bench=Benchmark_Ctx_JSONP -benchmem -count=4 @@ -2914,11 +2895,6 @@ func Test_Ctx_SendStream(t *testing.T) { c.SendStream(bufio.NewReader(bytes.NewReader([]byte("Hello bufio")))) require.Equal(t, "Hello bufio", string(c.Response().Body())) - - file, err := os.Open("./.github/index.html") - require.NoError(t, err) - c.SendStream(bufio.NewReader(file)) - require.True(t, c.Response().Header.ContentLength() > 200) } // go test -run Test_Ctx_Set @@ -3381,4 +3357,4 @@ func Test_Ctx_IsFromLocal(t *testing.T) { require.False(t, c.IsFromLocal()) } -} \ No newline at end of file +} diff --git a/helpers_test.go b/helpers_test.go index cef05fac48..d7f658a88e 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -46,7 +46,7 @@ func Test_Utils_UniqueRouteStack(t *testing.T) { func Test_Utils_getGroupPath(t *testing.T) { t.Parallel() res := getGroupPath("/v1", "/") - require.Equal(t, "/v1", res) + require.Equal(t, "/v1/", res) res = getGroupPath("/v1/", "/") require.Equal(t, "/v1/", res) diff --git a/middleware/cache/cache_test.go b/middleware/cache/cache_test.go index 6c79778fd4..f2f98c3302 100644 --- a/middleware/cache/cache_test.go +++ b/middleware/cache/cache_test.go @@ -618,10 +618,11 @@ func Test_Cache_WithHead(t *testing.T) { app := fiber.New() app.Use(New()) - app.Get("/", func(c fiber.Ctx) error { + handler := func(c fiber.Ctx) error { now := fmt.Sprintf("%d", time.Now().UnixNano()) return c.SendString(now) - }) + } + app.Route("/").Get(handler).Head(handler) req := httptest.NewRequest("HEAD", "/", nil) resp, err := app.Test(req) @@ -646,9 +647,11 @@ func Test_Cache_WithHeadThenGet(t *testing.T) { app := fiber.New() app.Use(New()) - app.Get("/", func(c fiber.Ctx) error { + + handler := func(c fiber.Ctx) error { return c.SendString(c.Query("cache")) - }) + } + app.Route("/").Get(handler).Head(handler) headResp, err := app.Test(httptest.NewRequest("HEAD", "/?cache=123", nil)) require.NoError(t, err)