diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 7a84e21e40..d23c99af24 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 [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. +Before making any changes to this repository, we kindly request you to initiate discussions for proposed changes that do not yet have an associated [issue](https://github.com/gofiber/fiber/issues). Please use our [Discord](https://gofiber.io/discord) server to initiate these discussions. For [issue](https://github.com/gofiber/fiber/issues) that already exist, you may proceed with discussions using our [issue](https://github.com/gofiber/fiber/issues) tracker or any other suitable method, in consultation with the repository owners. Your collaboration is greatly appreciated. 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. @@ -22,6 +22,6 @@ All pull request that contains a feature or fix is mandatory to have unit tests. 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). +2. Tweet about the project [on your 𝕏 (Twitter)](https://x.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). diff --git a/.github/README.md b/.github/README.md index 03c596440e..bf3e2e531e 100644 --- a/.github/README.md +++ b/.github/README.md @@ -67,6 +67,12 @@ + + + + + +
@@ -145,7 +151,7 @@ go get -u github.com/gofiber/fiber/v3 - [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 [18 languages](https://docs.gofiber.io/) +- Translated in [19 languages](https://docs.gofiber.io/) - And much more, [explore Fiber](https://docs.gofiber.io/) ## 💡 Philosophy @@ -157,8 +163,9 @@ Fiber is **inspired** by Express, the most popular web framework on the Internet We **listen** to our users in [issues](https://github.com/gofiber/fiber/issues), Discord [channel](https://gofiber.io/discord) _and all over the Internet_ to create a **fast**, **flexible** and **friendly** Go web framework for **any** task, **deadline** and developer **skill**! Just like Express does in the JavaScript world. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. -* Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. + +- Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.21. +- 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 @@ -604,7 +611,7 @@ 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. | @@ -625,23 +632,23 @@ Here is a list of middleware that are included within the Fiber framework. | [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Adds a requestid to every request. | | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler if a predicate is true. | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | ## 🧬 External Middleware List of externally hosted middleware modules and maintained by the [Fiber team](https://github.com/orgs/gofiber/people). -| Middleware | Description | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | +| Middleware | Description | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | ## 🕶️ Awesome List @@ -652,7 +659,7 @@ For more articles, middlewares, examples or tools check our [awesome list](https 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=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. Tweet about the project [on your 𝕏 (Twitter)](https://x.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. 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). diff --git a/.github/README_az.md b/.github/README_az.md index f6486333eb..f1b6174e98 100644 --- a/.github/README_az.md +++ b/.github/README_az.md @@ -66,6 +66,12 @@ + + + + + +
@@ -125,7 +131,6 @@ Bu testlər [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r Go dilinin `1.17` və ya daha yuxarı versiyanın [yükləndiyindən](https://go.dev/dl/) əmin olun. - Bir qovluq yaratdıqdan sonra, `go mod init github.com/your/repo` komandasını eyni qovluğun daxilində işə salaraq layihənizi başladın ([go modulları haqqında əlavə bilgilər](https://go.dev/blog/using-go-modules)). Növbəti addım olaraq Fiber-i [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) komandasını işlədərək yükləyin: ```bash @@ -145,7 +150,7 @@ go get -u github.com/gofiber/fiber/v2 - [WebSocket dəstəyi](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- [18 dildə](https://docs.gofiber.io/) mövcudluğu +- [19 dildə](https://docs.gofiber.io/) mövcudluğu Daha ətraflı məlumat üçün [rəsmi sənədləşməyə](https://docs.gofiber.io/) baxış keçirə bilərsiniz. @@ -158,8 +163,9 @@ Fiber internet üzərində olan ən məşhur web framework-lərdən biri olan Ex Biz istifadəçilərdən gələn [issue-a](https://github.com/gofiber/fiber/issues), Discord [kanalımıza](https://gofiber.io/discord) və bütün interneti əhatə edən vasitələrdən gələn rəyləri nəzərə alırıq. Bunun nəzdində, biz sürətli və rahat şəkildə hər bir tapşırığın səviyyəsinə uyğun olan — dostcasına bir Go web framework-ü olmağı hədəfləmişik (Express-in JavaScript dünyasında etdiyi kimi). ## ⚠️ Limitlər -* Fiber unsafe prinsiplərə əsaslanaraq çalışdığından, o hər zaman Go-nun son versiyası ilə uyğunlaşmaya bilər. Buna görə də, Fiber 2.40.0 — Go 1.17 və 1.20 versiyaları ilə test edilərək saz vəziyyətə gətirilmişdir. -* Fiber net/http interfeysləri ilə uyğun deyil. Yəni gqlgen, go-swagger kimi net/http ekosisteminin parçası olan layihələri istifadə edə bilməzsiniz. + +- Fiber unsafe prinsiplərə əsaslanaraq çalışdığından, o hər zaman Go-nun son versiyası ilə uyğunlaşmaya bilər. Buna görə də, Fiber 2.40.0 — Go 1.17 və 1.21 versiyaları ilə test edilərək saz vəziyyətə gətirilmişdir. +- Fiber net/http interfeysləri ilə uyğun deyil. Yəni gqlgen, go-swagger kimi net/http ekosisteminin parçası olan layihələri istifadə edə bilməzsiniz. ## 👀 Misallar @@ -604,45 +610,45 @@ func main() { Aşağıda Fiber-in daxilində olan middleware-lər siyahı şəklində göstərilmişdir. -| Middleware | Açıqlama | -|:---------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Sadə bir auth middleware-dir və HTTP Basic Auth yaratmaq üçün istifadə olunur. Keçərli vəsiqə (credentials) bilgiləri üçün sonrakı handler-i, əksik və ya keçərsiz vəsiqə bilgiləri üçün 401 qaytarır. | -| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Response-ı dayandırır və keşə yerləşdirir. | -| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Fiber üçün sıxışdırma (compression) middleware-dir. Default olaraq `deflate`, `gzip` və `brotli` dəstəkləyir. | -| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Çeşidli seçimlərlə başlanğıclar arası mənbə paylaşımı (CORS) aktivləşdirir. | -| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | CSRF exploit-dən qorunmasını təmin edir. | -| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware-i cookie dəyərlərini şifrələyir. | -| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Environment dəyərlərini göstərilən config-ə görə təyin edir. | -| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Keşlərin daha səmərəli istifadəsinə və bant genişliyinə qənaət etməyə imkan verən ETag middleware-i; məzmun dəyişməyibsə veb serverin response-nı təkrar göndərməsinin qarşısını alır. | -| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware, HTTP serverlərinin bəzi runtime dəyərlərini JSON formatında göstərir. | -| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Əgər faylın yolu (path) göstərilmişdirsə, artıq loglarda olan favicon-u yox sayıb onu saxlanan depodan götürür. | -| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Fiber üçün fayl sistem middleware-i. Alireza Salary-ə xüsusi təşəkkürlər. | -| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Fiber üçün rate limitləyən middleware. Açıq API-ə və ya şifrə yeniləmə kimi endpoint-ə yönəlik təkrarlanan request-in qarşısını alır. | -| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP istək/cavab (request/response) logger-i. | -| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Monitor middleware-i serverin metriklərini report edər ("Express-status-monitor"-dan qaynaqlanıb). | -| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Matthew Lee-yə xüsusi təşəkkürlər \(@mthli\). | -| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Birdən çox server-ə proxy istəyi göndərməyiniz üçündür. | -| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Recover middleware-i stack chain-ni hər hansı bir yerindəki paniklərdən qurtulmasına kömək edir və kontrolu mərkəzləşdirilmiş [ErrorHandler-ə](https://docs.gofiber.io/guide/error-handling) ötürür.| -| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Hər request üçün ayrı request id yaradır. | -| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session üçün middleware. Qeyd: Bu middleware Fiber-in öz storage struktrunu istifadə edir. | -| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware-i verilən şərt true olduğu halda handler-i görməyərək üstündən ötüb keçir. | -| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Bir request üçün maksimum vaxt əlavə edir. Əgər arada fasilə yaranarsa, onda proses məhz ErrorHandler-ə göndərilərək icra edilir. | -| [keyauth](https://github.com/gofiber/keyauth) | Key giriş middleware-i, key əsaslı bir authentication metodudur. | -| [redirect](https://github.com/gofiber/redirect) | Yönləndirmə üçün middleware. | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware-i verilən qanunlara əsasən URL yolunu (path) yenidən yazır. Geri dönüşün icrası üçün uyğunluq təşkil edən təsviri linklərin yaradılması üçün nəzərdə tutulmuşdur. | -| [adaptor](https://github.com/gofiber/adaptor) | Fiber request handler-dən net/http handler-ə çevirici. @arsmn-ə xüsusi təşəkkürlər! | -| [helmet](https://github.com/gofiber/helmet) | Fərqli HTTP header istifadə edərək tətbiqi daha təhlükəsiz saxlamağa kömək edir. | +| Middleware | Açıqlama | +| :------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Sadə bir auth middleware-dir və HTTP Basic Auth yaratmaq üçün istifadə olunur. Keçərli vəsiqə (credentials) bilgiləri üçün sonrakı handler-i, əksik və ya keçərsiz vəsiqə bilgiləri üçün 401 qaytarır. | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Response-ı dayandırır və keşə yerləşdirir. | +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Fiber üçün sıxışdırma (compression) middleware-dir. Default olaraq `deflate`, `gzip` və `brotli` dəstəkləyir. | +| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Çeşidli seçimlərlə başlanğıclar arası mənbə paylaşımı (CORS) aktivləşdirir. | +| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | CSRF exploit-dən qorunmasını təmin edir. | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Encrypt middleware-i cookie dəyərlərini şifrələyir. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Environment dəyərlərini göstərilən config-ə görə təyin edir. | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Keşlərin daha səmərəli istifadəsinə və bant genişliyinə qənaət etməyə imkan verən ETag middleware-i; məzmun dəyişməyibsə veb serverin response-nı təkrar göndərməsinin qarşısını alır. | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Expvar middleware, HTTP serverlərinin bəzi runtime dəyərlərini JSON formatında göstərir. | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Əgər faylın yolu (path) göstərilmişdirsə, artıq loglarda olan favicon-u yox sayıb onu saxlanan depodan götürür. | +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Fiber üçün fayl sistem middleware-i. Alireza Salary-ə xüsusi təşəkkürlər. | +| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Fiber üçün rate limitləyən middleware. Açıq API-ə və ya şifrə yeniləmə kimi endpoint-ə yönəlik təkrarlanan request-in qarşısını alır. | +| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP istək/cavab (request/response) logger-i. | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Monitor middleware-i serverin metriklərini report edər ("Express-status-monitor"-dan qaynaqlanıb). | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Matthew Lee-yə xüsusi təşəkkürlər \(@mthli\). | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Birdən çox server-ə proxy istəyi göndərməyiniz üçündür. | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Recover middleware-i stack chain-ni hər hansı bir yerindəki paniklərdən qurtulmasına kömək edir və kontrolu mərkəzləşdirilmiş [ErrorHandler-ə](https://docs.gofiber.io/guide/error-handling) ötürür. | +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Hər request üçün ayrı request id yaradır. | +| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session üçün middleware. Qeyd: Bu middleware Fiber-in öz storage struktrunu istifadə edir. | +| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware-i verilən şərt true olduğu halda handler-i görməyərək üstündən ötüb keçir. | +| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Bir request üçün maksimum vaxt əlavə edir. Əgər arada fasilə yaranarsa, onda proses məhz ErrorHandler-ə göndərilərək icra edilir. | +| [keyauth](https://github.com/gofiber/keyauth) | Key giriş middleware-i, key əsaslı bir authentication metodudur. | +| [redirect](https://github.com/gofiber/redirect) | Yönləndirmə üçün middleware. | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware-i verilən qanunlara əsasən URL yolunu (path) yenidən yazır. Geri dönüşün icrası üçün uyğunluq təşkil edən təsviri linklərin yaradılması üçün nəzərdə tutulmuşdur. | +| [adaptor](https://github.com/gofiber/adaptor) | Fiber request handler-dən net/http handler-ə çevirici. @arsmn-ə xüsusi təşəkkürlər! | +| [helmet](https://github.com/gofiber/helmet) | Fərqli HTTP header istifadə edərək tətbiqi daha təhlükəsiz saxlamağa kömək edir. | ## 🧬 Xarici Middleware [Fiber komandası](https://github.com/orgs/gofiber/people) tərəfindən dəstəklənən və inkişaf etdirilən middleware-in siyahısı. -| Middleware | Description | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT, JSON Web Token(JWT) girişi qaytaran bir middleware-dir. | +| Middleware | Description | +| :------------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT, JSON Web Token(JWT) girişi qaytaran bir middleware-dir. | | [storage](https://github.com/gofiber/storage) | Fiber-in Storage arxitekturasını dəstəkləyən bir sıra storage driver verir. Bu sayədə storage-ə ehtiyac duyan Fiber middleware-də rahatlıqla istifadə oluna bilər. | -| [template](https://github.com/gofiber/template) | Bu paket, Fiber `v1.10.x`, Go versiyası 1.13 və ya daha yuxarı olduqda istifadə oluna bilər. 8 template mühərriki var. | -| [websocket](https://github.com/gofiber/websocket) | Yerlilərin dəstəyi ilə WebSocket-ə əsaslanan Fiber üçün Fasthttp. | +| [template](https://github.com/gofiber/template) | Bu paket, Fiber `v1.10.x`, Go versiyası 1.13 və ya daha yuxarı olduqda istifadə oluna bilər. 8 template mühərriki var. | +| [websocket](https://github.com/gofiber/websocket) | Yerlilərin dəstəyi ilə WebSocket-ə əsaslanan Fiber üçün Fasthttp. | ## 🕶️ Möhtəşəm Siyahı @@ -653,7 +659,7 @@ Aşağıda Fiber-in daxilində olan middleware-lər siyahı şəklində göstər Əgər `Fiber`-ə dəstək olmaq və ya **təşəkkür etmək** istəyirsinizsə: 1. Layihəni [GitHub Ulduzu](https://github.com/gofiber/fiber/stargazers) ilə işarələyin. -2. Layihə haqqında [şəxsi twitter hesabınızda](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber) paylaşın. +2. Layihə haqqında [şəxsi 𝕏 (Twitter) hesabınızda](https://x.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber) paylaşın. 3. [Medium](https://medium.com/), [Dev.to](https://dev.to/) və ya şəxsi bloqunuz üzərindən bir incələmə və ya tədris yönümlü bir yazı dərc edin. 4. Bizim üçün, sadəcə bir [fincan kofe alın](https://buymeacoff.ee/fenny). @@ -661,26 +667,26 @@ Aşağıda Fiber-in daxilində olan middleware-lər siyahı şəklində göstər Fiber açıq qaynaqlı bir layihə olduğu üçün, gəlirlərini yalnız ianələr vasitəsilə təmin edir və bu da domain adı, gitbook, netlify, serverless hosting xərcləri üçün istifadə olunur. Belə olduğu halda, Fiber-ə ən yaxşı dəstək elə bizim üçün ☕ [**bir kofe almaqdan gələ bilər**](https://buymeacoff.ee/fenny). -| | İstifadəçi | İanə | -| :--------------------------------------------------------- | :----------------------------------------------- | :------- | -| ![](https://avatars.githubusercontent.com/u/204341?s=25) | [@destari](https://github.com/destari) | ☕ x 10 | -| ![](https://avatars.githubusercontent.com/u/63164982?s=25) | [@dembygenesis](https://github.com/dembygenesis) | ☕ x 5 | -| ![](https://avatars.githubusercontent.com/u/56607882?s=25) | [@thomasvvugt](https://github.com/thomasvvugt) | ☕ x 5 | -| ![](https://avatars.githubusercontent.com/u/27820675?s=25) | [@hendratommy](https://github.com/hendratommy) | ☕ x 5 | -| ![](https://avatars.githubusercontent.com/u/1094221?s=25) | [@ekaputra07](https://github.com/ekaputra07) | ☕ x 5 | -| ![](https://avatars.githubusercontent.com/u/194590?s=25) | [@jorgefuertes](https://github.com/jorgefuertes) | ☕ x 5 | -| ![](https://avatars.githubusercontent.com/u/186637?s=25) | [@candidosales](https://github.com/candidosales) | ☕ x 5 | -| ![](https://avatars.githubusercontent.com/u/29659953?s=25) | [@l0nax](https://github.com/l0nax) | ☕ x 3 | -| ![](https://avatars.githubusercontent.com/u/635852?s=25) | [@bihe](https://github.com/bihe) | ☕ x 3 | -| ![](https://avatars.githubusercontent.com/u/307334?s=25) | [@justdave](https://github.com/justdave) | ☕ x 3 | -| ![](https://avatars.githubusercontent.com/u/11155743?s=25) | [@koddr](https://github.com/koddr) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/29042462?s=25) | [@lapolinar](https://github.com/lapolinar) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/2978730?s=25) | [@diegowifi](https://github.com/diegowifi) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/44171355?s=25) | [@ssimk0](https://github.com/ssimk0) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/5638101?s=25) | [@raymayemir](https://github.com/raymayemir) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/619996?s=25) | [@melkorm](https://github.com/melkorm) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/31022056?s=25) | [@marvinjwendt](https://github.com/marvinjwendt) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/31921460?s=25) | [@toishy](https://github.com/toishy) | ☕ x 1 | +| | İstifadəçi | İanə | +| :--------------------------------------------------------- | :----------------------------------------------- | :------ | +| ![](https://avatars.githubusercontent.com/u/204341?s=25) | [@destari](https://github.com/destari) | ☕ x 10 | +| ![](https://avatars.githubusercontent.com/u/63164982?s=25) | [@dembygenesis](https://github.com/dembygenesis) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/56607882?s=25) | [@thomasvvugt](https://github.com/thomasvvugt) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/27820675?s=25) | [@hendratommy](https://github.com/hendratommy) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/1094221?s=25) | [@ekaputra07](https://github.com/ekaputra07) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/194590?s=25) | [@jorgefuertes](https://github.com/jorgefuertes) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/186637?s=25) | [@candidosales](https://github.com/candidosales) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/29659953?s=25) | [@l0nax](https://github.com/l0nax) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/635852?s=25) | [@bihe](https://github.com/bihe) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/307334?s=25) | [@justdave](https://github.com/justdave) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/11155743?s=25) | [@koddr](https://github.com/koddr) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/29042462?s=25) | [@lapolinar](https://github.com/lapolinar) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/2978730?s=25) | [@diegowifi](https://github.com/diegowifi) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/44171355?s=25) | [@ssimk0](https://github.com/ssimk0) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/5638101?s=25) | [@raymayemir](https://github.com/raymayemir) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/619996?s=25) | [@melkorm](https://github.com/melkorm) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/31022056?s=25) | [@marvinjwendt](https://github.com/marvinjwendt) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/31921460?s=25) | [@toishy](https://github.com/toishy) | ☕ x 1 | ## ‎‍💻 Koda Töhfə Verənlər diff --git a/.github/README_ckb.md b/.github/README_ckb.md index 5358572752..cb2de3db55 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -66,6 +66,10 @@ + + + +
@@ -143,7 +147,7 @@ go get -u github.com/gofiber/fiber/v3 - پشتگیریی [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) -- وەرگێڕراوە بۆ [18 زمان](https://docs.gofiber.io/) +- وەرگێڕراوە بۆ [19 زمان](https://docs.gofiber.io/) - زیاتریش، [فایبەر بپشکنە](https://docs.gofiber.io/) ## 💡 فەلسەفە @@ -158,8 +162,8 @@ go get -u github.com/gofiber/fiber/v3 ## ⚠️ سنوورەکان -* تایبەتمەندیی `unsafe` بەکار دەهێنێت کە وای لێ دەکات لەگەڵ هەندێک وەشانی گۆ نەگونجێت. -* لەگەڵ `net/http` ناگونجێت. +- تایبەتمەندیی `unsafe` بەکار دەهێنێت کە وای لێ دەکات لەگەڵ هەندێک وەشانی گۆ نەگونجێت. +- لەگەڵ `net/http` ناگونجێت. ## 👀 نموونەکان @@ -604,7 +608,7 @@ func main() { ئەمە لیستی ئەو کاڵانەیە کە لەناو فایبەر جێگیر کراون. -| کاڵا | دەربارە | +| کاڵا | دەربارە | | :------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [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 | @@ -627,22 +631,22 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 کاڵا دەرەکییەکان ئەمە لیستی ئەو کاڵا دەرەکییانەیە کە لەلایەن [تیمی فایبەر](https://github.com/orgs/gofiber/people) بەڕێوە دەبرێن. -| کاڵا | دەربارە | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | +| کاڵا | دەربارە | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | ## 🕶️ Awesome List @@ -653,7 +657,7 @@ For more articles, middlewares, examples or tools check our [awesome list](https ئەگەر دەتەوێت دەستخۆشی لە فایبەر بکەیت: 1. [ئەستێرەیەک](https://github.com/gofiber/fiber/stargazers)ـی بۆ دا بنێ. -2. لە تویتەر [تویتی لەسەر بکە](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. لە تویتەر [تویتی لەسەر بکە](https://x.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. بە وێبڵۆگێک باسی بکە. 4. [پشتگیری بکە](https://buymeacoff.ee/fenny). diff --git a/.github/README_de.md b/.github/README_de.md index 8cddd01415..7990c585ab 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -66,6 +66,12 @@ + + + + + +
@@ -143,7 +149,7 @@ go get -u github.com/gofiber/fiber/v3 - [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 [18 Sprachen](https://docs.gofiber.io/) +- Verfügbar in [19 Sprachen](https://docs.gofiber.io/) - Und vieles mehr - [erkunde Fiber](https://docs.gofiber.io/) ## 💡 Philosophie @@ -153,8 +159,9 @@ Neue Gopher, welche von [Node.js](https://nodejs.org/en/about/) zu [Go](https:// Fiber ist **inspiriert** von Express.js, dem beliebtesten Web-Framework im Internet. Wir haben die **Leichtigkeit** von Express und die **Rohleistung** von Go kombiniert. Wenn du jemals eine Webanwendung mit Node.js implementiert hast (_mit Express.js oder ähnlichem_), werden dir viele Methoden und Prinzipien **sehr vertraut** vorkommen. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. -* Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. + +- Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.21. +- 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 @@ -597,22 +604,22 @@ Hier finden Sie eine Liste der Middleware, die im Fiber-Framework enthalten ist. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 External Middleware Liste der extern gehosteten Middleware-Module, die vom [Fiber team](https://github.com/orgs/gofiber/people) gepflegt werden. -| Middleware | Description | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. || [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | -| [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | -| [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | +| Middleware | Description | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | --- | --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | | [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | +| [template](https://github.com/gofiber/template) | This package contains 8 template engines that can be used with Fiber `v1.10.x` Go version 1.13 or higher is required. | +| [websocket](https://github.com/gofiber/websocket) | Based on Fasthttp WebSocket for Fiber with Locals support! | ## 🕶️ Awesome List @@ -623,7 +630,7 @@ Weitere Artikel, Middlewares, Beispiele oder Tools finden Sie in unserer [awesom Falls du **danke** sagen möchtest und/oder aktiv die Entwicklung von `fiber` fördern möchtest: 1. Füge dem Projekt einen [GitHub Stern](https://github.com/gofiber/fiber/stargazers) hinzu. -2. Twittere über das Projekt [auf deinem 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. Twittere über das Projekt [auf deinem 𝕏 (Twitter)](https://x.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. Schreibe eine Rezension auf [Medium](https://medium.com/), [Dev.to](https://dev.to/) oder einem persönlichem Blog. 4. Unterstütze das Projekt, indem du ☕ [uns einen Kaffee kaufst](https://buymeacoff.ee/fenny). diff --git a/.github/README_eg.md b/.github/README_eg.md new file mode 100644 index 0000000000..3eea1171ac --- /dev/null +++ b/.github/README_eg.md @@ -0,0 +1,717 @@ +

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

+

+ فايبر هي ويب فريمورك مستوحاه من اكسبريس ومبنيه على فاست اتش تي تي بي وهي اسرع محركات الويب للغه جو. مصممة عشان تسهل و تسرع التطوير ومابتعملش memory allocation زيادة. وبتهتم بالبيرفورمانس. +

+ +## ⚡️ بداية سريعة + +```go +package main + +import "github.com/gofiber/fiber/v2" + +func main() { + app := fiber.New() + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World 👋!") + }) + + app.Listen(":3000") +} +``` + +## 🤖 القياسات + +القياسات دي اتعملت عن طريق [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) و [Go Web](https://github.com/smallnest/go-web-framework-benchmark). لو عاوز تشوف كل النتايج زور [الويكي بتاعتنا](https://docs.gofiber.io/extra/benchmarks). + +

+ + +

+ +## ⚙️ التسطيب + +أتأكد انك مسطب جو ([تحميل](https://go.dev/dl/)). الاصدار `1.17` او اعلى. + +ابدأ البروجكت بتاعك بعمل فولدر وبعدين رن الكوماند ده `go mod init github.com/your/repo` ([اعرف اكتر](https://go.dev/blog/using-go-modules)) بعدين سطب فايبر بكوماند [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them): + +```bash +go get -u github.com/gofiber/fiber/v2 +``` + +## 🎯 المميزات + +- [راوتنج](https://docs.gofiber.io/guide/routing) متين +- سيرف [فايلات ستاتك](https://docs.gofiber.io/api/app#static) +- [بيرفورمانس](https://docs.gofiber.io/extra/benchmarks) فشيخ +- [استهلاك قليل للميموري](https://docs.gofiber.io/extra/benchmarks) +- [APIs](https://docs.gofiber.io/api/ctx) +- [ميدلويرز](https://docs.gofiber.io/category/-middleware) و بتدعم [Next](https://docs.gofiber.io/api/ctx#next) +- برمجة سيرفر [سريعة](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) +- [تيمبلت اينجنز](https://github.com/gofiber/template) +- [بتدعم الويب سوكتس](https://github.com/gofiber/websocket) +- [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) +- [ريت ليمت](https://docs.gofiber.io/api/middleware/limiter) +- مترجمة [لـ19 لغة](https://docs.gofiber.io/) +- وحاجات اكتر, [تصفح فايبر](https://docs.gofiber.io/) + +## 💡 الفكرة + +الجوفرز الجداد اللي بيسوتشوا من [نود جي اس](https://nodejs.org/en/about/) لـ[جو](https://go.dev/doc/) بيتعاملوا مع مرحلة تعلم قبل ما يبدأوا يبنوا تطبيقاتهم و مايكروسيرفساتهم. فايبر, كـ**ويب فريمورك**, اتعملت بفكرة **البساطة** و بتتبع **طريقة يونكس**, عشان الجوفرز الجداد يقدروا يدخلوا عالم جو بسرعة و بثقة. + +فايبر **مستوحاة** من اكسبريس اللي هي اشهر ويب فريمورك عالانترنت. احنا جمعنا بين **سهولة** اكسبريس و **سرعة** جو. لو انت عملت تطبيق ويب في نود جي اس (_باستخدام اكسبريس او حاجة شبهها_), هتلاقي ان معظم الطرق و المبادئ بتاعت فايبر **مألوفة** جدا. + +احنا **بنسمع** لمستخدمينا في [الايشوز](https://github.com/gofiber/fiber/issues) و [قناة الديسكورد](https://gofiber.io/discord) و _في كل حتة عالنت_ عشان نعمل فريمورك ويب جو **سريع**, **مرن** و **سهل** **لاي تاسك**, **ديدلاين** واي **مستوى** مبرمج! زي اكسبريس في عالم الجافاسكريبت. + +## ⚠️ القيود + +- بسبب استخدام فايبر لـunsafe ممكن انها متتوافقش مع اخر اصدار من جو. فايبر 2.40.0 اتتست بـجو من اصدار 1.17 لـ1.21 +- فايبر مش متوافقة مع واجهات net/http. ده يعني انك مش هتقدر تستخدم مشاريع زي gqlgen, go-swagger, او اي حاجة تانية متعلقة بـnet/http + +## 👀 أمثلة + +دي بعض الامثلة الشائعة. لو عايز تشوف امثلة اكتر, زور [Recipes repository](https://github.com/gofiber/recipes) او زور [API documentation](https://docs.gofiber.io). + +#### 📖 [**الراوتنج البسيط**](https://docs.gofiber.io/#basic-routing) + +```go +func main() { + app := fiber.New() + + // GET /api/register + app.Get("/api/*", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("✋ %s", c.Params("*")) + return c.SendString(msg) // => ✋ register + }) + + // GET /flights/LAX-SFO + app.Get("/flights/:from-:to", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("💸 From: %s, To: %s", c.Params("from"), c.Params("to")) + return c.SendString(msg) // => 💸 From: LAX, To: SFO + }) + + // GET /dictionary.txt + app.Get("/:file.:ext", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("📃 %s.%s", c.Params("file"), c.Params("ext")) + return c.SendString(msg) // => 📃 dictionary.txt + }) + + // GET /john/75 + app.Get("/:name/:age/:gender?", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("👴 %s is %s years old", c.Params("name"), c.Params("age")) + return c.SendString(msg) // => 👴 john is 75 years old + }) + + // GET /john + app.Get("/:name", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("Hello, %s 👋!", c.Params("name")) + return c.SendString(msg) // => Hello john 👋! + }) + + log.Fatal(app.Listen(":3000")) +} + +``` + +#### 📖 [**تسمية الراوتس**](https://docs.gofiber.io/api/app#name) + +```go +func main() { + app := fiber.New() + + // GET /api/register + app.Get("/api/*", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("✋ %s", c.Params("*")) + return c.SendString(msg) // => ✋ register + }).Name("api") + + data, _ := json.MarshalIndent(app.GetRoute("api"), "", " ") + fmt.Print(string(data)) + // Prints: + // { + // "method": "GET", + // "name": "api", + // "path": "/api/*", + // "params": [ + // "*1" + // ] + // } + + + log.Fatal(app.Listen(":3000")) +} + +``` + +#### 📖 [**ازاي تسيرف فايلات ستاتك**](https://docs.gofiber.io/api/app#static) + +```go +func main() { + app := fiber.New() + + app.Static("/", "./public") + // => http://localhost:3000/js/script.js + // => http://localhost:3000/css/style.css + + app.Static("/prefix", "./public") + // => http://localhost:3000/prefix/js/script.js + // => http://localhost:3000/prefix/css/style.css + + app.Static("*", "./public/index.html") + // => http://localhost:3000/any/path/shows/index/html + + log.Fatal(app.Listen(":3000")) +} + +``` + +#### 📖 [**الميدلويرز ونيكست**](https://docs.gofiber.io/api/ctx#next) + +```go +func main() { + app := fiber.New() + + // Match any route + app.Use(func(c *fiber.Ctx) error { + fmt.Println("🥇 First handler") + return c.Next() + }) + + // Match all routes starting with /api + app.Use("/api", func(c *fiber.Ctx) error { + fmt.Println("🥈 Second handler") + return c.Next() + }) + + // GET /api/list + app.Get("/api/list", func(c *fiber.Ctx) error { + fmt.Println("🥉 Last handler") + return c.SendString("Hello, World 👋!") + }) + + log.Fatal(app.Listen(":3000")) +} + +``` + +
+ 📚 اعرض امثلة اكتر + +### محركات الفيوز + +📖 [Config](https://docs.gofiber.io/api/fiber#config) +📖 [Engines](https://github.com/gofiber/template) +📖 [Render](https://docs.gofiber.io/api/ctx#render) + +فايبر بتستخدم [html/template](https://pkg.go.dev/html/template/) لما مايكونش في محرك فيوز متعرف + +لو عاوز تستخدم فيوز جزئية او محرك فيوز تاني زي [amber](https://github.com/eknkc/amber), [handlebars](https://github.com/aymerick/raymond), [mustache](https://github.com/cbroglie/mustache) او [pug](https://github.com/Joker/jade) وغيره.. + +بص على [الباكدج](https://github.com/gofiber/template) بتاعنا اللي بيدعم محركات فيوز متعددة + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/template/pug" +) + +func main() { + // You can setup Views engine before initiation app: + app := fiber.New(fiber.Config{ + Views: pug.New("./views", ".pug"), + }) + + // And now, you can call template `./views/home.pug` like this: + app.Get("/", func(c *fiber.Ctx) error { + return c.Render("home", fiber.Map{ + "title": "Homepage", + "year": 1999, + }) + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### تجميع الراوتس في سلسلة + +📖 [Group](https://docs.gofiber.io/api/app#group) + +```go +func middleware(c *fiber.Ctx) error { + fmt.Println("Don't mind me!") + return c.Next() +} + +func handler(c *fiber.Ctx) error { + return c.SendString(c.Path()) +} + +func main() { + app := fiber.New() + + // Root API route + api := app.Group("/api", middleware) // /api + + // API v1 routes + v1 := api.Group("/v1", middleware) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + // API v2 routes + v2 := api.Group("/v2", middleware) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + // ... +} + +``` + +### ميدل وير لوجر + +📖 [Logger](https://docs.gofiber.io/api/middleware/logger) + +```go +package main + +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/logger" +) + +func main() { + app := fiber.New() + + app.Use(logger.New()) + + // ... + + log.Fatal(app.Listen(":3000")) +} +``` + +### هيدر الكروس اوريجن (CORS) + +📖 [CORS](https://docs.gofiber.io/api/middleware/cors) + +```go +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cors" +) + +func main() { + app := fiber.New() + + app.Use(cors.New()) + + // ... + + log.Fatal(app.Listen(":3000")) +} +``` + +جرب الCORS بانك تبعت اي دومين في هيدر `Origin` وتشوف الرد بتاع السيرفر + +```bash +curl -H "Origin: http://example.com" --verbose http://localhost:3000 +``` + +### ريسبومس 404 معدل + +📖 [HTTP Methods](https://docs.gofiber.io/api/ctx#status) + +```go +func main() { + app := fiber.New() + + app.Static("/", "./public") + + app.Get("/demo", func(c *fiber.Ctx) error { + return c.SendString("This is a demo!") + }) + + app.Post("/register", func(c *fiber.Ctx) error { + return c.SendString("Welcome!") + }) + + // Last middleware to match anything + app.Use(func(c *fiber.Ctx) error { + return c.SendStatus(404) + // => 404 "Not Found" + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### JSON ريبسونس + +📖 [JSON](https://docs.gofiber.io/api/ctx#json) + +```go +type User struct { + Name string `json:"name"` + Age int `json:"age"` +} + +func main() { + app := fiber.New() + + app.Get("/user", func(c *fiber.Ctx) error { + return c.JSON(&User{"John", 20}) + // => {"name":"John", "age":20} + }) + + app.Get("/json", func(c *fiber.Ctx) error { + return c.JSON(fiber.Map{ + "success": true, + "message": "Hi John!", + }) + // => {"success":true, "message":"Hi John!"} + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### اضافة ويبسوكيت + +📖 [Websocket](https://github.com/gofiber/websocket) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/websocket" +) + +func main() { + app := fiber.New() + + app.Get("/ws", websocket.New(func(c *websocket.Conn) { + for { + mt, msg, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + log.Printf("recv: %s", msg) + err = c.WriteMessage(mt, msg) + if err != nil { + log.Println("write:", err) + break + } + } + })) + + log.Fatal(app.Listen(":3000")) + // ws://localhost:3000/ws +} +``` + +### Server-Sent Events + +📖 [More Info](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp" +) + +func main() { + app := fiber.New() + + app.Get("/sse", func(c *fiber.Ctx) error { + c.Set("Content-Type", "text/event-stream") + c.Set("Cache-Control", "no-cache") + c.Set("Connection", "keep-alive") + c.Set("Transfer-Encoding", "chunked") + + c.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) { + fmt.Println("WRITER") + var i int + + for { + i++ + msg := fmt.Sprintf("%d - the time is %v", i, time.Now()) + fmt.Fprintf(w, "data: Message: %s\n\n", msg) + fmt.Println(msg) + + w.Flush() + time.Sleep(5 * time.Second) + } + })) + + return nil + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### ميدلوير ريكوفر + +📖 [Recover](https://docs.gofiber.io/api/middleware/recover) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/recover" +) + +func main() { + app := fiber.New() + + app.Use(recover.New()) + + app.Get("/", func(c *fiber.Ctx) error { + panic("normally this would crash your app") + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +
+ +### استخدام بروكسي موثوق + +📖 [Config](https://docs.gofiber.io/api/fiber#config) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/recover" +) + +func main() { + app := fiber.New(fiber.Config{ + EnableTrustedProxyCheck: true, + TrustedProxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range + ProxyHeader: fiber.HeaderXForwardedFor, + }) + + // ... + + log.Fatal(app.Listen(":3000")) +} +``` + + + +## 🧬 ميدلوير داخلي + +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. | +| [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 if a predicate is true. | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | + +## 🧬 ميدلوير خارجي + +لستة ميدلويرز خارجية بتطور من [تيم فايبر](https://github.com/orgs/gofiber/people). + +| Middleware | Description | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | + +## 🕶️ لستة الجامدين + +لو عاوز تشوف مقالات او ميدل وير او امثلة او ادوات بص على اللستة دي [awesome list](https://github.com/gofiber/awesome-fiber). + +## 👍 شاركنا + +لو عاوز تقول **شكرا** او تدعمنا في تطوير `فايبر`: + +1. اعمل [GitHub Star](https://github.com/gofiber/fiber/stargazers) للبروجكت. +2. تويت عن البروجكت [على تويتر](https://x.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber). +3. اكتب ريفيو او توتوريال على [Medium](https://medium.com/), [Dev.to](https://dev.to/) او البلوج بتاعتك. +4. او ادعم المشروع [بكوباية شاي](https://buymeacoff.ee/fenny). + +## ☕ الداعمين + +فايبر مشروع اوبن سورس وشغال على التبرعات عشان ندفع فواتير الدومين والجيت بوك والنتليفاي والسيرفرات. لو عاوز تدعم فايبر تقدر تشتري كوباية شاي من [هنا](https://buymeacoff.ee/fenny). + +| | User | Donation | +| :--------------------------------------------------------- | :----------------------------------------------- | :------- | +| ![](https://avatars.githubusercontent.com/u/204341?s=25) | [@destari](https://github.com/destari) | ☕ x 10 | +| ![](https://avatars.githubusercontent.com/u/63164982?s=25) | [@dembygenesis](https://github.com/dembygenesis) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/56607882?s=25) | [@thomasvvugt](https://github.com/thomasvvugt) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/27820675?s=25) | [@hendratommy](https://github.com/hendratommy) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/1094221?s=25) | [@ekaputra07](https://github.com/ekaputra07) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/194590?s=25) | [@jorgefuertes](https://github.com/jorgefuertes) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/186637?s=25) | [@candidosales](https://github.com/candidosales) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/29659953?s=25) | [@l0nax](https://github.com/l0nax) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/635852?s=25) | [@bihe](https://github.com/bihe) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/307334?s=25) | [@justdave](https://github.com/justdave) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/11155743?s=25) | [@koddr](https://github.com/koddr) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/29042462?s=25) | [@lapolinar](https://github.com/lapolinar) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/2978730?s=25) | [@diegowifi](https://github.com/diegowifi) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/44171355?s=25) | [@ssimk0](https://github.com/ssimk0) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/5638101?s=25) | [@raymayemir](https://github.com/raymayemir) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/619996?s=25) | [@melkorm](https://github.com/melkorm) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/31022056?s=25) | [@marvinjwendt](https://github.com/marvinjwendt) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/31921460?s=25) | [@toishy](https://github.com/toishy) | ☕ x 1 | + +## ‎‍💻 Code Contributors + +Code Contributors + +## ⭐️ Stargazers + +Stargazers over time + +## ⚠️ License + +Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](https://github.com/gofiber/fiber/graphs/contributors). `Fiber` is free and open-source software licensed under the [MIT License](https://github.com/gofiber/fiber/blob/master/LICENSE). Official logo was created by [Vic Shóstak](https://github.com/koddr) and distributed under [Creative Commons](https://creativecommons.org/licenses/by-sa/4.0/) license (CC BY-SA 4.0 International). + +**Third-party library licenses** + +- [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) +- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) +- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) +- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) +- [msgp](https://github.com/tinylib/msgp/blob/master/LICENSE) +- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) +- [uuid](https://github.com/google/uuid/blob/master/LICENSE) +- [wmi](https://github.com/StackExchange/wmi/blob/master/LICENSE) diff --git a/.github/README_es.md b/.github/README_es.md index 46b0927a9e..9ec901c10f 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -66,6 +66,12 @@ + + + + + +
@@ -143,7 +149,7 @@ go get -u github.com/gofiber/fiber/v3 - [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 [18 idiomas](https://docs.gofiber.io/) +- Disponible en [19 idiomas](https://docs.gofiber.io/) - Y mucho más, [explora Fiber](https://docs.gofiber.io/) ## 💡 Filosofía @@ -153,8 +159,9 @@ Los nuevos gophers que hacen el cambio de [Node.js](https://nodejs.org/en/about/ Fiber está **inspirado** en Expressjs, el framework web más popular en Internet. Combinamos la **facilidad** de Express y **el rendimiento bruto** de Go. Si alguna vez ha implementado una aplicación web en Node.js ( _utilizando Express.js o similar_ ), muchos métodos y principios le parecerán **muy comunes** . ## ⚠️ Limitantes -* Debido a que Fiber utiliza unsafe, la biblioteca no siempre será compatible con la última versión de Go. Fiber 2.40.0 ha sido probado con las versiones de Go 1.17 a 1.20. -* Fiber no es compatible con interfaces net/http. Esto significa que no lo podrá usar en proyectos como qglgen, go-swagger, u otros que son parte del ecosistema net/http. + +- Debido a que Fiber utiliza unsafe, la biblioteca no siempre será compatible con la última versión de Go. Fiber 2.40.0 ha sido probado con las versiones de Go 1.17 a 1.21. +- 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 @@ -597,22 +604,22 @@ Aquí está una lista del middleware incluido en el marco web Fiber. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 Middleware Externo Lista de módulos de middleware alojados externamente, y mantenidos por el [equipo de Fiber](https://github.com/orgs/gofiber/people). -| Middleware | Descripción | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | +| Middleware | Descripción | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | ## 🕶️ Awesome List @@ -623,7 +630,7 @@ For more articles, middlewares, examples or tools check our [awesome list](https Si quiere **agradecer** y/o apoyar el desarrollo activo de `Fiber`: 1. Agrega una [estrella de GitHub](https://github.com/gofiber/fiber/stargazers) al proyecto. -2. Tuitea sobre el proyecto [en tu 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. Tuitea sobre el proyecto [en tu 𝕏 (Twitter)](https://x.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. Escribe una reseña o tutorial en [Medium](https://medium.com/) , [Dev.to](https://dev.to/) o blog personal. 4. Apoya el proyecto donando una [tasa de café](https://buymeacoff.ee/fenny). @@ -678,4 +685,3 @@ Copyright (c) 2019-presente [Fenny](https://github.com/fenny) y [contribuyentes] - [schema](https://github.com/gorilla/schema/blob/master/LICENSE) - [uuid](https://github.com/google/uuid/blob/master/LICENSE) - [wmi](https://github.com/StackExchange/wmi/blob/master/LICENSE) - diff --git a/.github/README_fa.md b/.github/README_fa.md index 7c6eec901f..bae4e44867 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -66,6 +66,12 @@ + + + + + +
@@ -172,7 +178,7 @@ go get -u github.com/gofiber/fiber/v3 - [پشتیبانی از وب سوکت](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - قابلیت [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) -- ترجمه در [18 زبان](https://docs.gofiber.io/) +- ترجمه در [19 زبان](https://docs.gofiber.io/) - و امکانات بیشتر, [دیدن در داکیومنت](https://docs.gofiber.io/)
@@ -193,8 +199,9 @@ Fiber از Express الهام گرفته, که محبوب ترین فری

## ⚠️ محدودیت ها -* به دلیل استفاده ناامن از Fiber, ممکن است کتابخانه همیشه با آخرین نسخه Go سازگار نباشد. Fiber 2.40.0 با زبان گو نسخه 1.17 تا 1.20 تست شده است. -* فریمورک Fiber با پکیج net/http سازگار نیست. این بدان معناست شما نمی توانید از پکیج های مانند go-swagger, gqlgen یا سایر پروژه هایی که بخشی از اکوسیستم net/http هستند استفاده کنید. + +- به دلیل استفاده ناامن از Fiber, ممکن است کتابخانه همیشه با آخرین نسخه Go سازگار نباشد. Fiber 2.40.0 با زبان گو نسخه 1.17 تا 1.21 تست شده است. +- فریمورک Fiber با پکیج net/http سازگار نیست. این بدان معناست شما نمی توانید از پکیج های مانند go-swagger, gqlgen یا سایر پروژه هایی که بخشی از اکوسیستم net/http هستند استفاده کنید.
@@ -286,7 +293,6 @@ func main() { - #### 📖 [**Serving Static Files**](https://docs.gofiber.io/api/app#static)
@@ -696,6 +702,7 @@ func main() { log.Fatal(app.Listen(":3000")) } ``` +
@@ -711,34 +718,34 @@ func main() {
-| Middleware | توضیحات | -| :------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [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) |مقادیر کوکی هارا رمزنگاری میکند.| -| [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 در صورتی که مسیر یک فایل را داده باشید.| -| [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 صدا زده میشود.| -| [keyauth](https://github.com/gofiber/keyauth) | این میدلور احراز هویت مبتنی بر کلید را فراهم می کند. | -| [redirect](https://github.com/gofiber/redirect) | برای ریدایرکت کردن از این میدلور میتوانید استفاده کنید. | -| [rewrite](https://github.com/gofiber/rewrite) | مسیر URL را براساس قوانین مشخص شده بازنویسی می کند. این میتواند برای سازگاری با ورژن های قبلی یا برای ساخت لینک های تمیز تر و توصیفی تر مفید باشد. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | با استفاده از HTTP هدر های مختلف به ایمن سازی برنامه شما کمک می کند. | +| Middleware | توضیحات | +| :------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- | --- | +| [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) | مقادیر کوکی هارا رمزنگاری میکند. | +| [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 در صورتی که مسیر یک فایل را داده باشید. | +| [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 صدا زده میشود. | +| [keyauth](https://github.com/gofiber/keyauth) | این میدلور احراز هویت مبتنی بر کلید را فراهم می کند. | +| [redirect](https://github.com/gofiber/redirect) | برای ریدایرکت کردن از این میدلور میتوانید استفاده کنید. | +| [rewrite](https://github.com/gofiber/rewrite) | مسیر URL را براساس قوانین مشخص شده بازنویسی می کند. این میتواند برای سازگاری با ورژن های قبلی یا برای ساخت لینک های تمیز تر و توصیفی تر مفید باشد. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | با استفاده از HTTP هدر های مختلف به ایمن سازی برنامه شما کمک می کند. |


@@ -753,16 +760,16 @@ func main() {
-| Middleware | توضیحات | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | +| Middleware | توضیحات | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | ## 🕶️ Awesome List - [awesome list](https://github.com/gofiber/awesome-fiber) برای مقاله، میدلور، مثال ها و ابزار های بیشتر لطفا از این لینک بازدید کنید +[awesome list](https://github.com/gofiber/awesome-fiber) برای مقاله، میدلور، مثال ها و ابزار های بیشتر لطفا از این لینک بازدید کنید
@@ -773,7 +780,7 @@ func main() { اگر شما میخواهید **تشکر** کنید و یا از توسعه فعال Fiber حمایت کنید : 1. یک [GitHub Star](https://github.com/gofiber/fiber/stargazers) به پروژه اضافه کنید. -2. ارسال توییت درباره Fiber برروی [صفحه توییتر شما](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. ارسال توییت درباره Fiber برروی [صفحه توییتر شما](https://x.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber). 3. یک آموزش یا نظر برروی [Medium](https://medium.com/), [Dev.to](https://dev.to/) یا وبلاگ شخصیتان. 4. پشتیبانی پروژه با حمایت مالی از طریق [یک فنجان قهوه](https://buymeacoff.ee/fenny). diff --git a/.github/README_fr.md b/.github/README_fr.md index 6229d13b1e..307fe1125f 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -66,6 +66,12 @@
+ + + + + +
@@ -143,7 +149,7 @@ go get -u github.com/gofiber/fiber/v3 - [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 [18 languages](https://docs.gofiber.io/) +- Available in [19 languages](https://docs.gofiber.io/) - Et plus encore, [explorez Fiber](https://docs.gofiber.io/) ## 💡 Philosophie @@ -153,8 +159,9 @@ Les nouveaux gophers qui passent de [Node.js](https://nodejs.org/en/about/) à [ Fiber est **inspiré** par Express, le framework web le plus populaire d'Internet. Nous avons combiné la **facilité** d'Express, et la **performance brute** de Go. Si vous avez déja développé une application web en Node.js (_en utilisant Express ou équivalent_), alors de nombreuses méthodes et principes vous sembleront **familiers**. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. -* Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. + +- Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.21. +- 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 @@ -599,22 +606,22 @@ Here is a list of middleware that are included within the Fiber framework. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 External Middleware List of externally hosted middleware modules and maintained by the [Fiber team](https://github.com/orgs/gofiber/people). -| Middleware | Description | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | +| Middleware | Description | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | ## 🕶️ Awesome List @@ -625,7 +632,7 @@ For more articles, middlewares, examples or tools check our [awesome list](https Si vous voulez nous remercier et/ou soutenir le développement actif de `Fiber`: 1. Ajoutez une [GitHub Star](https://github.com/gofiber/fiber/stargazers) à ce projet. -2. Twittez à propos de ce projet [sur votre 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. Twittez à propos de ce projet [sur votre 𝕏 (Twitter)](https://x.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. Ecrivez un article (review, tutorial) sur [Medium](https://medium.com/), [Dev.to](https://dev.to/), ou encore un blog personnel. 4. Support the project by donating a [cup of coffee](https://buymeacoff.ee/fenny). diff --git a/.github/README_he.md b/.github/README_he.md index 06f9dbe3cc..b0957902f1 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -66,6 +66,12 @@ + + + + + +
@@ -198,8 +204,9 @@ Fiber נוצרה **בהשראת** Express, ה-web framework הפופולרית
## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. -* Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. + +- Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.21. +- 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. ## 👀 דוגמאות @@ -715,11 +722,11 @@ Here is a list of middleware that are included within the Fiber framework. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. |
@@ -737,12 +744,12 @@ Here is a list of middleware that are included within the Fiber framework.
-| Middleware | Description | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | +| Middleware | Description | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! |
@@ -773,7 +780,7 @@ For more articles, middlewares, examples or tools check our [awesome list](https
1. תוסיפו [GitHub Star](https://github.com/gofiber/fiber/stargazers) לפרויקט. -2. צייצו לגבי הפרויקט [בטוויטר שלכם](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. צייצו לגבי הפרויקט [בטוויטר שלכם](https://x.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber). 3. כתבו ביקורת או מדריך ב-[Medium](https://medium.com/), [Dev.to](https://dev.to/) או בבלוג האישי שלכם. 4. תמכו בפרויקט על ידי תרומת [כוס קפה](https://buymeacoff.ee/fenny).
diff --git a/.github/README_id.md b/.github/README_id.md index 4f31f2a7b5..13749580af 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -66,6 +66,12 @@
+ + + + + +
@@ -143,7 +149,7 @@ go get -u github.com/gofiber/fiber/v3 - [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 [18 bahasa](https://docs.gofiber.io/) +- Tersedia dalam [19 bahasa](https://docs.gofiber.io/) - Dan masih banyak lagi, [kunjungi Fiber](https://docs.gofiber.io/) ## 💡 Filosofi @@ -156,8 +162,8 @@ Kami **mendengarkan** para pengguna di [GitHub Issues](https://github.com/gofibe ## ⚠️ Limitasi -* Karena penggunaan Fiber yang tidak aman, perpustakaan mungkin tidak selalu kompatibel dengan versi Go terbaru. Fiber 2.40.0 telah diuji dengan Go versi 1.17 hingga 1.20. -* Fiber tidak kompatibel dengan antarmuka net/http. Ini berarti kamu tidak akan dapat menggunakan proyek seperti gqlgen, go-swagger, atau lainnya yang merupakan bagian dari ekosistem net/http. +- Karena penggunaan Fiber yang tidak aman, perpustakaan mungkin tidak selalu kompatibel dengan versi Go terbaru. Fiber 2.40.0 telah diuji dengan Go versi 1.17 hingga 1.21. +- 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 @@ -600,22 +606,22 @@ Kumpulan `middleware` yang ada didalam kerangka kerja Fiber. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 Middleware External Kumpulan `middleware` yang dihost external dan diurus oleh [Tim Fiber](https://github.com/orgs/gofiber/people). -| Middleware | Description | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | +| Middleware | Description | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | ## 🕶️ Awesome List @@ -626,7 +632,7 @@ Untuk artikel lainnya, middlewares, contoh atau tools check kami [awesome list]( Apabila anda ingin mengucapkan **terima kasih** dan/atau mendukung pengembangan `Fiber`: 1. Berikan bintang atau [GitHub Star](https://github.com/gofiber/fiber/stargazers) ke proyek ini. -2. Bagikan [di Twitter anda](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. Bagikan [di 𝕏 (Twitter) anda](https://x.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. Buat ulasan atau tutorial di [Medium](https://medium.com/), [Dev.to](https://dev.to/) atau blog pribadi anda. 4. Dukung proyek ini dengan membelikan [secangkir kopi](https://buymeacoff.ee/fenny). @@ -681,4 +687,3 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [schema](https://github.com/gorilla/schema/blob/master/LICENSE) - [uuid](https://github.com/google/uuid/blob/master/LICENSE) - [wmi](https://github.com/StackExchange/wmi/blob/master/LICENSE) - diff --git a/.github/README_it.md b/.github/README_it.md index b5ecf26179..20e20816b1 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -66,6 +66,12 @@ + + + + + +
@@ -143,7 +149,7 @@ go get -u github.com/gofiber/fiber/v3 - [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 [18 lingue](https://docs.gofiber.io/) +- Disponible in [19 lingue](https://docs.gofiber.io/) - E molto altro ancora, [esplora Fiber](https://docs.gofiber.io/) ## 💡 Filosofia @@ -154,8 +160,8 @@ Fiber è **ispirato** da Express, il web framework più popolare su internet. Ab ## ⚠️ Limitazioni -* Dato che Fiber utilizza unsafe, la libreria non sempre potrebbe essere compatibile con l'ultima versione di Go. Fiber 2.40.0 è stato testato con la versioni 1.17 alla 1.20 di Go. -* Fiber non è compatibile con le interfacce net/http. Questo significa che non è possibile utilizzare progetti come qglgen, go-swagger, o altri che fanno parte dell'ecosistema net/http. +- Dato che Fiber utilizza unsafe, la libreria non sempre potrebbe essere compatibile con l'ultima versione di Go. Fiber 2.40.0 è stato testato con la versioni 1.17 alla 1.21 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 @@ -600,45 +606,45 @@ 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 giuste e il codice 401 Unauthorized per credenziali mancanti o invalide. | -| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Intercetta e mette nella cache la risposta | -| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Middleware di compressione per Fiber, supporta `deflate`, `gzip` e `brotli` di default. | -| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Ti permette di usare cross-origin resource sharing \(CORS\) con tante opzioni. | -| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Ti protegge da attachi CSRF. | -| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Middleware che encrypta i valori dei cookie. | -| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Esporre le variabili di ambiente fornendo una configurazione facoltativa. | -| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Middleware che permette alle cache di essere più efficienti e salvare banda, come un web server che non deve rimandare il messagio pieno se il contenuto non è cambiato. | -| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Middleware che serve via il suo runtime server HTTP varianti esposte in formato JSON. | -| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignora favicon dai logs o serve dalla memoria se un filepath è specificato. | -| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Middleware per il FileSystem per Fiber, grazie tante e crediti a Alireza Salary | -| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Middleware per Rate-limiting per Fiber. Usato per limitare richieste continue agli APIs publici e/o endpoints come un password reset. | -| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | Logger HTTP per richiesta/risposta. | -| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Middleware per monitorare che riporta metriche server, ispirato da express-status-monitor | -| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Grazie tante a Matthew Lee \(@mthli\) | -| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Ti permette di fare richieste proxy a multipli server. | -| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Middleware per recuperare dagli attachi di panico da tutte le parti nella stack chain e affida il controllo al [ ErrorHandler](https://docs.gofiber.io/guide/error-handling) centralizzato. | -| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Aggiunge un requestid a ogni richiesta. | -| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Middleware per sessioni. NOTA: Questo middleware usa il nostro Storage package. | -| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Middleware che salta un wrapped handler se un predicate è vero. | -| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Aggiunge un tempo massimo per una richiesta e lo manda a ErrorHandler se si supera. | -| [keyauth](https://github.com/gofiber/keyauth) | Usa auth basato su chiavi. | -| [redirect](https://github.com/gofiber/redirect) | Middleware per reinderizzare | -| [rewrite](https://github.com/gofiber/rewrite) | Riscrive la path all URL con le regole date. Può essere di aiuto per compatibilità o per creare link puliti e più descrittivi. | -| [adaptor](https://github.com/gofiber/adaptor) | Converte gli handler net/http a/da i request handlers di Fiber, grazie tante a @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Aiuta a mettere sicurezza alla tua app usando vari header HTTP. | +| Middleware | 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 giuste e il codice 401 Unauthorized per credenziali mancanti o invalide. | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Intercetta e mette nella cache la risposta | +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Middleware di compressione per Fiber, supporta `deflate`, `gzip` e `brotli` di default. | +| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Ti permette di usare cross-origin resource sharing \(CORS\) con tante opzioni. | +| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Ti protegge da attachi CSRF. | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Middleware che encrypta i valori dei cookie. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Esporre le variabili di ambiente fornendo una configurazione facoltativa. | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Middleware che permette alle cache di essere più efficienti e salvare banda, come un web server che non deve rimandare il messagio pieno se il contenuto non è cambiato. | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Middleware che serve via il suo runtime server HTTP varianti esposte in formato JSON. | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignora favicon dai logs o serve dalla memoria se un filepath è specificato. | +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Middleware per il FileSystem per Fiber, grazie tante e crediti a Alireza Salary | +| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Middleware per Rate-limiting per Fiber. Usato per limitare richieste continue agli APIs publici e/o endpoints come un password reset. | +| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | Logger HTTP per richiesta/risposta. | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Middleware per monitorare che riporta metriche server, ispirato da express-status-monitor | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Grazie tante a Matthew Lee \(@mthli\) | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Ti permette di fare richieste proxy a multipli server. | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Middleware per recuperare dagli attachi di panico da tutte le parti nella stack chain e affida il controllo al [ ErrorHandler](https://docs.gofiber.io/guide/error-handling) centralizzato. | +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Aggiunge un requestid a ogni richiesta. | +| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Middleware per sessioni. NOTA: Questo middleware usa il nostro Storage package. | +| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Middleware che salta un wrapped handler se un predicate è vero. | +| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Aggiunge un tempo massimo per una richiesta e lo manda a ErrorHandler se si supera. | +| [keyauth](https://github.com/gofiber/keyauth) | Usa auth basato su chiavi. | +| [redirect](https://github.com/gofiber/redirect) | Middleware per reinderizzare | +| [rewrite](https://github.com/gofiber/rewrite) | Riscrive la path all URL con le regole date. Può essere di aiuto per compatibilità o per creare link puliti e più descrittivi. | +| [adaptor](https://github.com/gofiber/adaptor) | Converte gli handler net/http a/da i request handlers di Fiber, grazie tante a @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Aiuta a mettere sicurezza alla tua app usando vari header HTTP. | ## 🧬 Middleware Esterni La lista dei moduli middleware hostati esternamente e mantenuti dal [team di Fiber](https://github.com/orgs/gofiber/people). -| Middleware | Descrizione | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | Usa JSON Web Token \(JWT\) auth. | -| [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! | +| Middleware | Descrizione | +| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | Usa JSON Web Token \(JWT\) auth. | +| [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 @@ -649,7 +655,7 @@ Per piu articoli, middlewares, esempi o attrezzi puoi usare la [awesome list](ht 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. 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). +2. Twitta del progetto [su 𝕏 (Twitter)](https://x.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). diff --git a/.github/README_ja.md b/.github/README_ja.md index e6c350b5b2..bd9e98fe35 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -66,6 +66,12 @@ + + + + + +
@@ -144,7 +150,7 @@ go get -u github.com/gofiber/fiber/v3 - [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) -- [18 ヶ国語](https://docs.gofiber.io/)に翻訳 +- [19 ヶ国語](https://docs.gofiber.io/)に翻訳 - [Fiber](https://docs.gofiber.io/)をもっと知る ## 💡 哲学 @@ -158,7 +164,7 @@ Fiber は人気の高い Web フレームワークである Expressjs に**イ ## ⚠️ 制限事項 -- Fiber は unsafe パッケージを使用しているため、最新の Go バージョンと互換性がない場合があります。Fiber 2.40.0 は、Go のバージョン 1.17 から 1.20 でテストされています。 +- Fiber は unsafe パッケージを使用しているため、最新の Go バージョンと互換性がない場合があります。Fiber 2.40.0 は、Go のバージョン 1.17 から 1.21 でテストされています。 - Fiber は net/http インターフェースと互換性がありません。つまり、gqlgen や go-swagger など、net/http のエコシステムの一部であるプロジェクトを使用することができません。 ## 👀 例 @@ -602,22 +608,22 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 外部ミドルウェア [Fiber team](https://github.com/orgs/gofiber/people) により管理・運用されているミドルウェアの一覧です。 -| Middleware | Description | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | +| Middleware | Description | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | ## 🕶️ Awesome List @@ -628,7 +634,7 @@ func main() { `Fiber`に開発支援してくださるなら: 1. [GitHub Star](https://github.com/gofiber/fiber/stargazers)をつけてください 。 -2. [あなたの Twitter で](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber)プロジェクトについてツイートしてください。 +2. [あなたの 𝕏 (Twitter) で](https://x.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber)プロジェクトについてツイートしてください。 3. [Medium](https://medium.com/) 、 [Dev.to](https://dev.to/)、または個人のブログでレビューやチュートリアルを書いてください。 4. [cup of coffee](https://buymeacoff.ee/fenny)の寄付でプロジェクトを支援しましょう。 diff --git a/.github/README_ko.md b/.github/README_ko.md index c6b2eed2c1..c6d820390d 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -66,6 +66,12 @@ + + + + + +
@@ -143,7 +149,7 @@ go get -u github.com/gofiber/fiber/v3 - [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 [18 languages](https://docs.gofiber.io/) +- Available in [19 languages](https://docs.gofiber.io/) - 더 알고 싶다면, [Fiber 둘러보기](https://docs.gofiber.io/) ## 💡 철학 @@ -155,8 +161,9 @@ Fiber는 인터넷에서 가장 인기있는 웹 프레임워크인 Express에 우리는 **어떤한** 작업, **마감일정**, 개발자의 **기술**이던간에 **빠르고**, **유연하고**, **익숙한** Go 웹 프레임워크를 만들기 위해 사용자들의 [이슈들](https://github.com/gofiber/fiber/issues)을(그리고 모든 인터넷을 통해) **듣고 있습니다**! Express가 자바스크립트 세계에서 하는 것 처럼요. ## ⚠️ 한계점 -* Fiber는 unsafe 패키지를 사용하기 때문에 최신 Go버전과 호환되지 않을 수 있습니다.Fiber 2.40.0은 Go 버전 1.17에서 1.20로 테스트되고 있습니다. -* Fiber는 net/http 인터페이스와 호환되지 않습니다.즉, gqlgen이나 go-swagger 등 net/http 생태계의 일부인 프로젝트를 사용할 수 없습니다. + +- Fiber는 unsafe 패키지를 사용하기 때문에 최신 Go버전과 호환되지 않을 수 있습니다. Fiber 2.40.0은 Go 버전 1.17부터 1.21까지 테스트되고 있습니다. +- Fiber는 net/http 인터페이스와 호환되지 않습니다.즉, gqlgen이나 go-swagger 등 net/http 생태계의 일부인 프로젝트를 사용할 수 없습니다. ## 👀 예제 @@ -603,22 +610,22 @@ Fiber 프레임워크에 포함되는 미들웨어 목록입니다. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 External Middleware [Fiber team](https://github.com/orgs/gofiber/people)에 의해 관리 및 운용되고 있는 미들웨어 목록입니다. -| Middleware | Description | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | +| Middleware | Description | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | ## 🕶️ Awesome List @@ -629,13 +636,13 @@ For more articles, middlewares, examples or tools check our [awesome list](https `Fiber`의 활발한 개발을 지원하고 감사 인사를 하고 싶다면: 1. 프로젝트에 [GitHub Star](https://github.com/gofiber/fiber/stargazers)를 추가하세요. -2. [트위터에서](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. [트위터에서](https://x.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber) 프로젝트에 대해 트윗하세요. 3. [Medium](https://medium.com/), [Dev.to](https://dev.to/) 또는 개인 블로그에 리뷰 또는 튜토리얼을 작성하세요. 4. Support the project by donating a [cup of coffee](https://buymeacoff.ee/fenny). -## ☕ Supporters +## ☕ 후원자 -Fiber is an open source project that runs on donations to pay the bills e.g. our domain name, gitbook, netlify and serverless hosting. If you want to support Fiber, you can ☕ [**buy a coffee here**](https://buymeacoff.ee/fenny). +Fiber는 오픈소스 프로젝트로, 기부를 통해 도메인 이름, gitbook, netlify, 서버리스 호스팅 등의 비용을 충당하고 있습니다. Fiber를 후원하고 싶다면 ☕ [**여기서 커피를 사주세요**](https://buymeacoff.ee/fenny). | | User | Donation | | :--------------------------------------------------------- | :----------------------------------------------- | :------- | diff --git a/.github/README_nl.md b/.github/README_nl.md index 5d58f24286..4f4421e984 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -66,6 +66,12 @@ + + + + + +
@@ -143,7 +149,7 @@ go get -u github.com/gofiber/fiber/v3 - [WebSocket ondersteuning](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [Rate Limiter](https://docs.gofiber.io/category/-middleware/limiter) -- Vertaald in [18 talen](https://docs.gofiber.io/) +- Vertaald in [19 talen](https://docs.gofiber.io/) - En nog veel meer, [ontdek Fiber](https://docs.gofiber.io/) ## 💡 Filosofie @@ -155,8 +161,9 @@ Fiber is **geïnspireerd** door Express, het populairste webframework op interne We **luisteren** naar onze gebruikers in [issues](https://github.com/gofiber/fiber/issues) (_en overal op het internet_) om een **snelle**, **flexibele** en **vriendelijk** Go web framework te maken voor **elke** taak, **deadline** en ontwikkelaar **vaardigheid**! Net zoals Express dat doet in de JavaScript-wereld. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. -* Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. + +- Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.21. +- 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 @@ -603,22 +610,22 @@ Here is a list of middleware that are included within the Fiber framework. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | ## 🧬 External Middleware List of externally hosted middleware modules and maintained by the [Fiber team](https://github.com/orgs/gofiber/people). -| Middleware | Description | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | +| Middleware | Description | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | ## 🕶️ Awesome List @@ -629,7 +636,7 @@ For more articles, middlewares, examples or tools check our [awesome list](https Om de actieve ontwikkelingen van `Fiber` te ondersteunen of om een **bedankje** te geven: 1. Voeg een [GitHub Star](https://github.com/gofiber/fiber/stargazers) toe aan het project. -2. Tweet over het project [op je Twitter account](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. Tweet over het project [op je 𝕏 (Twitter) account](https://x.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. Schrijf een recensie of tutorial op [Medium](https://medium.com/), [Dev.to](https://dev.to/) of een persoonlijke blog. 4. Support the project by donating a [cup of coffee](https://buymeacoff.ee/fenny). diff --git a/.github/README_pl.md b/.github/README_pl.md new file mode 100644 index 0000000000..5cff10c87e --- /dev/null +++ b/.github/README_pl.md @@ -0,0 +1,715 @@ +

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

+

+ Fiber jest frameworkiem webowym inspirowanym javascriptowym frameworkiem Express. Został zbudowany na podstawie Fasthttp, najszybszym silniku HTTP powstałym w Go. Został zaprojektowany tak, aby ułatwić szybkie programowanie +z myślą o wydajności oraz zerowej alokacji pamięci. +

+ +## ⚡️ Szybki start + +```go +package main + +import "github.com/gofiber/fiber/v2" + +func main() { + app := fiber.New() + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World 👋!") + }) + + app.Listen(":3000") +} +``` + +## 🤖 Testy wydajności +Testy te zostały przeprowadzone przez [TechEmpower](https://www.techempower.com/benchmarks/#section=data-r19&hw=ph&test=plaintext) oraz [Go Web](https://github.com/smallnest/go-web-framework-benchmark). Jeżeli chcesz zobaczyć wszystkie wyniki, proszę, odwiedź naszą [Wiki](https://docs.gofiber.io/extra/benchmarks). + + +

+ + +

+ +## ⚙️ Instalacja + +Upewnij się, że masz zainstalowane Go ([pobierz](https://go.dev/dl/)). Wymagana jest wersja `1.17` lub wyższa. + +Zainicjalizuj swój projekt poprzez stworzenie folderu i użycie komendy `go mod init github.com/your/repo` ([zobacz więcej](https://go.dev/blog/using-go-modules)) w tym folderze. Następnie zainstaluj Fiber'a przy użyciu komendy `go get`: + +```bash +go get -u github.com/gofiber/fiber/v2 +``` + +## 🎯 Funkcjonalności + +- Stabilny [routing](https://docs.gofiber.io/guide/routing) +- Serwowanie [pliki statyczne](https://docs.gofiber.io/api/app#static) +- Ekstremalna [wydajność](https://docs.gofiber.io/extra/benchmarks) +- [Niskie zużycie](https://docs.gofiber.io/extra/benchmarks) pamięci +- [Endpointy API](https://docs.gofiber.io/api/ctx) +- Wsparcie [Middleware](https://docs.gofiber.io/category/-middleware) oraz [Next](https://docs.gofiber.io/api/ctx#next) +- [Szybkie](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) programowanie po stronie servera +- [Silniki szablonów HTML](https://github.com/gofiber/template) +- [Wsparcie WebSocket](https://github.com/gofiber/websocket) +- [Wydarzenia wysyłane przez serwer](https://github.com/gofiber/recipes/tree/master/sse) +- [Rate Limiter](https://docs.gofiber.io/api/middleware/limiter) +- Tłumaczenia w [20 językach](https://docs.gofiber.io/) +- Oraz wiele więcej, [odkryj Fiber'a](https://docs.gofiber.io/) + +## 💡 Nasza filozofia +Nowi gophersi, którzy przenoszą się z [Node.js](https://nodejs.org/en/about/) na [Go](https://go.dev/doc/), mierzą się z problemami nauczania, zanim będą mogli rozpocząć budowanie swoich aplikacji internetowych lub mikroserwisów. Fiber, jako framework, został stworzony z myślą o minimalizmie i podąża za filozofią UNIX, aby nowi programiści w Go mogli szybko wkroczyć do świata Go, ciesząc się serdecznym i godnym zaufania przyjęciem. + +Fiber jest **inspirowany** javascriptowym frameworkiem Express, najpopularniejszym frameworkiem webowym w internecie. Połączyliśmy **łatwość** Express'a z **czystą wydajnością** Go. Jeżeli kiedykolwiek tworzyłeś aplikację webową w Node.js (_korzystając z Express'a lub podobnych_), wtedy wiele metod i zasad będzie dla ciebie **bardzo znajomych**. + + +**Słuchamy** naszych użytkowników w [issues](https://github.com/gofiber/fiber/issues), na kanale [Discord](https://gofiber.io/discord) _i wszędzie w Internecie_, aby stworzyć **szybki**, **elastyczny** i **przyjazny** framework webowy dla Go, który nadaje się do **wszelkich** zadań, **terminów** i **umiejętności** programistów! Tak jak Express w świecie JavaScript. + +## ⚠️ Ograniczenia + +- Z uwagi na użycie unsafe przez Fiber'a, biblioteka nie zawsze będzie kompatybilna z najnowszą wersją Go. Fiber 2.40.0 został przetestowany z Go w wersjach 1.17 i 1.21. +- Fiber nie jest kompatybilny z interfejsami net/http. To oznacza, że nie będziesz w stanie korzystać (bezpośrednio) z projektów takich jak gqlgen, go-swagger lub innych, które są częścią ekosystemu net/http. + + +## 👀 Przykłady + + +Poniżej znajdują się niektóre przykłady. Jeśli chcesz zobaczyć więcej przykładów kodu, odwiedź nasze [repozytorium Recipes](https://github.com/gofiber/recipes) lub odwiedź naszą [dokumentację API](https://docs.gofiber.io). + +#### 📖 [**Podstawowy Routing**](https://docs.gofiber.io/#basic-routing) + +```go +func main() { + app := fiber.New() + + // GET /api/register + app.Get("/api/*", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("✋ %s", c.Params("*")) + return c.SendString(msg) // => ✋ register + }) + + // GET /flights/LAX-SFO + app.Get("/flights/:from-:to", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("💸 From: %s, To: %s", c.Params("from"), c.Params("to")) + return c.SendString(msg) // => 💸 From: LAX, To: SFO + }) + + // GET /dictionary.txt + app.Get("/:file.:ext", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("📃 %s.%s", c.Params("file"), c.Params("ext")) + return c.SendString(msg) // => 📃 dictionary.txt + }) + + // GET /john/75 + app.Get("/:name/:age/:gender?", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("👴 %s is %s years old", c.Params("name"), c.Params("age")) + return c.SendString(msg) // => 👴 john is 75 years old + }) + + // GET /john + app.Get("/:name", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("Hello, %s 👋!", c.Params("name")) + return c.SendString(msg) // => Hello john 👋! + }) + + log.Fatal(app.Listen(":3000")) +} + +``` + +#### 📖 [**Nazywanie Route'ów**](https://docs.gofiber.io/api/app#name) + +```go +func main() { + app := fiber.New() + + // GET /api/register + app.Get("/api/*", func(c *fiber.Ctx) error { + msg := fmt.Sprintf("✋ %s", c.Params("*")) + return c.SendString(msg) // => ✋ register + }).Name("api") + + data, _ := json.MarshalIndent(app.GetRoute("api"), "", " ") + fmt.Print(string(data)) + // Prints: + // { + // "method": "GET", + // "name": "api", + // "path": "/api/*", + // "params": [ + // "*1" + // ] + // } + + + log.Fatal(app.Listen(":3000")) +} + +``` + +#### 📖 [**Serwowanie plików statycznych**](https://docs.gofiber.io/api/app#static) + +```go +func main() { + app := fiber.New() + + app.Static("/", "./public") + // => http://localhost:3000/js/script.js + // => http://localhost:3000/css/style.css + + app.Static("/prefix", "./public") + // => http://localhost:3000/prefix/js/script.js + // => http://localhost:3000/prefix/css/style.css + + app.Static("*", "./public/index.html") + // => http://localhost:3000/any/path/shows/index/html + + log.Fatal(app.Listen(":3000")) +} + +``` + +#### 📖 [**Middleware i Next**](https://docs.gofiber.io/api/ctx#next) + +```go +func main() { + app := fiber.New() + + // Match any route + app.Use(func(c *fiber.Ctx) error { + fmt.Println("🥇 First handler") + return c.Next() + }) + + // Match all routes starting with /api + app.Use("/api", func(c *fiber.Ctx) error { + fmt.Println("🥈 Second handler") + return c.Next() + }) + + // GET /api/list + app.Get("/api/list", func(c *fiber.Ctx) error { + fmt.Println("🥉 Last handler") + return c.SendString("Hello, World 👋!") + }) + + log.Fatal(app.Listen(":3000")) +} + +``` + +
+ 📚 Pokaż więcej przykładów + +### Silniki widoków + +📖 [Config](https://docs.gofiber.io/api/fiber#config) +📖 [Silniki](https://github.com/gofiber/template) +📖 [Render](https://docs.gofiber.io/api/ctx#render) + +Fiber domyślnie korzysta z [html/template](https://pkg.go.dev/html/template/), kiedy nie wybrano żadnego silnika. + +Jeżeli chcesz wykonywać lub korzystać z innego silnika jak [amber](https://github.com/eknkc/amber), [handlebars](https://github.com/aymerick/raymond), [mustache](https://github.com/cbroglie/mustache), [pug](https://github.com/Joker/jade) itd. sprawdź naszą paczkę [Template](https://github.com/gofiber/template), która wspiera wiele silników widoków. + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/template/pug" +) + +func main() { + // You can setup Views engine before initiation app: + app := fiber.New(fiber.Config{ + Views: pug.New("./views", ".pug"), + }) + + // And now, you can call template `./views/home.pug` like this: + app.Get("/", func(c *fiber.Ctx) error { + return c.Render("home", fiber.Map{ + "title": "Homepage", + "year": 1999, + }) + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### Grupowanie route'ów w łańcuchy + +📖 [Group](https://docs.gofiber.io/api/app#group) + +```go +func middleware(c *fiber.Ctx) error { + fmt.Println("Don't mind me!") + return c.Next() +} + +func handler(c *fiber.Ctx) error { + return c.SendString(c.Path()) +} + +func main() { + app := fiber.New() + + // Root API route + api := app.Group("/api", middleware) // /api + + // API v1 routes + v1 := api.Group("/v1", middleware) // /api/v1 + v1.Get("/list", handler) // /api/v1/list + v1.Get("/user", handler) // /api/v1/user + + // API v2 routes + v2 := api.Group("/v2", middleware) // /api/v2 + v2.Get("/list", handler) // /api/v2/list + v2.Get("/user", handler) // /api/v2/user + + // ... +} + +``` + +### Middleware Logger + +📖 [Logger](https://docs.gofiber.io/api/middleware/logger) + +```go +package main + +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/logger" +) + +func main() { + app := fiber.New() + + app.Use(logger.New()) + + // ... + + log.Fatal(app.Listen(":3000")) +} +``` + +### Cross-Origin Resource Sharing (CORS) + +📖 [CORS](https://docs.gofiber.io/api/middleware/cors) + +```go +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/cors" +) + +func main() { + app := fiber.New() + + app.Use(cors.New()) + + // ... + + log.Fatal(app.Listen(":3000")) +} +``` +Sprawdź CORS poprzez przesłanie jakiejkolwiek domeny w nagłówku `Origin`: + +```bash +curl -H "Origin: http://example.com" --verbose http://localhost:3000 +``` + +### Niestandardowa odpowiedź 404 + +📖 [Metody HTTP](https://docs.gofiber.io/api/ctx#status) + +```go +func main() { + app := fiber.New() + + app.Static("/", "./public") + + app.Get("/demo", func(c *fiber.Ctx) error { + return c.SendString("This is a demo!") + }) + + app.Post("/register", func(c *fiber.Ctx) error { + return c.SendString("Welcome!") + }) + + // Last middleware to match anything + app.Use(func(c *fiber.Ctx) error { + return c.SendStatus(404) + // => 404 "Not Found" + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### Odpowiedź JSON + +📖 [JSON](https://docs.gofiber.io/api/ctx#json) + +```go +type User struct { + Name string `json:"name"` + Age int `json:"age"` +} + +func main() { + app := fiber.New() + + app.Get("/user", func(c *fiber.Ctx) error { + return c.JSON(&User{"John", 20}) + // => {"name":"John", "age":20} + }) + + app.Get("/json", func(c *fiber.Ctx) error { + return c.JSON(fiber.Map{ + "success": true, + "message": "Hi John!", + }) + // => {"success":true, "message":"Hi John!"} + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### Dodanie WebSocket + +📖 [Websocket](https://github.com/gofiber/websocket) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/websocket" +) + +func main() { + app := fiber.New() + + app.Get("/ws", websocket.New(func(c *websocket.Conn) { + for { + mt, msg, err := c.ReadMessage() + if err != nil { + log.Println("read:", err) + break + } + log.Printf("recv: %s", msg) + err = c.WriteMessage(mt, msg) + if err != nil { + log.Println("write:", err) + break + } + } + })) + + log.Fatal(app.Listen(":3000")) + // ws://localhost:3000/ws +} +``` + +### Wydarzenia wysyłane przez serwer + +📖 [More Info](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/valyala/fasthttp" +) + +func main() { + app := fiber.New() + + app.Get("/sse", func(c *fiber.Ctx) error { + c.Set("Content-Type", "text/event-stream") + c.Set("Cache-Control", "no-cache") + c.Set("Connection", "keep-alive") + c.Set("Transfer-Encoding", "chunked") + + c.Context().SetBodyStreamWriter(fasthttp.StreamWriter(func(w *bufio.Writer) { + fmt.Println("WRITER") + var i int + + for { + i++ + msg := fmt.Sprintf("%d - the time is %v", i, time.Now()) + fmt.Fprintf(w, "data: Message: %s\n\n", msg) + fmt.Println(msg) + + w.Flush() + time.Sleep(5 * time.Second) + } + })) + + return nil + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +### Middleware Recover + +📖 [Recover](https://docs.gofiber.io/api/middleware/recover) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/recover" +) + +func main() { + app := fiber.New() + + app.Use(recover.New()) + + app.Get("/", func(c *fiber.Ctx) error { + panic("normally this would crash your app") + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +
+ +### Używanie zaufanego proxy + +📖 [Config](https://docs.gofiber.io/api/fiber#config) + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/recover" +) + +func main() { + app := fiber.New(fiber.Config{ + EnableTrustedProxyCheck: true, + TrustedProxies: []string{"0.0.0.0", "1.1.1.1/30"}, // IP address or IP address range + ProxyHeader: fiber.HeaderXForwardedFor, + }) + + // ... + + log.Fatal(app.Listen(":3000")) +} +``` + + + +## 🧬 Wbudowane Middleware + +Poniżej znajduje się lista middleware, które są zawarte wraz z frameworkiem Fiber. + +| Middleware | Opis | +| :------------------------------------------------------------------------------------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Podstawowe middleware zapewniające podstawowe uwierzytelnienie HTTP. Wywołuje ono handler Next dla poprawnych danych uwierzytelniających oraz 401 Unauthorized dla niepoprawnych lub brakujacych danych. | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Przechwytuje i cache'uje odpowiedzi | +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Middleware kompresji dla Fiber'a, podstawowo wspiera `deflate`, `gzip` i `brotli`. | +| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Zezwala na cross-origin resource sharing \(CORS\) z wieloma opcjami konfiguracji. | +| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Chroni przed exploitami CSRF. | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Middleware szyfrujące wartości ciasteczek. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Odsłania zmienne środowiskowe oraz zapewnia dodatkową konfigurację. | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Middleware ETag, które pozwala cache być bardziej wydajnym i oszczędzać transfer danych, jako, że serwer web nie musi wysyłać pełnej odpowiedzi, jeżeli dane się nie zmieniły. | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Middleware Expvar, które udostępnia warianty uruchomieniowe przez swój serwer HTTP, w formacie JSON | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignoruje favicony z logów lub serwuje je z pamięci, gdy ścieżka do pliku została podana | +| [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) | Logger zapytań/odpowiedzi HTTP. | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Middleware Monitor, które reportuje metryki serwera, inspirowane express-status-monitor | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Specjalne podziękowania dla Matthew Lee \(@mthli\) | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Pozwala ci przesyłać zapytania dalej do wielu serwerów | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Middleware Recover przywraca działanie po wystąpieniu awarii w dowolnym miejscu w programie i przekazuje kontrolę do scentralizowanego typu [ ErrorHandler](https://docs.gofiber.io/guide/error-handling). | +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Dodaje requestid do każdego zapytania. | +| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Middleware sesji. UWAGA: To middleware korzysta z naszej paczki Storage | +| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Middleware skip, które pomija opakowany handler, jeżeli założona zasada jest spełniona | +| [rewrite](https://github.com/gofiber/rewrite) | Middleware Rewrite przepisuje scieżkę URL bazując na podanych zasadach. Może być przydatne w przypadku potrzeby kompatybilności wstecznej lub po prostu tworzeniu czystszych i bardziej przejrzystych linków. | +| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Dodaje maksymalny czas dla zapytania i podaje go dalej do ErrorHandler, gdy limit został przekroczony. | +| [adaptor](https://github.com/gofiber/adaptor) | Konwertuje handlery net/http do/z zapytania Fiber'a, specjalne podziękowania dla @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Pomaga zabezpieczyć twoją aplikację poprzez ustawianie wielu nagłówków HTTP. | +| [redirect](https://github.com/gofiber/redirect) | Middleware przekierowywujące | +| [keyauth](https://github.com/gofiber/keyauth) | Middleware Key auth zapewnia uwierzytelnienie na podstawie klucza. | + +## 🧬 Zewnętrzne middleware +Lista zewnętrznie hostowanych modułów middleware i utrzymywanych przez [zpesół Fiber'a](https://github.com/orgs/gofiber/people). + +| Middleware | Description | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | + +## 🕶️ Awesome List + +Po więcej artykułów, middleware, przykładów lub narzędzi sprawdź naszą [awesome list](https://github.com/gofiber/awesome-fiber). + +## 👍 Wspomaganie + +Jeżeli chcesz podziękować i/lub wesprzeć aktywny rozwój `Fiber'a`: + +1. Dodaj [Gwiazdkę GitHub](https://github.com/gofiber/fiber/stargazers) dla tego projektu. +2. Zatweetuj o tym projekcie [na twoim 𝕏 (Twitter)](https://x.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. Napisz recenzję lub tutorial na [Medium](https://medium.com/), [Dev.to](https://dev.to/) lub personalnym blogu. +4. Wesprzyj projekt, przekazując darowiznę w postaci [filiżanki kawy](https://buymeacoff.ee/fenny). + +## ☕ Wspierający + +Fiber to projekt open source, który działa dzięki darowiznom, aby pokryć koszty, takie jak nasza nazwa domeny, GitBook, Netlify oraz hosting serverless. Jeśli chcesz wesprzeć Fiber, możesz ☕ [**tutaj kupić kawę**](https://buymeacoff.ee/fenny). + +| | Użytkownik | Dotacja | +| :--------------------------------------------------------- |:-------------------------------------------------|:--------| +| ![](https://avatars.githubusercontent.com/u/204341?s=25) | [@destari](https://github.com/destari) | ☕ x 10 | +| ![](https://avatars.githubusercontent.com/u/63164982?s=25) | [@dembygenesis](https://github.com/dembygenesis) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/56607882?s=25) | [@thomasvvugt](https://github.com/thomasvvugt) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/27820675?s=25) | [@hendratommy](https://github.com/hendratommy) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/1094221?s=25) | [@ekaputra07](https://github.com/ekaputra07) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/194590?s=25) | [@jorgefuertes](https://github.com/jorgefuertes) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/186637?s=25) | [@candidosales](https://github.com/candidosales) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/29659953?s=25) | [@l0nax](https://github.com/l0nax) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/635852?s=25) | [@bihe](https://github.com/bihe) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/307334?s=25) | [@justdave](https://github.com/justdave) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/11155743?s=25) | [@koddr](https://github.com/koddr) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/29042462?s=25) | [@lapolinar](https://github.com/lapolinar) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/2978730?s=25) | [@diegowifi](https://github.com/diegowifi) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/44171355?s=25) | [@ssimk0](https://github.com/ssimk0) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/5638101?s=25) | [@raymayemir](https://github.com/raymayemir) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/619996?s=25) | [@melkorm](https://github.com/melkorm) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/31022056?s=25) | [@marvinjwendt](https://github.com/marvinjwendt) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/31921460?s=25) | [@toishy](https://github.com/toishy) | ☕ x 1 | + +## ‎‍💻 Współtwórcy projektu + +Code Contributors + +## ⭐️ Obserwujący projekt + +Stargazers over time + +## ⚠️ Licencja + +Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors](https://github.com/gofiber/fiber/graphs/contributors). `Fiber` is free and open-source software licensed under the [MIT License](https://github.com/gofiber/fiber/blob/master/LICENSE). Official logo was created by [Vic Shóstak](https://github.com/koddr) and distributed under [Creative Commons](https://creativecommons.org/licenses/by-sa/4.0/) license (CC BY-SA 4.0 International). + +**Licencje bibliotek od innych twórców** + +- [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) +- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) +- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) +- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) +- [msgp](https://github.com/tinylib/msgp/blob/master/LICENSE) +- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) +- [uuid](https://github.com/google/uuid/blob/master/LICENSE) +- [wmi](https://github.com/StackExchange/wmi/blob/master/LICENSE) diff --git a/.github/README_pt.md b/.github/README_pt.md index f0d69774e2..07d4b89c62 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -66,6 +66,12 @@ + + + + + +
@@ -143,7 +149,7 @@ go get -u github.com/gofiber/fiber/v3 - [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 [18 línguas](https://docs.gofiber.io/) +- Disponível em [19 línguas](https://docs.gofiber.io/) - E muito mais, [explore o Fiber](https://docs.gofiber.io/) ## 💡 Filosofia @@ -152,9 +158,10 @@ Os novos gophers que mudaram do [Node.js](https://nodejs.org/en/about/) para o [ O Fiber é **inspirado** no Express, o framework web mais popular da Internet. Combinamos a **facilidade** do Express e com o **desempenho bruto** do Go. Se você já implementou um aplicativo web com Node.js ( _usando Express.js ou similar_ ), então muitos métodos e princípios parecerão **muito familiares** para você. -## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. -* Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. +## ⚠️ Limitações + +- Devido ao uso de "unsafe" pelo Fiber, a biblioteca pode nem sempre ser compatível com a última versão do Go. Fiber 2.40.0 foi testado com as versões Go de 1.17 a 1.21. +- Fiber não é compatível com as interfaces net/http. Isso significa que você não poderá usar projetos como gqlgen, go-swagger ou quaisquer outros que fazem parte do ecossistema net/http. ## 👀 Exemplos @@ -203,7 +210,7 @@ func main() { ``` -#### 📖 [**Route Naming**](https://docs.gofiber.io/api/app#name) +#### 📖 [**Nome de Rotas**](https://docs.gofiber.io/api/app#name) ```go func main() { @@ -570,60 +577,60 @@ func main() { -## 🧬 Internal Middleware - -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. | -| [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. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | - -## 🧬 External Middleware - -List of externally hosted middleware modules and maintained by the [Fiber team](https://github.com/orgs/gofiber/people). - -| Middleware | Description | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | - -## 🕶️ Awesome List - -For more articles, middlewares, examples or tools check our [awesome list](https://github.com/gofiber/awesome-fiber). +## 🧬 Middleware Interno + +Aqui está uma lista de middlewares que estão incluídos no framework Fiber. + +| Middleware | Descrição | +| :------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Autenticação básica fornece uma autenticação HTTP básica. Ele chama o próximo manipulador para credenciais válidas e 401 Não Autorizado para credenciais ausentes ou inválidas. | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Intercepta e armazena em cache as respostas | +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Middleware de compressão para o Fiber, suporta `deflate`, `gzip` e `brotli` por padrão. | +| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | Habilita o compartilhamento de recursos de origem cruzada (CORS) com várias opções. | +| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | Protege contra exploits CSRF. | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | Criptografa valores de cookie. | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | Expõe variáveis de ambiente fornecendo uma configuração opcional. | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | Permite que caches sejam mais eficientes e economizem largura de banda, pois um servidor web não precisa reenviar uma resposta completa se o conteúdo não mudou. | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | Serve via seu servidor HTTP variantes expostas em tempo de execução no formato JSON. | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | Ignora favicon dos logs ou serve da memória se um caminho de arquivo for fornecido. | +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Sistema de Arquivos para o Fiber, agradecimentos especiais e créditos a Alireza Salary | +| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | Limitação de taxa para o Fiber. Use para limitar solicitações repetidas para APIs públicas e/ou endpoints como redefinição de senha. | +| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | Logger de solicitação/resposta HTTP. | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | Middleware de monitoramento que relata métricas do servidor, inspirado pelo express-status-monitor | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | Agradecimentos especiais a Matthew Lee (@mthli) | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | Permite que você faça proxy de solicitações a vários servidores | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Recupera de panics em qualquer lugar da cadeia de chamadas e passa o controle para o [ErrorHandler](https://docs.gofiber.io/guide/error-handling) centralizado. | +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | Adiciona um ID de solicitação a cada pedido. | +| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Middleware de sessão. NOTA: Este middleware usa nosso pacote Storage. | +| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Pula um handler envolto se um predicado for verdadeiro. | +| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adiciona um tempo máximo para uma solicitação e encaminha para ErrorHandler se ele for excedido. | +| [keyauth](https://github.com/gofiber/keyauth) | Autenticação por chave fornece uma autenticação baseada em chave. | +| [redirect](https://github.com/gofiber/redirect) | Middleware de redirecionamento | +| [rewrite](https://github.com/gofiber/rewrite) | Reescreve o caminho da URL com base nas regras fornecidas. Pode ser útil para compatibilidade retroativa ou para criar links mais limpos e descritivos. | +| [adaptor](https://github.com/gofiber/adaptor) | Conversor para handlers net/http para/para manipuladores de solicitação Fiber, agradecimentos especiais ao @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Ajuda a proteger seus aplicativos definindo vários cabeçalhos HTTP. | + +## 🧬 Middleware Externo + +Lista de módulos de middleware hospedados externamente e mantidos pela [equipe Fiber](https://github.com/orgs/gofiber/people). + +| Middleware | Descrição | +| :------------------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT retorna um middleware de autenticação com tokens JWT. | +| [storage](https://github.com/gofiber/storage) | Drivers de armazenamento prontos que implementam a interface Storage, projetados para serem usados com vários middlewares do Fiber. | +| [template](https://github.com/gofiber/template) | Este pacote contém 8 mecanismos de template que podem ser usados com Fiber `v1.10.x`. É necessário Go versão 1.13 ou superior. | +| [websocket](https://github.com/gofiber/websocket) | Baseado no WebSocket do Fasthttp para Fiber com suporte a Locals | + +## 🕶️ Lista Incrível + +Para mais artigos, middlewares, exemplos ou ferramentas, confira nossa [lista incrível](https://github.com/gofiber/awesome-fiber). ## 👍 Contribuindo Se você quer **agradecer** e/ou apoiar o desenvolvimento ativo do `Fiber`: 1. Deixe uma [estrela no GitHub](https://github.com/gofiber/fiber/stargazers) do projeto. -2. Tweet sobre o projeto [no seu 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. Tweet sobre o projeto [no seu 𝕏 (Twitter)](https://x.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. Escreva um review ou tutorial no [Medium](https://medium.com/), [Dev.to](https://dev.to/) ou blog pessoal. 4. Apoie o projeto pagando uma [xícara de café](https://buymeacoff.ee/fenny). diff --git a/.github/README_ru.md b/.github/README_ru.md index c5f25be7be..47c555e8de 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -66,6 +66,12 @@ + + + + + +
@@ -143,7 +149,7 @@ go get -u github.com/gofiber/fiber/v3 - [Поддержка 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) -- Документация доступна на [18 языках](https://docs.gofiber.io/) +- Документация доступна на [19 языках](https://docs.gofiber.io/) - И многое другое, [посетите наш Wiki](https://docs.gofiber.io/) ## 💡 Философия @@ -155,8 +161,9 @@ Fiber **вдохновлен** Express, самым популярным веб Мы **прислушиваемся** к нашим пользователям в [issues](https://github.com/gofiber/fiber/issues), Discord [канале](https://gofiber.io/discord) _и в остальном Интернете_, чтобы создать **быстрый**, **гибкий** и **дружелюбный** веб фреймворк на Go для **любых** задач, **дедлайнов** и **уровней** разработчиков! Как это делает Express в мире JavaScript. ## ⚠️ Ограничения -* Из-за того, что Fiber использует пакет unsafe, библиотека не всегда может быть совместима с последней версией Go. Fiber 2.40.0 был протестирован с версиями Go от 1.17 до 1.20. -* Fiber не совместим с интерфейсами net/http. Это означает, что вы не сможете использовать такие проекты, как gqlgen, go-swagger или любые другие, которые являются частью экосистемы net/http. + +- Из-за того, что Fiber использует пакет unsafe, библиотека не всегда может быть совместима с последней версией Go. Fiber 2.40.0 был протестирован с версиями Go от 1.17 до 1.21. +- Fiber не совместим с интерфейсами net/http. Это означает, что вы не сможете использовать такие проекты, как gqlgen, go-swagger или любые другие, которые являются частью экосистемы net/http. ## 👀 Примеры @@ -581,7 +588,7 @@ func main() { Вот список middleware, входящих в состав фреймворка Fiber. -| Middleware | Описание | +| 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 | @@ -604,22 +611,22 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 Внешние Middleware Список модулей middleware, размещенных на внешнем хостинге от [Fiber team](https://github.com/orgs/gofiber/people). -| Middleware | Описание | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | +| Middleware | Описание | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | ## 🕶️ Полезный список @@ -630,7 +637,7 @@ func main() { Если вы хотите сказать **спасибо** и/или поддержать активное развитие `Fiber`: 1. Добавьте [GitHub Star](https://github.com/gofiber/fiber/stargazers) в проект. -2. Напишите о проекте [в вашем Twitter](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber). +2. Напишите о проекте [в вашем 𝕏 (Twitter)](https://x.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber). 3. Сделайте обзор фреймворка на [Medium](https://medium.com/), [Dev.to](https://dev.to/) или в личном блоге. 4. Поддержите проект, купив [чашку кофе](https://buymeacoff.ee/fenny). @@ -671,7 +678,7 @@ Fiber — это проект с открытым исходным кодом, ## ⚠️ Лицензия -Copyright (c) 2019-настоящее время [Fenny](https://github.com/fenny) и [Контрибьютеры](https://github.com/gofiber/fiber/graphs/contributors). `Fiber` - это свободное программное обсепечение с открытым исходным кодом лицензированное под [MIT License](https://github.com/gofiber/fiber/blob/master/LICENSE). Официальный логотип создан [Vic Shóstak](https://github.com/koddr) и распространяется под [Creative Commons](https://creativecommons.org/licenses/by-sa/4.0/) лицензией (CC BY-SA 4.0 International). +Copyright (c) 2019-настоящее время [Fenny](https://github.com/fenny) и [Контрибьютеры](https://github.com/gofiber/fiber/graphs/contributors). `Fiber` - это свободное программное обсепечение с открытым исходным кодом лицензированное под [MIT License](https://github.com/gofiber/fiber/blob/master/LICENSE). Официальный логотип создан [Vic Shóstak](https://github.com/koddr) и распространяется под [Creative Commons](https://creativecommons.org/licenses/by-sa/4.0/) лицензией (CC BY-SA 4.0 International). **Third-party library licenses** diff --git a/.github/README_sa.md b/.github/README_sa.md index 61ec729bee..ccc24cb6fc 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -66,6 +66,12 @@ + + + + + +
@@ -156,7 +162,7 @@ go get -u github.com/gofiber/fiber/v3 - [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) -- ترجم الى [18 لغة أخرى](https://docs.gofiber.io/) +- ترجم الى [19 لغة أخرى](https://docs.gofiber.io/) - وأكثر بكثير, [استكشف Fiber](https://docs.gofiber.io/) ## 💡 فلسفة @@ -169,8 +175,9 @@ Fiber هو **مستوحى** من Express, إطار الويب الأكثر شع ** و تطوير **مهارات**! فقط مثل Express تفعل لـ JavaScript عالم. ## ⚠️ Limitations -* Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.20. -* Fiber is not compatible with net/http interfaces. This means you will not be able to use projects like gqlgen, go-swagger, or any others which are part of the net/http ecosystem. + +- Due to Fiber's usage of unsafe, the library may not always be compatible with the latest Go version. Fiber 2.40.0 has been tested with Go versions 1.17 to 1.21. +- 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. ## 👀 أمثلة @@ -668,22 +675,22 @@ Here is a list of middleware that are included within the Fiber framework. | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session middleware. NOTE: This middleware uses our Storage package. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Adds a max time for a request and forwards to ErrorHandler if it is exceeded. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | -| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware provides a key based authentication. | +| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware rewrites the URL path based on provided rules. It can be helpful for backward compatibility or just creating cleaner and more descriptive links. | +| [adaptor](https://github.com/gofiber/adaptor) | Converter for net/http handlers to/from Fiber request handlers, special thanks to @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Helps secure your apps by setting various HTTP headers. | ## 🧬 External Middleware List of externally hosted middleware modules and maintained by the [Fiber team](https://github.com/orgs/gofiber/people). -| Middleware | Description | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | -| [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! | +| Middleware | Description | +| :------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT returns a JSON Web Token \(JWT\) auth middleware. | +| [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! | ## 🕶️ Awesome List @@ -694,7 +701,7 @@ For more articles, middlewares, examples or tools check our [awesome list](https إذا كنت تريد أن تقول **شكرا جزيل** و/او دعم التنمية النشطة للـ `Fiber`: 1. اضف [GitHub نجمة](https://github.com/gofiber/fiber/stargazers) للمشروع. -2. غرد عن المشروع [في تويتر ](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. غرد عن المشروع [في تويتر ](https://x.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber). 3. اكتب مراجعة أو برنامج تعليمي عن [Medium](https://medium.com/), [Dev.to](https://dev.to/) او في موقعك الشخصي. 4. دعم المشروع بالتبرع بـ [كوب من القهوة](https://buymeacoff.ee/fenny). diff --git a/.github/README_tr.md b/.github/README_tr.md index 4b93d27a7b..614ca532c2 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -63,6 +63,12 @@ + + + + + +
@@ -143,7 +149,7 @@ go get -u github.com/gofiber/fiber/v3 - [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) -- [18 dilde](https://docs.gofiber.io/) mevcut +- [19 dilde](https://docs.gofiber.io/) mevcut - Ve daha fazlası, [Fiber'ı keşfet](https://docs.gofiber.io/) ## 💡 Felsefe @@ -154,7 +160,7 @@ Fiber, internet üzerinde en popüler web framework'ü olan Express'ten **esinle ## ⚠️ Sınırlamalar -- Fiber unsafe kullanımı sebebiyle Go'nun son sürümüyle her zaman uyumlu olmayabilir. Fiber 2.40.0, Go 1.17 ile 1.20 sürümleriyle test edildi. +- Fiber unsafe kullanımı sebebiyle Go'nun son sürümüyle her zaman uyumlu olmayabilir. Fiber 2.40.0, Go 1.17 ile 1.21 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 @@ -574,47 +580,48 @@ 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. | -| [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. | -| [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. | -| [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. | -| [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. | -| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Bir request için maksimum süre ekler ve aşılırsa ErrorHandler'a iletir. | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware, key tabanlı bir authentication sağlar. | -| [redirect](https://github.com/gofiber/redirect) | Yönlendirme middleware 'ı. | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware, sağlanan kurallara göre URL yolunu yeniden yazar. Geriye dönük uyumluluk için veya yalnızca daha temiz ve daha açıklayıcı bağlantılar oluşturmak için yardımcı olabilir. | -| [adaptor](https://github.com/gofiber/adaptor) | Fiber request handlerdan net/http handlerları için dönüştürücü, @arsmn'a özel teşekkürler! | -| [helmet](https://github.com/gofiber/helmet) | Çeşitli HTTP headerları ayarlayarak uygulamalarınızın güvenliğini sağlamaya yardımcı olur. | +| 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. | +| [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. | +| [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. | +| [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. | +| [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. | +| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Bir request için maksimum süre ekler ve aşılırsa ErrorHandler'a iletir. | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth middleware, key tabanlı bir authentication sağlar. | +| [redirect](https://github.com/gofiber/redirect) | Yönlendirme middleware 'ı. | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite middleware, sağlanan kurallara göre URL yolunu yeniden yazar. Geriye dönük uyumluluk için veya yalnızca daha temiz ve daha açıklayıcı bağlantılar oluşturmak için yardımcı olabilir. | +| [adaptor](https://github.com/gofiber/adaptor) | Fiber request handlerdan net/http handlerları için dönüştürücü, @arsmn'a özel teşekkürler! | +| [helmet](https://github.com/gofiber/helmet) | Çeşitli HTTP headerları ayarlayarak uygulamalarınızın güvenliğini sağlamaya yardımcı olur. | ## 🧬 Harici Middlewarelar Harici olarak barındırılan middlewareların modüllerinin listesi. Bu middlewarelar, [Fiber ekibi](https://github.com/orgs/gofiber/people) tarafından geliştirilir. -| Middleware | Açıklama | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [jwt](https://github.com/gofiber/jwt) | JWT, bir JSON Web Token \(JWT\) yetkilendirmesi döndüren middleware. | -| [storage](https://github.com/gofiber/storage) | Fiber'in Storage yapısını destekleyen birçok storage driver'ı verir. Bu sayede depolama gerektiren Fiber middlewarelarında kolaylıkla kullanılabilir. | -| [template](https://github.com/gofiber/template) | Bu paket, Fiber `v2.x.x`, Go sürüm 1.17 veya üzeri gerekli olduğunda kullanılabilecek 9 template motoru içerir. | -| [websocket](https://github.com/gofiber/websocket) | Yereller desteğiyle Fiber için Fasthttp WebSocket'a dayalıdır! | +| Middleware | Açıklama | +| :------------------------------------------------ | :---------------------------------------------------------------------------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT, bir JSON Web Token \(JWT\) yetkilendirmesi döndüren middleware. | +| [storage](https://github.com/gofiber/storage) | Fiber'in Storage yapısını destekleyen birçok storage driver'ı verir. Bu sayede depolama gerektiren Fiber middlewarelarında kolaylıkla kullanılabilir. | +| [template](https://github.com/gofiber/template) | Bu paket, Fiber `v2.x.x`, Go sürüm 1.17 veya üzeri gerekli olduğunda kullanılabilecek 9 template motoru içerir. | +| [websocket](https://github.com/gofiber/websocket) | Yereller desteğiyle Fiber için Fasthttp WebSocket'a dayalıdır! | ## 🕶️ Awesome Listesi + Daha fazla yazı, middleware, örnek veya araç için [awesome list](https://github.com/gofiber/awesome-fiber) reposunu kontrol etmeyi unutmayın. ## 👍 Destek @@ -622,7 +629,7 @@ Daha fazla yazı, middleware, örnek veya araç için [awesome list](https://git Eğer **teşekkür etmek** veya `Fiber`'ın aktif geliştirilmesini desteklemek istiyorsanız: 1. Projeye [yıldız](https://github.com/gofiber/fiber/stargazers) verebilirsiniz. -2. [Twitter hesabınızdan](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) proje hakkında tweet atabilirsiniz. +2. [𝕏 (Twitter) hesabınızdan](https://x.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) proje hakkında tweet atabilirsiniz. 3. [Medium](https://medium.com/), [Dev.to](https://dev.to/) veya kişisel blogunuz üzerinden bir inceleme veya eğitici yazı yazabilirsiniz. 4. Projeye [bir fincan kahve](https://buymeacoff.ee/fenny) bağışlayarak destek olabilirsiniz. diff --git a/.github/README_uk.md b/.github/README_uk.md index 9556e924e1..ef9f36536b 100644 --- a/.github/README_uk.md +++ b/.github/README_uk.md @@ -69,6 +69,12 @@ + + + + + +
@@ -144,19 +150,19 @@ go get -u github.com/gofiber/fiber/v2 ## 🎯 Особливості -- Надійна [маршрутизація](https://docs.gofiber.io/routing) -- Доступ до [статичних файлів](https://docs.gofiber.io/api/app#static) -- Екстремальна [продуктивність](https://docs.gofiber.io/extra/benchmarks) -- [Низький обсяг споживання пам'яті](https://docs.gofiber.io/extra/benchmarks) -- [Кінцеві точки API](https://docs.gofiber.io/api/ctx) -- [Middleware](https://docs.gofiber.io/middleware) та підтримка [Next](https://docs.gofiber.io/api/ctx#next) -- [Швидке](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) програмування на стороні сервера -- [Двигуни шаблонів](https://github.com/gofiber/template) -- [Підтримка WebSocket](https://github.com/gofiber/websocket) -- [Server-Sent Events](https://github.com/gofiber/recipes/tree/master/sse) -- [Обмежувач швидкості](https://docs.gofiber.io/api/middleware/limiter) -- Документація доступна [18 мовами](https://docs.gofiber.io/) -- І багато іншого, [відвідайте наш Wiki](https://docs.gofiber.io/) +- Надійна [маршрутизація](https://docs.gofiber.io/routing) +- Доступ до [статичних файлів](https://docs.gofiber.io/api/app#static) +- Екстремальна [продуктивність](https://docs.gofiber.io/extra/benchmarks) +- [Низький обсяг споживання пам'яті](https://docs.gofiber.io/extra/benchmarks) +- [Кінцеві точки API](https://docs.gofiber.io/api/ctx) +- [Middleware](https://docs.gofiber.io/middleware) та підтримка [Next](https://docs.gofiber.io/api/ctx#next) +- [Швидке](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497) програмування на стороні сервера +- [Двигуни шаблонів](https://github.com/gofiber/template) +- [Підтримка WebSocket](https://github.com/gofiber/websocket) +- [Server-Sent Events](https://github.com/gofiber/recipes/tree/master/sse) +- [Обмежувач швидкості](https://docs.gofiber.io/api/middleware/limiter) +- Документація доступна [19 мовами](https://docs.gofiber.io/) +- І багато іншого, [відвідайте наш Wiki](https://docs.gofiber.io/) ## 💡 Філософія @@ -168,8 +174,8 @@ Fiber **натхненний** Express, найпопулярнішим веб-ф ## ⚠️ Обмеження -- Через те, що Fiber використовує unsafe, бібліотека не завжди може бути сумісною з останньою версією Go. Fiber 2.40.0 було протестовано з Go версій 1.17 до 1.20. -- Fiber не сумісний з інтерфейсами net/http. Це означає, що ви не зможете використовувати такі проекти, як gqlgen, go-swagger або будь-які інші, які є частиною екосистеми net/http. +- Через те, що Fiber використовує unsafe, бібліотека не завжди може бути сумісною з останньою версією Go. Fiber 2.40.0 було протестовано з Go версій 1.17 до 1.21. +- Fiber не сумісний з інтерфейсами net/http. Це означає, що ви не зможете використовувати такі проекти, як gqlgen, go-swagger або будь-які інші, які є частиною екосистеми net/http. ## 👀 Приклади @@ -304,7 +310,7 @@ func main() { Fiber за умовчанням використовує [html/template](https://pkg.go.dev/html/template/), якщо жодного двигуна не було вказано. -Якщо ви хочете виконати частково або використовувати інший двигун, наприклад [amber](https://github.com/eknkc/amber), [handlebars](https://github.com/aymerick/raymond), [mustache]( https://github.com/cbroglie/mustache) або [jade](https://github.com/Joker/jade), тощо. +Якщо ви хочете виконати частково або використовувати інший двигун, наприклад [amber](https://github.com/eknkc/amber), [handlebars](https://github.com/aymerick/raymond), [mustache](https://github.com/cbroglie/mustache) або [jade](https://github.com/Joker/jade), тощо. Перегляньте наш пакет [Шаблон](https://github.com/gofiber/template), який підтримує кілька двигунів перегляду. @@ -610,7 +616,7 @@ func main() { Ось список middleware, яке входить до складу Fiber фреймворку. | Middleware | Опис | -|:---------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| :------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | Middleware який забезпечує базову автентифікацію по HTTP. | | [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | Middleware який перехоплює та кешує відповіді | | [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | стиснення для Fiber, воно за замовчуванням підтримує `deflate`, `gzip` і `brotli`. | @@ -632,11 +638,11 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Middleware для сеансів. ПРИМІТКА: Цей middleware використовує наш пакет зберігання. | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Middleware який пропускає упакований обробник, якщо предикат є істинним. | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | Додає максимальний час для запиту та пересилає до ErrorHandler, якщо його перевищено. | -| [keyauth](https://github.com/gofiber/keyauth) | Middleware для автентифікації по ключам. | -| [redirect](https://github.com/gofiber/redirect) | Middleware для перенаправлення. | -| [rewrite](https://github.com/gofiber/rewrite) | Middleware для перезапису URL-адреси на основі наданих правил. | -| [adaptor](https://github.com/gofiber/adaptor) | Конвентор для обробників net/http до/з обробників запитів Fiber, особлива подяка @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | Допомагає захистити ваші програми, встановлюючи різні заголовки HTTP. | +| [keyauth](https://github.com/gofiber/keyauth) | Middleware для автентифікації по ключам. | +| [redirect](https://github.com/gofiber/redirect) | Middleware для перенаправлення. | +| [rewrite](https://github.com/gofiber/rewrite) | Middleware для перезапису URL-адреси на основі наданих правил. | +| [adaptor](https://github.com/gofiber/adaptor) | Конвентор для обробників net/http до/з обробників запитів Fiber, особлива подяка @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | Допомагає захистити ваші програми, встановлюючи різні заголовки HTTP. | ## 🧬 Зовнішні Middleware @@ -658,7 +664,7 @@ func main() { Якщо ви хочете сказати **дякую** та/або підтримати активний розвиток `Fiber`: 1. Додайте [зірку GitHub](https://github.com/gofiber/fiber/stargazers) до проекту. -2. Напишіть про проект [у своєму Twitter](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber). +2. Напишіть про проект [у своєму 𝕏 (Twitter)](https://x.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber). 3. Напишіть огляд або підручник на [Medium](https://medium.com/), [Dev.to](https://dev.to/) або особистому блогу. 4. Підтримайте проект, пожертвувавши [чашку кави](https://buymeacoff.ee/fenny). @@ -668,24 +674,24 @@ Fiber – це проект із відкритим вихідним кодом, | | Користувач | Пожертвування | | :--------------------------------------------------------- | :----------------------------------------------- | :------------ | -| ![](https://avatars.githubusercontent.com/u/204341?s=25) | [@destari](https://github.com/destari) | ☕ x 10 | -| ![](https://avatars.githubusercontent.com/u/63164982?s=25) | [@dembygenesis](https://github.com/dembygenesis) | ☕ x 5 | -| ![](https://avatars.githubusercontent.com/u/56607882?s=25) | [@thomasvvugt](https://github.com/thomasvvugt) | ☕ x 5 | -| ![](https://avatars.githubusercontent.com/u/27820675?s=25) | [@hendratommy](https://github.com/hendratommy) | ☕ x 5 | -| ![](https://avatars.githubusercontent.com/u/1094221?s=25) | [@ekaputra07](https://github.com/ekaputra07) | ☕ x 5 | -| ![](https://avatars.githubusercontent.com/u/194590?s=25) | [@jorgefuertes](https://github.com/jorgefuertes) | ☕ x 5 | -| ![](https://avatars.githubusercontent.com/u/186637?s=25) | [@candidosales](https://github.com/candidosales) | ☕ x 5 | -| ![](https://avatars.githubusercontent.com/u/29659953?s=25) | [@l0nax](https://github.com/l0nax) | ☕ x 3 | -| ![](https://avatars.githubusercontent.com/u/635852?s=25) | [@bihe](https://github.com/bihe) | ☕ x 3 | -| ![](https://avatars.githubusercontent.com/u/307334?s=25) | [@justdave](https://github.com/justdave) | ☕ x 3 | -| ![](https://avatars.githubusercontent.com/u/11155743?s=25) | [@koddr](https://github.com/koddr) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/29042462?s=25) | [@lapolinar](https://github.com/lapolinar) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/2978730?s=25) | [@diegowifi](https://github.com/diegowifi) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/44171355?s=25) | [@ssimk0](https://github.com/ssimk0) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/5638101?s=25) | [@raymayemir](https://github.com/raymayemir) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/619996?s=25) | [@melkorm](https://github.com/melkorm) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/31022056?s=25) | [@marvinjwendt](https://github.com/marvinjwendt) | ☕ x 1 | -| ![](https://avatars.githubusercontent.com/u/31921460?s=25) | [@toishy](https://github.com/toishy) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/204341?s=25) | [@destari](https://github.com/destari) | ☕ x 10 | +| ![](https://avatars.githubusercontent.com/u/63164982?s=25) | [@dembygenesis](https://github.com/dembygenesis) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/56607882?s=25) | [@thomasvvugt](https://github.com/thomasvvugt) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/27820675?s=25) | [@hendratommy](https://github.com/hendratommy) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/1094221?s=25) | [@ekaputra07](https://github.com/ekaputra07) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/194590?s=25) | [@jorgefuertes](https://github.com/jorgefuertes) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/186637?s=25) | [@candidosales](https://github.com/candidosales) | ☕ x 5 | +| ![](https://avatars.githubusercontent.com/u/29659953?s=25) | [@l0nax](https://github.com/l0nax) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/635852?s=25) | [@bihe](https://github.com/bihe) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/307334?s=25) | [@justdave](https://github.com/justdave) | ☕ x 3 | +| ![](https://avatars.githubusercontent.com/u/11155743?s=25) | [@koddr](https://github.com/koddr) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/29042462?s=25) | [@lapolinar](https://github.com/lapolinar) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/2978730?s=25) | [@diegowifi](https://github.com/diegowifi) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/44171355?s=25) | [@ssimk0](https://github.com/ssimk0) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/5638101?s=25) | [@raymayemir](https://github.com/raymayemir) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/619996?s=25) | [@melkorm](https://github.com/melkorm) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/31022056?s=25) | [@marvinjwendt](https://github.com/marvinjwendt) | ☕ x 1 | +| ![](https://avatars.githubusercontent.com/u/31921460?s=25) | [@toishy](https://github.com/toishy) | ☕ x 1 | ## ‎‍💻 Автори коду @@ -701,15 +707,15 @@ Fiber – це проект із відкритим вихідним кодом, **Ліцензії сторонніх бібліотек** -- [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) -- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) -- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) -- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) -- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) -- [msgp](https://github.com/tinylib/msgp/blob/master/LICENSE) -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [uuid](https://github.com/google/uuid/blob/master/LICENSE) -- [wmi](https://github.com/StackExchange/wmi/blob/master/LICENSE) +- [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) +- [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) +- [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) +- [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) +- [msgp](https://github.com/tinylib/msgp/blob/master/LICENSE) +- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) +- [uuid](https://github.com/google/uuid/blob/master/LICENSE) +- [wmi](https://github.com/StackExchange/wmi/blob/master/LICENSE) diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index d9122531d9..8409ade511 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -66,6 +66,12 @@ + + + + + +
@@ -145,7 +151,7 @@ go get -u github.com/gofiber/fiber/v3 - [支持 WebSocket](https://github.com/gofiber/websocket) - [Server-Sent events](https://github.com/gofiber/recipes/tree/master/sse) - [频率限制](https://docs.gofiber.io/api/middleware/limiter) -- [被翻译成 18 种语言](https://docs.gofiber.io/) +- [被翻译成 19 种语言](https://docs.gofiber.io/) - 更多请[探索文档](https://docs.gofiber.io/) ## 💡 哲学 @@ -160,7 +166,7 @@ go get -u github.com/gofiber/fiber/v3 以及在互联网上的所有诉求,为了创建一个能让有着任何技术栈的开发者都能在 deadline 前完成任务的**迅速**,**灵活**以及**友好**的 `Go web` 框架,就像 `Express` 在 `JavaScript` 世界中一样。 ## ⚠️ 限制 -* 由于 Fiber 使用了 unsafe 特性,导致其可能与最新的 Go 版本不兼容。Fiber 2.40.0 已经在 Go 1.17 到 1.20 上测试过。 +* 由于 Fiber 使用了 unsafe 特性,导致其可能与最新的 Go 版本不兼容。Fiber 2.40.0 已经在 Go 1.17 到 1.21 上测试过。 * Fiber 与 net/http 接口不兼容。也就是说你无法直接使用例如 gqlen,go-swagger 或者任何其他属于 net/http 生态的项目。 ## 👀 示例 @@ -631,7 +637,7 @@ For more articles, middlewares, examples or tools check our [awesome list](https 如果想**感谢**我们或支持 `Fiber` 的积极发展: 1. 为 [`Fiber`](https://github.com/gofiber/fiber/stargazers) 点个 ⭐ 星星。 -2. 在 [Twitter](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber) 上发布有关项目的[推文](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber)。 +2. 在 [𝕏 (Twitter)](https://x.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber) 上发布有关项目的[推文](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber)。 3. 在 [Medium](https://medium.com/),[Dev.to](https://dev.to/) 或个人博客上写评论或教程。 4. 通过捐赠[一杯咖啡](https://buymeacoff.ee/fenny)来支持本项目。 diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index 84ea8c4f5c..ceb4d0d297 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -69,6 +69,12 @@ + + + + + +
@@ -154,7 +160,7 @@ go get -u github.com/gofiber/fiber/v3 - [支援 WebSocket](https://github.com/gofiber/websocket) - [Server-Sent Events](https://github.com/gofiber/recipes/tree/master/sse) - 支援[速率限制](https://docs.gofiber.io/api/middleware/limiter) -- 有 [18 門語言](https://docs.gofiber.io/)的翻譯 +- 有 [19 門語言](https://docs.gofiber.io/)的翻譯 - 還有很多功能,[開始探索 Fiber](https://docs.gofiber.io/) ## 💡 設計哲學 @@ -167,7 +173,7 @@ Fiber **啟發自** Express——網際網路上最知名的 Web 框架,我們 ## ⚠️ 限制 -- 由於 Fiber 有用到 Unsafe,本函式庫有時可能無法相容最新版的 Go 語言。Fiber 2.40.0 已在 Go 1.17 至 1.20 的版本測試過。 +- 由於 Fiber 有用到 Unsafe,本函式庫有時可能無法相容最新版的 Go 語言。Fiber 2.40.0 已在 Go 1.17 至 1.21 的版本測試過。 - Fiber 不相容 net/http 的介面,意味著您無法使用像是 gqlgen、go-swagger 或其他任何屬於 net/http 生態系統的專案。 ## 👀 範例 @@ -636,22 +642,22 @@ func main() { | [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | 連線階段中介模組。注意:這個中介模組有用到我們的 Storage 套件。 | | [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | 略過中介模組,會在條件成立時略過封裝過的處理常式。 | | [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | 為請求加上最長時限,並在逾時後轉送至錯誤處理常式 (ErrorHandler)。 | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth 中介模組提供以金鑰為基礎的認證模式。 | -| [redirect](https://github.com/gofiber/redirect) | 用來重新導向的中介模組。 | -| [rewrite](https://github.com/gofiber/rewrite) | 重寫 (Rewrite) 中介模組:根據提供規則重寫 URL 路徑,適合用來向後相容,或者是製作更乾淨且更好懂的連結。 | -| [adaptor](https://github.com/gofiber/adaptor) | 將 net/http 處理常式轉換至 Fiber 處理常式,或者是反著做。特別感謝 @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | 透過設定多種 HTTP 標頭,協助保護您應用程式的安全。 | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth 中介模組提供以金鑰為基礎的認證模式。 | +| [redirect](https://github.com/gofiber/redirect) | 用來重新導向的中介模組。 | +| [rewrite](https://github.com/gofiber/rewrite) | 重寫 (Rewrite) 中介模組:根據提供規則重寫 URL 路徑,適合用來向後相容,或者是製作更乾淨且更好懂的連結。 | +| [adaptor](https://github.com/gofiber/adaptor) | 將 net/http 處理常式轉換至 Fiber 處理常式,或者是反著做。特別感謝 @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | 透過設定多種 HTTP 標頭,協助保護您應用程式的安全。 | ## 🧬 外掛中介模組 這裡列出由 [Fiber 團隊](https://github.com/orgs/gofiber/people) 維護、存放在外部的中介模組。 -| 中介模組 | 描述 | -| :------------------------------------------------ | :----------------------------------------------------------------------------------------------------- | -| [jwt](https://github.com/gofiber/jwt) | JWT 回傳 JSON Web Token \(JWT\) 認證中介模組。 | -| [storage](https://github.com/gofiber/storage) | 已經做好,實作 Storage 介面的儲存區驅動模組,設計用來與各種 Fiber 中介模組搭配使用。 | -| [template](https://github.com/gofiber/template) | 本套件包含 8 種樣板引擎,可以和 Fiber `v1.10.x` 一起使用。需要 Go 1.13 或更新版本。 | -| [websocket](https://github.com/gofiber/websocket) | 適用於 Fiber,建基於 Fasthttp 的 WebSocket。支援本機空間 (Locals)! | +| 中介模組 | 描述 | +| :------------------------------------------------ | :----------------------------------------------------------------------------------- | +| [jwt](https://github.com/gofiber/jwt) | JWT 回傳 JSON Web Token \(JWT\) 認證中介模組。 | +| [storage](https://github.com/gofiber/storage) | 已經做好,實作 Storage 介面的儲存區驅動模組,設計用來與各種 Fiber 中介模組搭配使用。 | +| [template](https://github.com/gofiber/template) | 本套件包含 8 種樣板引擎,可以和 Fiber `v1.10.x` 一起使用。需要 Go 1.13 或更新版本。 | +| [websocket](https://github.com/gofiber/websocket) | 適用於 Fiber,建基於 Fasthttp 的 WebSocket。支援本機空間 (Locals)! | ## 🕶️ Awesome List @@ -662,7 +668,7 @@ func main() { 如果您想和我們 **道謝**,或者是支持 `Fiber` 繼續積極開發下去(也可以兩個都做): 1. 送給專案一顆 [GitHub 星星](https://github.com/gofiber/fiber/stargazers)。 -2. [在您的 Twitter 上](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber)發出關於本專案的推文。 +2. [在您的 𝕏 (Twitter) 上](https://x.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber)發出關於本專案的推文。 3. 在 [Medium](https://medium.com/)、[Dev.to](https://dev.to/) 或者是個人部落格上寫下評論或教學。 4. 捐專案 [一杯咖啡的費用](https://buymeacoff.ee/fenny) 以示支持。 diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 134447d7bc..ebc7c6f96c 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -1,37 +1,44 @@ name-template: 'v$RESOLVED_VERSION' tag-template: 'v$RESOLVED_VERSION' categories: - - title: '🚀 New' - labels: - - '✏️ Feature' - - title: '🧹 Updates' - labels: - - '🧹 Updates' - - '🤖 Dependencies' - - title: '🐛 Fixes' - labels: - - '☢️ Bug' - - title: '📚 Documentation' - labels: - - '📒 Documentation' + - title: '❗ Breaking Changes' + labels: + - '❗ BreakingChange' + - title: '🚀 New' + labels: + - '✏️ Feature' + - title: '🧹 Updates' + labels: + - '🧹 Updates' + - '🤖 Dependencies' + - title: '🐛 Fixes' + labels: + - '☢️ Bug' + - title: '📚 Documentation' + labels: + - '📒 Documentation' change-template: '- $TITLE (#$NUMBER)' change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. +exclude-contributors: + - dependabot + - dependabot[bot] version-resolver: - major: - labels: - - 'major' - minor: - labels: - - 'minor' - - '✏️ Feature' - patch: - labels: - - 'patch' - - '📒 Documentation' - - '☢️ Bug' - - '🤖 Dependencies' - - '🧹 Updates' - default: patch + major: + labels: + - 'major' + - '❗ BreakingChange' + minor: + labels: + - 'minor' + - '✏️ Feature' + patch: + labels: + - 'patch' + - '📒 Documentation' + - '☢️ Bug' + - '🤖 Dependencies' + - '🧹 Updates' + default: patch template: | $CHANGES diff --git a/.github/scripts/sync_docs.sh b/.github/scripts/sync_docs.sh index 4b58a1e128..a05e1ab238 100755 --- a/.github/scripts/sync_docs.sh +++ b/.github/scripts/sync_docs.sh @@ -36,12 +36,15 @@ elif [ "$EVENT" == "release" ]; then # Check if contrib_versions.json exists and modify it if required if [[ -f $VERSION_FILE ]]; then - jq --arg new_version "$new_version" 'del(.[] | select(. == $new_version))' $VERSION_FILE >temp.json && mv temp.json $VERSION_FILE - jq -S . ${VERSION_FILE} >temp.json && mv temp.json ${VERSION_FILE} + jq --arg new_version "$new_version" 'del(.[] | select(. == $new_version))' $VERSION_FILE > temp.json && mv temp.json $VERSION_FILE fi # Run docusaurus versioning command $DOCUSAURUS_COMMAND "${new_version}" + + if [[ -f $VERSION_FILE ]]; then + jq 'sort | reverse' ${VERSION_FILE} > temp.json && mv temp.json ${VERSION_FILE} + fi fi # Push changes diff --git a/.github/workflows/auto-labeler.yml b/.github/workflows/auto-labeler.yml index ef7299117e..3a18ed8aaa 100644 --- a/.github/workflows/auto-labeler.yml +++ b/.github/workflows/auto-labeler.yml @@ -1,21 +1,23 @@ name: Auto labeler + on: - issues: - types: [ opened, edited, milestoned ] - pull_request_target: - types: [ opened ] + issues: + types: [opened, edited, milestoned] + pull_request_target: + types: [opened] permissions: contents: read issues: write pull-requests: write statuses: write checks: write + jobs: - labeler: - runs-on: ubuntu-latest - steps: - - name: Check Labels - id: labeler - uses: fuxingloh/multi-labeler@v2 - with: - github-token: ${{secrets.GITHUB_TOKEN}} + labeler: + runs-on: ubuntu-latest + steps: + - name: Check Labels + id: labeler + uses: fuxingloh/multi-labeler@v2 + with: + github-token: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index a751eae918..641651bcd2 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -4,45 +4,50 @@ on: - master - main paths: - - '**' - - '!docs/**' - - '!**.md' + - "**" + - "!docs/**" + - "!**.md" pull_request: paths: - - '**' - - '!docs/**' - - '!**.md' + - "**" + - "!docs/**" + - "!**.md" + name: Benchmark jobs: Compare: runs-on: ubuntu-latest steps: - - name: Install Go - uses: actions/setup-go@v4 - with: - go-version: 1.20.x - - name: Fetch Repository - uses: actions/checkout@v3 - - name: Run Benchmark - run: set -o pipefail; go test ./... -benchmem -run=^$ -bench . | tee output.txt - - name: Get Previous Benchmark Results - uses: actions/cache@v3 - with: - path: ./cache - # TODO: reactivate it later -> when v3 is the stable one - key: ${{ runner.os }}-benchmark-v3 - - name: Save Benchmark Results - uses: benchmark-action/github-action-benchmark@v1.16.2 - with: - tool: 'go' - output-file-path: output.txt - github-token: ${{ secrets.BENCHMARK_TOKEN }} - benchmark-data-dir-path: 'benchmarks' - fail-on-alert: true - comment-on-alert: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} - # Enable Job Summary for PRs - deactivated because of issues - #summary-always: ${{ github.event_name != 'push' && github.event_name != 'workflow_dispatch' }} - # TODO: reactivate it later -> when v3 is the stable one - #auto-push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} - auto-push: false - save-data-file: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} + - name: Fetch Repository + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v4 + with: + # NOTE: Keep this in sync with the version from go.mod + go-version: "1.20.x" + + - name: Run Benchmark + run: set -o pipefail; go test ./... -benchmem -run=^$ -bench . | tee output.txt + + - name: Get Previous Benchmark Results + uses: actions/cache@v3 + with: + path: ./cache + key: ${{ runner.os }}-benchmark + + - name: Save Benchmark Results + uses: benchmark-action/github-action-benchmark@v1.16.2 + with: + tool: "go" + output-file-path: output.txt + github-token: ${{ secrets.BENCHMARK_TOKEN }} + benchmark-data-dir-path: "benchmarks" + fail-on-alert: true + comment-on-alert: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} + # Enable Job Summary for PRs - deactivated because of issues + #summary-always: ${{ github.event_name != 'push' && github.event_name != 'workflow_dispatch' }} + # TODO: reactivate it later -> when v3 is the stable one + #auto-push: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} + auto-push: false + save-data-file: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e95e715d50..738f01ef67 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -7,16 +7,16 @@ on: - main - v3-beta paths: - - '**' - - '!docs/**' - - '!**.md' + - "**" + - "!docs/**" + - "!**.md" pull_request: paths: - - '**' - - '!docs/**' - - '!**.md' + - "**" + - "!docs/**" + - "!**.md" schedule: - - cron: '0 3 * * 6' + - cron: "0 3 * * 6" jobs: analyse: @@ -24,40 +24,40 @@ jobs: runs-on: ubuntu-latest steps: - - name: Checkout repository - uses: actions/checkout@v3 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - # Override language selection by uncommenting this and choosing your languages - with: - languages: go - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 https://git.io/JvXDl - - # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines - # and modify them (or add more) to build your code if your project - # uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + - name: Checkout repository + uses: actions/checkout@v4 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + # Override language selection by uncommenting this and choosing your languages + with: + languages: go + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index fa43748571..a33af1f814 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -10,16 +10,19 @@ on: pull_request: permissions: contents: read + jobs: golangci: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-go@v4 with: # NOTE: Keep this in sync with the version from go.mod - go-version: 1.20.x + go-version: "1.20.x" + - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index f4a5cea982..cedaab432f 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -1,26 +1,26 @@ name: Release Drafter on: - push: - # branches to consider in the event; optional, defaults to all - branches: - - master - - main + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + - main jobs: - update_release_draft: - runs-on: ubuntu-latest - steps: - # (Optional) GitHub Enterprise requires GHE_HOST variable set - #- name: Set GHE_HOST - # run: | - # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV + update_release_draft: + runs-on: ubuntu-latest + steps: + # (Optional) GitHub Enterprise requires GHE_HOST variable set + #- name: Set GHE_HOST + # run: | + # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV - # Drafts your next Release notes as Pull Requests are merged into "master" - - uses: release-drafter/release-drafter@v5 - # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml - # with: - # config-name: my-config.yml - # disable-autolabeler: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + # with: + # config-name: my-config.yml + # disable-autolabeler: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/sync-docs.yml b/.github/workflows/sync-docs.yml index c7f895de6a..eb35d75bd2 100644 --- a/.github/workflows/sync-docs.yml +++ b/.github/workflows/sync-docs.yml @@ -1,4 +1,4 @@ -name: 'Sync docs' +name: "Sync docs" on: push: @@ -6,26 +6,24 @@ on: - master - main paths: - - 'docs/**' + - "docs/**" release: - types: [ published ] + types: [published] jobs: sync-docs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 2 + - name: Setup Node.js environment - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: - node-version: '18' - - - name: Install JQ - run: sudo apt-get install jq + node-version: "18" - name: Sync docs run: ./.github/scripts/sync_docs.sh diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index da1c72d31a..69cd97abb4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,32 +5,35 @@ on: - main - v3-beta paths: - - '**' - - '!docs/**' - - '!**.md' + - "**" + - "!docs/**" + - "!**.md" pull_request: paths: - - '**' - - '!docs/**' - - '!**.md' + - "**" + - "!docs/**" + - "!**.md" + name: Test jobs: Build: strategy: matrix: - go-version: [1.19.x, 1.20.x] + go-version: [1.20.x, 1.21.x] platform: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: - - name: Fetch Repository - uses: actions/checkout@v3 - - name: Install Go - uses: actions/setup-go@v4 - with: - go-version: ${{ matrix.go-version }} - - name: Run Test - uses: nick-fields/retry@v2 - with: - max_attempts: 3 - timeout_minutes: 15 - command: go test ./... -v -race -count=1 + - name: Fetch Repository + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} + + - name: Run Test + uses: nick-fields/retry@v2 + with: + max_attempts: 3 + timeout_minutes: 15 + command: go test ./... -v -race -count=1 diff --git a/.github/workflows/vulncheck.yml b/.github/workflows/vulncheck.yml index 4c13423020..85e96fdd00 100644 --- a/.github/workflows/vulncheck.yml +++ b/.github/workflows/vulncheck.yml @@ -1,19 +1,19 @@ name: Run govulncheck on: - push: - branches: - - master - - main - paths: - - '**' - - '!docs/**' - - '!**.md' - pull_request: - paths: - - '**' - - '!docs/**' - - '!**.md' + push: + branches: + - master + - main + paths: + - "**" + - "!docs/**" + - "!**.md" + pull_request: + paths: + - "**" + - "!docs/**" + - "!**.md" jobs: govulncheck-check: @@ -21,15 +21,18 @@ jobs: env: GO111MODULE: on steps: - - name: Fetch Repository - uses: actions/checkout@v3 - - name: Install Go - uses: actions/setup-go@v4 - with: - go-version: 'stable' - check-latest: true - cache: false - - name: Install Govulncheck - run: go install golang.org/x/vuln/cmd/govulncheck@latest - - name: Run Govulncheck - run: govulncheck ./... \ No newline at end of file + - name: Fetch Repository + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: "stable" + check-latest: true + cache: false + + - name: Install Govulncheck + run: go install golang.org/x/vuln/cmd/govulncheck@latest + + - name: Run Govulncheck + run: govulncheck ./... diff --git a/app.go b/app.go index 78b03af5bd..5f0007a35f 100644 --- a/app.go +++ b/app.go @@ -23,7 +23,7 @@ import ( "sync" "time" - "github.com/gofiber/fiber/v2/log" + "github.com/gofiber/fiber/v3/log" "github.com/gofiber/utils/v2" "github.com/valyala/fasthttp" @@ -374,6 +374,13 @@ type Config struct { // // Optional. Default: DefaultMethods RequestMethods []string + + // EnableSplittingOnParsers splits the query/body/header parameters by comma when it's true. + // For example, you can use it to parse multiple values from a query parameter like this: + // /api?foo=bar,baz == foo[]=bar&foo[]=baz + // + // Optional. Default: false + EnableSplittingOnParsers bool `json:"enable_splitting_on_parsers"` } // Static defines configuration options when defining static assets. @@ -602,9 +609,11 @@ func (app *App) Name(name string) Router { for _, routes := range app.stack { for _, route := range routes { - if route.Path == app.latestRoute.path { - route.Name = name + isMethodValid := route.Method == app.latestRoute.Method || app.latestRoute.use || + (app.latestRoute.Method == MethodGet && route.Method == MethodHead) + if route.Path == app.latestRoute.Path && isMethodValid { + route.Name = name if route.group != nil { route.Name = route.group.name + route.Name } diff --git a/app_test.go b/app_test.go index c50df64ae0..7efe961725 100644 --- a/app_test.go +++ b/app_test.go @@ -754,9 +754,7 @@ func Test_App_Shutdown(t *testing.T) { t.Parallel() app := &App{} if err := app.Shutdown(); err != nil { - if err.Error() != "shutdown: server is not running" { - t.Fatal() - } + require.Equal(t, "shutdown: server is not running", err.Error()) } }) } @@ -1422,7 +1420,6 @@ func (invalidView) Render(io.Writer, string, any, ...string) error { panic("impl // go test -run Test_App_Init_Error_View func Test_App_Init_Error_View(t *testing.T) { - t.Parallel() app := New(Config{Views: invalidView{}}) defer func() { @@ -1578,7 +1575,6 @@ func Test_App_Server(t *testing.T) { } func Test_App_Error_In_Fasthttp_Server(t *testing.T) { - t.Parallel() app := New() app.config.ErrorHandler = func(c Ctx, err error) error { return errors.New("fake error") @@ -1820,3 +1816,79 @@ func Test_Middleware_Route_Naming_With_Use(t *testing.T) { } } } + +func Test_Route_Naming_Issue_2671_2685(t *testing.T) { + app := New() + + app.Get("/", emptyHandler).Name("index") + require.Equal(t, "/", app.GetRoute("index").Path) + + app.Get("/a/:a_id", emptyHandler).Name("a") + require.Equal(t, "/a/:a_id", app.GetRoute("a").Path) + + app.Post("/b/:bId", emptyHandler).Name("b") + require.Equal(t, "/b/:bId", app.GetRoute("b").Path) + + c := app.Group("/c") + c.Get("", emptyHandler).Name("c.get") + require.Equal(t, "/c", app.GetRoute("c.get").Path) + + c.Post("", emptyHandler).Name("c.post") + require.Equal(t, "/c", app.GetRoute("c.post").Path) + + c.Get("/d", emptyHandler).Name("c.get.d") + require.Equal(t, "/c/d", app.GetRoute("c.get.d").Path) + + d := app.Group("/d/:d_id") + d.Get("", emptyHandler).Name("d.get") + require.Equal(t, "/d/:d_id", app.GetRoute("d.get").Path) + + d.Post("", emptyHandler).Name("d.post") + require.Equal(t, "/d/:d_id", app.GetRoute("d.post").Path) + + e := app.Group("/e/:eId") + e.Get("", emptyHandler).Name("e.get") + require.Equal(t, "/e/:eId", app.GetRoute("e.get").Path) + + e.Post("", emptyHandler).Name("e.post") + require.Equal(t, "/e/:eId", app.GetRoute("e.post").Path) + + e.Get("f", emptyHandler).Name("e.get.f") + require.Equal(t, "/e/:eId/f", app.GetRoute("e.get.f").Path) + + postGroup := app.Group("/post/:postId") + postGroup.Get("", emptyHandler).Name("post.get") + require.Equal(t, "/post/:postId", app.GetRoute("post.get").Path) + + postGroup.Post("", emptyHandler).Name("post.update") + require.Equal(t, "/post/:postId", app.GetRoute("post.update").Path) + + // Add testcase for routes use the same PATH on different methods + app.Get("/users", emptyHandler).Name("get-users") + app.Post("/users", emptyHandler).Name("add-user") + getUsers := app.GetRoute("get-users") + require.Equal(t, getUsers.Path, "/users") + + addUser := app.GetRoute("add-user") + require.Equal(t, addUser.Path, "/users") + + // Add testcase for routes use the same PATH on different methods (for groups) + newGrp := app.Group("/name-test") + newGrp.Get("/users", emptyHandler).Name("grp-get-users") + newGrp.Post("/users", emptyHandler).Name("grp-add-user") + getUsers = app.GetRoute("grp-get-users") + require.Equal(t, getUsers.Path, "/name-test/users") + + addUser = app.GetRoute("grp-add-user") + require.Equal(t, addUser.Path, "/name-test/users") + + // Add testcase for HEAD route naming + app.Get("/simple-route", emptyHandler).Name("simple-route") + app.Head("/simple-route", emptyHandler).Name("simple-route2") + + sRoute := app.GetRoute("simple-route") + require.Equal(t, sRoute.Path, "/simple-route") + + sRoute2 := app.GetRoute("simple-route2") + require.Equal(t, sRoute2.Path, "/simple-route") +} diff --git a/ctx.go b/ctx.go index 9eec222191..2ffcf12874 100644 --- a/ctx.go +++ b/ctx.go @@ -8,7 +8,6 @@ import ( "bytes" "context" "crypto/tls" - "encoding/json" "fmt" "io" "mime/multipart" @@ -65,8 +64,9 @@ type TLSHandler struct { clientHelloInfo *tls.ClientHelloInfo } -// GetClientInfo Callback function to set CHI -// TODO: Why is this a getter which sets stuff? +// GetClientInfo Callback function to set ClientHelloInfo +// Must comply with the method structure of https://cs.opensource.google/go/go/+/refs/tags/go1.20:src/crypto/tls/common.go;l=554-563 +// Since we overlay the method of the tls config in the listener method func (t *TLSHandler) GetClientInfo(info *tls.ClientHelloInfo) (*tls.Certificate, error) { t.clientHelloInfo = info return nil, nil //nolint:nilnil // Not returning anything useful here is probably fine @@ -170,31 +170,92 @@ func (c *DefaultCtx) BaseURL() string { return c.baseURI } -// Body contains the raw body submitted in a POST request. +// BodyRaw contains the raw body submitted in a POST request. // Returned value is only valid within the handler. Do not store any references. // Make copies or use the Immutable setting instead. +func (c *DefaultCtx) BodyRaw() []byte { + return c.fasthttp.Request.Body() +} + +func (c *DefaultCtx) tryDecodeBodyInOrder( + originalBody *[]byte, + encodings []string, +) ([]byte, uint8, error) { + var ( + err error + body []byte + decodesRealized uint8 + ) + + for index, encoding := range encodings { + decodesRealized++ + switch encoding { + case StrGzip: + body, err = c.fasthttp.Request.BodyGunzip() + case StrBr, StrBrotli: + body, err = c.fasthttp.Request.BodyUnbrotli() + case StrDeflate: + body, err = c.fasthttp.Request.BodyInflate() + default: + decodesRealized-- + if len(encodings) == 1 { + body = c.fasthttp.Request.Body() + } + return body, decodesRealized, nil + } + + if err != nil { + return nil, decodesRealized, err + } + + // Only execute body raw update if it has a next iteration to try to decode + if index < len(encodings)-1 && decodesRealized > 0 { + if index == 0 { + tempBody := c.fasthttp.Request.Body() + *originalBody = make([]byte, len(tempBody)) + copy(*originalBody, tempBody) + } + c.fasthttp.Request.SetBodyRaw(body) + } + } + + return body, decodesRealized, nil +} + +// Body contains the raw body submitted in a POST request. +// This method will decompress the body if the 'Content-Encoding' header is provided. +// It returns the original (or decompressed) body data which is valid only within the handler. +// Don't store direct references to the returned data. +// If you need to keep the body's data later, make a copy or use the Immutable option. func (c *DefaultCtx) Body() []byte { - var err error - var encoding string - var body []byte + var ( + err error + body, originalBody []byte + headerEncoding string + encodingOrder = []string{"", "", ""} + ) + // faster than peek c.Request().Header.VisitAll(func(key, value []byte) { if c.app.getString(key) == HeaderContentEncoding { - encoding = c.app.getString(value) + headerEncoding = c.app.getString(value) } }) - switch encoding { - case StrGzip: - body, err = c.fasthttp.Request.BodyGunzip() - case StrBr, StrBrotli: - body, err = c.fasthttp.Request.BodyUnbrotli() - case StrDeflate: - body, err = c.fasthttp.Request.BodyInflate() - default: - body = c.fasthttp.Request.Body() + // Split and get the encodings list, in order to attend the + // rule defined at: https://www.rfc-editor.org/rfc/rfc9110#section-8.4-5 + encodingOrder = getSplicedStrList(headerEncoding, encodingOrder) + if len(encodingOrder) == 0 { + return c.fasthttp.Request.Body() } + var decodesRealized uint8 + body, decodesRealized, err = c.tryDecodeBodyInOrder(&originalBody, encodingOrder) + + // Ensure that the body will be the original + if originalBody != nil && decodesRealized > 0 { + c.fasthttp.Request.SetBodyRaw(originalBody) + } if err != nil { return []byte(err.Error()) } @@ -512,7 +573,7 @@ iploop: j++ } - for i < j && headerValue[i] == ' ' { + for i < j && (headerValue[i] == ' ' || headerValue[i] == ',') { i++ } @@ -624,9 +685,9 @@ func (c *DefaultCtx) JSON(data any) error { // This method is identical to JSON, except that it opts-in to JSONP callback support. // By default, the callback name is simply callback. func (c *DefaultCtx) JSONP(data any, callback ...string) error { - raw, err := json.Marshal(data) + raw, err := c.app.config.JSONEncoder(data) if err != nil { - return fmt.Errorf("failed to marshal: %w", err) + return err } var result, cb string @@ -690,17 +751,24 @@ func (c *DefaultCtx) Location(path string) { c.setCanonical(HeaderLocation, path) } -// Method contains a string corresponding to the HTTP method of the request: GET, POST, PUT and so on. +// Method returns the HTTP request method for the context, optionally overridden by the provided argument. +// If no override is given or if the provided override is not a valid HTTP method, it returns the current method from the context. +// Otherwise, it updates the context's method and returns the overridden method as a string. func (c *DefaultCtx) Method(override ...string) string { - if len(override) > 0 { - method := utils.ToUpper(override[0]) - mINT := c.app.methodInt(method) - if mINT == -1 { - return c.method - } - c.method = method - c.methodINT = mINT + if len(override) == 0 { + // Nothing to override, just return current method from context + return c.method + } + + method := utils.ToUpper(override[0]) + mINT := c.app.methodInt(method) + if mINT == -1 { + // Provided override does not valid HTTP method, no override, return current method + return c.method } + + c.method = method + c.methodINT = mINT return c.method } @@ -1073,6 +1141,11 @@ func (c *DefaultCtx) Render(name string, bind Map, layouts ...string) error { buf := bytebufferpool.Get() defer bytebufferpool.Put(buf) + // Initialize empty bind map if bind is nil + if bind == nil { + bind = make(Map) + } + // Pass-locals-to-views, bind, appListKeys c.renderExtensions(bind) @@ -1467,11 +1540,12 @@ func (c *DefaultCtx) IsProxyTrusted() bool { return false } +var localHosts = [...]string{"127.0.0.1", "::1"} + // IsLocalHost will return true if address is a localhost address. func (*DefaultCtx) isLocalHost(address string) bool { - localHosts := []string{"127.0.0.1", "0.0.0.0", "::1"} for _, h := range localHosts { - if strings.Contains(address, h) { + if address == h { return true } } @@ -1480,11 +1554,7 @@ func (*DefaultCtx) isLocalHost(address string) bool { // IsFromLocal will return true if request came from local. func (c *DefaultCtx) IsFromLocal() bool { - ips := c.IPs() - if len(ips) == 0 { - ips = append(ips, c.IP()) - } - return c.isLocalHost(ips[0]) + return c.isLocalHost(c.fasthttp.RemoteIP().String()) } // You can bind body, cookie, headers etc. into the map, map slice, struct easily by using Binding method. diff --git a/ctx_interface.go b/ctx_interface.go index 4478a041dd..af2317ffc4 100644 --- a/ctx_interface.go +++ b/ctx_interface.go @@ -227,6 +227,29 @@ type Ctx interface { // Make copies or use the Immutable setting to use the value outside the Handler. Query(key string, defaultValue ...string) string + // Queries returns a map of query parameters and their values. + // + // GET /?name=alex&wanna_cake=2&id= + // Queries()["name"] == "alex" + // Queries()["wanna_cake"] == "2" + // Queries()["id"] == "" + // + // GET /?field1=value1&field1=value2&field2=value3 + // Queries()["field1"] == "value2" + // Queries()["field2"] == "value3" + // + // GET /?list_a=1&list_a=2&list_a=3&list_b[]=1&list_b[]=2&list_b[]=3&list_c=1,2,3 + // Queries()["list_a"] == "3" + // Queries()["list_b[]"] == "3" + // Queries()["list_c"] == "1,2,3" + // + // GET /api/search?filters.author.name=John&filters.category.name=Technology&filters[customer][name]=Alice&filters[status]=pending + // Queries()["filters.author.name"] == "John" + // Queries()["filters.category.name"] == "Technology" + // Queries()["filters[customer][name]"] == "Alice" + // Queries()["filters[status]"] == "pending" + Queries() map[string]string + // QueryInt returns integer value of key string parameter in the url. // Default to empty or invalid key is 0. // diff --git a/ctx_test.go b/ctx_test.go index a317a114ae..3debea8291 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -9,6 +9,7 @@ import ( "bufio" "bytes" "compress/gzip" + "compress/zlib" "context" "crypto/tls" "encoding/xml" @@ -16,6 +17,7 @@ import ( "fmt" "io" "mime/multipart" + "net" "net/http/httptest" "os" "path/filepath" @@ -345,53 +347,213 @@ func Benchmark_Ctx_BaseURL(b *testing.B) { func Test_Ctx_Body(t *testing.T) { t.Parallel() app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed c.Request().SetBody([]byte("john=doe")) require.Equal(t, []byte("john=doe"), c.Body()) } +func Benchmark_Ctx_Body(b *testing.B) { + const input = "john=doe" + + app := New() + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + + c.Request().SetBody([]byte(input)) + for i := 0; i < b.N; i++ { + _ = c.Body() + } + + require.Equal(b, []byte(input), c.Body()) +} + // go test -run Test_Ctx_Body_With_Compression func Test_Ctx_Body_With_Compression(t *testing.T) { t.Parallel() - app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + tests := []struct { + name string + contentEncoding string + body []byte + expectedBody []byte + }{ + { + name: "gzip", + contentEncoding: "gzip", + body: []byte("john=doe"), + expectedBody: []byte("john=doe"), + }, + { + name: "unsupported_encoding", + contentEncoding: "undefined", + body: []byte("keeps_ORIGINAL"), + expectedBody: []byte("keeps_ORIGINAL"), + }, + { + name: "gzip then unsupported", + contentEncoding: "gzip, undefined", + body: []byte("Go, be gzipped"), + expectedBody: []byte("Go, be gzipped"), + }, + { + name: "invalid_deflate", + contentEncoding: "gzip,deflate", + body: []byte("I'm not correctly compressed"), + expectedBody: []byte(zlib.ErrHeader.Error()), + }, + } - c.Request().Header.Set("Content-Encoding", "gzip") - var b bytes.Buffer - gz := gzip.NewWriter(&b) - _, err := gz.Write([]byte("john=doe")) - require.NoError(t, err) - err = gz.Flush() - require.NoError(t, err) - err = gz.Close() - require.NoError(t, err) - c.Request().SetBody(b.Bytes()) - require.Equal(t, []byte("john=doe"), c.Body()) + for _, testObject := range tests { + tCase := testObject // Duplicate object to ensure it will be unique across all runs + t.Run(tCase.name, func(t *testing.T) { + app := New() + c := app.NewCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed + c.Request().Header.Set("Content-Encoding", tCase.contentEncoding) + + if strings.Contains(tCase.contentEncoding, "gzip") { + var b bytes.Buffer + gz := gzip.NewWriter(&b) + + _, err := gz.Write(tCase.body) + require.NoError(t, err) + + err = gz.Flush() + require.NoError(t, err) + + err = gz.Close() + require.NoError(t, err) + tCase.body = b.Bytes() + } + + c.Request().SetBody(tCase.body) + body := c.Body() + require.Equal(t, tCase.expectedBody, body) + + // Check if body raw is the same as previous before decompression + require.Equal( + t, tCase.body, c.Request().Body(), + "Body raw must be the same as set before", + ) + }) + } } // go test -v -run=^$ -bench=Benchmark_Ctx_Body_With_Compression -benchmem -count=4 func Benchmark_Ctx_Body_With_Compression(b *testing.B) { - app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) + encodingErr := errors.New("failed to encoding data") - c.Request().Header.Set("Content-Encoding", "gzip") - var buf bytes.Buffer - gz := gzip.NewWriter(&buf) - _, err := gz.Write([]byte("john=doe")) - require.NoError(b, err) - err = gz.Flush() - require.NoError(b, err) - err = gz.Close() - require.NoError(b, err) + var ( + compressGzip = func(data []byte) ([]byte, error) { + var buf bytes.Buffer + writer := gzip.NewWriter(&buf) + if _, err := writer.Write(data); err != nil { + return nil, encodingErr + } + if err := writer.Flush(); err != nil { + return nil, encodingErr + } + if err := writer.Close(); err != nil { + return nil, encodingErr + } + return buf.Bytes(), nil + } + compressDeflate = func(data []byte) ([]byte, error) { + var buf bytes.Buffer + writer := zlib.NewWriter(&buf) + if _, err := writer.Write(data); err != nil { + return nil, encodingErr + } + if err := writer.Flush(); err != nil { + return nil, encodingErr + } + if err := writer.Close(); err != nil { + return nil, encodingErr + } + return buf.Bytes(), nil + } + ) + compressionTests := []struct { + contentEncoding string + compressWriter func([]byte) ([]byte, error) + }{ + { + contentEncoding: "gzip", + compressWriter: compressGzip, + }, + { + contentEncoding: "gzip,invalid", + compressWriter: compressGzip, + }, + { + contentEncoding: "deflate", + compressWriter: compressDeflate, + }, + { + contentEncoding: "gzip,deflate", + compressWriter: func(data []byte) ([]byte, error) { + var ( + buf bytes.Buffer + writer interface { + io.WriteCloser + Flush() error + } + err error + ) + + // deflate + { + writer = zlib.NewWriter(&buf) + if _, err = writer.Write(data); err != nil { + return nil, encodingErr + } + if err = writer.Flush(); err != nil { + return nil, encodingErr + } + if err = writer.Close(); err != nil { + return nil, encodingErr + } + } - c.Request().SetBody(buf.Bytes()) + data = make([]byte, buf.Len()) + copy(data, buf.Bytes()) + buf.Reset() + + // gzip + { + writer = gzip.NewWriter(&buf) + if _, err = writer.Write(data); err != nil { + return nil, encodingErr + } + if err = writer.Flush(); err != nil { + return nil, encodingErr + } + if err = writer.Close(); err != nil { + return nil, encodingErr + } + } - for i := 0; i < b.N; i++ { - _ = c.Body() + return buf.Bytes(), nil + }, + }, } - require.Equal(b, []byte("john=doe"), c.Body()) + for _, ct := range compressionTests { + b.Run(ct.contentEncoding, func(b *testing.B) { + app := New() + const input = "john=doe" + c := app.NewCtx(&fasthttp.RequestCtx{}) + + c.Request().Header.Set("Content-Encoding", ct.contentEncoding) + compressedBody, err := ct.compressWriter([]byte(input)) + require.NoError(b, err) + + c.Request().SetBody(compressedBody) + for i := 0; i < b.N; i++ { + _ = c.Body() + } + + require.Equal(b, []byte(input), c.Body()) + }) + } } // go test -run Test_Ctx_Context @@ -2359,6 +2521,25 @@ func Test_Ctx_JSON(t *testing.T) { testEmpty("", `""`) testEmpty(0, "0") testEmpty([]int{}, "[]") + + t.Run("custom json encoder", func(t *testing.T) { + t.Parallel() + + app := New(Config{ + JSONEncoder: func(v interface{}) ([]byte, error) { + return []byte(`["custom","json"]`), nil + }, + }) + c := app.NewCtx(&fasthttp.RequestCtx{}) + + err := c.JSON(Map{ // map has no order + "Name": "Grame", + "Age": 20, + }) + require.NoError(t, err) + require.Equal(t, `["custom","json"]`, string(c.Response().Body())) + require.Equal(t, "application/json", string(c.Response().Header.Peek("content-type"))) + }) } // go test -run=^$ -bench=Benchmark_Ctx_JSON -benchmem -count=4 @@ -2407,6 +2588,25 @@ func Test_Ctx_JSONP(t *testing.T) { require.NoError(t, err) require.Equal(t, `john({"Age":20,"Name":"Grame"});`, string(c.Response().Body())) require.Equal(t, "text/javascript; charset=utf-8", string(c.Response().Header.Peek("content-type"))) + + t.Run("custom json encoder", func(t *testing.T) { + t.Parallel() + + app := New(Config{ + JSONEncoder: func(v interface{}) ([]byte, error) { + return []byte(`["custom","json"]`), nil + }, + }) + c := app.NewCtx(&fasthttp.RequestCtx{}) + + err := c.JSONP(Map{ // map has no order + "Name": "Grame", + "Age": 20, + }) + require.NoError(t, err) + require.Equal(t, `callback(["custom","json"]);`, string(c.Response().Body())) + 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 @@ -2465,6 +2665,32 @@ func Test_Ctx_XML(t *testing.T) { testEmpty("", ``) testEmpty(0, "0") testEmpty([]int{}, "") + + t.Run("custom xml encoder", func(t *testing.T) { + t.Parallel() + + app := New(Config{ + XMLEncoder: func(v interface{}) ([]byte, error) { + return []byte(`xml`), nil + }, + }) + c := app.NewCtx(&fasthttp.RequestCtx{}) + + type xmlResult struct { + XMLName xml.Name `xml:"Users"` + Names []string `xml:"Names"` + Ages []int `xml:"Ages"` + } + + err := c.XML(xmlResult{ + Names: []string{"Grame", "John"}, + Ages: []int{1, 12, 20}, + }) + + require.NoError(t, err) + require.Equal(t, `xml`, string(c.Response().Body())) + require.Equal(t, "application/xml", string(c.Response().Header.Peek("content-type"))) + }) } // go test -run=^$ -bench=Benchmark_Ctx_XML -benchmem -count=4 @@ -2574,10 +2800,6 @@ func Test_Ctx_Render(t *testing.T) { }) require.NoError(t, err) - buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail - defer bytebufferpool.Put(buf) - require.Equal(t, "

Hello, World!

", string(c.Response().Body())) err = c.Render("./.github/testdata/template-non-exists.html", nil) @@ -2598,11 +2820,6 @@ func Test_Ctx_RenderWithoutLocals(t *testing.T) { err := c.Render("./.github/testdata/index.tmpl", Map{}) require.NoError(t, err) - - buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail - defer bytebufferpool.Put(buf) - require.Equal(t, "

", string(c.Response().Body())) } @@ -2611,18 +2828,26 @@ func Test_Ctx_RenderWithLocals(t *testing.T) { app := New(Config{ PassLocalsToViews: true, }) - c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Locals("Title", "Hello, World!") + t.Run("EmptyBind", func(t *testing.T) { + c := app.NewCtx(&fasthttp.RequestCtx{}) - err := c.Render("./.github/testdata/index.tmpl", Map{}) - require.NoError(t, err) + c.Locals("Title", "Hello, World!") + err := c.Render("./.github/testdata/index.tmpl", Map{}) - buf := bytebufferpool.Get() - _, _ = buf.WriteString("overwrite") //nolint:errcheck // This will never fail - defer bytebufferpool.Put(buf) + require.NoError(t, err) + require.Equal(t, "

Hello, World!

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

Hello, World!

", string(c.Response().Body())) + t.Run("NilBind", func(t *testing.T) { + c := app.NewCtx(&fasthttp.RequestCtx{}) + + c.Locals("Title", "Hello, World!") + err := c.Render("./.github/testdata/index.tmpl", nil) + + require.NoError(t, err) + require.Equal(t, "

Hello, World!

", string(c.Response().Body())) + }) } func Test_Ctx_RenderWithBindVars(t *testing.T) { @@ -3381,6 +3606,77 @@ func Benchmark_Ctx_SendString_B(b *testing.B) { require.Equal(b, []byte("Hello, world!"), c.Response().Body()) } +// go test -run Test_Ctx_Queries -v +func Test_Ctx_Queries(t *testing.T) { + t.Parallel() + app := New() + c := app.NewCtx(&fasthttp.RequestCtx{}) + + c.Request().SetBody([]byte(``)) + c.Request().Header.SetContentType("") + c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football&favouriteDrinks=milo,coke,pepsi&alloc=&no=1&field1=value1&field1=value2&field2=value3&list_a=1&list_a=2&list_a=3&list_b[]=1&list_b[]=2&list_b[]=3&list_c=1,2,3") + + queries := c.Queries() + require.Equal(t, "1", queries["id"]) + require.Equal(t, "tom", queries["name"]) + require.Equal(t, "basketball,football", queries["hobby"]) + require.Equal(t, "milo,coke,pepsi", queries["favouriteDrinks"]) + require.Equal(t, "", queries["alloc"]) + require.Equal(t, "1", queries["no"]) + require.Equal(t, "value2", queries["field1"]) + require.Equal(t, "value3", queries["field2"]) + require.Equal(t, "3", queries["list_a"]) + require.Equal(t, "3", queries["list_b[]"]) + require.Equal(t, "1,2,3", queries["list_c"]) + + c.Request().URI().SetQueryString("filters.author.name=John&filters.category.name=Technology&filters[customer][name]=Alice&filters[status]=pending") + + queries = c.Queries() + require.Equal(t, "John", queries["filters.author.name"]) + require.Equal(t, "Technology", queries["filters.category.name"]) + require.Equal(t, "Alice", queries["filters[customer][name]"]) + require.Equal(t, "pending", queries["filters[status]"]) + + c.Request().URI().SetQueryString("tags=apple,orange,banana&filters[tags]=apple,orange,banana&filters[category][name]=fruits&filters.tags=apple,orange,banana&filters.category.name=fruits") + + queries = c.Queries() + require.Equal(t, "apple,orange,banana", queries["tags"]) + require.Equal(t, "apple,orange,banana", queries["filters[tags]"]) + require.Equal(t, "fruits", queries["filters[category][name]"]) + require.Equal(t, "apple,orange,banana", queries["filters.tags"]) + require.Equal(t, "fruits", queries["filters.category.name"]) + + c.Request().URI().SetQueryString("filters[tags][0]=apple&filters[tags][1]=orange&filters[tags][2]=banana&filters[category][name]=fruits") + + queries = c.Queries() + require.Equal(t, "apple", queries["filters[tags][0]"]) + require.Equal(t, "orange", queries["filters[tags][1]"]) + require.Equal(t, "banana", queries["filters[tags][2]"]) + require.Equal(t, "fruits", queries["filters[category][name]"]) +} + +// go test -v -run=^$ -bench=Benchmark_Ctx_Queries -benchmem -count=4 +func Benchmark_Ctx_Queries(b *testing.B) { + app := New() + c := app.NewCtx(&fasthttp.RequestCtx{}) + + b.ReportAllocs() + b.ResetTimer() + c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football&favouriteDrinks=milo,coke,pepsi&alloc=&no=1") + + var queries map[string]string + for n := 0; n < b.N; n++ { + queries = c.Queries() + } + + require.Equal(b, "1", queries["id"]) + require.Equal(b, "tom", queries["name"]) + require.Equal(b, "basketball,football", queries["hobby"]) + require.Equal(b, "milo,coke,pepsi", queries["favouriteDrinks"]) + require.Equal(b, "", queries["alloc"]) + require.Equal(b, "1", queries["no"]) +} + // go test -run Test_Ctx_BodyStreamWriter func Test_Ctx_BodyStreamWriter(t *testing.T) { t.Parallel() @@ -3393,21 +3689,17 @@ func Test_Ctx_BodyStreamWriter(t *testing.T) { } fmt.Fprintf(w, "body writer line 2\n") }) - if !ctx.IsBodyStream() { - t.Fatal("IsBodyStream must return true") - } + + require.Equal(t, true, ctx.IsBodyStream()) s := ctx.Response.String() br := bufio.NewReader(bytes.NewBufferString(s)) var resp fasthttp.Response - if err := resp.Read(br); err != nil { - t.Fatalf("Error when reading response: %s", err) - } + require.Equal(t, nil, resp.Read(br)) + body := string(resp.Body()) expectedBody := "body writer line 1\nbody writer line 2\n" - if body != expectedBody { - t.Fatalf("unexpected body: %q. Expecting %q", body, expectedBody) - } + require.Equal(t, expectedBody, body) } // go test -v -run=^$ -bench=Benchmark_Ctx_BodyStreamWriter -benchmem -count=4 @@ -3456,14 +3748,10 @@ func TestCtx_ParamsInt(t *testing.T) { num, err := c.ParamsInt("user") // Check the number matches - if num != 1111 { - t.Fatalf("Expected number 1111 from the path, got %d", num) - } + require.Equal(t, 1111, num) // Check no errors are returned, because we want NO errors in this one - if err != nil { - t.Fatalf("Expected nil error for 1111 test, got " + err.Error()) - } + require.Equal(t, nil, err) return nil }) @@ -3476,14 +3764,10 @@ func TestCtx_ParamsInt(t *testing.T) { num, err := c.ParamsInt("user") // Check the number matches - if num != 0 { - t.Fatalf("Expected number 0 from the path, got %d", num) - } + require.Equal(t, 0, num) // Check an error is returned, because we want NO errors in this one - if err == nil { - t.Fatal("Expected non nil error for bad req test, got nil") - } + require.Equal(t, true, err != nil) return nil }) @@ -3496,14 +3780,10 @@ func TestCtx_ParamsInt(t *testing.T) { num, err := c.ParamsInt("user", 1111) // Check the number matches - if num != 2222 { - t.Fatalf("Expected number 2222 from the path, got %d", num) - } + require.Equal(t, 2222, num) // Check no errors are returned, because we want NO errors in this one - if err != nil { - t.Fatalf("Expected nil error for 2222 test, got " + err.Error()) - } + require.Equal(t, nil, err) return nil }) @@ -3516,14 +3796,10 @@ func TestCtx_ParamsInt(t *testing.T) { num, err := c.ParamsInt("user", 1111) // Check the number matches - if num != 1111 { - t.Fatalf("Expected number 1111 from the path, got %d", num) - } + require.Equal(t, 1111, num) // Check an error is returned, because we want NO errors in this one - if err != nil { - t.Fatalf("Expected nil error for 1111 test, got " + err.Error()) - } + require.Equal(t, nil, err) return nil }) @@ -3541,69 +3817,140 @@ func TestCtx_ParamsInt(t *testing.T) { require.NoError(t, err) } -// go test -run Test_Ctx_GetRespHeader -func Test_Ctx_GetRespHeader(t *testing.T) { +// go test -run Test_Ctx_IsFromLocal_X_Forwarded +func Test_Ctx_IsFromLocal_X_Forwarded(t *testing.T) { t.Parallel() - app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) - - c.Set("test", "Hello, World 👋!") - c.Response().Header.Set(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 -func Test_Ctx_IsFromLocal(t *testing.T) { - t.Parallel() - // Test "0.0.0.0", "127.0.0.1" and "::1". + // Test unset X-Forwarded-For header. { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - - require.True(t, c.IsFromLocal()) + // fasthttp returns "0.0.0.0" as IP as there is no remote address. + require.Equal(t, "0.0.0.0", c.IP()) + require.False(t, c.IsFromLocal()) } - // This is a test for "0.0.0.0" + // Test when setting X-Forwarded-For header to localhost "127.0.0.1" { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Request().Header.Set(HeaderXForwardedFor, "0.0.0.0") - - require.True(t, c.IsFromLocal()) + c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1") + defer app.ReleaseCtx(c) + require.False(t, c.IsFromLocal()) } - - // This is a test for "127.0.0.1" + // Test when setting X-Forwarded-For header to localhost "::1" { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Request().Header.Set(HeaderXForwardedFor, "127.0.0.1") - - require.True(t, c.IsFromLocal()) + c.Request().Header.Set(HeaderXForwardedFor, "::1") + defer app.ReleaseCtx(c) + require.False(t, c.IsFromLocal()) } - - // This is a test for "localhost" + // Test when setting X-Forwarded-For to full localhost IPv6 address "0:0:0:0:0:0:0:1" { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - - require.True(t, c.IsFromLocal()) + c.Request().Header.Set(HeaderXForwardedFor, "0:0:0:0:0:0:0:1") + defer app.ReleaseCtx(c) + require.False(t, c.IsFromLocal()) } - - // This is testing "::1", it is the compressed format IPV6 loopback address 0:0:0:0:0:0:0:1. - // It is the equivalent of the IPV4 address 127.0.0.1. + // Test for a random IP address. { app := New() c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Request().Header.Set(HeaderXForwardedFor, "::1") + c.Request().Header.Set(HeaderXForwardedFor, "93.46.8.90") - require.True(t, c.IsFromLocal()) + require.False(t, c.IsFromLocal()) } +} + +// go test -run Test_Ctx_IsFromLocal_RemoteAddr +func Test_Ctx_IsFromLocal_RemoteAddr(t *testing.T) { + t.Parallel() + + localIPv4 := net.Addr(&net.TCPAddr{IP: net.ParseIP("127.0.0.1")}) + localIPv6 := net.Addr(&net.TCPAddr{IP: net.ParseIP("::1")}) + localIPv6long := net.Addr(&net.TCPAddr{IP: net.ParseIP("0:0:0:0:0:0:0:1")}) + + zeroIPv4 := net.Addr(&net.TCPAddr{IP: net.IPv4zero}) + someIPv4 := net.Addr(&net.TCPAddr{IP: net.ParseIP("93.46.8.90")}) + someIPv6 := net.Addr(&net.TCPAddr{IP: net.ParseIP("2001:0db8:85a3:0000:0000:8a2e:0370:7334")}) + + // Test for the case fasthttp remoteAddr is set to "127.0.0.1". { app := New() - c := app.NewCtx(&fasthttp.RequestCtx{}) - c.Request().Header.Set(HeaderXForwardedFor, "93.46.8.90") + fastCtx := &fasthttp.RequestCtx{} + fastCtx.SetRemoteAddr(localIPv4) + c := app.NewCtx(fastCtx) - require.False(t, c.IsFromLocal()) + require.Equal(t, "127.0.0.1", c.IP()) + require.Equal(t, true, c.IsFromLocal()) + } + // Test for the case fasthttp remoteAddr is set to "::1". + { + app := New() + fastCtx := &fasthttp.RequestCtx{} + fastCtx.SetRemoteAddr(localIPv6) + c := app.NewCtx(fastCtx) + require.Equal(t, "::1", c.IP()) + require.Equal(t, true, c.IsFromLocal()) } + // Test for the case fasthttp remoteAddr is set to "0:0:0:0:0:0:0:1". + { + app := New() + fastCtx := &fasthttp.RequestCtx{} + fastCtx.SetRemoteAddr(localIPv6long) + c := app.NewCtx(fastCtx) + // fasthttp should return "::1" for "0:0:0:0:0:0:0:1". + // otherwise IsFromLocal() will break. + require.Equal(t, "::1", c.IP()) + require.Equal(t, true, c.IsFromLocal()) + } + // Test for the case fasthttp remoteAddr is set to "0.0.0.0". + { + app := New() + fastCtx := &fasthttp.RequestCtx{} + fastCtx.SetRemoteAddr(zeroIPv4) + c := app.NewCtx(fastCtx) + require.Equal(t, "0.0.0.0", c.IP()) + require.Equal(t, false, c.IsFromLocal()) + } + // Test for the case fasthttp remoteAddr is set to "93.46.8.90". + { + app := New() + fastCtx := &fasthttp.RequestCtx{} + fastCtx.SetRemoteAddr(someIPv4) + c := app.NewCtx(fastCtx) + require.Equal(t, "93.46.8.90", c.IP()) + require.Equal(t, false, c.IsFromLocal()) + } + // Test for the case fasthttp remoteAddr is set to "2001:0db8:85a3:0000:0000:8a2e:0370:7334". + { + app := New() + fastCtx := &fasthttp.RequestCtx{} + fastCtx.SetRemoteAddr(someIPv6) + c := app.NewCtx(fastCtx) + require.Equal(t, "2001:db8:85a3::8a2e:370:7334", c.IP()) + require.Equal(t, false, c.IsFromLocal()) + } +} + +// go test -run Test_Ctx_extractIPsFromHeader -v +func Test_Ctx_extractIPsFromHeader(t *testing.T) { + app := New() + c := app.NewCtx(&fasthttp.RequestCtx{}) + c.Request().Header.Set("x-forwarded-for", "1.1.1.1,8.8.8.8 , /n, \n,1.1, a.c, 6.,6., , a,,42.118.81.169,10.0.137.108") + ips := c.IPs() + res := ips[len(ips)-2] + require.Equal(t, "42.118.81.169", res) +} + +// go test -run Test_Ctx_extractIPsFromHeader -v +func Test_Ctx_extractIPsFromHeader_EnableValidateIp(t *testing.T) { + app := New() + app.config.EnableIPValidation = true + c := app.NewCtx(&fasthttp.RequestCtx{}) + c.Request().Header.Set("x-forwarded-for", "1.1.1.1,8.8.8.8 , /n, \n,1.1, a.c, 6.,6., , a,,42.118.81.169,10.0.137.108") + ips := c.IPs() + res := ips[len(ips)-2] + require.Equal(t, "42.118.81.169", res) } diff --git a/docs/api/app.md b/docs/api/app.md index 5f788ca356..93dae1bc64 100644 --- a/docs/api/app.md +++ b/docs/api/app.md @@ -643,7 +643,7 @@ resp, _ := app.Test(req) // Do something with results: if resp.StatusCode == fiber.StatusOK { - body, _ := ioutil.ReadAll(resp.Body) + body, _ := io.ReadAll(resp.Body) fmt.Println(string(body)) // => Hello, World! } ``` diff --git a/docs/api/client.md b/docs/api/client.md index c3ed4b11a6..7ec9f3b033 100644 --- a/docs/api/client.md +++ b/docs/api/client.md @@ -537,6 +537,61 @@ agent.SetResponse(resp) ReleaseResponse(resp) ``` +
Example handling for response values + +```go title="Example handling response" +// Create a Fiber HTTP client agent +agent := fiber.Get("https://httpbin.org/get") + +// Acquire a response object to store the result +resp := fiber.AcquireResponse() +agent.SetResponse(resp) + +// Perform the HTTP GET request +code, body, errs := agent.String() +if errs != nil { + // Handle any errors that occur during the request + panic(errs) +} + +// Print the HTTP response code and body +fmt.Println("Response Code:", code) +fmt.Println("Response Body:", body) + +// Visit and print all the headers in the response +resp.Header.VisitAll(func(key, value []byte) { + fmt.Println("Header", string(key), "value", string(value)) +}) + +// Release the response to free up resources +fiber.ReleaseResponse(resp) +``` + +Output: +```txt title="Output" +Response Code: 200 +Response Body: { + "args": {}, + "headers": { + "Host": "httpbin.org", + "User-Agent": "fiber", + "X-Amzn-Trace-Id": "Root=1-653763d0-2555d5ba3838f1e9092f9f72" + }, + "origin": "83.137.191.1", + "url": "https://httpbin.org/get" +} + +Header Content-Length value 226 +Header Content-Type value application/json +Header Server value gunicorn/19.9.0 +Header Date value Tue, 24 Oct 2023 06:27:28 GMT +Header Connection value keep-alive +Header Access-Control-Allow-Origin value * +Header Access-Control-Allow-Credentials value true +``` + +
+ ### Dest Dest sets custom dest. The contents of dest will be replaced by the response body, if the dest is too small a new slice will be allocated. diff --git a/docs/api/ctx.md b/docs/api/ctx.md index aa48c60929..860799f451 100644 --- a/docs/api/ctx.md +++ b/docs/api/ctx.md @@ -49,6 +49,37 @@ app.Get("/", func(c *fiber.Ctx) error { }) ``` +Media-Type parameters are supported. + +```go title="Example 3" +// Accept: text/plain, application/json; version=1; foo=bar + +app.Get("/", func(c *fiber.Ctx) error { + // Extra parameters in the accept are ignored + c.Accepts("text/plain;format=flowed") // "text/plain;format=flowed" + + // An offer must contain all parameters present in the Accept type + c.Accepts("application/json") // "" + + // Parameter order and capitalization does not matter. Quotes on values are stripped. + c.Accepts(`application/json;foo="bar";VERSION=1`) // "application/json;foo="bar";VERSION=1" +}) +``` + +```go title="Example 4" +// Accept: text/plain;format=flowed;q=0.9, text/plain +// i.e., "I prefer text/plain;format=flowed less than other forms of text/plain" +app.Get("/", func(c *fiber.Ctx) error { + // Beware: the order in which offers are listed matters. + // Although the client specified they prefer not to receive format=flowed, + // the text/plain Accept matches with "text/plain;format=flowed" first, so it is returned. + c.Accepts("text/plain;format=flowed", "text/plain") // "text/plain;format=flowed" + + // Here, things behave as expected: + c.Accepts("text/plain", "text/plain;format=flowed") // "text/plain" +}) +``` + Fiber provides similar functions for the other accept headers. ```go @@ -57,13 +88,13 @@ Fiber provides similar functions for the other accept headers. // Accept-Language: en;q=0.8, nl, ru app.Get("/", func(c *fiber.Ctx) error { - c.AcceptsCharsets("utf-16", "iso-8859-1") + c.AcceptsCharsets("utf-16", "iso-8859-1") // "iso-8859-1" - c.AcceptsEncodings("compress", "br") + c.AcceptsEncodings("compress", "br") // "compress" - c.AcceptsLanguages("pt", "nl", "ru") + c.AcceptsLanguages("pt", "nl", "ru") // "nl" // ... }) @@ -171,6 +202,7 @@ app.Get("/", func(c *fiber.Ctx) error { ``` ## Bind + Add vars to default view var map binding to template engine. Variables are read by the Render method and may be overwritten. @@ -190,12 +222,12 @@ app.Get("/", func(c *fiber.Ctx) error { }) ``` -## Body +## BodyRaw Returns the raw request **body**. ```go title="Signature" -func (c *Ctx) Body() []byte +func (c *Ctx) BodyRaw() []byte ``` ```go title="Example" @@ -203,6 +235,26 @@ func (c *Ctx) Body() []byte app.Post("/", func(c *fiber.Ctx) error { // Get raw body from POST request: + return c.Send(c.BodyRaw()) // []byte("user=john") +}) +``` + +> _Returned value is only valid within the handler. Do not store any references. +> Make copies or use the_ [_**`Immutable`**_](ctx.md) _setting instead._ [_Read more..._](../#zero-allocation) + +## Body + +As per the header `Content-Encoding`, this method will try to perform a file decompression from the **body** bytes. In case no `Content-Encoding` header is sent, it will perform as [BodyRaw](#bodyraw). + +```go title="Signature" +func (c *Ctx) Body() []byte +``` + +```go title="Example" +// echo 'user=john' | gzip | curl -v -i --data-binary @- -H "Content-Encoding: gzip" http://localhost:8080 + +app.Post("/", func(c *fiber.Ctx) error { + // Decompress body from POST request based on the Content-Encoding and return the raw content: return c.Send(c.Body()) // []byte("user=john") }) ``` @@ -216,13 +268,13 @@ Binds the request body to a struct. It is important to specify the correct struct tag based on the content type to be parsed. For example, if you want to parse a JSON body with a field called Pass, you would use a struct field of `json:"pass"`. -| content-type | struct tag | -|---|---| -| `application/x-www-form-urlencoded` | form | -| `multipart/form-data` | form | -| `application/json` | json | -| `application/xml` | xml | -| `text/xml` | xml | +| content-type | struct tag | +| ----------------------------------- | ---------- | +| `application/x-www-form-urlencoded` | form | +| `multipart/form-data` | form | +| `application/json` | json | +| `application/xml` | xml | +| `text/xml` | xml | ```go title="Signature" func (c *Ctx) BodyParser(out interface{}) error @@ -382,6 +434,38 @@ app.Get("/", func(c *fiber.Ctx) error { }) ``` +## CookieParser + +This method is similar to [BodyParser](ctx.md#bodyparser), but for cookie parameters. +It is important to use the struct tag "cookie". For example, if you want to parse a cookie with a field called Age, you would use a struct field of `cookie:"age"`. + +```go title="Signature" +func (c *Ctx) CookieParser(out interface{}) error +``` + +```go title="Example" +// Field names should start with an uppercase letter +type Person struct { + Name string `cookie:"name"` + Age int `cookie:"age"` + Job bool `cookie:"job"` +} + +app.Get("/", func(c *fiber.Ctx) error { + p := new(Person) + + if err := c.CookieParser(p); err != nil { + return err + } + + log.Println(p.Name) // Joseph + log.Println(p.Age) // 23 + log.Println(p.Job) // true +}) +// Run tests with the following curl command +// curl.exe --cookie "name=Joseph; age=23; job=true" http://localhost:8000/ +``` + ## Cookies Get cookie value by key, you could pass an optional default value that will be returned if the cookie key does not exist. @@ -530,10 +614,10 @@ app.Get("/", func(c *fiber.Ctx) error { ## GetReqHeaders -Returns the HTTP request headers. +Returns the HTTP request headers as a map. Since a header can be set multiple times in a single request, the values of the map are slices of strings containing all the different values of the header. ```go title="Signature" -func (c *Ctx) GetReqHeaders() map[string]string +func (c *Ctx) GetReqHeaders() map[string][]string ``` > _Returned value is only valid within the handler. Do not store any references. @@ -565,10 +649,10 @@ app.Get("/", func(c *fiber.Ctx) error { ## GetRespHeaders -Returns the HTTP response headers. +Returns the HTTP response headers as a map. Since a header can be set multiple times in a single request, the values of the map are slices of strings containing all the different values of the header. ```go title="Signature" -func (c *Ctx) GetRespHeaders() map[string]string +func (c *Ctx) GetRespHeaders() map[string][]string ``` > _Returned value is only valid within the handler. Do not store any references. @@ -693,6 +777,7 @@ app.Get("/", func(c *fiber.Ctx) error { ## IsFromLocal Returns true if request came from localhost + ```go title="Signature" func (c *Ctx) IsFromLocal() bool { ``` @@ -837,7 +922,7 @@ app.Post("/", func(c *fiber.Ctx) error { c.Location("http://example.com") c.Location("/foo/bar") - + return nil }) ``` @@ -1024,6 +1109,7 @@ app.Get("/user/:id", func(c *fiber.Ctx) error { This method is equivalent of using `atoi` with ctx.Params ## ParamsParser + This method is similar to BodyParser, but for path parameters. It is important to use the struct tag "params". For example, if you want to parse a path parameter with a field called Pass, you would use a struct field of params:"pass" ```go title="Signature" @@ -1034,7 +1120,7 @@ func (c *Ctx) ParamsParser(out interface{}) error // GET http://example.com/user/111 app.Get("/user/:id", func(c *fiber.Ctx) error { param := struct {ID uint `params:"id"`}{} - + c.ParamsParser(¶m) // "{"id": 111}" // ... @@ -1176,7 +1262,6 @@ app.Get("/", func(c *fiber.Ctx) error { This property is an object containing a property for each query boolean parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. - :::caution Please note if that parameter is not in the request, false will be returned. If the parameter is not a boolean, it is still tried to be converted and usually returned as false. @@ -1232,12 +1317,10 @@ app.Get("/", func(c *fiber.Ctx) error { }) ``` - ## QueryInt This property is an object containing a property for each query integer parameter in the route, you could pass an optional default value that will be returned if the query key does not exist. - :::caution Please note if that parameter is not in the request, zero will be returned. If the parameter is not a number, it is still tried to be converted and usually returned as 1. @@ -1522,7 +1605,7 @@ func (c *Ctx) Route() *Route app.Get("/hello/:name", func(c *fiber.Ctx) error { r := c.Route() fmt.Println(r.Method, r.Path, r.Params, r.Handlers) - // GET /hello/:name handler [name] + // GET /hello/:name handler [name] // ... }) @@ -1690,6 +1773,10 @@ app.Get("/file-with-url-chars", func(c *fiber.Ctx) error { }) ``` +:::info +For sending files from embedded file system [this functionality](./middleware/filesystem.md#sendfile) can be used +::: + ## SendStatus Sets the status code and the correct status message in the body, if the response body is **empty**. @@ -1768,7 +1855,7 @@ var timeConverter = func(value string) reflect.Value { customTime := fiber.ParserType{ Customtype: CustomTime{}, Converter: timeConverter, -} +} // Add setting to the Decoder fiber.SetParserDecoder(fiber.ParserConfig{ @@ -1804,7 +1891,6 @@ app.Get("/query", func(c *fiber.Ctx) error { ``` - ## SetUserContext Sets the user specified implementation for context interface. @@ -2020,7 +2106,7 @@ XML also sets the content header to **application/xml**. ::: ```go title="Signature" -func (c *Ctx) XML(data interface{}) error +func (c *Ctx) XML(data interface{}) error ``` ```go title="Example" diff --git a/docs/api/fiber.md b/docs/api/fiber.md index fa0ad2412d..1f9a91b8bf 100644 --- a/docs/api/fiber.md +++ b/docs/api/fiber.md @@ -40,7 +40,7 @@ app := fiber.New(fiber.Config{ **Config fields** | Property | Type | Description | Default | -| ---------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------- | +| ---------------------------- | --------------------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| --------------------- | | AppName | `string` | This allows to setup app name for the app | `""` | | BodyLimit | `int` | Sets the maximum allowed size for a request body, if the size exceeds the configured limit, it sends `413 - Request Entity Too Large` response. | `4 * 1024 * 1024` | | CaseSensitive | `bool` | When enabled, `/Foo` and `/foo` are different routes. When disabled, `/Foo`and `/foo` are treated the same. | `false` | @@ -56,6 +56,7 @@ app := fiber.New(fiber.Config{ | ETag | `bool` | Enable or disable ETag header generation, since both weak and strong etags are generated using the same hashing method \(CRC-32\). Weak ETags are the default when enabled. | `false` | | EnableIPValidation | `bool` | If set to true, `c.IP()` and `c.IPs()` will validate IP addresses before returning them. Also, `c.IP()` will return only the first valid IP rather than just the raw header value that may be a comma seperated string.

**WARNING:** There is a small performance cost to doing this validation. Keep disabled if speed is your only concern and your application is behind a trusted proxy that already validates this header. | `false` | | EnablePrintRoutes | `bool` | EnablePrintRoutes enables print all routes with their method, path, name and handler.. | `false` | +| EnableSplittingOnParsers | `bool` | EnableSplittingOnParsers splits the query/body/header parameters by comma when it's true.

For example, you can use it to parse multiple values from a query parameter like this: `/api?foo=bar,baz == foo[]=bar&foo[]=baz` | `false` | | EnableTrustedProxyCheck | `bool` | When set to true, fiber will check whether proxy is trusted, using TrustedProxies list.

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

**WARNING:** When prefork is set to true, only "tcp4" and "tcp6" can be chosen. | `NetworkTCP4` | +| Network | `string` | Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only)

**WARNING:** When prefork is set to true, only "tcp4" and "tcp6" can be chosen. | `NetworkTCP4` | | PassLocalsToViews | `bool` | PassLocalsToViews Enables passing of the locals set on a fiber.Ctx to the template engine. See our **Template Middleware** for supported engines. | `false` | | Prefork | `bool` | Enables use of the[`SO_REUSEPORT`](https://lwn.net/Articles/542629/)socket option. This will spawn multiple Go processes listening on the same port. learn more about [socket sharding](https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/). **NOTE: if enabled, the application will need to be ran through a shell because prefork mode sets environment variables. If you're using Docker, make sure the app is ran with `CMD ./app` or `CMD ["sh", "-c", "/app"]`. For more info, see** [**this**](https://github.com/gofiber/fiber/issues/1021#issuecomment-730537971) **issue comment.** | `false` | | ProxyHeader | `string` | This will enable `c.IP()` to return the value of the given header key. By default `c.IP()`will return the Remote IP from the TCP connection, this property can be useful if you are behind a load balancer e.g. _X-Forwarded-\*_. | `""` | | ReadBufferSize | `int` | per-connection buffer size for requests' reading. This also limits the maximum header size. Increase this buffer if your clients send multi-KB RequestURIs and/or multi-KB headers \(for example, BIG cookies\). | `4096` | | ReadTimeout | `time.Duration` | The amount of time allowed to read the full request, including the body. The default timeout is unlimited. | `nil` | -| RequestMethods | `[]string` | RequestMethods provides customizibility for HTTP methods. You can add/remove methods as you wish. | `DefaultMethods` | +| RequestMethods | `[]string` | RequestMethods provides customizibility for HTTP methods. You can add/remove methods as you wish. | `DefaultMethods` | | ServerHeader | `string` | Enables the `Server` HTTP header with the given value. | `""` | | StreamRequestBody | `bool` | StreamRequestBody enables request body streaming, and calls the handler sooner when given body is larger then the current limit. | `false` | | StrictRouting | `bool` | When enabled, the router treats `/foo` and `/foo/` as different. Otherwise, the router treats `/foo` and `/foo/` as the same. | `false` | diff --git a/docs/api/log.md b/docs/api/log.md index 9b741b13f7..e53d6d4b0a 100644 --- a/docs/api/log.md +++ b/docs/api/log.md @@ -80,7 +80,7 @@ log.Fatalw("", "fruit", "banana") If you are in a project and just want to use a simple log function that can be printed at any time in the global, we provide a global log. ```go -import "github.com/gofiber/fiber/v2/log" +import "github.com/gofiber/fiber/v3/log" log.Info("info") log.Warn("warn") @@ -92,7 +92,7 @@ You can also find an already implemented adaptation under contrib, or use your o ```go import ( "log" - fiberlog "github.com/gofiber/fiber/v2/log" + fiberlog "github.com/gofiber/fiber/v3/log" ) var _ log.AllLogger = (*customLogger)(nil) @@ -113,7 +113,7 @@ The default logger is LevelTrace. Note that this method is not **concurrent-safe**. ```go -import "github.com/gofiber/fiber/v2/log" +import "github.com/gofiber/fiber/v3/log" log.SetLevel(log.LevelInfo) ``` diff --git a/docs/api/middleware/cache.md b/docs/api/middleware/cache.md index 3a87306d3b..e014694103 100644 --- a/docs/api/middleware/cache.md +++ b/docs/api/middleware/cache.md @@ -36,7 +36,7 @@ app.Use(cache.New()) // Or extend your config for customization app.Use(cache.New(cache.Config{ Next: func(c *fiber.Ctx) bool { - return c.Query("refresh") == "true" + return c.Query("noCache") == "true" }, Expiration: 30 * time.Minute, CacheControl: true, @@ -64,20 +64,20 @@ app.Get("/", func(c *fiber.Ctx) error { ## Config -| Property | Type | Description | Default | -|:---------------------|:------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | -| Expiration | `time.Duration` | Expiration is the time that a cached response will live. | `1 * time.Minute` | -| CacheHeader | `string` | CacheHeader is the header on the response header that indicates the cache status, with the possible return values "hit," "miss," or "unreachable." | `X-Cache` | -| CacheControl | `bool` | CacheControl enables client-side caching if set to true. | `false` | -| KeyGenerator | `func(*fiber.Ctx) string` | Key allows you to generate custom keys. | `func(c *fiber.Ctx) string { return utils.CopyString(c.Path()) }` | -| ExpirationGenerator | `func(*fiber.Ctx, *cache.Config) time.Duration` | ExpirationGenerator allows you to generate custom expiration keys based on the request. | `nil` | -| Storage | `fiber.Storage` | Store is used to store the state of the middleware. | In-memory store | -| Store (Deprecated) | `fiber.Storage` | Deprecated: Use Storage instead. | In-memory store | -| Key (Deprecated) | `func(*fiber.Ctx) string` | Deprecated: Use KeyGenerator instead. | `nil` | -| StoreResponseHeaders | `bool` | StoreResponseHeaders allows you to store additional headers generated by next middlewares & handler. | `false` | -| MaxBytes | `uint` | MaxBytes is the maximum number of bytes of response bodies simultaneously stored in cache. | `0` (No limit) | -| Methods | `[]string` | Methods specifies the HTTP methods to cache. | `[]string{fiber.MethodGet, fiber.MethodHead}` | +| Property | Type | Description | Default | +|:---------------------|:------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function that is executed before creating the cache entry and can be used to execute the request without cache creation. If an entry already exists, it will be used. If you want to completely bypass the cache functionality in certain cases, you should use the [skip middleware](./skip.md). | `nil` | +| Expiration | `time.Duration` | Expiration is the time that a cached response will live. | `1 * time.Minute` | +| CacheHeader | `string` | CacheHeader is the header on the response header that indicates the cache status, with the possible return values "hit," "miss," or "unreachable." | `X-Cache` | +| CacheControl | `bool` | CacheControl enables client-side caching if set to true. | `false` | +| KeyGenerator | `func(*fiber.Ctx) string` | Key allows you to generate custom keys. | `func(c *fiber.Ctx) string { return utils.CopyString(c.Path()) }` | +| ExpirationGenerator | `func(*fiber.Ctx, *cache.Config) time.Duration` | ExpirationGenerator allows you to generate custom expiration keys based on the request. | `nil` | +| Storage | `fiber.Storage` | Store is used to store the state of the middleware. | In-memory store | +| Store (Deprecated) | `fiber.Storage` | Deprecated: Use Storage instead. | In-memory store | +| Key (Deprecated) | `func(*fiber.Ctx) string` | Deprecated: Use KeyGenerator instead. | `nil` | +| StoreResponseHeaders | `bool` | StoreResponseHeaders allows you to store additional headers generated by next middlewares & handler. | `false` | +| MaxBytes | `uint` | MaxBytes is the maximum number of bytes of response bodies simultaneously stored in cache. | `0` (No limit) | +| Methods | `[]string` | Methods specifies the HTTP methods to cache. | `[]string{fiber.MethodGet, fiber.MethodHead}` | ## Default Config diff --git a/docs/api/middleware/compress.md b/docs/api/middleware/compress.md index f284f5e92c..472f9d9804 100644 --- a/docs/api/middleware/compress.md +++ b/docs/api/middleware/compress.md @@ -6,6 +6,10 @@ id: compress Compression middleware for [Fiber](https://github.com/gofiber/fiber) that will compress the response using `gzip`, `deflate` and `brotli` compression depending on the [Accept-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding) header. +:::note +The compression middleware refrains from compressing bodies that are smaller than 200 bytes. This decision is based on the observation that, in such cases, the compressed size is likely to exceed the original size, making compression inefficient. [more](https://github.com/valyala/fasthttp/blob/497922a21ef4b314f393887e9c6147b8c3e3eda4/http.go#L1713-L1715) +::: + ## Signatures ```go diff --git a/docs/api/middleware/cors.md b/docs/api/middleware/cors.md index af9b8c5f2b..9a28342fd3 100644 --- a/docs/api/middleware/cors.md +++ b/docs/api/middleware/cors.md @@ -6,6 +6,10 @@ id: cors CORS middleware for [Fiber](https://github.com/gofiber/fiber) that can be used to enable [Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) with various options. +The middleware conforms to the `access-control-allow-origin` specification by parsing `AllowOrigins`. First, the middleware checks if there is a matching allowed origin for the requesting 'origin' header. If there is a match, it returns exactly one matching domain from the list of allowed origins. + +For more control, `AllowOriginsFunc` can be used to programatically determine if an origin is allowed. If no match was found in `AllowOrigins` and if `AllowOriginsFunc` returns true then the 'access-control-allow-origin' response header is set to the 'origin' request header. + ## Signatures ```go @@ -54,16 +58,16 @@ app.Use(cors.New(cors.Config{ ## Config -| Property | Type | Description | Default | -|:-----------------|:---------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------| -| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | -| AllowOriginsFunc | `func(origin string) bool` | AllowOriginsFunc defines a function that will set the 'access-control-allow-origin' response header to the 'origin' request header when returned true. | `nil` | -| AllowOrigins | `string` | AllowOrigin defines a list of origins that may access the resource. | `"*"` | -| AllowMethods | `string` | AllowMethods defines a list methods allowed when accessing the resource. This is used in response to a preflight request. | `"GET,POST,HEAD,PUT,DELETE,PATCH"` | -| AllowHeaders | `string` | AllowHeaders defines a list of request headers that can be used when making the actual request. This is in response to a preflight request. | `""` | -| AllowCredentials | `bool` | AllowCredentials indicates whether or not the response to the request can be exposed when the credentials flag is true. | `false` | -| ExposeHeaders | `string` | ExposeHeaders defines a whitelist headers that clients are allowed to access. | `""` | -| MaxAge | `int` | MaxAge indicates how long (in seconds) the results of a preflight request can be cached. | `0` | +| Property | Type | Description | Default | +|:-----------------|:---------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------| +| Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| AllowOriginsFunc | `func(origin string) bool` | AllowOriginsFunc defines a function that will set the 'access-control-allow-origin' response header to the 'origin' request header when returned true. | `nil` | +| AllowOrigins | `string` | AllowOrigin defines a comma separated list of origins that may access the resource. | `"*"` | +| AllowMethods | `string` | AllowMethods defines a list of methods allowed when accessing the resource. This is used in response to a preflight request. | `"GET,POST,HEAD,PUT,DELETE,PATCH"` | +| AllowHeaders | `string` | AllowHeaders defines a list of request headers that can be used when making the actual request. This is in response to a preflight request. | `""` | +| AllowCredentials | `bool` | AllowCredentials indicates whether or not the response to the request can be exposed when the credentials flag is true. | `false` | +| ExposeHeaders | `string` | ExposeHeaders defines a whitelist headers that clients are allowed to access. | `""` | +| MaxAge | `int` | MaxAge indicates how long (in seconds) the results of a preflight request can be cached. If you pass MaxAge 0, Access-Control-Max-Age header will not be added and browser will use 5 seconds by default. To disable caching completely, pass MaxAge value negative. It will set the Access-Control-Max-Age header 0. | `0` | ## Default Config diff --git a/docs/api/middleware/csrf.md b/docs/api/middleware/csrf.md index dbbf6007d9..58cd4f13d0 100644 --- a/docs/api/middleware/csrf.md +++ b/docs/api/middleware/csrf.md @@ -4,16 +4,99 @@ id: csrf # CSRF -CSRF middleware for [Fiber](https://github.com/gofiber/fiber) that provides [Cross-site request forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) protection by passing a csrf token via cookies. This cookie value will be used to compare against the client csrf token on requests, other than those defined as "safe" by RFC7231 \(GET, HEAD, OPTIONS, or TRACE\). When the csrf token is invalid, this middleware will return the `fiber.ErrForbidden` error. +The CSRF middleware for [Fiber](https://github.com/gofiber/fiber) provides protection against [Cross-Site Request Forgery](https://en.wikipedia.org/wiki/Cross-site_request_forgery) (CSRF) attacks using tokens. These tokens verify requests made using methods other than those defined as "safe" by [RFC9110#section-9.2.1](https://datatracker.ietf.org/doc/html/rfc9110.html#section-9.2.1) (Safe-Methods: GET, HEAD, OPTIONS, and TRACE). If a potential attack is detected this middleware will, by default, return a 403 Forbidden error. -CSRF Tokens are generated on GET requests. You can retrieve the CSRF token with `c.Locals(contextKey)`, where `contextKey` is the string you set in the config (see Custom Config below). +This middleware can be used with or without a user session and offers two token validation patterns. In addition, it implements strict referer checking for HTTPS requests, ensuring the security of your application. For HTTPS requests, even if a subdomain can set or modify cookies on your domain, it can't force a user to post to your application since that request won't come from your own exact domain. -When no `csrf_` cookie is set, or the token has expired, a new token will be generated and `csrf_` cookie set. +## Token Generation + +CSRF tokens are generated on 'safe' requests and when the existing token has expired or hasn't been set yet. If `SingleUseToken` is `true`, a new token is generated after each use. Retrieve the CSRF token using `c.Locals(contextKey)`, where `contextKey` is defined in the configuration. + +## Security Considerations + +This middleware is designed to protect against CSRF attacks but does not protect against other attack vectors, such as XSS. It should be used in combination with other security measures. + +:::danger +Never use 'safe' methods to mutate data, for example, never use a GET request to modify a resource. This middleware will not protect against CSRF attacks on 'safe' methods. +::: + +### Token Validation Patterns + +#### Double Submit Cookie Pattern (Default) + +In the default configuration, the middleware generates and stores tokens using the `fiber.Storage` interface. These tokens are not associated with a user session, and a Double Submit Cookie pattern is used to validate the token. The token is stored in a cookie and sent as a header on requests. The middleware compares the cookie value with the header value to validate the token. This is a secure pattern that does not require a user session. + +When using this pattern, it's important to delete the token when the authorization status changes, see: [Token Lifecycle](#token-lifecycle) for more information. + +:::caution +When using this method, it's important to set the `CookieSameSite` option to `Lax` or `Strict` and ensure that the Extractor is not `CsrfFromCookie`, and KeyLookup is not `cookie:`. +::: :::note -This middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for this middleware saves data to memory, see the examples below for other databases. +When using this pattern, this middleware uses our [Storage](https://github.com/gofiber/storage) package to support various databases through a single interface. The default configuration for Storage saves data to memory. See [Custom Storage/Database](#custom-storagedatabase) for customizing the storage. +::: + +#### Synchronizer Token Pattern (Session) + +When using this middleware with a user session, the middleware can be configured to store the token in the session. This method is recommended when using a user session, as it is generally more secure than the Double Submit Cookie Pattern. + +When using this pattern it's important to regenerate the session when the authorization status changes, this will also delete the token. See: [Token Lifecycle](#token-lifecycle) for more information. + +:::caution +When using this method, pre-sessions are required and will be created if a session is not already present. This means the middleware will create a session for every safe request, even if the request does not require a session. Therefore, the existence of a session should not be used to indicate that a user is logged in or authenticated; a session value should be used for this purpose. +::: + +### Defense In Depth + +When using this middleware, it's recommended to serve your pages over HTTPS, set the `CookieSecure` option to `true`, and set the `CookieSameSite` option to `Lax` or `Strict`. This ensures that the cookie is only sent over HTTPS and not on requests from external sites. + +:::note +Cookie prefixes __Host- and __Secure- can be used to further secure the cookie. However, these prefixes are not supported by all browsers and there are some other limitations. See [MDN#Set-Cookie#cookie_prefixes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#cookie_prefixes) for more information. + +To use these prefixes, set the `CookieName` option to `__Host-csrf_` or `__Secure-csrf_`. +::: + +### Referer Checking + +For HTTPS requests, this middleware performs strict referer checking. Even if a subdomain can set or modify cookies on your domain, it can't force a user to post to your application since that request won't come from your own exact domain. + +:::caution +Referer checking is required for https requests protected by CSRF. All modern browsers will automatically include the Referer header in requests, including those made with the JS Fetch API. However, if you are using this middleware with a custom client you must ensure that the client sends a valid Referer header. +::: + + +### Token Lifecycle + +Tokens are valid until they expire or until they are deleted. By default, tokens are valid for 1 hour, and each subsequent request extends the expiration by 1 hour. The token only expires if the user doesn't make a request for the duration of the expiration time. + +#### Token Reuse + +By default, tokens may be used multiple times. If you want to delete the token after it has been used, you can set the `SingleUseToken` option to `true`. This will delete the token after it has been used, and a new token will be generated on the next request. + +:::info +Using `SingleUseToken` comes with usability trade-offs and is not enabled by default. It can interfere with the user experience if the user has multiple tabs open or uses the back button. ::: +#### Deleting Tokens + +When the authorization status changes, the CSRF token MUST be deleted, and a new one generated. This can be done by calling `handler.DeleteToken(c)`. + +```go +if handler, ok := app.AcquireCtx(ctx).Locals(ConfigDefault.HandlerContextKey).(*CSRFHandler); ok { + if err := handler.DeleteToken(app.AcquireCtx(ctx)); err != nil { + // handle error + } +} +``` + +:::tip +If you are using this middleware with the fiber session middleware, then you can simply call `session.Destroy()`, `session.Regenerate()`, or `session.Reset()` to delete session and the token stored therein. +::: + +### BREACH + +It's important to note that the token is sent as a header on every request. If you include the token in a page that is vulnerable to [BREACH](https://en.wikipedia.org/wiki/BREACH), an attacker may be able to extract the token. To mitigate this, ensure your pages are served over HTTPS, disable HTTP compression, and implement rate limiting for requests. + ## Signatures ```go @@ -22,7 +105,7 @@ func New(config ...Config) fiber.Handler ## Examples -Import the middleware package that is part of the Fiber web framework +Import the middleware package that is part of the Fiber web framework: ```go import ( @@ -31,7 +114,7 @@ import ( ) ``` -After you initiate your Fiber app, you can use the following possibilities: +After initializing your Fiber app, you can use the following code to initialize the middleware: ```go // Initialize default config @@ -43,24 +126,22 @@ app.Use(csrf.New(csrf.Config{ CookieName: "csrf_", CookieSameSite: "Lax", Expiration: 1 * time.Hour, - KeyGenerator: utils.UUID, + KeyGenerator: utils.UUIDv4, Extractor: func(c *fiber.Ctx) (string, error) { ... }, })) ``` -:::note +:::info KeyLookup will be ignored if Extractor is explicitly set. ::: ## Config -### Config - | Property | Type | Description | Default | |:------------------|:-----------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------| | Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | | KeyLookup | `string` | KeyLookup is a string in the form of "`:`" that is used to create an Extractor that extracts the token from the request. Possible values: "`header:`", "`query:`", "`param:`", "`form:`", "`cookie:`". Ignored if an Extractor is explicitly set. | "header:X-CSRF-Token" | -| CookieName | `string` | Name of the session cookie. This cookie will store the session key. | "csrf_" | +| CookieName | `string` | Name of the csrf cookie. This cookie will store the csrf key. | "csrf_" | | CookieDomain | `string` | Domain of the CSRF cookie. | "" | | CookiePath | `string` | Path of the CSRF cookie. | "" | | CookieSecure | `bool` | Indicates if the CSRF cookie is secure. | false | @@ -68,26 +149,53 @@ KeyLookup will be ignored if Extractor is explicitly set. | CookieSameSite | `string` | Value of SameSite cookie. | "Lax" | | CookieSessionOnly | `bool` | Decides whether the cookie should last for only the browser session. Ignores Expiration if set to true. | false | | Expiration | `time.Duration` | Expiration is the duration before the CSRF token will expire. | 1 * time.Hour | -| Storage | `fiber.Storage` | Store is used to store the state of the middleware. | memory.New() | +| SingleUseToken | `bool` | SingleUseToken indicates if the CSRF token be destroyed and a new one generated on each use. (See TokenLifecycle) | false | +| Storage | `fiber.Storage` | Store is used to store the state of the middleware. | `nil` | +| Session | `*session.Store` | Session is used to store the state of the middleware. Overrides Storage if set. | `nil` | +| SessionKey | `string` | SessionKey is the key used to store the token in the session. | "fiber.csrf.token" | | ContextKey | `string` | Context key to store the generated CSRF token into the context. If left empty, the token will not be stored in the context. | "" | | KeyGenerator | `func() string` | KeyGenerator creates a new CSRF token. | utils.UUID | | CookieExpires | `time.Duration` (Deprecated) | Deprecated: Please use Expiration. | 0 | -| Cookie | `*fiber.Cookie` (Deprecated) | Deprecated: Please use Cookie* related fields. | nil | +| Cookie | `*fiber.Cookie` (Deprecated) | Deprecated: Please use Cookie* related fields. | `nil` | | TokenLookup | `string` (Deprecated) | Deprecated: Please use KeyLookup. | "" | | ErrorHandler | `fiber.ErrorHandler` | ErrorHandler is executed when an error is returned from fiber.Handler. | DefaultErrorHandler | | Extractor | `func(*fiber.Ctx) (string, error)` | Extractor returns the CSRF token. If set, this will be used in place of an Extractor based on KeyLookup. | Extractor based on KeyLookup | +| HandlerContextKey | `string` | HandlerContextKey is used to store the CSRF Handler into context. | "fiber.csrf.handler" | -## Default Config +### Default Config ```go var ConfigDefault = Config{ - KeyLookup: "header:" + HeaderName, - CookieName: "csrf_", - CookieSameSite: "Lax", - Expiration: 1 * time.Hour, - KeyGenerator: utils.UUID, - ErrorHandler: defaultErrorHandler, - Extractor: CsrfFromHeader(HeaderName), + KeyLookup: "header:" + HeaderName, + CookieName: "csrf_", + CookieSameSite: "Lax", + Expiration: 1 * time.Hour, + KeyGenerator: utils.UUIDv4, + ErrorHandler: defaultErrorHandler, + Extractor: CsrfFromHeader(HeaderName), + SessionKey: "fiber.csrf.token", + HandlerContextKey: "fiber.csrf.handler", +} +``` + +### Recommended Config (with session) + +It's recommended to use this middleware with [fiber/middleware/session](https://docs.gofiber.io/api/middleware/session) to store the CSRF token in the session. This is generally more secure than the default configuration. + +```go +var ConfigDefault = Config{ + KeyLookup: "header:" + HeaderName, + CookieName: "csrf_", + CookieSameSite: "Lax", + CookieSessionOnly: true, + CookieHTTPOnly: true, + Expiration: 1 * time.Hour, + KeyGenerator: utils.UUIDv4, + ErrorHandler: defaultErrorHandler, + Extractor: CsrfFromHeader(HeaderName), + Session: session.Store, + SessionKey: "fiber.csrf.token", + HandlerContextKey: "fiber.csrf.handler", } ``` @@ -99,7 +207,44 @@ const ( ) ``` -### Custom Storage/Database +## Sentinel Errors + +The CSRF middleware utilizes a set of sentinel errors to handle various scenarios and communicate errors effectively. These can be used within a [custom error handler](#custom-error-handler) to handle errors returned by the middleware. + +### Errors Returned to Error Handler + +- `ErrTokenNotFound`: Indicates that the CSRF token was not found. +- `ErrTokenInvalid`: Indicates that the CSRF token is invalid. +- `ErrNoReferer`: Indicates that the referer was not supplied. +- `ErrBadReferer`: Indicates that the referer is invalid. + +If you are using the default error handler, it will return a 403 Forbidden error for any of these errors without providing any additional information to the client. + +## Custom Error Handler + +You can use a custom error handler to handle errors returned by the CSRF middleware. The error handler is executed when an error is returned from the middleware. The error handler is passed the error returned from the middleware and the fiber.Ctx. + +Example, returning a JSON response for API requests and rendering an error page for other requests: + +```go +app.Use(csrf.New(csrf.Config{ + ErrorHandler: func(c *fiber.Ctx, err error) error { + accepts := c.Accepts("html", "json") + path := c.Path() + if accepts == "json" || strings.HasPrefix(path, "/api/") { + return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ + "error": "Forbidden", + }) + } + return c.Status(fiber.StatusForbidden).Render("error", fiber.Map{ + "Title": "Forbidden", + "Status": fiber.StatusForbidden, + }, "layouts/main") + }, +})) +``` + +## Custom Storage/Database You can use any storage from our [storage](https://github.com/gofiber/storage/) package. diff --git a/docs/api/middleware/favicon.md b/docs/api/middleware/favicon.md index 3fea8b129a..b1a9b0df6c 100644 --- a/docs/api/middleware/favicon.md +++ b/docs/api/middleware/favicon.md @@ -45,6 +45,7 @@ app.Use(favicon.New(favicon.Config{ | Property | Type | Description | Default | |:-------------|:------------------------|:---------------------------------------------------------------------------------|:---------------------------| | Next | `func(*fiber.Ctx) bool` | Next defines a function to skip this middleware when returned true. | `nil` | +| Data | `[]byte` | Raw data of the favicon file. This can be used instead of `File`. | `nil` | | File | `string` | File holds the path to an actual favicon that will be cached. | "" | | URL | `string` | URL for favicon handler. | "/favicon.ico" | | FileSystem | `http.FileSystem` | FileSystem is an optional alternate filesystem to search for the favicon in. | `nil` | diff --git a/docs/api/middleware/filesystem.md b/docs/api/middleware/filesystem.md index 38e3622db7..bbeff14bf7 100644 --- a/docs/api/middleware/filesystem.md +++ b/docs/api/middleware/filesystem.md @@ -253,3 +253,48 @@ var ConfigDefault = Config{ ContentTypeCharset: "", } ``` + +## Utils + +### SendFile + +Serves a file from an [HTTP file system](https://pkg.go.dev/net/http#FileSystem) at the specified path. + +```go title="Signature" title="Signature" +func SendFile(c *fiber.Ctx, filesystem http.FileSystem, path string) error +``` +Import the middleware package that is part of the Fiber web framework + +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/middleware/filesystem" +) +``` + +```go title="Example" +// Define a route to serve a specific file +app.Get("/download", func(c *fiber.Ctx) error { + // Serve the file using SendFile function + err := filesystem.SendFile(c, http.Dir("your/filesystem/root"), "path/to/your/file.txt") + if err != nil { + // Handle the error, e.g., return a 404 Not Found response + return c.Status(fiber.StatusNotFound).SendString("File not found") + } + + return nil +}) +``` + +```go title="Example" +// Serve static files from the "build" directory using Fiber's built-in middleware. +app.Use("/", filesystem.New(filesystem.Config{ + Root: http.FS(f), // Specify the root directory for static files. + PathPrefix: "build", // Define the path prefix where static files are served. +})) + +// For all other routes (wildcard "*"), serve the "index.html" file from the "build" directory. +app.Use("*", func(ctx *fiber.Ctx) error { + return filesystem.SendFile(ctx, http.FS(f), "build/index.html") +}) +``` diff --git a/docs/api/middleware/requestid.md b/docs/api/middleware/requestid.md index 200ebf4bdd..c8ca8d599e 100644 --- a/docs/api/middleware/requestid.md +++ b/docs/api/middleware/requestid.md @@ -4,7 +4,7 @@ id: requestid # RequestID -RequestID middleware for [Fiber](https://github.com/gofiber/fiber) that adds an indentifier to the response. +RequestID middleware for [Fiber](https://github.com/gofiber/fiber) that adds an identifier to the response. ## Signatures diff --git a/docs/api/middleware/session.md b/docs/api/middleware/session.md index 65d23681e7..631599598c 100644 --- a/docs/api/middleware/session.md +++ b/docs/api/middleware/session.md @@ -16,12 +16,14 @@ This middleware uses our [Storage](https://github.com/gofiber/storage) package t func New(config ...Config) *Store func (s *Store) RegisterType(i interface{}) func (s *Store) Get(c *fiber.Ctx) (*Session, error) +func (s *Store) Delete(id string) error func (s *Store) Reset() error func (s *Session) Get(key string) interface{} func (s *Session) Set(key string, val interface{}) func (s *Session) Delete(key string) func (s *Session) Destroy() error +func (s *Session) Reset() error func (s *Session) Regenerate() error func (s *Session) Save() error func (s *Session) Fresh() bool diff --git a/docs/guide/hooks.md b/docs/guide/hooks.md index 0b7db96b33..b001786535 100644 --- a/docs/guide/hooks.md +++ b/docs/guide/hooks.md @@ -1,6 +1,6 @@ --- id: hooks -title: 🪝 Hooks +title: 🎣 Hooks sidebar_position: 6 --- diff --git a/docs/guide/routing.md b/docs/guide/routing.md index 615d1aa2f2..b6534532b0 100644 --- a/docs/guide/routing.md +++ b/docs/guide/routing.md @@ -80,7 +80,7 @@ app.Get("/user/*", func(c *fiber.Ctx) error { }) // This route path will match requests to "/v1/some/resource/name:customVerb", since the parameter character is escaped -app.Get("/v1/some/resource/name\\:customVerb", func(c *fiber.Ctx) error { +app.Get(`/v1/some/resource/name\:customVerb`, func(c *fiber.Ctx) error { return c.SendString("Hello, Community") }) ``` @@ -90,7 +90,7 @@ Since the hyphen \(`-`\) and the dot \(`.`\) are interpreted literally, they can ::: :::info -All special parameter characters can also be escaped with `"\\"` and lose their value, so you can use them in the route if you want, like in the custom methods of the [google api design guide](https://cloud.google.com/apis/design/custom_methods). +All special parameter characters can also be escaped with `"\\"` and lose their value, so you can use them in the route if you want, like in the custom methods of the [google api design guide](https://cloud.google.com/apis/design/custom_methods). It's recommended to use backticks `` ` `` because in go's regex documentation, they always use backticks to make sure it is unambiguous and the escape character doesn't interfere with regex patterns in an unexpected way. ::: ```go @@ -162,7 +162,7 @@ Constraints aren't validation for parameters. If constraint aren't valid for par | range(min,max) | :age | 91 (Integer value must be at least 18 but no more than 120) | | alpha | :name | Rick (String must consist of one or more alphabetical characters, a-z and case-insensitive) | | datetime | :dob | 2005-11-01 | -| regex(expression) | :date | 2022-08-27 (Must match regular expression) | +| regex(expression) | :date | 2022-08-27 (Must match regular expression) | **Examples** @@ -203,7 +203,7 @@ app.Get("/:test", func(c *fiber.Ctx) error { Fiber precompiles regex query when to register routes. So there're no performance overhead for regex constraint. ```go -app.Get("/:date", func(c *fiber.Ctx) error { +app.Get(`/:date`, func(c *fiber.Ctx) error { return c.SendString(c.Params("date")) }) @@ -262,6 +262,13 @@ app.Get("/", func(c *fiber.Ctx) error { `Use` method path is a **mount**, or **prefix** path, and limits middleware to only apply to any paths requested that begin with it. +### Constraints on Adding Routes Dynamically + +:::caution +Adding routes dynamically after the application has started is not supported due to design and performance considerations. Make sure to define all your routes before the application starts. +::: + + ## Grouping If you have many endpoints, you can organize your routes using `Group`. diff --git a/go.mod b/go.mod index 72fcc42b69..7d69c8838e 100644 --- a/go.mod +++ b/go.mod @@ -3,24 +3,23 @@ module github.com/gofiber/fiber/v3 go 1.20 require ( - github.com/gofiber/fiber/v2 v2.48.0 github.com/gofiber/utils/v2 v2.0.0-beta.3 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.4.0 github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.19 github.com/stretchr/testify v1.8.4 github.com/tinylib/msgp v1.1.8 github.com/valyala/bytebufferpool v1.0.0 - github.com/valyala/fasthttp v1.48.0 + github.com/valyala/fasthttp v1.50.0 ) require ( - github.com/andybalholm/brotli v1.0.5 // indirect + github.com/andybalholm/brotli v1.0.6 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.2 // indirect github.com/philhofer/fwd v1.1.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect - golang.org/x/sys v0.10.0 // indirect + golang.org/x/sys v0.14.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4cba3818ad..f8ce830357 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,13 @@ -github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= +github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gofiber/fiber/v2 v2.48.0 h1:cRVMCb9aUJDsyHxGFLwz/sGzDggdailZZyptU9F9cU0= -github.com/gofiber/fiber/v2 v2.48.0/go.mod h1:xqJgfqrc23FJuqGOW6DVgi3HyZEm2Mn9pRqUb2kHSX8= github.com/gofiber/utils/v2 v2.0.0-beta.3 h1:pfOhUDDVjBJpkWv6C5jaDyYLvpui7zQ97zpyFFsUOKw= github.com/gofiber/utils/v2 v2.0.0-beta.3/go.mod h1:jsl17+MsKfwJjM3ONCE9Rzji/j8XNbwjhUVTjzgfDCo= -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.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -25,8 +23,8 @@ github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.48.0 h1:oJWvHb9BIZToTQS3MuQ2R3bJZiNSa2KiNdeI8A+79Tc= -github.com/valyala/fasthttp v1.48.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= +github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= +github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -49,8 +47,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= diff --git a/helpers.go b/helpers.go index 952668f358..e86ed81fe4 100644 --- a/helpers.go +++ b/helpers.go @@ -17,7 +17,7 @@ import ( "time" "unsafe" - "github.com/gofiber/fiber/v2/log" + "github.com/gofiber/fiber/v3/log" "github.com/gofiber/utils/v2" "github.com/valyala/bytebufferpool" @@ -25,13 +25,14 @@ import ( ) // acceptType is a struct that holds the parsed value of an Accept header -// along with quality, specificity, and order. -// used for sorting accept headers. +// along with quality, specificity, parameters, and order. +// Used for sorting accept headers. type acceptedType struct { spec string quality float64 specificity int order int + params string } // getTLSConfig returns a net listener's tls config @@ -220,7 +221,7 @@ func getGroupPath(prefix, path string) string { // acceptsOffer This function determines if an offer matches a given specification. // It checks if the specification ends with a '*' or if the offer has the prefix of the specification. // Returns true if the offer matches the specification, false otherwise. -func acceptsOffer(spec, offer string) bool { +func acceptsOffer(spec, offer, _ string) bool { if len(spec) >= 1 && spec[len(spec)-1] == '*' { return true } else if strings.HasPrefix(spec, offer) { @@ -233,36 +234,300 @@ func acceptsOffer(spec, offer string) bool { // It checks if the specification is equal to */* (i.e., all types are accepted). // It gets the MIME type of the offer (either from the offer itself or by its file extension). // It checks if the offer MIME type matches the specification MIME type or if the specification is of the form /* and the offer MIME type has the same MIME type. +// It checks if the offer contains every parameter present in the specification. // Returns true if the offer type matches the specification, false otherwise. -func acceptsOfferType(spec, offerType string) bool { +func acceptsOfferType(spec, offerType, specParams string) bool { + var offerMime, offerParams string + + if i := strings.IndexByte(offerType, ';'); i == -1 { + offerMime = offerType + } else { + offerMime = offerType[:i] + offerParams = offerType[i:] + } + // Accept: */* if spec == "*/*" { - return true + return paramsMatch(specParams, offerParams) } var mimetype string - if strings.IndexByte(offerType, '/') != -1 { - mimetype = offerType // MIME type + if strings.IndexByte(offerMime, '/') != -1 { + mimetype = offerMime // MIME type } else { - mimetype = utils.GetMIME(offerType) // extension + mimetype = utils.GetMIME(offerMime) // extension } if spec == mimetype { // Accept: / - return true + return paramsMatch(specParams, offerParams) } s := strings.IndexByte(mimetype, '/') // Accept: /* if strings.HasPrefix(spec, mimetype[:s]) && (spec[s:] == "/*" || mimetype[s:] == "/*") { - return true + return paramsMatch(specParams, offerParams) } return false } +// paramsMatch returns whether offerParams contains all parameters present in specParams. +// Matching is case insensitive, and surrounding quotes are stripped. +// To align with the behavior of res.format from Express, the order of parameters is +// ignored, and if a parameter is specified twice in the incoming Accept, the last +// provided value is given precedence. +// In the case of quoted values, RFC 9110 says that we must treat any character escaped +// by a backslash as equivalent to the character itself (e.g., "a\aa" is equivalent to "aaa"). +// For the sake of simplicity, we forgo this and compare the value as-is. Besides, it would +// be highly unusual for a client to escape something other than a double quote or backslash. +// See https://www.rfc-editor.org/rfc/rfc9110#name-parameters +func paramsMatch(specParamStr, offerParams string) bool { + if specParamStr == "" { + return true + } + + // Preprocess the spec params to more easily test + // for out-of-order parameters + specParams := make([][2]string, 0, 2) + forEachParameter(specParamStr, func(s1, s2 string) bool { + if s1 == "q" || s1 == "Q" { + return false + } + for i := range specParams { + if utils.EqualFold(s1, specParams[i][0]) { + specParams[i][1] = s2 + return false + } + } + specParams = append(specParams, [2]string{s1, s2}) + return true + }) + + allSpecParamsMatch := true + for i := range specParams { + foundParam := false + forEachParameter(offerParams, func(offerParam, offerVal string) bool { + if utils.EqualFold(specParams[i][0], offerParam) { + foundParam = true + allSpecParamsMatch = utils.EqualFold(specParams[i][1], offerVal) + return false + } + return true + }) + if !foundParam || !allSpecParamsMatch { + return false + } + } + return allSpecParamsMatch +} + +// getSplicedStrList function takes a string and a string slice as an argument, divides the string into different +// elements divided by ',' and stores these elements in the string slice. +// It returns the populated string slice as an output. +// +// If the given slice hasn't enough space, it will allocate more and return. +func getSplicedStrList(headerValue string, dst []string) []string { + if headerValue == "" { + return nil + } + + var ( + index int + character rune + lastElementEndsAt uint8 + insertIndex int + ) + for index, character = range headerValue + "$" { + if character == ',' || index == len(headerValue) { + if insertIndex >= len(dst) { + oldSlice := dst + dst = make([]string, len(dst)+(len(dst)>>1)+2) + copy(dst, oldSlice) + } + dst[insertIndex] = strings.TrimLeft(headerValue[lastElementEndsAt:index], " ") + lastElementEndsAt = uint8(index + 1) + insertIndex++ + } + } + + if len(dst) > insertIndex { + dst = dst[:insertIndex] + } + return dst +} + +// forEachMediaRange parses an Accept or Content-Type header, calling functor +// on each media range. +// See: https://www.rfc-editor.org/rfc/rfc9110#name-content-negotiation-fields +func forEachMediaRange(header string, functor func(string)) { + hasDQuote := strings.IndexByte(header, '"') != -1 + + for len(header) > 0 { + n := 0 + header = strings.TrimLeft(header, " ") + quotes := 0 + escaping := false + + if hasDQuote { + // Complex case. We need to keep track of quotes and quoted-pairs (i.e., characters escaped with \ ) + loop: + for n < len(header) { + switch header[n] { + case ',': + if quotes%2 == 0 { + break loop + } + case '"': + if !escaping { + quotes++ + } + case '\\': + if quotes%2 == 1 { + escaping = !escaping + } + } + n++ + } + } else { + // Simple case. Just look for the next comma. + if n = strings.IndexByte(header, ','); n == -1 { + n = len(header) + } + } + + functor(header[:n]) + + if n >= len(header) { + return + } + header = header[n+1:] + } +} + +// forEachParamter parses a given parameter list, calling functor +// on each valid parameter. If functor returns false, we stop processing. +// It expects a leading ';'. +// See: https://www.rfc-editor.org/rfc/rfc9110#section-5.6.6 +// According to RFC-9110 2.4, it is up to our discretion whether +// to attempt to recover from errors in HTTP semantics. Therefor, +// we take the simple approach and exit early when a semantic error +// is detected in the header. +// +// parameter = parameter-name "=" parameter-value +// parameter-name = token +// parameter-value = ( token / quoted-string ) +// parameters = *( OWS ";" OWS [ parameter ] ) +func forEachParameter(params string, functor func(string, string) bool) { + for len(params) > 0 { + // eat OWS ";" OWS + params = strings.TrimLeft(params, " ") + if len(params) == 0 || params[0] != ';' { + return + } + params = strings.TrimLeft(params[1:], " ") + + n := 0 + + // make sure the parameter is at least one character long + if len(params) == 0 || !validHeaderFieldByte(params[n]) { + return + } + n++ + for n < len(params) && validHeaderFieldByte(params[n]) { + n++ + } + + // We should hit a '=' (that has more characters after it) + // If not, the parameter is invalid. + // param=foo + // ~~~~~^ + if n >= len(params)-1 || params[n] != '=' { + return + } + param := params[:n] + n++ + + if params[n] == '"' { + // Handle quoted strings and quoted-pairs (i.e., characters escaped with \ ) + // See: https://www.rfc-editor.org/rfc/rfc9110#section-5.6.4 + foundEndQuote := false + escaping := false + n++ + m := n + for ; n < len(params); n++ { + if params[n] == '"' && !escaping { + foundEndQuote = true + break + } + // Recipients that process the value of a quoted-string MUST handle + // a quoted-pair as if it were replaced by the octet following the backslash + escaping = params[n] == '\\' && !escaping + } + if !foundEndQuote { + // Not a valid parameter + return + } + if !functor(param, params[m:n]) { + return + } + n++ + } else if validHeaderFieldByte(params[n]) { + // Parse a normal value, which should just be a token. + m := n + n++ + for n < len(params) && validHeaderFieldByte(params[n]) { + n++ + } + if !functor(param, params[m:n]) { + return + } + } else { + // Value was invalid + return + } + params = params[n:] + } +} + +// validHeaderFieldByte returns true if a valid tchar +// +// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / +// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA +// +// See: https://www.rfc-editor.org/rfc/rfc9110#section-5.6.2 +// Function copied from net/textproto: +// https://github.com/golang/go/blob/master/src/net/textproto/reader.go#L663 +func validHeaderFieldByte(c byte) bool { + // mask is a 128-bit bitmap with 1s for allowed bytes, + // so that the byte c can be tested with a shift and an and. + // If c >= 128, then 1<>64)) != 0 +} + // getOffer return valid offer for header negotiation -func getOffer(header string, isAccepted func(spec, offer string) bool, offers ...string) string { +func getOffer(header string, isAccepted func(spec, offer, specParams string) bool, offers ...string) string { if len(offers) == 0 { return "" } @@ -270,49 +535,52 @@ func getOffer(header string, isAccepted func(spec, offer string) bool, offers .. return offers[0] } + acceptedTypes := make([]acceptedType, 0, 8) + order := 0 + // Parse header and get accepted types with their quality and specificity // See: https://www.rfc-editor.org/rfc/rfc9110#name-content-negotiation-fields - spec, commaPos, order := "", 0, 0 - acceptedTypes := make([]acceptedType, 0, 20) - for len(header) > 0 { + forEachMediaRange(header, func(accept string) { order++ + spec, quality, params := accept, 1.0, "" - // Skip spaces - header = strings.TrimLeft(header, " ") - - // Get spec - commaPos = strings.IndexByte(header, ',') - if commaPos != -1 { - spec = strings.TrimSpace(header[:commaPos]) - } else { - spec = strings.TrimLeft(header, " ") - } + if i := strings.IndexByte(accept, ';'); i != -1 { + spec = accept[:i] - // Get quality - quality := 1.0 - if factorSign := strings.IndexByte(spec, ';'); factorSign != -1 { - factor := strings.Trim(spec[factorSign+1:], " ") - if strings.HasPrefix(factor, "q=") { - if q, err := fasthttp.ParseUfloat(utils.UnsafeBytes(factor[2:])); err == nil { + // The vast majority of requests will have only the q parameter with + // no whitespace. Check this first to see if we can skip + // the more involved parsing. + if strings.HasPrefix(accept[i:], ";q=") && strings.IndexByte(accept[i+3:], ';') == -1 { + if q, err := fasthttp.ParseUfloat([]byte(strings.TrimRight(accept[i+3:], " "))); err == nil { quality = q } - } - spec = spec[:factorSign] - } - - // Skip if quality is 0.0 - // See: https://www.rfc-editor.org/rfc/rfc9110#quality.values - if quality == 0.0 { - if commaPos != -1 { - header = header[commaPos+1:] } else { - break + hasParams := false + forEachParameter(accept[i:], func(param, val string) bool { + if param == "q" || param == "Q" { + if q, err := fasthttp.ParseUfloat([]byte(val)); err == nil { + quality = q + } + return false + } + hasParams = true + return true + }) + if hasParams { + params = accept[i:] + } + } + // Skip this accept type if quality is 0.0 + // See: https://www.rfc-editor.org/rfc/rfc9110#quality.values + if quality == 0.0 { + return } - continue } + spec = strings.TrimRight(spec, " ") + // Get specificity - specificity := 0 + var specificity int // check for wildcard this could be a mime */* or a wildcard character * if spec == "*/*" || spec == "*" { specificity = 1 @@ -325,15 +593,8 @@ func getOffer(header string, isAccepted func(spec, offer string) bool, offers .. } // Add to accepted types - acceptedTypes = append(acceptedTypes, acceptedType{spec, quality, specificity, order}) - - // Next - if commaPos != -1 { - header = header[commaPos+1:] - } else { - break - } - } + acceptedTypes = append(acceptedTypes, acceptedType{spec, quality, specificity, order, params}) + }) if len(acceptedTypes) > 1 { // Sort accepted types by quality and specificity, preserving order of equal elements @@ -346,7 +607,7 @@ func getOffer(header string, isAccepted func(spec, offer string) bool, offers .. if len(offer) == 0 { continue } - if isAccepted(acceptedType.spec, offer) { + if isAccepted(acceptedType.spec, offer, acceptedType.params) { return offer } } @@ -356,30 +617,30 @@ func getOffer(header string, isAccepted func(spec, offer string) bool, offers .. } // sortAcceptedTypes sorts accepted types by quality and specificity, preserving order of equal elements -// -// Parameters are not supported, they are ignored when sorting by specificity. -// +// A type with parameters has higher priority than an equivalent one without parameters. +// e.g., text/html;a=1;b=2 comes before text/html;a=1 // See: https://www.rfc-editor.org/rfc/rfc9110#name-content-negotiation-fields -func sortAcceptedTypes(at *[]acceptedType) { - if at == nil || len(*at) < 2 { +func sortAcceptedTypes(acceptedTypes *[]acceptedType) { + if acceptedTypes == nil || len(*acceptedTypes) < 2 { return } - acceptedTypes := *at + at := *acceptedTypes - for i := 1; i < len(acceptedTypes); i++ { + for i := 1; i < len(at); i++ { lo, hi := 0, i-1 for lo <= hi { mid := (lo + hi) / 2 - if acceptedTypes[i].quality < acceptedTypes[mid].quality || - (acceptedTypes[i].quality == acceptedTypes[mid].quality && acceptedTypes[i].specificity < acceptedTypes[mid].specificity) || - (acceptedTypes[i].quality == acceptedTypes[mid].quality && acceptedTypes[i].specificity == acceptedTypes[mid].specificity && acceptedTypes[i].order > acceptedTypes[mid].order) { + if at[i].quality < at[mid].quality || + (at[i].quality == at[mid].quality && at[i].specificity < at[mid].specificity) || + (at[i].quality == at[mid].quality && at[i].specificity < at[mid].specificity && len(at[i].params) < len(at[mid].params)) || + (at[i].quality == at[mid].quality && at[i].specificity == at[mid].specificity && len(at[i].params) == len(at[mid].params) && at[i].order > at[mid].order) { lo = mid + 1 } else { hi = mid - 1 } } for j := i; j > lo; j-- { - acceptedTypes[j-1], acceptedTypes[j] = acceptedTypes[j], acceptedTypes[j-1] + at[j-1], at[j] = at[j], at[j-1] } } } @@ -582,7 +843,7 @@ const ( MIMEApplicationJavaScriptCharsetUTF8 = "application/javascript; charset=utf-8" ) -// HTTP status codes were copied from https://github.com/nginx/nginx/blob/67d2a9541826ecd5db97d604f23460210fd3e517/conf/mime.types with the following updates: +// HTTP status codes were copied from net/http with the following updates: // - Rename StatusNonAuthoritativeInfo to StatusNonAuthoritativeInformation // - Add StatusSwitchProxy (306) // NOTE: Keep this list in sync with statusMessage diff --git a/helpers_fuzz_test.go b/helpers_fuzz_test.go new file mode 100644 index 0000000000..2fce6475dd --- /dev/null +++ b/helpers_fuzz_test.go @@ -0,0 +1,23 @@ +//go:build go1.18 + +package fiber + +import ( + "testing" +) + +// go test -v -run=^$ -fuzz=FuzzUtilsGetOffer +func FuzzUtilsGetOffer(f *testing.F) { + inputs := []string{ + `application/json; v=1; foo=bar; q=0.938; extra=param, text/plain;param="big fox"; q=0.43`, + `text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8`, + `*/*`, + `text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c`, + } + for _, input := range inputs { + f.Add(input) + } + f.Fuzz(func(_ *testing.T, spec string) { + getOffer(spec, acceptsOfferType, `application/json;version=1;v=1;foo=bar`, `text/plain;param="big fox"`) + }) +} diff --git a/helpers_test.go b/helpers_test.go index 37d975d453..122ed0386d 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -30,6 +30,28 @@ func Test_Utils_GetOffer(t *testing.T) { require.Equal(t, "text/html", getOffer("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", acceptsOfferType, "text/html")) require.Equal(t, "application/pdf", getOffer("text/plain;q=0,application/pdf;q=0.9,*/*;q=0.000", acceptsOfferType, "application/pdf", "application/json")) require.Equal(t, "application/pdf", getOffer("text/plain;q=0,application/pdf;q=0.9,*/*;q=0.000", acceptsOfferType, "application/pdf", "application/json")) + require.Equal(t, "text/plain;a=1", getOffer("text/plain;a=1", acceptsOfferType, "text/plain;a=1")) + require.Equal(t, "", getOffer("text/plain;a=1;b=2", acceptsOfferType, "text/plain;b=2")) + + // Spaces, quotes, out of order params, and case insensitivity + require.Equal(t, "text/plain", getOffer("text/plain ", acceptsOfferType, "text/plain")) + require.Equal(t, "text/plain", getOffer("text/plain;q=0.4 ", acceptsOfferType, "text/plain")) + require.Equal(t, "text/plain", getOffer("text/plain;q=0.4 ;", acceptsOfferType, "text/plain")) + require.Equal(t, "text/plain", getOffer("text/plain;q=0.4 ; p=foo", acceptsOfferType, "text/plain")) + require.Equal(t, "text/plain;b=2;a=1", getOffer("text/plain ;a=1;b=2", acceptsOfferType, "text/plain;b=2;a=1")) + require.Equal(t, "text/plain;a=1", getOffer("text/plain; a=1 ", acceptsOfferType, "text/plain;a=1")) + require.Equal(t, `text/plain;a="1;b=2\",text/plain"`, getOffer(`text/plain;a="1;b=2\",text/plain";q=0.9`, acceptsOfferType, `text/plain;a=1;b=2`, `text/plain;a="1;b=2\",text/plain"`)) + require.Equal(t, "text/plain;A=CAPS", getOffer(`text/plain;a="caPs"`, acceptsOfferType, "text/plain;A=CAPS")) + + // Priority + require.Equal(t, "text/plain", getOffer("text/plain", acceptsOfferType, "text/plain", "text/plain;a=1")) + require.Equal(t, "text/plain;a=1", getOffer("text/plain", acceptsOfferType, "text/plain;a=1", "text/plain")) + require.Equal(t, "text/plain;a=1", getOffer("text/plain,text/plain;a=1", acceptsOfferType, "text/plain", "text/plain;a=1")) + require.Equal(t, "text/plain", getOffer("text/plain;q=0.899,text/plain;a=1;q=0.898", acceptsOfferType, "text/plain", "text/plain;a=1")) + require.Equal(t, "text/plain;a=1;b=2", getOffer("text/plain,text/plain;a=1,text/plain;a=1;b=2", acceptsOfferType, "text/plain", "text/plain;a=1", "text/plain;a=1;b=2")) + + // Takes the last value specified + require.Equal(t, "text/plain;a=1;b=2", getOffer("text/plain;a=1;b=1;B=2", acceptsOfferType, "text/plain;a=1;b=1", "text/plain;a=1;b=2")) require.Equal(t, "", getOffer("utf-8, iso-8859-1;q=0.5", acceptsOffer)) require.Equal(t, "", getOffer("utf-8, iso-8859-1;q=0.5", acceptsOffer, "ascii")) @@ -40,6 +62,7 @@ func Test_Utils_GetOffer(t *testing.T) { require.Equal(t, "", getOffer("gzip, deflate;q=0", acceptsOffer, "deflate")) } +// go test -v -run=^$ -bench=Benchmark_Utils_GetOffer -benchmem -count=4 func Benchmark_Utils_GetOffer(b *testing.B) { headers := []string{ "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", @@ -60,6 +83,309 @@ func Benchmark_Utils_GetOffer(b *testing.B) { } } +// go test -v -run=^$ -bench=Benchmark_Utils_GetOffer_WithParams -benchmem -count=4 +func Benchmark_Utils_GetOffer_WithParams(b *testing.B) { + headers := []string{ + "text/html;p=1,application/xhtml+xml;p=1;b=2,application/xml;a=2;q=0.9,*/*;q=0.8", + "application/json; version=1", + "utf-8, iso-8859-1;q=0.5", + } + offers := [][]string{ + {"text/html;p=1", "application/xml;a=2", "application/xml+xhtml; p=1; b=2"}, + {"application/json; version=2"}, + {`utf-8;charset="utf-16"`}, + } + for n := 0; n < b.N; n++ { + for i, header := range headers { + getOffer(header, acceptsOfferType, offers[i]...) + } + } +} + +func Test_Utils_ForEachParameter(t *testing.T) { + testCases := []struct { + description string + paramStr string + expectedParams [][]string + }{ + { + description: "empty input", + paramStr: ``, + }, + { + description: "no parameters", + paramStr: `; `, + }, + { + description: "naked equals", + paramStr: `; = `, + }, + { + description: "no value", + paramStr: `;s=`, + }, + { + description: "no name", + paramStr: `;=bar`, + }, + { + description: "illegal characters in name", + paramStr: `; foo@bar=baz`, + }, + { + description: "value starts with illegal characters", + paramStr: `; foo=@baz; param=val`, + }, + { + description: "unterminated quoted value", + paramStr: `; foo="bar`, + }, + { + description: "illegal character after value terminates parsing", + paramStr: `; foo=bar@baz; param=val`, + expectedParams: [][]string{ + {"foo", "bar"}, + }, + }, + { + description: "parses parameters", + paramStr: `; foo=bar; PARAM=BAZ`, + expectedParams: [][]string{ + {"foo", "bar"}, + {"PARAM", "BAZ"}, + }, + }, + { + description: "stops parsing when functor returns false", + paramStr: `; foo=bar; end=baz; extra=unparsed`, + expectedParams: [][]string{ + {"foo", "bar"}, + {"end", "baz"}, + }, + }, + { + description: "stops parsing when encountering a non-parameter string", + paramStr: `; foo=bar; gzip; param=baz`, + expectedParams: [][]string{ + {"foo", "bar"}, + }, + }, + { + description: "quoted string with escapes and special characters", + // Note: the sequence \\\" is effectively an escaped backslash \\ and + // an escaped double quote \" + paramStr: `;foo="20t\w,b\\\"b;s=k o"`, + expectedParams: [][]string{ + {"foo", `20t\w,b\\\"b;s=k o`}, + }, + }, + { + description: "complex", + paramStr: ` ; foo=1 ; bar="\"value\""; end="20tw,b\\\"b;s=k o" ; action=skip `, + expectedParams: [][]string{ + {"foo", "1"}, + {"bar", `\"value\"`}, + {"end", `20tw,b\\\"b;s=k o`}, + }, + }, + } + for _, tc := range testCases { + n := 0 + forEachParameter(tc.paramStr, func(p, v string) bool { + require.Equal(t, true, n < len(tc.expectedParams), "Received more parameters than expected: "+p+"="+v) + require.Equal(t, tc.expectedParams[n][0], p, tc.description) + require.Equal(t, tc.expectedParams[n][1], v, tc.description) + n++ + + // Stop parsing at the first parameter called "end" + return p != "end" + }) + require.Equal(t, len(tc.expectedParams), n, tc.description+": number of parameters differs") + } + // Check that we exited on the second parameter (bar) +} + +// go test -v -run=^$ -bench=Benchmark_Utils_ForEachParameter -benchmem -count=4 +func Benchmark_Utils_ForEachParameter(b *testing.B) { + for n := 0; n < b.N; n++ { + forEachParameter(` ; josua=1 ; vermant="20tw\",bob;sack o" ; version=1; foo=bar; `, func(s1, s2 string) bool { + return true + }) + } +} + +func Test_Utils_ParamsMatch(t *testing.T) { + testCases := []struct { + description string + accept string + offer string + match bool + }{ + { + description: "empty accept and offer", + accept: "", + offer: "", + match: true, + }, + { + description: "accept is empty, offer has params", + accept: "", + offer: ";foo=bar", + match: true, + }, + { + description: "offer is empty, accept has params", + accept: ";foo=bar", + offer: "", + match: false, + }, + { + description: "accept has extra parameters", + accept: ";foo=bar;a=1", + offer: ";foo=bar", + match: false, + }, + { + description: "matches regardless of order", + accept: "; a=1; b=2", + offer: ";b=2;a=1", + match: true, + }, + { + description: "case insensitive", + accept: ";ParaM=FoO", + offer: ";pAram=foO", + match: true, + }, + { + description: "ignores q", + accept: ";q=0.42", + offer: "", + match: true, + }, + } + + for _, tc := range testCases { + require.Equal(t, tc.match, paramsMatch(tc.accept, tc.offer), tc.description) + } +} + +func Benchmark_Utils_ParamsMatch(b *testing.B) { + var match bool + for n := 0; n < b.N; n++ { + match = paramsMatch(`; appLe=orange; param="foo"`, `;param=foo; apple=orange`) + } + require.Equal(b, true, match) +} + +func Test_Utils_AcceptsOfferType(t *testing.T) { + testCases := []struct { + description string + spec string + specParams string + offerType string + accepts bool + }{ + { + description: "no params, matching", + spec: "application/json", + offerType: "application/json", + accepts: true, + }, + { + description: "no params, mismatch", + spec: "application/json", + offerType: "application/xml", + accepts: false, + }, + { + description: "params match", + spec: "application/json", + specParams: `; format=foo; version=1`, + offerType: "application/json;version=1;format=foo;q=0.1", + accepts: true, + }, + { + description: "spec has extra params", + spec: "text/html", + specParams: "; charset=utf-8", + offerType: "text/html", + accepts: false, + }, + { + description: "offer has extra params", + spec: "text/html", + offerType: "text/html;charset=utf-8", + accepts: true, + }, + { + description: "ignores optional whitespace", + spec: "application/json", + specParams: `;format=foo; version=1`, + offerType: "application/json; version=1 ; format=foo ", + accepts: true, + }, + { + description: "ignores optional whitespace", + spec: "application/json", + specParams: `;format="foo bar"; version=1`, + offerType: `application/json;version="1";format="foo bar"`, + accepts: true, + }, + } + for _, tc := range testCases { + accepts := acceptsOfferType(tc.spec, tc.offerType, tc.specParams) + require.Equal(t, tc.accepts, accepts, tc.description) + } +} + +func Test_Utils_GetSplicedStrList(t *testing.T) { + testCases := []struct { + description string + headerValue string + expectedList []string + }{ + { + description: "normal case", + headerValue: "gzip, deflate,br", + expectedList: []string{"gzip", "deflate", "br"}, + }, + { + description: "no matter the value", + headerValue: " gzip,deflate, br, zip", + expectedList: []string{"gzip", "deflate", "br", "zip"}, + }, + { + description: "headerValue is empty", + headerValue: "", + expectedList: nil, + }, + { + description: "has a comma without element", + headerValue: "gzip,", + expectedList: []string{"gzip", ""}, + }, + } + + for _, tc := range testCases { + t.Run(tc.description, func(t *testing.T) { + dst := make([]string, 10) + result := getSplicedStrList(tc.headerValue, dst) + require.Equal(t, tc.expectedList, result) + }) + } +} + +func Benchmark_Utils_GetSplicedStrList(b *testing.B) { + destination := make([]string, 5) + result := destination + const input = `deflate, gzip,br,brotli` + for n := 0; n < b.N; n++ { + result = getSplicedStrList(input, destination) + } + require.Equal(b, []string{"deflate", "gzip", "br", "brotli"}, result) +} + func Test_Utils_SortAcceptedTypes(t *testing.T) { t.Parallel() acceptedTypes := []acceptedType{ @@ -74,6 +400,7 @@ func Test_Utils_SortAcceptedTypes(t *testing.T) { {spec: "image/*", quality: 1, specificity: 2, order: 8}, {spec: "image/gif", quality: 1, specificity: 3, order: 9}, {spec: "text/plain", quality: 1, specificity: 3, order: 10}, + {spec: "application/json", quality: 0.999, specificity: 3, params: ";a=1", order: 11}, } sortAcceptedTypes(&acceptedTypes) require.Equal(t, acceptedTypes, []acceptedType{ @@ -85,6 +412,7 @@ func Test_Utils_SortAcceptedTypes(t *testing.T) { {spec: "image/gif", quality: 1, specificity: 3, order: 9}, {spec: "text/plain", quality: 1, specificity: 3, order: 10}, {spec: "image/*", quality: 1, specificity: 2, order: 8}, + {spec: "application/json", quality: 0.999, specificity: 3, params: ";a=1", order: 11}, {spec: "application/json", quality: 0.999, specificity: 3, order: 3}, {spec: "text/*", quality: 0.5, specificity: 2, order: 1}, {spec: "*/*", quality: 0.1, specificity: 1, order: 2}, diff --git a/hooks.go b/hooks.go index 6b0b860c90..37310a4a16 100644 --- a/hooks.go +++ b/hooks.go @@ -1,7 +1,7 @@ package fiber import ( - "github.com/gofiber/fiber/v2/log" + "github.com/gofiber/fiber/v3/log" ) // OnRouteHandler Handlers define a function to create hooks for Fiber. diff --git a/log/default.go b/log/default.go index e78f1e3daf..c898cd6c83 100644 --- a/log/default.go +++ b/log/default.go @@ -188,7 +188,11 @@ func (l *defaultLogger) Panicw(msg string, keysAndValues ...interface{}) { } func (l *defaultLogger) WithContext(_ context.Context) CommonLogger { - return l + return &defaultLogger{ + stdlog: l.stdlog, + level: l.level, + depth: l.depth - 1, + } } func (l *defaultLogger) SetLevel(level Level) { diff --git a/log/default_test.go b/log/default_test.go index 7562b0a169..ee0bef8470 100644 --- a/log/default_test.go +++ b/log/default_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/gofiber/fiber/v2/utils" + "github.com/stretchr/testify/require" ) const work = "work" @@ -40,7 +40,7 @@ func Test_DefaultLogger(t *testing.T) { Warn("work may fail") Error("work failed") Panic("work panic") - utils.AssertEqual(t, "[Trace] trace work\n"+ + require.Equal(t, "[Trace] trace work\n"+ "[Debug] received work order\n"+ "[Info] starting work\n"+ "[Warn] work may fail\n"+ @@ -61,7 +61,7 @@ func Test_DefaultFormatLogger(t *testing.T) { Errorf("%s failed", work) Panicf("%s panic", work) - utils.AssertEqual(t, "[Trace] trace work\n"+ + require.Equal(t, "[Trace] trace work\n"+ "[Debug] received work order\n"+ "[Info] starting work\n"+ "[Warn] work may fail\n"+ @@ -84,7 +84,7 @@ func Test_CtxLogger(t *testing.T) { WithContext(ctx).Errorf("%s failed", work) WithContext(ctx).Panicf("%s panic", work) - utils.AssertEqual(t, "[Trace] trace work\n"+ + require.Equal(t, "[Trace] trace work\n"+ "[Debug] received work order\n"+ "[Info] starting work\n"+ "[Warn] work may fail\n"+ @@ -151,11 +151,27 @@ func Test_LogfKeyAndValues(t *testing.T) { depth: 4, } l.privateLogw(tt.level, tt.format, tt.keysAndValues) - utils.AssertEqual(t, tt.wantOutput, buf.String()) + require.Equal(t, tt.wantOutput, buf.String()) }) } } +func Test_WithContextCaller(t *testing.T) { + logger = &defaultLogger{ + stdlog: log.New(os.Stderr, "", log.Lshortfile), + depth: 4, + } + + var w byteSliceWriter + SetOutput(&w) + ctx := context.TODO() + + WithContext(ctx).Info("") + Info("") + + require.Equal(t, "default_test.go:169: [Info] \ndefault_test.go:170: [Info] \n", string(w.b)) +} + func Test_SetLevel(t *testing.T) { setLogger := &defaultLogger{ stdlog: log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile|log.Lmicroseconds), @@ -163,34 +179,34 @@ func Test_SetLevel(t *testing.T) { } setLogger.SetLevel(LevelTrace) - utils.AssertEqual(t, LevelTrace, setLogger.level) - utils.AssertEqual(t, LevelTrace.toString(), setLogger.level.toString()) + require.Equal(t, LevelTrace, setLogger.level) + require.Equal(t, LevelTrace.toString(), setLogger.level.toString()) setLogger.SetLevel(LevelDebug) - utils.AssertEqual(t, LevelDebug, setLogger.level) - utils.AssertEqual(t, LevelDebug.toString(), setLogger.level.toString()) + require.Equal(t, LevelDebug, setLogger.level) + require.Equal(t, LevelDebug.toString(), setLogger.level.toString()) setLogger.SetLevel(LevelInfo) - utils.AssertEqual(t, LevelInfo, setLogger.level) - utils.AssertEqual(t, LevelInfo.toString(), setLogger.level.toString()) + require.Equal(t, LevelInfo, setLogger.level) + require.Equal(t, LevelInfo.toString(), setLogger.level.toString()) setLogger.SetLevel(LevelWarn) - utils.AssertEqual(t, LevelWarn, setLogger.level) - utils.AssertEqual(t, LevelWarn.toString(), setLogger.level.toString()) + require.Equal(t, LevelWarn, setLogger.level) + require.Equal(t, LevelWarn.toString(), setLogger.level.toString()) setLogger.SetLevel(LevelError) - utils.AssertEqual(t, LevelError, setLogger.level) - utils.AssertEqual(t, LevelError.toString(), setLogger.level.toString()) + require.Equal(t, LevelError, setLogger.level) + require.Equal(t, LevelError.toString(), setLogger.level.toString()) setLogger.SetLevel(LevelFatal) - utils.AssertEqual(t, LevelFatal, setLogger.level) - utils.AssertEqual(t, LevelFatal.toString(), setLogger.level.toString()) + require.Equal(t, LevelFatal, setLogger.level) + require.Equal(t, LevelFatal.toString(), setLogger.level.toString()) setLogger.SetLevel(LevelPanic) - utils.AssertEqual(t, LevelPanic, setLogger.level) - utils.AssertEqual(t, LevelPanic.toString(), setLogger.level.toString()) + require.Equal(t, LevelPanic, setLogger.level) + require.Equal(t, LevelPanic.toString(), setLogger.level.toString()) setLogger.SetLevel(8) - utils.AssertEqual(t, 8, int(setLogger.level)) - utils.AssertEqual(t, "[?8] ", setLogger.level.toString()) + require.Equal(t, 8, int(setLogger.level)) + require.Equal(t, "[?8] ", setLogger.level.toString()) } diff --git a/log/fiberlog_test.go b/log/fiberlog_test.go index 15b1a2cd9d..eb7b077a9b 100644 --- a/log/fiberlog_test.go +++ b/log/fiberlog_test.go @@ -5,12 +5,12 @@ import ( "os" "testing" - "github.com/gofiber/fiber/v2/utils" + "github.com/stretchr/testify/require" ) func Test_DefaultSystemLogger(t *testing.T) { defaultL := DefaultLogger() - utils.AssertEqual(t, logger, defaultL) + require.Equal(t, logger, defaultL) } func Test_SetLogger(t *testing.T) { @@ -20,5 +20,5 @@ func Test_SetLogger(t *testing.T) { } SetLogger(setLog) - utils.AssertEqual(t, logger, setLog) + require.Equal(t, logger, setLog) } diff --git a/middleware/adaptor/adaptor.go b/middleware/adaptor/adaptor.go index 79f779865a..91ff2d715e 100644 --- a/middleware/adaptor/adaptor.go +++ b/middleware/adaptor/adaptor.go @@ -116,14 +116,9 @@ func handlerFunc(app *fiber.App, h ...fiber.Handler) http.HandlerFunc { defer fasthttp.ReleaseRequest(req) // Convert net/http -> fasthttp request if r.Body != nil { - body, err := io.ReadAll(r.Body) - if err != nil { - http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError) - return - } + n, err := io.Copy(req.BodyWriter(), r.Body) + req.Header.SetContentLength(int(n)) - req.Header.SetContentLength(len(body)) - _, err = req.BodyWriter().Write(body) if err != nil { http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError) return diff --git a/middleware/adaptor/adaptor_test.go b/middleware/adaptor/adaptor_test.go index 044e59fb93..4728c4bc60 100644 --- a/middleware/adaptor/adaptor_test.go +++ b/middleware/adaptor/adaptor_test.go @@ -2,6 +2,7 @@ package adaptor import ( + "bytes" "context" "fmt" "io" @@ -9,7 +10,6 @@ import ( "net/http" "net/http/httptest" "net/url" - "reflect" "testing" "github.com/gofiber/fiber/v3" @@ -33,61 +33,33 @@ func Test_HTTPHandler(t *testing.T) { "XXX-Remote-Addr": "123.43.4543.345", } expectedURL, err := url.ParseRequestURI(expectedRequestURI) - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + require.NoError(t, err) + expectedContextKey := "contextKey" expectedContextValue := "contextValue" callsCount := 0 nethttpH := func(w http.ResponseWriter, r *http.Request) { callsCount++ - if r.Method != expectedMethod { - t.Fatalf("unexpected method %q. Expecting %q", r.Method, expectedMethod) - } - if r.Proto != expectedProto { - t.Fatalf("unexpected proto %q. Expecting %q", r.Proto, expectedProto) - } - if r.ProtoMajor != expectedProtoMajor { - t.Fatalf("unexpected protoMajor %d. Expecting %d", r.ProtoMajor, expectedProtoMajor) - } - if r.ProtoMinor != expectedProtoMinor { - t.Fatalf("unexpected protoMinor %d. Expecting %d", r.ProtoMinor, expectedProtoMinor) - } - if r.RequestURI != expectedRequestURI { - t.Fatalf("unexpected requestURI %q. Expecting %q", r.RequestURI, expectedRequestURI) - } - if r.ContentLength != int64(expectedContentLength) { - t.Fatalf("unexpected contentLength %d. Expecting %d", r.ContentLength, expectedContentLength) - } - if len(r.TransferEncoding) != 0 { - t.Fatalf("unexpected transferEncoding %q. Expecting []", r.TransferEncoding) - } - if r.Host != expectedHost { - t.Fatalf("unexpected host %q. Expecting %q", r.Host, expectedHost) - } - if r.RemoteAddr != expectedRemoteAddr { - t.Fatalf("unexpected remoteAddr %q. Expecting %q", r.RemoteAddr, expectedRemoteAddr) - } + require.Equal(t, expectedMethod, r.Method, "Method") + require.Equal(t, expectedProto, r.Proto, "Proto") + require.Equal(t, expectedProtoMajor, r.ProtoMajor, "ProtoMajor") + require.Equal(t, expectedProtoMinor, r.ProtoMinor, "ProtoMinor") + require.Equal(t, expectedRequestURI, r.RequestURI, "RequestURI") + require.Equal(t, expectedContentLength, int(r.ContentLength), "ContentLength") + require.Equal(t, 0, len(r.TransferEncoding), "TransferEncoding") + require.Equal(t, expectedHost, r.Host, "Host") + require.Equal(t, expectedRemoteAddr, r.RemoteAddr, "RemoteAddr") + body, err := io.ReadAll(r.Body) - if err != nil { - t.Fatalf("unexpected error when reading request body: %s", err) - } - if string(body) != expectedBody { - t.Fatalf("unexpected body %q. Expecting %q", body, expectedBody) - } - if !reflect.DeepEqual(r.URL, expectedURL) { - t.Fatalf("unexpected URL: %#v. Expecting %#v", r.URL, expectedURL) - } - if r.Context().Value(expectedContextKey) != expectedContextValue { - t.Fatalf("unexpected context value for key %q. Expecting %q", expectedContextKey, expectedContextValue) - } + require.NoError(t, err) + require.Equal(t, expectedBody, string(body), "Body") + require.Equal(t, expectedURL, r.URL, "URL") + require.Equal(t, expectedContextValue, r.Context().Value(expectedContextKey), "Context") for k, expectedV := range expectedHeader { v := r.Header.Get(k) - if v != expectedV { - t.Fatalf("unexpected header value %q for key %q. Expecting %q", v, k, expectedV) - } + require.Equal(t, expectedV, v, "Header") } w.Header().Set("Header1", "value1") @@ -110,37 +82,24 @@ func Test_HTTPHandler(t *testing.T) { } remoteAddr, err := net.ResolveTCPAddr("tcp", expectedRemoteAddr) - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + require.NoError(t, err) + fctx.Init(&req, remoteAddr, nil) app := fiber.New() ctx := app.NewCtx(&fctx) defer app.ReleaseCtx(ctx) err = fiberH(ctx) - if err != nil { - t.Fatalf("unexpected error: %s", err) - } - - if callsCount != 1 { - t.Fatalf("unexpected callsCount: %d. Expecting 1", callsCount) - } + require.NoError(t, err) + require.Equal(t, 1, callsCount, "callsCount") resp := &fctx.Response - if resp.StatusCode() != fiber.StatusBadRequest { - t.Fatalf("unexpected statusCode: %d. Expecting %d", resp.StatusCode(), fiber.StatusBadRequest) - } - if string(resp.Header.Peek("Header1")) != "value1" { - t.Fatalf("unexpected header value: %q. Expecting %q", resp.Header.Peek("Header1"), "value1") - } - if string(resp.Header.Peek("Header2")) != "value2" { - t.Fatalf("unexpected header value: %q. Expecting %q", resp.Header.Peek("Header2"), "value2") - } + require.Equal(t, http.StatusBadRequest, resp.StatusCode(), "StatusCode") + require.Equal(t, "value1", string(resp.Header.Peek("Header1")), "Header1") + require.Equal(t, "value2", string(resp.Header.Peek("Header2")), "Header2") + expectedResponseBody := fmt.Sprintf("request body is %q", expectedBody) - if string(resp.Body()) != expectedResponseBody { - t.Fatalf("unexpected response body %q. Expecting %q", resp.Body(), expectedResponseBody) - } + require.Equal(t, expectedResponseBody, string(resp.Body()), "Body") } type contextKey string @@ -219,32 +178,20 @@ func Test_HTTPMiddleware(t *testing.T) { for _, tt := range tests { req, err := http.NewRequestWithContext(context.Background(), tt.method, tt.url, nil) - if err != nil { - t.Fatalf(`%s: %s`, t.Name(), err) - } + require.NoError(t, err) + resp, err := app.Test(req) - if err != nil { - t.Fatalf(`%s: %s`, t.Name(), err) - } - if resp.StatusCode != tt.statusCode { - t.Fatalf(`%s: StatusCode: got %v - expected %v`, t.Name(), resp.StatusCode, tt.statusCode) - } + require.NoError(t, err) + require.Equal(t, tt.statusCode, resp.StatusCode, "StatusCode") } req, err := http.NewRequestWithContext(context.Background(), fiber.MethodPost, "/", nil) - if err != nil { - t.Fatalf(`%s: %s`, t.Name(), err) - } + require.NoError(t, err) + resp, err := app.Test(req) - if err != nil { - t.Fatalf(`%s: %s`, t.Name(), err) - } - if resp.Header.Get("context_okay") != "okay" { - t.Fatalf(`%s: Header context_okay: got %v - expected %v`, t.Name(), resp.Header.Get("context_okay"), "okay") - } - if resp.Header.Get("context_second_okay") != "okay" { - t.Fatalf(`%s: Header context_second_okay: got %v - expected %v`, t.Name(), resp.Header.Get("context_second_okay"), "okay") - } + require.NoError(t, err) + require.Equal(t, resp.Header.Get("context_okay"), "okay") + require.Equal(t, resp.Header.Get("context_second_okay"), "okay") } func Test_FiberHandler(t *testing.T) { @@ -281,43 +228,24 @@ func testFiberToHandlerFunc(t *testing.T, checkDefaultPort bool, app ...*fiber.A "XXX-Remote-Addr": "123.43.4543.345", } expectedURL, err := url.ParseRequestURI(expectedRequestURI) - if err != nil { - t.Fatalf("unexpected error: %s", err) - } + require.NoError(t, err) callsCount := 0 fiberH := func(c fiber.Ctx) error { callsCount++ - if c.Method() != expectedMethod { - t.Fatalf("unexpected method %q. Expecting %q", c.Method(), expectedMethod) - } - if string(c.Context().RequestURI()) != expectedRequestURI { - t.Fatalf("unexpected requestURI %q. Expecting %q", string(c.Context().RequestURI()), expectedRequestURI) - } - contentLength := c.Context().Request.Header.ContentLength() - if contentLength != expectedContentLength { - t.Fatalf("unexpected contentLength %d. Expecting %d", contentLength, expectedContentLength) - } - if c.Hostname() != expectedHost { - t.Fatalf("unexpected host %q. Expecting %q", c.Hostname(), expectedHost) - } - remoteAddr := c.Context().RemoteAddr().String() - if remoteAddr != expectedRemoteAddr { - t.Fatalf("unexpected remoteAddr %q. Expecting %q", remoteAddr, expectedRemoteAddr) - } + require.Equal(t, expectedMethod, c.Method(), "Method") + require.Equal(t, expectedRequestURI, string(c.Context().RequestURI()), "RequestURI") + require.Equal(t, expectedContentLength, c.Context().Request.Header.ContentLength(), "ContentLength") + require.Equal(t, expectedHost, c.Hostname(), "Host") + require.Equal(t, expectedRemoteAddr, c.Context().RemoteAddr().String(), "RemoteAddr") + body := string(c.Body()) - if body != expectedBody { - t.Fatalf("unexpected body %q. Expecting %q", body, expectedBody) - } - if c.OriginalURL() != expectedURL.String() { - t.Fatalf("unexpected URL: %#v. Expecting %#v", c.OriginalURL(), expectedURL) - } + require.Equal(t, expectedBody, body, "Body") + require.Equal(t, expectedURL.String(), c.OriginalURL(), "URL") for k, expectedV := range expectedHeader { v := c.Get(k) - if v != expectedV { - t.Fatalf("unexpected header value %q for key %q. Expecting %q", v, k, expectedV) - } + require.Equal(t, expectedV, v, "Header") } c.Set("Header1", "value1") @@ -356,19 +284,12 @@ func testFiberToHandlerFunc(t *testing.T, checkDefaultPort bool, app ...*fiber.A var w netHTTPResponseWriter handlerFunc.ServeHTTP(&w, &r) - if w.StatusCode() != http.StatusBadRequest { - t.Fatalf("unexpected statusCode: %d. Expecting %d", w.StatusCode(), http.StatusBadRequest) - } - if w.Header().Get("Header1") != "value1" { - t.Fatalf("unexpected header value: %q. Expecting %q", w.Header().Get("Header1"), "value1") - } - if w.Header().Get("Header2") != "value2" { - t.Fatalf("unexpected header value: %q. Expecting %q", w.Header().Get("Header2"), "value2") - } + require.Equal(t, http.StatusBadRequest, w.StatusCode(), "StatusCode") + require.Equal(t, "value1", w.Header().Get("Header1"), "Header1") + require.Equal(t, "value2", w.Header().Get("Header2"), "Header2") + expectedResponseBody := fmt.Sprintf("request body is %q", expectedBody) - if string(w.body) != expectedResponseBody { - t.Fatalf("unexpected response body %q. Expecting %q", string(w.body), expectedResponseBody) - } + require.Equal(t, expectedResponseBody, string(w.body), "Body") } func setFiberContextValueMiddleware(next fiber.Handler, key string, value interface{}) fiber.Handler { @@ -386,16 +307,9 @@ func Test_FiberHandler_RequestNilBody(t *testing.T) { callsCount := 0 fiberH := func(c fiber.Ctx) error { callsCount++ - if c.Method() != expectedMethod { - t.Fatalf("unexpected method %q. Expecting %q", c.Method(), expectedMethod) - } - if string(c.Request().RequestURI()) != expectedRequestURI { - t.Fatalf("unexpected requestURI %q. Expecting %q", string(c.Request().RequestURI()), expectedRequestURI) - } - contentLength := c.Request().Header.ContentLength() - if contentLength != expectedContentLength { - t.Fatalf("unexpected contentLength %d. Expecting %d", contentLength, expectedContentLength) - } + require.Equal(t, expectedMethod, c.Method(), "Method") + require.Equal(t, expectedRequestURI, string(c.Context().RequestURI()), "RequestURI") + require.Equal(t, expectedContentLength, c.Context().Request.Header.ContentLength(), "ContentLength") _, err := c.Write([]byte("request body is nil")) return err @@ -411,9 +325,7 @@ func Test_FiberHandler_RequestNilBody(t *testing.T) { nethttpH.ServeHTTP(&w, &r) expectedResponseBody := "request body is nil" - if string(w.body) != expectedResponseBody { - t.Fatalf("unexpected response body %q. Expecting %q", string(w.body), expectedResponseBody) - } + require.Equal(t, expectedResponseBody, string(w.body), "Body") } type netHTTPBody struct { @@ -485,3 +397,88 @@ func Test_ConvertRequest(t *testing.T) { require.NoError(t, err) require.Equal(t, "Request URL: /test?hello=world&another=test", string(body)) } + +// Benchmark for FiberHandlerFunc +func Benchmark_FiberHandlerFunc_1MB(b *testing.B) { + fiberH := func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + } + handlerFunc := FiberHandlerFunc(fiberH) + + // Create body content + bodyContent := make([]byte, 1*1024*1024) + bodyBuffer := bytes.NewBuffer(bodyContent) + + r := http.Request{ + Method: http.MethodPost, + Body: http.NoBody, + } + + // Replace the empty Body with our buffer + r.Body = io.NopCloser(bodyBuffer) + defer r.Body.Close() //nolint:errcheck // not needed + + // Create recorder + w := httptest.NewRecorder() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + handlerFunc.ServeHTTP(w, &r) + } +} + +func Benchmark_FiberHandlerFunc_10MB(b *testing.B) { + fiberH := func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + } + handlerFunc := FiberHandlerFunc(fiberH) + + // Create body content + bodyContent := make([]byte, 10*1024*1024) + bodyBuffer := bytes.NewBuffer(bodyContent) + + r := http.Request{ + Method: http.MethodPost, + Body: http.NoBody, + } + + // Replace the empty Body with our buffer + r.Body = io.NopCloser(bodyBuffer) + defer r.Body.Close() //nolint:errcheck // not needed + + // Create recorder + w := httptest.NewRecorder() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + handlerFunc.ServeHTTP(w, &r) + } +} + +func Benchmark_FiberHandlerFunc_50MB(b *testing.B) { + fiberH := func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + } + handlerFunc := FiberHandlerFunc(fiberH) + + // Create body content + bodyContent := make([]byte, 50*1024*1024) + bodyBuffer := bytes.NewBuffer(bodyContent) + + r := http.Request{ + Method: http.MethodPost, + Body: http.NoBody, + } + + // Replace the empty Body with our buffer + r.Body = io.NopCloser(bodyBuffer) + defer r.Body.Close() //nolint:errcheck // not needed + + // Create recorder + w := httptest.NewRecorder() + + b.ResetTimer() + for i := 0; i < b.N; i++ { + handlerFunc.ServeHTTP(w, &r) + } +} diff --git a/middleware/cache/cache.go b/middleware/cache/cache.go index 14ec063191..0ace704e35 100644 --- a/middleware/cache/cache.go +++ b/middleware/cache/cache.go @@ -141,10 +141,8 @@ func New(config ...Config) fiber.Handler { if len(e.cencoding) > 0 { c.Response().Header.SetBytesV(fiber.HeaderContentEncoding, e.cencoding) } - if e.headers != nil { - for k, v := range e.headers { - c.Response().Header.SetBytesV(k, v) - } + for k, v := range e.headers { + c.Response().Header.SetBytesV(k, v) } // Set Cache-Control header if enabled if cfg.CacheControl { diff --git a/middleware/compress/compress_test.go b/middleware/compress/compress_test.go index 0960cfef39..ea7ec6b447 100644 --- a/middleware/compress/compress_test.go +++ b/middleware/compress/compress_test.go @@ -53,6 +53,7 @@ func Test_Compress_Different_Level(t *testing.T) { t.Parallel() levels := []Level{LevelBestSpeed, LevelBestCompression} for _, level := range levels { + level := level t.Run(fmt.Sprintf("level %d", level), func(t *testing.T) { t.Parallel() app := fiber.New() diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index de66849b8c..d33aeeadc6 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -54,6 +54,9 @@ type Config struct { // MaxAge indicates how long (in seconds) the results of a preflight request // can be cached. + // If you pass MaxAge 0, Access-Control-Max-Age header will not be added and + // browser will use 5 seconds by default. + // To disable caching completely, pass MaxAge value negative. It will set the Access-Control-Max-Age header 0. // // Optional. Default value 0. MaxAge int @@ -187,6 +190,8 @@ func New(config ...Config) fiber.Handler { // Set MaxAge is set if cfg.MaxAge > 0 { c.Set(fiber.HeaderAccessControlMaxAge, maxAge) + } else if cfg.MaxAge < 0 { + c.Set(fiber.HeaderAccessControlMaxAge, "0") } // Send 204 No Content diff --git a/middleware/cors/cors_test.go b/middleware/cors/cors_test.go index 4173d8addb..c83c9d96fd 100644 --- a/middleware/cors/cors_test.go +++ b/middleware/cors/cors_test.go @@ -26,6 +26,20 @@ func Test_CORS_Empty_Config(t *testing.T) { testDefaultOrEmptyConfig(t, app) } +func Test_CORS_Negative_MaxAge(t *testing.T) { + t.Parallel() + + app := fiber.New() + app.Use(New(Config{MaxAge: -1})) + + ctx := &fasthttp.RequestCtx{} + ctx.Request.Header.SetMethod(fiber.MethodOptions) + ctx.Request.Header.Set(fiber.HeaderOrigin, "localhost") + app.Handler()(ctx) + + require.Equal(t, "0", string(ctx.Response.Header.Peek(fiber.HeaderAccessControlMaxAge))) +} + func testDefaultOrEmptyConfig(t *testing.T, app *fiber.App) { t.Helper() diff --git a/middleware/csrf/config.go b/middleware/csrf/config.go index c512082cbf..ffea9fc7bd 100644 --- a/middleware/csrf/config.go +++ b/middleware/csrf/config.go @@ -6,6 +6,8 @@ import ( "time" "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/log" + "github.com/gofiber/fiber/v3/middleware/session" "github.com/gofiber/utils/v2" ) @@ -32,6 +34,7 @@ type Config struct { // Name of the session cookie. This cookie will store session key. // Optional. Default value "csrf_". + // Overridden if KeyLookup == "cookie:" CookieName string // Domain of the CSRF cookie. @@ -63,11 +66,29 @@ type Config struct { // Optional. Default: 1 * time.Hour Expiration time.Duration + // SingleUseToken indicates if the CSRF token be destroyed + // and a new one generated on each use. + // + // Optional. Default: false + SingleUseToken bool + // Store is used to store the state of the middleware // // Optional. Default: memory.New() + // Ignored if Session is set. Storage fiber.Storage + // Session is used to store the state of the middleware + // + // Optional. Default: nil + // If set, the middleware will use the session store instead of the storage + Session *session.Store + + // SessionKey is the key used to store the token in the session + // + // Default: "fiber.csrf.token" + SessionKey string + // Context key to store generated CSRF token into context. // If left empty, token will not be stored in context. // @@ -79,6 +100,15 @@ type Config struct { // Optional. Default: utils.UUID KeyGenerator func() string + // Deprecated: Please use Expiration + CookieExpires time.Duration + + // Deprecated: Please use Cookie* related fields + Cookie *fiber.Cookie + + // Deprecated: Please use KeyLookup + TokenLookup string + // ErrorHandler is executed when an error is returned from fiber.Handler. // // Optional. Default: DefaultErrorHandler @@ -90,19 +120,26 @@ type Config struct { // // Optional. Default will create an Extractor based on KeyLookup. Extractor func(c fiber.Ctx) (string, error) + + // HandlerContextKey is used to store the CSRF Handler into context + // + // Default: "fiber.csrf.handler" + HandlerContextKey string } const HeaderName = "X-Csrf-Token" // ConfigDefault is the default config var ConfigDefault = Config{ - KeyLookup: "header:" + HeaderName, - CookieName: "csrf_", - CookieSameSite: "Lax", - Expiration: 1 * time.Hour, - KeyGenerator: utils.UUID, - ErrorHandler: defaultErrorHandler, - Extractor: CsrfFromHeader(HeaderName), + KeyLookup: "header:" + HeaderName, + CookieName: "csrf_", + CookieSameSite: "Lax", + Expiration: 1 * time.Hour, + KeyGenerator: utils.UUIDv4, + ErrorHandler: defaultErrorHandler, + Extractor: CsrfFromHeader(HeaderName), + SessionKey: "fiber.csrf.token", + HandlerContextKey: "fiber.csrf.handler", } // default ErrorHandler that process return error from fiber.Handler @@ -121,6 +158,31 @@ func configDefault(config ...Config) Config { cfg := config[0] // Set default values + if cfg.TokenLookup != "" { + log.Warn("[CSRF] TokenLookup is deprecated, please use KeyLookup") + cfg.KeyLookup = cfg.TokenLookup + } + if int(cfg.CookieExpires.Seconds()) > 0 { + log.Warn("[CSRF] CookieExpires is deprecated, please use Expiration") + cfg.Expiration = cfg.CookieExpires + } + if cfg.Cookie != nil { + log.Warn("[CSRF] Cookie is deprecated, please use Cookie* related fields") + if cfg.Cookie.Name != "" { + cfg.CookieName = cfg.Cookie.Name + } + if cfg.Cookie.Domain != "" { + cfg.CookieDomain = cfg.Cookie.Domain + } + if cfg.Cookie.Path != "" { + cfg.CookiePath = cfg.Cookie.Path + } + cfg.CookieSecure = cfg.Cookie.Secure + cfg.CookieHTTPOnly = cfg.Cookie.HTTPOnly + if cfg.Cookie.SameSite != "" { + cfg.CookieSameSite = cfg.Cookie.SameSite + } + } if cfg.KeyLookup == "" { cfg.KeyLookup = ConfigDefault.KeyLookup } @@ -139,6 +201,12 @@ func configDefault(config ...Config) Config { if cfg.ErrorHandler == nil { cfg.ErrorHandler = ConfigDefault.ErrorHandler } + if cfg.SessionKey == "" { + cfg.SessionKey = ConfigDefault.SessionKey + } + if cfg.HandlerContextKey == "" { + cfg.HandlerContextKey = ConfigDefault.HandlerContextKey + } // Generate the correct extractor to get the token from the correct location selectors := strings.Split(cfg.KeyLookup, ":") @@ -160,7 +228,14 @@ func configDefault(config ...Config) Config { case "param": cfg.Extractor = CsrfFromParam(selectors[1]) case "cookie": + if cfg.Session == nil { + log.Warn("[CSRF] Cookie extractor is not recommended without a session store") + } + if cfg.CookieSameSite == "None" || cfg.CookieSameSite != "Lax" && cfg.CookieSameSite != "Strict" { + log.Warn("[CSRF] Cookie extractor is only recommended for use with SameSite=Lax or SameSite=Strict") + } cfg.Extractor = CsrfFromCookie(selectors[1]) + cfg.CookieName = selectors[1] // Cookie name is the same as the key } } diff --git a/middleware/csrf/csrf.go b/middleware/csrf/csrf.go index bd6c32cac8..c4edaaaa4f 100644 --- a/middleware/csrf/csrf.go +++ b/middleware/csrf/csrf.go @@ -2,22 +2,43 @@ package csrf import ( "errors" + "net/url" + "reflect" "time" "github.com/gofiber/fiber/v3" ) -var errTokenNotFound = errors.New("csrf token not found") +var ( + ErrTokenNotFound = errors.New("csrf token not found") + ErrTokenInvalid = errors.New("csrf token invalid") + ErrNoReferer = errors.New("referer not supplied") + ErrBadReferer = errors.New("referer invalid") + dummyValue = []byte{'+'} +) + +type CSRFHandler struct { + config *Config + sessionManager *sessionManager + storageManager *storageManager +} // New creates a new middleware handler func New(config ...Config) fiber.Handler { // Set default config cfg := configDefault(config...) - // Create manager to simplify storage operations ( see manager.go ) - manager := newManager(cfg.Storage) + // Create manager to simplify storage operations ( see *_manager.go ) + var sessionManager *sessionManager + var storageManager *storageManager + if cfg.Session != nil { + // Register the Token struct in the session store + cfg.Session.RegisterType(Token{}) - dummyValue := []byte{'+'} + sessionManager = newSessionManager(cfg.Session, cfg.SessionKey) + } else { + storageManager = newStorageManager(cfg.Storage) + } // Return new handler return func(c fiber.Ctx) error { @@ -26,36 +47,69 @@ func New(config ...Config) fiber.Handler { return c.Next() } + // Store the CSRF handler in the context if a context key is specified + if cfg.HandlerContextKey != "" { + c.Locals(cfg.HandlerContextKey, &CSRFHandler{ + config: &cfg, + sessionManager: sessionManager, + storageManager: storageManager, + }) + } + var token string // Action depends on the HTTP method switch c.Method() { case fiber.MethodGet, fiber.MethodHead, fiber.MethodOptions, fiber.MethodTrace: - // Declare empty token and try to get existing CSRF from cookie - token = c.Cookies(cfg.CookieName) + cookieToken := c.Cookies(cfg.CookieName) + + if cookieToken != "" { + raw := getRawFromStorage(c, cookieToken, cfg, sessionManager, storageManager) + + if raw != nil { + token = cookieToken // Token is valid, safe to set it + } + } default: // Assume that anything not defined as 'safe' by RFC7231 needs protection + // Enforce an origin check for HTTPS connections. + if c.Protocol() == "https" { + if err := refererMatchesHost(c); err != nil { + return cfg.ErrorHandler(c, err) + } + } + // Extract token from client request i.e. header, query, param, form or cookie - token, err := cfg.Extractor(c) + extractedToken, err := cfg.Extractor(c) if err != nil { return cfg.ErrorHandler(c, err) } - // if token does not exist in Storage - if manager.getRaw(token) == nil { - // Expire cookie - c.Cookie(&fiber.Cookie{ - Name: cfg.CookieName, - Domain: cfg.CookieDomain, - Path: cfg.CookiePath, - Expires: time.Now().Add(-1 * time.Minute), - Secure: cfg.CookieSecure, - HTTPOnly: cfg.CookieHTTPOnly, - SameSite: cfg.CookieSameSite, - SessionOnly: cfg.CookieSessionOnly, - }) - return cfg.ErrorHandler(c, errTokenNotFound) + if extractedToken == "" { + return cfg.ErrorHandler(c, ErrTokenNotFound) + } + + // If not using CsrfFromCookie extractor, check that the token matches the cookie + // This is to prevent CSRF attacks by using a Double Submit Cookie method + // Useful when we do not have access to the users Session + if !isCsrfFromCookie(cfg.Extractor) && !compareStrings(extractedToken, c.Cookies(cfg.CookieName)) { + return cfg.ErrorHandler(c, ErrTokenInvalid) + } + + raw := getRawFromStorage(c, extractedToken, cfg, sessionManager, storageManager) + + if raw == nil { + // If token is not in storage, expire the cookie + expireCSRFCookie(c, cfg) + // and return an error + return cfg.ErrorHandler(c, ErrTokenNotFound) + } + if cfg.SingleUseToken { + // If token is single use, delete it from storage + deleteTokenFromStorage(c, extractedToken, cfg, sessionManager, storageManager) + } else { + token = extractedToken // Token is valid, safe to set it } } @@ -65,29 +119,16 @@ func New(config ...Config) fiber.Handler { token = cfg.KeyGenerator() } - // Add/update token to Storage - manager.setRaw(token, dummyValue, cfg.Expiration) - - // Create cookie to pass token to client - cookie := &fiber.Cookie{ - Name: cfg.CookieName, - Value: token, - Domain: cfg.CookieDomain, - Path: cfg.CookiePath, - Expires: time.Now().Add(cfg.Expiration), - Secure: cfg.CookieSecure, - HTTPOnly: cfg.CookieHTTPOnly, - SameSite: cfg.CookieSameSite, - SessionOnly: cfg.CookieSessionOnly, - } - // Set cookie to response - c.Cookie(cookie) + // Create or extend the token in the storage + createOrExtendTokenInStorage(c, token, cfg, sessionManager, storageManager) + + // Update the CSRF cookie + updateCSRFCookie(c, cfg, token) - // Protect clients from caching the response by telling the browser - // a new header value is generated + // Tell the browser that a new header value is generated c.Vary(fiber.HeaderCookie) - // Store token in context if set + // Store the token in the context if a context key is specified if cfg.ContextKey != "" { c.Locals(cfg.ContextKey, token) } @@ -96,3 +137,102 @@ func New(config ...Config) fiber.Handler { return c.Next() } } + +// getRawFromStorage returns the raw value from the storage for the given token +// returns nil if the token does not exist, is expired or is invalid +func getRawFromStorage(c fiber.Ctx, token string, cfg Config, sessionManager *sessionManager, storageManager *storageManager) []byte { + if cfg.Session != nil { + return sessionManager.getRaw(c, token, dummyValue) + } + return storageManager.getRaw(token) +} + +// createOrExtendTokenInStorage creates or extends the token in the storage +func createOrExtendTokenInStorage(c fiber.Ctx, token string, cfg Config, sessionManager *sessionManager, storageManager *storageManager) { + if cfg.Session != nil { + sessionManager.setRaw(c, token, dummyValue, cfg.Expiration) + } else { + storageManager.setRaw(token, dummyValue, cfg.Expiration) + } +} + +func deleteTokenFromStorage(c fiber.Ctx, token string, cfg Config, sessionManager *sessionManager, storageManager *storageManager) { + if cfg.Session != nil { + sessionManager.delRaw(c) + } else { + storageManager.delRaw(token) + } +} + +// Update CSRF cookie +// if expireCookie is true, the cookie will expire immediately +func updateCSRFCookie(c fiber.Ctx, cfg Config, token string) { + setCSRFCookie(c, cfg, token, cfg.Expiration) +} + +func expireCSRFCookie(c fiber.Ctx, cfg Config) { + setCSRFCookie(c, cfg, "", -time.Hour) +} + +func setCSRFCookie(c fiber.Ctx, cfg Config, token string, expiry time.Duration) { + cookie := &fiber.Cookie{ + Name: cfg.CookieName, + Value: token, + Domain: cfg.CookieDomain, + Path: cfg.CookiePath, + Secure: cfg.CookieSecure, + HTTPOnly: cfg.CookieHTTPOnly, + SameSite: cfg.CookieSameSite, + SessionOnly: cfg.CookieSessionOnly, + Expires: time.Now().Add(expiry), + } + + // Set the CSRF cookie to the response + c.Cookie(cookie) +} + +// DeleteToken removes the token found in the context from the storage +// and expires the CSRF cookie +func (handler *CSRFHandler) DeleteToken(c fiber.Ctx) error { + // Get the config from the context + config := handler.config + if config == nil { + panic("CSRFHandler config not found in context") + } + // Extract token from the client request cookie + cookieToken := c.Cookies(config.CookieName) + if cookieToken == "" { + return config.ErrorHandler(c, ErrTokenNotFound) + } + // Remove the token from storage + deleteTokenFromStorage(c, cookieToken, *config, handler.sessionManager, handler.storageManager) + // Expire the cookie + expireCSRFCookie(c, *config) + return nil +} + +// isCsrfFromCookie checks if the extractor is set to ExtractFromCookie +func isCsrfFromCookie(extractor interface{}) bool { + return reflect.ValueOf(extractor).Pointer() == reflect.ValueOf(CsrfFromCookie).Pointer() +} + +// refererMatchesHost checks that the referer header matches the host header +// returns an error if the referer header is not present or is invalid +// returns nil if the referer header is valid +func refererMatchesHost(c fiber.Ctx) error { + referer := c.Get(fiber.HeaderReferer) + if referer == "" { + return ErrNoReferer + } + + refererURL, err := url.Parse(referer) + if err != nil { + return ErrBadReferer + } + + if refererURL.Scheme+"://"+refererURL.Host != c.Protocol()+"://"+c.Hostname() { + return ErrBadReferer + } + + return nil +} diff --git a/middleware/csrf/csrf_test.go b/middleware/csrf/csrf_test.go index 24c3b97dea..b770a9dacf 100644 --- a/middleware/csrf/csrf_test.go +++ b/middleware/csrf/csrf_test.go @@ -4,8 +4,10 @@ import ( "net/http/httptest" "strings" "testing" + "time" "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/middleware/session" "github.com/gofiber/utils/v2" "github.com/stretchr/testify/require" "github.com/valyala/fasthttp" @@ -58,11 +60,310 @@ func Test_CSRF(t *testing.T) { ctx.Response.Reset() ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) h(ctx) require.Equal(t, 200, ctx.Response.StatusCode()) } } +func Test_CSRF_WithSession(t *testing.T) { + t.Parallel() + + // session store + store := session.New(session.Config{ + KeyLookup: "cookie:_session", + }) + + // fiber instance + app := fiber.New() + + // fiber context + ctx := &fasthttp.RequestCtx{} + defer app.ReleaseCtx(app.NewCtx(ctx)) + + // get session + sess, err := store.Get(app.NewCtx(ctx)) + require.Equal(t, nil, err) + require.Equal(t, true, sess.Fresh()) + + // the session string is no longer be 123 + newSessionIDString := sess.ID() + app.NewCtx(ctx).Request().Header.SetCookie("_session", newSessionIDString) + + // middleware config + config := Config{ + Session: store, + } + + // middleware + app.Use(New(config)) + + app.Post("/", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + h := app.Handler() + + methods := [4]string{fiber.MethodGet, fiber.MethodHead, fiber.MethodOptions, fiber.MethodTrace} + + for _, method := range methods { + // Generate CSRF token + ctx.Request.Header.SetMethod(fiber.MethodGet) + ctx.Request.Header.SetCookie("_session", newSessionIDString) + h(ctx) + + // Without CSRF cookie + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.SetCookie("_session", newSessionIDString) + h(ctx) + require.Equal(t, 403, ctx.Response.StatusCode()) + + // Empty/invalid CSRF token + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, "johndoe") + ctx.Request.Header.SetCookie("_session", newSessionIDString) + h(ctx) + require.Equal(t, 403, ctx.Response.StatusCode()) + + // Valid CSRF token + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(method) + ctx.Request.Header.SetCookie("_session", newSessionIDString) + h(ctx) + token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + for _, header := range strings.Split(token, ";") { + if strings.Split(strings.TrimSpace(header), "=")[0] == ConfigDefault.CookieName { + token = strings.Split(header, "=")[1] + break + } + } + + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie("_session", newSessionIDString) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 200, ctx.Response.StatusCode()) + } +} + +// go test -run Test_CSRF_ExpiredToken +func Test_CSRF_ExpiredToken(t *testing.T) { + t.Parallel() + app := fiber.New() + + app.Use(New(Config{ + Expiration: 1 * time.Second, + })) + + app.Post("/", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + h := app.Handler() + ctx := &fasthttp.RequestCtx{} + + // Generate CSRF token + ctx.Request.Header.SetMethod(fiber.MethodGet) + h(ctx) + token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + token = strings.Split(strings.Split(token, ";")[0], "=")[1] + + // Use the CSRF token + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 200, ctx.Response.StatusCode()) + + // Wait for the token to expire + time.Sleep(1 * time.Second) + + // Expired CSRF token + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 403, ctx.Response.StatusCode()) +} + +// go test -run Test_CSRF_ExpiredToken_WithSession +func Test_CSRF_ExpiredToken_WithSession(t *testing.T) { + t.Parallel() + + // session store + store := session.New(session.Config{ + KeyLookup: "cookie:_session", + }) + + // fiber instance + app := fiber.New() + + // fiber context + ctx := &fasthttp.RequestCtx{} + defer app.ReleaseCtx(app.NewCtx(ctx)) + + // get session + sess, err := store.Get(app.NewCtx(ctx)) + require.Equal(t, nil, err) + require.Equal(t, true, sess.Fresh()) + + // get session id + newSessionIDString := sess.ID() + app.NewCtx(ctx).Request().Header.SetCookie("_session", newSessionIDString) + + // middleware config + config := Config{ + Session: store, + Expiration: 1 * time.Second, + } + + // middleware + app.Use(New(config)) + + app.Post("/", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + h := app.Handler() + + // Generate CSRF token + ctx.Request.Header.SetMethod(fiber.MethodGet) + ctx.Request.Header.SetCookie("_session", newSessionIDString) + h(ctx) + token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + for _, header := range strings.Split(token, ";") { + if strings.Split(strings.TrimSpace(header), "=")[0] == ConfigDefault.CookieName { + token = strings.Split(header, "=")[1] + break + } + } + + // Use the CSRF token + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie("_session", newSessionIDString) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 200, ctx.Response.StatusCode()) + + // Wait for the token to expire + time.Sleep(1 * time.Second) + + // Expired CSRF token + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie("_session", newSessionIDString) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 403, ctx.Response.StatusCode()) +} + +// go test -run Test_CSRF_MultiUseToken +func Test_CSRF_MultiUseToken(t *testing.T) { + t.Parallel() + app := fiber.New() + + app.Use(New(Config{ + KeyLookup: "header:X-CSRF-Token", + })) + + 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(fiber.MethodPost) + ctx.Request.Header.Set("X-CSRF-Token", "johndoe") + h(ctx) + require.Equal(t, 403, ctx.Response.StatusCode()) + + // Generate CSRF token + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodGet) + h(ctx) + token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + token = strings.Split(strings.Split(token, ";")[0], "=")[1] + + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set("X-CSRF-Token", token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + newToken := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + newToken = strings.Split(strings.Split(newToken, ";")[0], "=")[1] + require.Equal(t, 200, ctx.Response.StatusCode()) + + // Check if the token is not a dummy value + require.Equal(t, token, newToken) +} + +// go test -run Test_CSRF_SingleUseToken +func Test_CSRF_SingleUseToken(t *testing.T) { + t.Parallel() + app := fiber.New() + + app.Use(New(Config{ + SingleUseToken: true, + })) + + app.Post("/", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + h := app.Handler() + ctx := &fasthttp.RequestCtx{} + + // Generate CSRF token + ctx.Request.Header.SetMethod(fiber.MethodGet) + h(ctx) + token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + token = strings.Split(strings.Split(token, ";")[0], "=")[1] + + // Use the CSRF token + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 200, ctx.Response.StatusCode()) + newToken := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + newToken = strings.Split(strings.Split(newToken, ";")[0], "=")[1] + if token == newToken { + t.Error("new token should not be the same as the old token") + } + + // Use the CSRF token again + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 403, ctx.Response.StatusCode()) +} + // go test -run Test_CSRF_Next func Test_CSRF_Next(t *testing.T) { t.Parallel() @@ -124,9 +425,12 @@ func Test_CSRF_From_Form(t *testing.T) { token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) token = strings.Split(strings.Split(token, ";")[0], "=")[1] + ctx.Request.Reset() + ctx.Response.Reset() ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMEApplicationForm) ctx.Request.SetBodyString("_csrf=" + token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) h(ctx) require.Equal(t, 200, ctx.Response.StatusCode()) } @@ -146,7 +450,7 @@ func Test_CSRF_From_Query(t *testing.T) { // Invalid CSRF token ctx.Request.Header.SetMethod(fiber.MethodPost) - ctx.Request.SetRequestURI("/?_csrf=" + utils.UUID()) + ctx.Request.SetRequestURI("/?_csrf=" + utils.UUIDv4()) h(ctx) require.Equal(t, 403, ctx.Response.StatusCode()) @@ -163,6 +467,7 @@ func Test_CSRF_From_Query(t *testing.T) { ctx.Response.Reset() ctx.Request.SetRequestURI("/?_csrf=" + token) ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) h(ctx) require.Equal(t, 200, ctx.Response.StatusCode()) require.Equal(t, "OK", string(ctx.Response.Body())) @@ -183,7 +488,7 @@ func Test_CSRF_From_Param(t *testing.T) { // Invalid CSRF token ctx.Request.Header.SetMethod(fiber.MethodPost) - ctx.Request.SetRequestURI("/" + utils.UUID()) + ctx.Request.SetRequestURI("/" + utils.UUIDv4()) h(ctx) require.Equal(t, 403, ctx.Response.StatusCode()) @@ -191,7 +496,7 @@ func Test_CSRF_From_Param(t *testing.T) { ctx.Request.Reset() ctx.Response.Reset() ctx.Request.Header.SetMethod(fiber.MethodGet) - ctx.Request.SetRequestURI("/" + utils.UUID()) + ctx.Request.SetRequestURI("/" + utils.UUIDv4()) h(ctx) token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) token = strings.Split(strings.Split(token, ";")[0], "=")[1] @@ -200,6 +505,7 @@ func Test_CSRF_From_Param(t *testing.T) { ctx.Response.Reset() ctx.Request.SetRequestURI("/" + token) ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) h(ctx) require.Equal(t, 200, ctx.Response.StatusCode()) require.Equal(t, "OK", string(ctx.Response.Body())) @@ -221,7 +527,7 @@ func Test_CSRF_From_Cookie(t *testing.T) { // Invalid CSRF token ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.SetRequestURI("/") - ctx.Request.Header.Set(fiber.HeaderCookie, "csrf="+utils.UUID()+";") + ctx.Request.Header.Set(fiber.HeaderCookie, "csrf="+utils.UUIDv4()+";") h(ctx) require.Equal(t, 403, ctx.Response.StatusCode()) @@ -254,7 +560,7 @@ func Test_CSRF_From_Custom(t *testing.T) { selectors := strings.Split(body, "=") if len(selectors) != 2 || selectors[1] == "" { - return "", errMissingParam + return "", ErrMissingParam } return selectors[1], nil } @@ -282,19 +588,221 @@ func Test_CSRF_From_Custom(t *testing.T) { token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) token = strings.Split(strings.Split(token, ";")[0], "=")[1] + ctx.Request.Reset() + ctx.Response.Reset() ctx.Request.Header.SetMethod(fiber.MethodPost) ctx.Request.Header.Set(fiber.HeaderContentType, fiber.MIMETextPlain) ctx.Request.SetBodyString("_csrf=" + token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) h(ctx) require.Equal(t, 200, ctx.Response.StatusCode()) } +func Test_CSRF_Referer(t *testing.T) { + t.Parallel() + app := fiber.New() + + app.Use(New(Config{CookieSecure: true})) + + app.Post("/", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + h := app.Handler() + ctx := &fasthttp.RequestCtx{} + ctx.Request.Header.SetMethod(fiber.MethodGet) + ctx.Request.Header.Set(fiber.HeaderXForwardedProto, "https") + h(ctx) + token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + token = strings.Split(strings.Split(token, ";")[0], "=")[1] + + // Test Correct Referer with port + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.URI().SetScheme("https") + ctx.Request.URI().SetHost("example.com:8443") + ctx.Request.Header.SetProtocol("https") + ctx.Request.Header.SetHost("example.com:8443") + ctx.Request.Header.Set(fiber.HeaderReferer, ctx.Request.URI().String()) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 200, ctx.Response.StatusCode()) + + // Test Correct Referer with ReverseProxy + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.URI().SetScheme("https") + ctx.Request.URI().SetHost("10.0.1.42.com:8443") + ctx.Request.Header.SetProtocol("https") + ctx.Request.Header.SetHost("10.0.1.42:8443") + ctx.Request.Header.Set(fiber.HeaderXForwardedProto, "https") + ctx.Request.Header.Set(fiber.HeaderXForwardedHost, "example.com") + ctx.Request.Header.Set(fiber.HeaderXForwardedFor, `192.0.2.43, "[2001:db8:cafe::17]"`) + ctx.Request.Header.Set(fiber.HeaderReferer, "https://example.com") + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 200, ctx.Response.StatusCode()) + + // Test Correct Referer with ReverseProxy Missing X-Forwarded-* Headers + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.URI().SetScheme("https") + ctx.Request.URI().SetHost("10.0.1.42:8443") + ctx.Request.Header.SetProtocol("https") + ctx.Request.Header.SetHost("10.0.1.42:8443") + ctx.Request.Header.Set(fiber.HeaderXUrlScheme, "https") // We need to set this header to make sure c.Protocol() returns https + ctx.Request.Header.Set(fiber.HeaderReferer, "https://example.com") + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 403, ctx.Response.StatusCode()) + + // Test Correct Referer with path + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(fiber.HeaderXForwardedProto, "https") + ctx.Request.Header.Set(fiber.HeaderXForwardedHost, "example.com") + ctx.Request.Header.Set(fiber.HeaderReferer, "https://example.com/action/items?gogogo=true") + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 200, ctx.Response.StatusCode()) + + // Test Wrong Referer + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(fiber.HeaderXForwardedProto, "https") + ctx.Request.Header.Set(fiber.HeaderXForwardedHost, "example.com") + ctx.Request.Header.Set(fiber.HeaderReferer, "https://csrf.example.com") + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 403, ctx.Response.StatusCode()) +} + +func Test_CSRF_DeleteToken(t *testing.T) { + t.Parallel() + app := fiber.New() + + config := ConfigDefault + + app.Use(New(config)) + + app.Post("/", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + h := app.Handler() + ctx := &fasthttp.RequestCtx{} + + // Generate CSRF token + ctx.Request.Header.SetMethod(fiber.MethodGet) + h(ctx) + token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + token = strings.Split(strings.Split(token, ";")[0], "=")[1] + + // Delete the CSRF token + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + if handler, ok := app.NewCtx(ctx).Locals(ConfigDefault.HandlerContextKey).(*CSRFHandler); ok { + if err := handler.DeleteToken(app.NewCtx(ctx)); err != nil { + t.Fatal(err) + } + } + h(ctx) + + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 403, ctx.Response.StatusCode()) +} + +func Test_CSRF_DeleteToken_WithSession(t *testing.T) { + t.Parallel() + + // session store + store := session.New(session.Config{ + KeyLookup: "cookie:_session", + }) + + // fiber instance + app := fiber.New() + + // fiber context + ctx := &fasthttp.RequestCtx{} + + // get session + sess, err := store.Get(app.NewCtx(ctx)) + require.Equal(t, nil, err) + require.Equal(t, true, sess.Fresh()) + + // the session string is no longer be 123 + newSessionIDString := sess.ID() + app.NewCtx(ctx).Request().Header.SetCookie("_session", newSessionIDString) + + // middleware config + config := Config{ + Session: store, + } + + // middleware + app.Use(New(config)) + + app.Post("/", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + h := app.Handler() + + // Generate CSRF token + ctx.Request.Header.SetMethod(fiber.MethodGet) + ctx.Request.Header.SetCookie("_session", newSessionIDString) + h(ctx) + token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + token = strings.Split(strings.Split(token, ";")[0], "=")[1] + + // Delete the CSRF token + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + if handler, ok := app.NewCtx(ctx).Locals(ConfigDefault.HandlerContextKey).(*CSRFHandler); ok { + if err := handler.DeleteToken(app.NewCtx(ctx)); err != nil { + t.Fatal(err) + } + } + h(ctx) + + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + ctx.Request.Header.SetCookie("_session", newSessionIDString) + h(ctx) + require.Equal(t, 403, ctx.Response.StatusCode()) +} + func Test_CSRF_ErrorHandler_InvalidToken(t *testing.T) { t.Parallel() app := fiber.New() errHandler := func(ctx fiber.Ctx, err error) error { - require.Equal(t, errTokenNotFound, err) + require.Equal(t, ErrTokenInvalid, err) return ctx.Status(419).Send([]byte("invalid CSRF token")) } @@ -326,7 +834,7 @@ func Test_CSRF_ErrorHandler_EmptyToken(t *testing.T) { app := fiber.New() errHandler := func(ctx fiber.Ctx, err error) error { - require.Equal(t, errMissingHeader, err) + require.Equal(t, ErrMissingHeader, err) return ctx.Status(419).Send([]byte("empty CSRF token")) } @@ -352,25 +860,95 @@ func Test_CSRF_ErrorHandler_EmptyToken(t *testing.T) { require.Equal(t, "empty CSRF token", string(ctx.Response.Body())) } +func Test_CSRF_ErrorHandler_MissingReferer(t *testing.T) { + t.Parallel() + app := fiber.New() + + errHandler := func(ctx fiber.Ctx, err error) error { + require.Equal(t, ErrNoReferer, err) + return ctx.Status(419).Send([]byte("empty CSRF token")) + } + + app.Use(New(Config{ + CookieSecure: true, + ErrorHandler: errHandler, + })) + + app.Post("/", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + h := app.Handler() + ctx := &fasthttp.RequestCtx{} + ctx.Request.Header.SetMethod(fiber.MethodGet) + ctx.Request.Header.Set(fiber.HeaderXForwardedProto, "https") + h(ctx) + token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + token = strings.Split(strings.Split(token, ";")[0], "=")[1] + + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(fiber.HeaderXForwardedProto, "https") + ctx.Request.Header.Set(fiber.HeaderXForwardedHost, "example.com") + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.SetCookie(ConfigDefault.CookieName, token) + h(ctx) + require.Equal(t, 419, ctx.Response.StatusCode()) +} + +func Test_CSRF_Cookie_Injection_Exploit(t *testing.T) { + t.Parallel() + app := fiber.New() + + app.Use(New()) + + app.Post("/", func(c fiber.Ctx) error { + return c.SendStatus(fiber.StatusOK) + }) + + h := app.Handler() + ctx := &fasthttp.RequestCtx{} + + // Inject CSRF token + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodGet) + ctx.Request.Header.Set(fiber.HeaderCookie, "csrf_=pwned;") + ctx.Request.SetRequestURI("/") + h(ctx) + token := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) + token = strings.Split(strings.Split(token, ";")[0], "=")[1] + + // Exploit CSRF token we just injected + ctx.Request.Reset() + ctx.Response.Reset() + ctx.Request.Header.SetMethod(fiber.MethodPost) + ctx.Request.Header.Set(HeaderName, token) + ctx.Request.Header.Set(fiber.HeaderCookie, "csrf_=pwned;") + h(ctx) + require.Equal(t, 403, ctx.Response.StatusCode(), "CSRF exploit successful") +} + // TODO: use this test case and make the unsafe header value bug from https://github.com/gofiber/fiber/issues/2045 reproducible and permanently fixed/tested by this testcase // func Test_CSRF_UnsafeHeaderValue(t *testing.T) { // t.Parallel() // app := fiber.New() // app.Use(New()) -// app.Get("/", func(c *fiber.Ctx) error { +// app.Get("/", func(c fiber.Ctx) error { // return c.SendStatus(fiber.StatusOK) // }) -// app.Get("/test", func(c *fiber.Ctx) error { +// app.Get("/test", func(c fiber.Ctx) error { // return c.SendStatus(fiber.StatusOK) // }) -// app.Post("/", func(c *fiber.Ctx) error { +// app.Post("/", func(c fiber.Ctx) error { // return c.SendStatus(fiber.StatusOK) // }) // resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) -// utils.AssertEqual(t, nil, err) -// utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) +// require.Equal(t, nil, err) +// require.Equal(t, fiber.StatusOK, resp.StatusCode) // var token string // for _, c := range resp.Cookies() { diff --git a/middleware/csrf/extractors.go b/middleware/csrf/extractors.go index 0919dffdfc..5021301b86 100644 --- a/middleware/csrf/extractors.go +++ b/middleware/csrf/extractors.go @@ -7,11 +7,11 @@ import ( ) var ( - errMissingHeader = errors.New("missing csrf token in header") - errMissingQuery = errors.New("missing csrf token in query") - errMissingParam = errors.New("missing csrf token in param") - errMissingForm = errors.New("missing csrf token in form") - errMissingCookie = errors.New("missing csrf token in cookie") + ErrMissingHeader = errors.New("missing csrf token in header") + ErrMissingQuery = errors.New("missing csrf token in query") + ErrMissingParam = errors.New("missing csrf token in param") + ErrMissingForm = errors.New("missing csrf token in form") + ErrMissingCookie = errors.New("missing csrf token in cookie") ) // csrfFromParam returns a function that extracts token from the url param string. @@ -19,7 +19,7 @@ func CsrfFromParam(param string) func(c fiber.Ctx) (string, error) { return func(c fiber.Ctx) (string, error) { token := c.Params(param) if token == "" { - return "", errMissingParam + return "", ErrMissingParam } return token, nil } @@ -30,7 +30,7 @@ func CsrfFromForm(param string) func(c fiber.Ctx) (string, error) { return func(c fiber.Ctx) (string, error) { token := c.FormValue(param) if token == "" { - return "", errMissingForm + return "", ErrMissingForm } return token, nil } @@ -41,7 +41,7 @@ func CsrfFromCookie(param string) func(c fiber.Ctx) (string, error) { return func(c fiber.Ctx) (string, error) { token := c.Cookies(param) if token == "" { - return "", errMissingCookie + return "", ErrMissingCookie } return token, nil } @@ -52,7 +52,7 @@ func CsrfFromHeader(param string) func(c fiber.Ctx) (string, error) { return func(c fiber.Ctx) (string, error) { token := c.Get(param) if token == "" { - return "", errMissingHeader + return "", ErrMissingHeader } return token, nil } @@ -63,7 +63,7 @@ func CsrfFromQuery(param string) func(c fiber.Ctx) (string, error) { return func(c fiber.Ctx) (string, error) { token := c.Query(param) if token == "" { - return "", errMissingQuery + return "", ErrMissingQuery } return token, nil } diff --git a/middleware/csrf/helpers.go b/middleware/csrf/helpers.go new file mode 100644 index 0000000000..708762c7e3 --- /dev/null +++ b/middleware/csrf/helpers.go @@ -0,0 +1,13 @@ +package csrf + +import ( + "crypto/subtle" +) + +func compareTokens(a, b []byte) bool { + return subtle.ConstantTimeCompare(a, b) == 1 +} + +func compareStrings(a, b string) bool { + return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1 +} diff --git a/middleware/csrf/session_manager.go b/middleware/csrf/session_manager.go new file mode 100644 index 0000000000..87172eb838 --- /dev/null +++ b/middleware/csrf/session_manager.go @@ -0,0 +1,68 @@ +package csrf + +import ( + "time" + + "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3/log" + "github.com/gofiber/fiber/v3/middleware/session" +) + +type sessionManager struct { + key string + session *session.Store +} + +func newSessionManager(s *session.Store, k string) *sessionManager { + // Create new storage handler + sessionManager := &sessionManager{ + key: k, + } + if s != nil { + // Use provided storage if provided + sessionManager.session = s + } + return sessionManager +} + +// get token from session +func (m *sessionManager) getRaw(c fiber.Ctx, key string, raw []byte) []byte { + sess, err := m.session.Get(c) + if err != nil { + return nil + } + token, ok := sess.Get(m.key).(Token) + if ok { + if token.Expiration.Before(time.Now()) || key != token.Key || !compareTokens(raw, token.Raw) { + return nil + } + return token.Raw + } + + return nil +} + +// set token in session +func (m *sessionManager) setRaw(c fiber.Ctx, key string, raw []byte, exp time.Duration) { + sess, err := m.session.Get(c) + if err != nil { + return + } + // 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 + sess.Set(m.key, &Token{key, raw, time.Now().Add(exp)}) + if err := sess.Save(); err != nil { + log.Warn("csrf: failed to save session: ", err) + } +} + +// delete token from session +func (m *sessionManager) delRaw(c fiber.Ctx) { + sess, err := m.session.Get(c) + if err != nil { + return + } + sess.Delete(m.key) + if err := sess.Save(); err != nil { + log.Warn("csrf: failed to save session: ", err) + } +} diff --git a/middleware/csrf/manager.go b/middleware/csrf/storage_manager.go similarity index 62% rename from middleware/csrf/manager.go rename to middleware/csrf/storage_manager.go index e16e8d19fb..b6d7f0160d 100644 --- a/middleware/csrf/manager.go +++ b/middleware/csrf/storage_manager.go @@ -10,19 +10,19 @@ import ( ) // go:generate msgp -// msgp -file="manager.go" -o="manager_msgp.go" -tests=false -unexported +// msgp -file="storage_manager.go" -o="storage_manager_msgp.go" -tests=false -unexported type item struct{} //msgp:ignore manager -type manager struct { +type storageManager struct { pool sync.Pool memory *memory.Storage storage fiber.Storage } -func newManager(storage fiber.Storage) *manager { +func newStorageManager(storage fiber.Storage) *storageManager { // Create new storage handler - manager := &manager{ + storageManager := &storageManager{ pool: sync.Pool{ New: func() any { return new(item) @@ -31,16 +31,16 @@ func newManager(storage fiber.Storage) *manager { } if storage != nil { // Use provided storage if provided - manager.storage = storage + storageManager.storage = storage } else { // Fallback too memory storage - manager.memory = memory.New() + storageManager.memory = memory.New() } - return manager + return storageManager } // get raw data from storage or memory -func (m *manager) getRaw(key string) []byte { +func (m *storageManager) getRaw(key string) []byte { var raw []byte if m.storage != nil { raw, _ = m.storage.Get(key) //nolint:errcheck // TODO: Do not ignore error @@ -51,7 +51,7 @@ func (m *manager) getRaw(key string) []byte { } // set data to storage or memory -func (m *manager) setRaw(key string, raw []byte, exp time.Duration) { +func (m *storageManager) setRaw(key string, raw []byte, exp time.Duration) { if m.storage != nil { _ = m.storage.Set(key, raw, exp) //nolint:errcheck // TODO: Do not ignore error } else { @@ -59,3 +59,12 @@ func (m *manager) setRaw(key string, raw []byte, exp time.Duration) { m.memory.Set(utils.CopyString(key), raw, exp) } } + +// delete data from storage or memory +func (m *storageManager) delRaw(key string) { + if m.storage != nil { + _ = m.storage.Delete(key) //nolint:errcheck // TODO: Do not ignore error + } else { + m.memory.Delete(key) + } +} diff --git a/middleware/csrf/manager_msgp.go b/middleware/csrf/storage_manager_msgp.go similarity index 100% rename from middleware/csrf/manager_msgp.go rename to middleware/csrf/storage_manager_msgp.go diff --git a/middleware/csrf/token.go b/middleware/csrf/token.go new file mode 100644 index 0000000000..ee88b9aee0 --- /dev/null +++ b/middleware/csrf/token.go @@ -0,0 +1,11 @@ +package csrf + +import ( + "time" +) + +type Token struct { + Key string `json:"key"` + Raw []byte `json:"raw"` + Expiration time.Time `json:"expiration"` +} diff --git a/middleware/favicon/favicon.go b/middleware/favicon/favicon.go index 1e94ef983a..66fb6e6000 100644 --- a/middleware/favicon/favicon.go +++ b/middleware/favicon/favicon.go @@ -16,6 +16,11 @@ type Config struct { // Optional. Default: nil Next func(c fiber.Ctx) bool + // Raw data of the favicon file + // + // Optional. Default: nil + Data []byte `json:"-"` + // File holds the path to an actual favicon that will be cached // // Optional. Default: "" @@ -83,7 +88,11 @@ func New(config ...Config) fiber.Handler { icon []byte iconLen string ) - if cfg.File != "" { + if cfg.Data != nil { + // use the provided favicon data + icon = cfg.Data + iconLen = strconv.Itoa(len(cfg.Data)) + } else if cfg.File != "" { // read from configured filesystem if present if cfg.FileSystem != nil { f, err := cfg.FileSystem.Open(cfg.File) diff --git a/middleware/favicon/favicon_test.go b/middleware/favicon/favicon_test.go index a39330b074..0ae8bb9bdc 100644 --- a/middleware/favicon/favicon_test.go +++ b/middleware/favicon/favicon_test.go @@ -1,14 +1,18 @@ package favicon import ( + "fmt" + "net/http" "net/http/httptest" "os" + "strings" "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 @@ -69,13 +73,72 @@ func Test_Middleware_Favicon_Found(t *testing.T) { }) resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/favicon.ico", nil)) - - require.NoError(t, err, "app.Test(req)") + require.NoError(t, nil, 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_Custom_Favicon_Url +func Test_Custom_Favicon_Url(t *testing.T) { + app := fiber.New() + const customURL = "/favicon.svg" + app.Use(New(Config{ + File: "../../.github/testdata/favicon.ico", + URL: customURL, + })) + + app.Get("/", func(c fiber.Ctx) error { + return nil + }) + + resp, err := app.Test(httptest.NewRequest(http.MethodGet, customURL, nil)) + + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") + utils.AssertEqual(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) +} + +// go test -run Test_Custom_Favicon_Data +func Test_Custom_Favicon_Data(t *testing.T) { + data, err := os.ReadFile("../../.github/testdata/favicon.ico") + utils.AssertEqual(t, nil, err) + + app := fiber.New() + + app.Use(New(Config{ + Data: data, + })) + + app.Get("/", func(c fiber.Ctx) error { + return nil + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/favicon.ico", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode, "Status code") + utils.AssertEqual(t, "image/x-icon", resp.Header.Get(fiber.HeaderContentType)) + utils.AssertEqual(t, "public, max-age=31536000", resp.Header.Get(fiber.HeaderCacheControl), "CacheControl Control") +} + +// mockFS wraps local filesystem for the purposes of +// Test_Middleware_Favicon_FileSystem located below +// TODO use os.Dir if fiber upgrades to 1.16 +type mockFS struct{} + +func (mockFS) Open(name string) (http.File, error) { + if name == "/" { + name = "." + } else { + name = strings.TrimPrefix(name, "/") + } + file, err := os.Open(name) //nolint:gosec // We're in a test func, so this is fine + if err != nil { + return nil, fmt.Errorf("failed to open: %w", err) + } + return file, nil +} + // go test -run Test_Middleware_Favicon_FileSystem func Test_Middleware_Favicon_FileSystem(t *testing.T) { t.Parallel() diff --git a/middleware/filesystem/filesystem.go b/middleware/filesystem/filesystem.go index 055cb9ae64..62d4f4f6bb 100644 --- a/middleware/filesystem/filesystem.go +++ b/middleware/filesystem/filesystem.go @@ -204,6 +204,8 @@ func New(config ...Config) fiber.Handler { return fiber.ErrForbidden } + c.Status(fiber.StatusOK) + modTime := stat.ModTime() contentLength := int(stat.Size()) @@ -246,7 +248,9 @@ func New(config ...Config) fiber.Handler { } } -// SendFile ... +// SendFile serves a file from an fs.FS filesystem at the specified path. +// It handles content serving, sets appropriate headers, and returns errors when needed. +// Usage: err := SendFile(ctx, fs, "/path/to/file.txt") func SendFile(c fiber.Ctx, filesystem fs.FS, path string) error { var ( file fs.File @@ -286,6 +290,8 @@ func SendFile(c fiber.Ctx, filesystem fs.FS, path string) error { return fiber.ErrForbidden } + c.Status(fiber.StatusOK) + modTime := stat.ModTime() contentLength := int(stat.Size()) diff --git a/middleware/filesystem/filesystem_test.go b/middleware/filesystem/filesystem_test.go index b7c24b8fcc..da89e74099 100644 --- a/middleware/filesystem/filesystem_test.go +++ b/middleware/filesystem/filesystem_test.go @@ -120,6 +120,7 @@ func Test_FileSystem(t *testing.T) { } for _, tt := range tests { + tt := tt t.Run(tt.name, func(t *testing.T) { resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, tt.url, nil)) require.NoError(t, err) diff --git a/middleware/idempotency/config.go b/middleware/idempotency/config.go index 1856b42b02..e002fcb100 100644 --- a/middleware/idempotency/config.go +++ b/middleware/idempotency/config.go @@ -77,7 +77,14 @@ var ConfigDefault = Config{ func configDefault(config ...Config) Config { // Return default config if nothing provided if len(config) < 1 { - return ConfigDefault + cfg := ConfigDefault + + cfg.Lock = NewMemoryLock() + cfg.Storage = memory.New(memory.Config{ + GCInterval: cfg.Lifetime / 2, // Half the lifetime interval + }) + + return cfg } // Override default config diff --git a/middleware/idempotency/idempotency.go b/middleware/idempotency/idempotency.go index 8c92248423..425abd5719 100644 --- a/middleware/idempotency/idempotency.go +++ b/middleware/idempotency/idempotency.go @@ -45,8 +45,10 @@ func New(config ...Config) fiber.Handler { _ = c.Status(res.StatusCode) - for header, val := range res.Headers { - c.Set(header, val) + for header, vals := range res.Headers { + for _, val := range vals { + c.Context().Response.Header.Add(header, val) + } } if len(res.Body) != 0 { @@ -116,7 +118,7 @@ func New(config ...Config) fiber.Handler { Body: utils.CopyBytes(c.Response().Body()), } { - headers := make(map[string]string) + headers := make(map[string][]string) if err := c.Bind().RespHeader(headers); err != nil { return fmt.Errorf("failed to bind to response headers: %w", err) } @@ -126,7 +128,7 @@ func New(config ...Config) fiber.Handler { res.Headers = headers } else { // Filter - res.Headers = make(map[string]string) + res.Headers = make(map[string][]string) for h := range headers { if _, ok := keepResponseHeadersMap[utils.ToLower(h)]; ok { res.Headers[h] = headers[h] diff --git a/middleware/idempotency/response.go b/middleware/idempotency/response.go index ca06bcb452..f42d1a3311 100644 --- a/middleware/idempotency/response.go +++ b/middleware/idempotency/response.go @@ -4,7 +4,7 @@ package idempotency type response struct { StatusCode int `msg:"sc"` - Headers map[string]string `msg:"hs"` + Headers map[string][]string `msg:"hs"` Body []byte `msg:"b"` } diff --git a/middleware/idempotency/response_msgp.go b/middleware/idempotency/response_msgp.go index 4eb4d7fcb0..410d118ca0 100644 --- a/middleware/idempotency/response_msgp.go +++ b/middleware/idempotency/response_msgp.go @@ -18,7 +18,10 @@ func (z *response) MarshalMsg(b []byte) (o []byte, err error) { o = msgp.AppendMapHeader(o, uint32(len(z.Headers))) for za0001, za0002 := range z.Headers { o = msgp.AppendString(o, za0001) - o = msgp.AppendString(o, za0002) + o = msgp.AppendArrayHeader(o, uint32(len(za0002))) + for za0003 := range za0002 { + o = msgp.AppendString(o, za0002[za0003]) + } } // string "b" o = append(o, 0xa1, 0x62) @@ -58,7 +61,7 @@ func (z *response) UnmarshalMsg(bts []byte) (o []byte, err error) { return } if z.Headers == nil { - z.Headers = make(map[string]string, zb0002) + z.Headers = make(map[string][]string, zb0002) } else if len(z.Headers) > 0 { for key := range z.Headers { delete(z.Headers, key) @@ -66,18 +69,31 @@ func (z *response) UnmarshalMsg(bts []byte) (o []byte, err error) { } for zb0002 > 0 { var za0001 string - var za0002 string + var za0002 []string zb0002-- za0001, bts, err = msgp.ReadStringBytes(bts) if err != nil { err = msgp.WrapError(err, "Headers") return } - za0002, bts, err = msgp.ReadStringBytes(bts) + var zb0003 uint32 + zb0003, bts, err = msgp.ReadArrayHeaderBytes(bts) if err != nil { err = msgp.WrapError(err, "Headers", za0001) return } + if cap(za0002) >= int(zb0003) { + za0002 = (za0002)[:zb0003] + } else { + za0002 = make([]string, zb0003) + } + for za0003 := range za0002 { + za0002[za0003], bts, err = msgp.ReadStringBytes(bts) + if err != nil { + err = msgp.WrapError(err, "Headers", za0001, za0003) + return + } + } z.Headers[za0001] = za0002 } case "b": @@ -104,7 +120,10 @@ func (z *response) Msgsize() (s int) { if z.Headers != nil { for za0001, za0002 := range z.Headers { _ = za0002 - s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002) + s += msgp.StringPrefixSize + len(za0001) + msgp.ArrayHeaderSize + for za0003 := range za0002 { + s += msgp.StringPrefixSize + len(za0002[za0003]) + } } } s += 2 + msgp.BytesPrefixSize + len(z.Body) diff --git a/middleware/logger/default_logger.go b/middleware/logger/default_logger.go index 1517c089a3..508f17cb08 100644 --- a/middleware/logger/default_logger.go +++ b/middleware/logger/default_logger.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "sync" - "time" "github.com/gofiber/fiber/v3" "github.com/gofiber/utils/v2" @@ -33,10 +32,10 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error { formatErr = colors.Red + " | " + data.ChainErr.Error() + colors.Reset } _, _ = buf.WriteString( //nolint:errcheck // This will never fail - fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+data.ErrPaddingStr+"s %s\n", + fmt.Sprintf("%s |%s %3d %s| %13v | %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), + data.Stop.Sub(data.Start), c.IP(), methodColor(c.Method(), colors), c.Method(), colors.Reset, c.Path(), @@ -48,10 +47,10 @@ func defaultLoggerInstance(c fiber.Ctx, data *Data, cfg Config) error { formatErr = " | " + data.ChainErr.Error() } _, _ = buf.WriteString( //nolint:errcheck // This will never fail - fmt.Sprintf("%s | %3d | %7v | %15s | %-7s | %-"+data.ErrPaddingStr+"s %s\n", + fmt.Sprintf("%s | %3d | %13v | %15s | %-7s | %-"+data.ErrPaddingStr+"s %s\n", data.Timestamp.Load().(string), c.Response().StatusCode(), - data.Stop.Sub(data.Start).Round(time.Millisecond), + data.Stop.Sub(data.Start), c.IP(), c.Method(), c.Path(), diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index e9196ab995..01cf8322e6 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -10,12 +10,14 @@ import ( "net/http" "net/http/httptest" "os" + "runtime" "sync" "testing" "time" "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" @@ -204,6 +206,117 @@ func Test_Logger_All(t *testing.T) { require.Equal(t, expected, buf.String()) } +func getLatencyTimeUnits() []struct { + unit string + div time.Duration +} { + // windows does not support µs sleep precision + // https://github.com/golang/go/issues/29485 + if runtime.GOOS == "windows" { + return []struct { + unit string + div time.Duration + }{ + {"ms", time.Millisecond}, + {"s", time.Second}, + } + } + return []struct { + unit string + div time.Duration + }{ + {"µs", time.Microsecond}, + {"ms", time.Millisecond}, + {"s", time.Second}, + } +} + +// go test -run Test_Logger_WithLatency +func Test_Logger_WithLatency(t *testing.T) { + t.Parallel() + buff := bytebufferpool.Get() + defer bytebufferpool.Put(buff) + app := fiber.New() + logger := New(Config{ + Output: buff, + Format: "${latency}", + }) + app.Use(logger) + + // Define a list of time units to test + timeUnits := getLatencyTimeUnits() + + // Initialize a new time unit + sleepDuration := 1 * time.Nanosecond + + // Define a test route that sleeps + app.Get("/test", func(c fiber.Ctx) error { + time.Sleep(sleepDuration) + return c.SendStatus(fiber.StatusOK) + }) + + // Loop through each time unit and assert that the log output contains the expected latency value + for _, tu := range timeUnits { + // Update the sleep duration for the next iteration + sleepDuration = 1 * tu.div + + // Create a new HTTP request to the test route + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil), int(2*time.Second)) + require.NoError(t, err) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + + // Assert that the log output contains the expected latency value in the current time unit + require.Equal(t, bytes.HasSuffix(buff.Bytes(), []byte(tu.unit)), true, fmt.Sprintf("Expected latency to be in %s, got %s", tu.unit, buff.String())) + + // Reset the buffer + buff.Reset() + } +} + +// go test -run Test_Logger_WithLatency_DefaultFormat +func Test_Logger_WithLatency_DefaultFormat(t *testing.T) { + t.Parallel() + buff := bytebufferpool.Get() + defer bytebufferpool.Put(buff) + app := fiber.New() + logger := New(Config{ + Output: buff, + }) + app.Use(logger) + + // Define a list of time units to test + timeUnits := getLatencyTimeUnits() + + // Initialize a new time unit + sleepDuration := 1 * time.Nanosecond + + // Define a test route that sleeps + app.Get("/test", func(c fiber.Ctx) error { + time.Sleep(sleepDuration) + return c.SendStatus(fiber.StatusOK) + }) + + // Loop through each time unit and assert that the log output contains the expected latency value + for _, tu := range timeUnits { + // Update the sleep duration for the next iteration + sleepDuration = 1 * tu.div + + // Create a new HTTP request to the test route + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/test", nil), int(2*time.Second)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + + // Assert that the log output contains the expected latency value in the current time unit + // parse out the latency value from the log output + latency := bytes.Split(buff.Bytes(), []byte(" | "))[2] + // Assert that the latency value is in the current time unit + utils.AssertEqual(t, bytes.HasSuffix(latency, []byte(tu.unit)), true, fmt.Sprintf("Expected latency to be in %s, got %s", tu.unit, latency)) + + // Reset the buffer + buff.Reset() + } +} + // go test -run Test_Query_Params func Test_Query_Params(t *testing.T) { t.Parallel() diff --git a/middleware/logger/tags.go b/middleware/logger/tags.go index afc0e34ad4..b4ce5cfda4 100644 --- a/middleware/logger/tags.go +++ b/middleware/logger/tags.go @@ -102,14 +102,14 @@ func createTagMap(cfg *Config) map[string]LogFunc { return output.Write(c.Response().Body()) }, TagReqHeaders: func(output Buffer, c fiber.Ctx, data *Data, extraParam string) (int, error) { - out := make(map[string]string, 0) + 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) + reqHeaders = append(reqHeaders, k+"="+strings.Join(v, ",")) } return output.Write([]byte(strings.Join(reqHeaders, "&"))) }, @@ -180,14 +180,14 @@ func createTagMap(cfg *Config) map[string]LogFunc { TagStatus: func(output Buffer, c fiber.Ctx, data *Data, extraParam string) (int, error) { if cfg.enableColors { colors := c.App().Config().ColorScheme - return output.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset)) + return output.WriteString(fmt.Sprintf("%s%3d%s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset)) } return appendInt(output, c.Response().StatusCode()) }, TagMethod: func(output Buffer, c fiber.Ctx, data *Data, extraParam string) (int, error) { if cfg.enableColors { colors := c.App().Config().ColorScheme - return output.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) + return output.WriteString(fmt.Sprintf("%s%s%s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) } return output.WriteString(c.Method()) }, @@ -196,17 +196,15 @@ func createTagMap(cfg *Config) map[string]LogFunc { }, TagLatency: func(output Buffer, c fiber.Ctx, data *Data, extraParam string) (int, error) { latency := data.Stop.Sub(data.Start) - return output.WriteString(fmt.Sprintf("%7v", latency)) + return output.WriteString(fmt.Sprintf("%13v", latency)) }, TagTime: func(output Buffer, c fiber.Ctx, data *Data, extraParam string) (int, error) { return output.WriteString(data.Timestamp.Load().(string)) //nolint:forcetypeassert // We always store a string in here }, } // merge with custom tags from user - if cfg.CustomTags != nil { - for k, v := range cfg.CustomTags { - tagFunctions[k] = v - } + for k, v := range cfg.CustomTags { + tagFunctions[k] = v } return tagFunctions diff --git a/middleware/pprof/pprof_test.go b/middleware/pprof/pprof_test.go index 5f25b42a7e..90b601a14c 100644 --- a/middleware/pprof/pprof_test.go +++ b/middleware/pprof/pprof_test.go @@ -11,7 +11,6 @@ import ( ) func Test_Non_Pprof_Path(t *testing.T) { - t.Parallel() app := fiber.New() app.Use(New()) @@ -30,7 +29,6 @@ func Test_Non_Pprof_Path(t *testing.T) { } func Test_Non_Pprof_Path_WithPrefix(t *testing.T) { - t.Parallel() app := fiber.New() app.Use(New(Config{Prefix: "/federated-fiber"})) @@ -49,7 +47,6 @@ func Test_Non_Pprof_Path_WithPrefix(t *testing.T) { } func Test_Pprof_Index(t *testing.T) { - t.Parallel() app := fiber.New() app.Use(New()) @@ -69,7 +66,6 @@ func Test_Pprof_Index(t *testing.T) { } func Test_Pprof_Index_WithPrefix(t *testing.T) { - t.Parallel() app := fiber.New() app.Use(New(Config{Prefix: "/federated-fiber"})) @@ -89,7 +85,6 @@ func Test_Pprof_Index_WithPrefix(t *testing.T) { } func Test_Pprof_Subs(t *testing.T) { - t.Parallel() app := fiber.New() app.Use(New()) @@ -104,8 +99,8 @@ func Test_Pprof_Subs(t *testing.T) { } for _, sub := range subs { + sub := sub t.Run(sub, func(t *testing.T) { - t.Parallel() target := "/debug/pprof/" + sub if sub == "profile" { target += "?seconds=1" @@ -118,7 +113,6 @@ func Test_Pprof_Subs(t *testing.T) { } func Test_Pprof_Subs_WithPrefix(t *testing.T) { - t.Parallel() app := fiber.New() app.Use(New(Config{Prefix: "/federated-fiber"})) @@ -133,8 +127,8 @@ func Test_Pprof_Subs_WithPrefix(t *testing.T) { } for _, sub := range subs { + sub := sub t.Run(sub, func(t *testing.T) { - t.Parallel() target := "/federated-fiber/debug/pprof/" + sub if sub == "profile" { target += "?seconds=1" @@ -147,7 +141,6 @@ func Test_Pprof_Subs_WithPrefix(t *testing.T) { } func Test_Pprof_Other(t *testing.T) { - t.Parallel() app := fiber.New() app.Use(New()) @@ -162,7 +155,6 @@ func Test_Pprof_Other(t *testing.T) { } func Test_Pprof_Other_WithPrefix(t *testing.T) { - t.Parallel() app := fiber.New() app.Use(New(Config{Prefix: "/federated-fiber"})) @@ -178,7 +170,6 @@ func Test_Pprof_Other_WithPrefix(t *testing.T) { // go test -run Test_Pprof_Next func Test_Pprof_Next(t *testing.T) { - t.Parallel() app := fiber.New() app.Use(New(Config{ @@ -194,7 +185,6 @@ func Test_Pprof_Next(t *testing.T) { // go test -run Test_Pprof_Next_WithPrefix func Test_Pprof_Next_WithPrefix(t *testing.T) { - t.Parallel() app := fiber.New() app.Use(New(Config{ diff --git a/middleware/session/session.go b/middleware/session/session.go index ab7ed74caf..c257343968 100644 --- a/middleware/session/session.go +++ b/middleware/session/session.go @@ -124,6 +124,33 @@ func (s *Session) Regenerate() error { return nil } +// Reset generates a new session id, deletes the old one from storage, and resets the associated data +func (s *Session) Reset() error { + // Reset local data + if s.data != nil { + s.data.Reset() + } + // Reset byte buffer + if s.byteBuffer != nil { + s.byteBuffer.Reset() + } + // Reset expiration + s.exp = 0 + + // Delete old id from storage + if err := s.config.Storage.Delete(s.id); err != nil { + return err + } + + // Expire session + s.delSession() + + // Generate a new session, and set session.fresh to true + s.refresh() + + return nil +} + // refresh generates a new session, and set session.fresh to be true func (s *Session) refresh() { // Create a new id diff --git a/middleware/session/session_test.go b/middleware/session/session_test.go index 7a125415d5..32eec95e20 100644 --- a/middleware/session/session_test.go +++ b/middleware/session/session_test.go @@ -281,6 +281,7 @@ func Test_Session_Save_Expiration(t *testing.T) { t.Parallel() t.Run("save to cookie", func(t *testing.T) { + const sessionDuration = 5 * time.Second t.Parallel() // session store store := New() @@ -297,7 +298,7 @@ func Test_Session_Save_Expiration(t *testing.T) { sess.Set("name", "john") // expire this session in 5 seconds - sess.SetExpiry(time.Second * 5) + sess.SetExpiry(sessionDuration) // save session err = sess.Save() @@ -309,7 +310,7 @@ func Test_Session_Save_Expiration(t *testing.T) { require.Equal(t, "john", sess.Get("name")) // just to make sure the session has been expired - time.Sleep(time.Second * 5) + time.Sleep(sessionDuration + (10 * time.Millisecond)) // here you should get a new session sess, err = store.Get(ctx) @@ -318,11 +319,11 @@ func Test_Session_Save_Expiration(t *testing.T) { }) } -// go test -run Test_Session_Reset -func Test_Session_Reset(t *testing.T) { +// go test -run Test_Session_Destroy +func Test_Session_Destroy(t *testing.T) { t.Parallel() - t.Run("reset from cookie", func(t *testing.T) { + t.Run("destroy from cookie", func(t *testing.T) { t.Parallel() // session store store := New() @@ -341,7 +342,7 @@ func Test_Session_Reset(t *testing.T) { require.Nil(t, name) }) - t.Run("reset from header", func(t *testing.T) { + t.Run("destroy from header", func(t *testing.T) { t.Parallel() // session store store := New(Config{ @@ -452,6 +453,74 @@ func Test_Session_Deletes_Single_Key(t *testing.T) { require.Nil(t, sess.Get("id")) } +// go test -run Test_Session_Reset +func Test_Session_Reset(t *testing.T) { + t.Parallel() + // fiber instance + app := fiber.New() + + // session store + store := New() + + // fiber context + ctx := app.NewCtx(&fasthttp.RequestCtx{}) + + t.Run("reset session data and id, and set fresh to be true", func(t *testing.T) { + // a random session uuid + originalSessionUUIDString := "" + + // now the session is in the storage + freshSession, err := store.Get(ctx) + require.NoError(t, err) + + originalSessionUUIDString = freshSession.ID() + + // set a value + freshSession.Set("name", "fenny") + freshSession.Set("email", "fenny@example.com") + + err = freshSession.Save() + 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) + require.NoError(t, err) + require.False(t, acquiredSession.Fresh()) + + err = acquiredSession.Reset() + require.NoError(t, err) + + require.False(t, acquiredSession.ID() == originalSessionUUIDString) + + // acquiredSession.fresh should be true after resetting + require.True(t, acquiredSession.Fresh()) + + // Check that the session data has been reset + keys := acquiredSession.Keys() + require.Equal(t, []string{}, keys) + + // Set a new value for 'name' and check that it's updated + acquiredSession.Set("name", "john") + require.Equal(t, "john", acquiredSession.Get("name")) + require.Nil(t, acquiredSession.Get("email")) + + // Save after resetting + err = acquiredSession.Save() + require.NoError(t, err) + + // Check that the session id is not in the header or cookie anymore + require.Equal(t, "", string(ctx.Response().Header.Peek(store.sessionName))) + require.Equal(t, "", string(ctx.Request().Header.Peek(store.sessionName))) + + // But the new session id should be in the header or cookie + require.Equal(t, acquiredSession.ID(), string(ctx.Response().Header.Peek(store.sessionName))) + require.Equal(t, acquiredSession.ID(), string(ctx.Request().Header.Peek(store.sessionName))) + }) +} + // go test -run Test_Session_Regenerate // Regression: https://github.com/gofiber/fiber/issues/1395 func Test_Session_Regenerate(t *testing.T) { @@ -486,9 +555,8 @@ func Test_Session_Regenerate(t *testing.T) { err = acquiredSession.Regenerate() require.NoError(t, err) - if acquiredSession.ID() == originalSessionUUIDString { - t.Fatal("regenerate should generate another different id") - } + require.False(t, acquiredSession.ID() == originalSessionUUIDString) + // acquiredSession.fresh should be true after regenerating require.True(t, acquiredSession.Fresh()) }) diff --git a/middleware/session/store.go b/middleware/session/store.go index e8f6990966..d178753a29 100644 --- a/middleware/session/store.go +++ b/middleware/session/store.go @@ -2,6 +2,7 @@ package session import ( "encoding/gob" + "errors" "fmt" "sync" @@ -11,6 +12,9 @@ import ( "github.com/valyala/fasthttp" ) +// ErrEmptySessionID is an error that occurs when the session ID is empty. +var ErrEmptySessionID = errors.New("session id cannot be empty") + type Store struct { Config } @@ -139,3 +143,11 @@ func (s *Store) responseCookies(c fiber.Ctx) (string, error) { func (s *Store) Reset() error { return s.Storage.Reset() } + +// Delete deletes a session by its id. +func (s *Store) Delete(id string) error { + if id == "" { + return ErrEmptySessionID + } + return s.Storage.Delete(id) +} diff --git a/middleware/session/store_test.go b/middleware/session/store_test.go index bb93ababc5..2fca25c34f 100644 --- a/middleware/session/store_test.go +++ b/middleware/session/store_test.go @@ -81,8 +81,36 @@ func TestStore_Get(t *testing.T) { acquiredSession, err := store.Get(ctx) require.NoError(t, err) - if acquiredSession.ID() != unexpectedID { - t.Fatal("server should not accept the unexpectedID which is not in the store") - } + require.Equal(t, unexpectedID, acquiredSession.ID()) }) } + +// go test -run TestStore_DeleteSession +func TestStore_DeleteSession(t *testing.T) { + t.Parallel() + // fiber instance + app := fiber.New() + // session store + store := New() + + // fiber context + ctx := app.NewCtx(&fasthttp.RequestCtx{}) + + // Create a new session + session, err := store.Get(ctx) + require.NoError(t, err) + + // Save the session ID + sessionID := session.ID() + + // Delete the session + err = store.Delete(sessionID) + require.NoError(t, err) + + // Try to get the session again + session, err = store.Get(ctx) + require.NoError(t, err) + + // The session ID should be different now, because the old session was deleted + require.Equal(t, session.ID() == sessionID, false) +} diff --git a/mount_test.go b/mount_test.go index 0ca9768e67..9460e51782 100644 --- a/mount_test.go +++ b/mount_test.go @@ -399,6 +399,79 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { require.Equal(t, "hi, i'm a custom sub sub fiber error", string(b), "Third fiber Response body") } +// go test -run Test_Mount_Route_Names +func Test_Mount_Route_Names(t *testing.T) { + // create sub-app with 2 handlers: + subApp1 := New() + subApp1.Get("/users", func(c Ctx) error { + url, err := c.GetRouteURL("add-user", Map{}) + require.Equal(t, err, nil) + require.Equal(t, url, "/app1/users", "handler: app1.add-user") // the prefix is /app1 because of the mount + // if subApp1 is not mounted, expected url just /users + return nil + }).Name("get-users") + subApp1.Post("/users", func(c Ctx) error { + route := c.App().GetRoute("get-users") + require.Equal(t, route.Method, MethodGet, "handler: app1.get-users method") + require.Equal(t, route.Path, "/app1/users", "handler: app1.get-users path") + return nil + }).Name("add-user") + + // create sub-app with 2 handlers inside a group: + subApp2 := New() + app2Grp := subApp2.Group("/users").Name("users.") + app2Grp.Get("", emptyHandler).Name("get") + app2Grp.Post("", emptyHandler).Name("add") + + // put both sub-apps into root app + rootApp := New() + _ = rootApp.Use("/app1", subApp1) + _ = rootApp.Use("/app2", subApp2) + + rootApp.startupProcess() + + // take route directly from sub-app + route := subApp1.GetRoute("get-users") + require.Equal(t, route.Method, MethodGet) + require.Equal(t, route.Path, "/users") + + route = subApp1.GetRoute("add-user") + require.Equal(t, route.Method, MethodPost) + require.Equal(t, route.Path, "/users") + + // take route directly from sub-app with group + route = subApp2.GetRoute("users.get") + require.Equal(t, route.Method, MethodGet) + require.Equal(t, route.Path, "/users") + + route = subApp2.GetRoute("users.add") + require.Equal(t, route.Method, MethodPost) + require.Equal(t, route.Path, "/users") + + // take route from root app (using names of sub-apps) + route = rootApp.GetRoute("add-user") + require.Equal(t, route.Method, MethodPost) + require.Equal(t, route.Path, "/app1/users") + + route = rootApp.GetRoute("users.add") + require.Equal(t, route.Method, MethodPost) + require.Equal(t, route.Path, "/app2/users") + + // GetRouteURL inside handler + req := httptest.NewRequest(MethodGet, "/app1/users", nil) + resp, err := rootApp.Test(req) + + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") + + // ctx.App().GetRoute() inside handler + req = httptest.NewRequest(MethodPost, "/app1/users", nil) + resp, err = rootApp.Test(req) + + require.NoError(t, err, "app.Test(req)") + require.Equal(t, StatusOK, resp.StatusCode, "Status code") +} + // go test -run Test_Ctx_Render_Mount func Test_Ctx_Render_Mount(t *testing.T) { t.Parallel() diff --git a/path_test.go b/path_test.go index cf9a2db064..071f7e9e0e 100644 --- a/path_test.go +++ b/path_test.go @@ -195,6 +195,17 @@ func Test_Utils_RemoveEscapeChar(t *testing.T) { require.Equal(t, "noEscapeChar", res) } +func Benchmark_Utils_RemoveEscapeChar(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + var res string + for n := 0; n < b.N; n++ { + res = RemoveEscapeChar(":test\\:bla") + } + + require.Equal(b, ":test:bla", res) +} + // go test -race -run Test_Path_matchParams func Benchmark_Path_matchParams(t *testing.B) { var ctxParams [maxParams]string diff --git a/path_testcases_test.go b/path_testcases_test.go index 5602a0284d..26ec5b748e 100644 --- a/path_testcases_test.go +++ b/path_testcases_test.go @@ -85,21 +85,21 @@ func init() { }, }, { - pattern: "/v1/some/resource/name\\:customVerb", + pattern: `/v1/some/resource/name\:customVerb`, testCases: []routeTestCase{ {url: "/v1/some/resource/name:customVerb", params: nil, match: true}, {url: "/v1/some/resource/name:test", params: nil, match: false}, }, }, { - pattern: "/v1/some/resource/:name\\:customVerb", + pattern: `/v1/some/resource/:name\:customVerb`, testCases: []routeTestCase{ {url: "/v1/some/resource/test:customVerb", params: []string{"test"}, match: true}, {url: "/v1/some/resource/test:test", params: nil, match: false}, }, }, { - pattern: "/v1/some/resource/name\\\\:customVerb?\\?/:param/*", + pattern: `/v1/some/resource/name\\:customVerb?\?/:param/*`, testCases: []routeTestCase{ {url: "/v1/some/resource/name:customVerb??/test/optionalWildCard/character", params: []string{"test", "optionalWildCard/character"}, match: true}, {url: "/v1/some/resource/name:customVerb??/test", params: []string{"test", ""}, match: true}, @@ -572,7 +572,7 @@ func init() { }, }, { - pattern: "/api/v1/:param", + pattern: `/api/v1/:param`, testCases: []routeTestCase{ {url: "/api/v1/entity", params: nil, match: false}, {url: "/api/v1/8728382", params: nil, match: false}, @@ -598,7 +598,7 @@ func init() { }, }, { - pattern: "/api/v1/:param", + pattern: `/api/v1/:param`, testCases: []routeTestCase{ {url: "/api/v1/ent", params: nil, match: false}, {url: "/api/v1/15", params: nil, match: false}, @@ -642,7 +642,7 @@ func init() { }, }, { - pattern: "/api/v1/:param", + pattern: `/api/v1/:param`, testCases: []routeTestCase{ {url: "/api/v1/entity", params: []string{"entity"}, match: true}, {url: "/api/v1/87283827683", params: []string{"87283827683"}, match: true}, @@ -651,7 +651,7 @@ func init() { }, }, { - pattern: "/api/v1/:param", + pattern: `/api/v1/:param`, testCases: []routeTestCase{ {url: "/api/v1/entity", params: nil, match: false}, {url: "/api/v1/87283827683", params: nil, match: false}, @@ -697,7 +697,7 @@ func init() { }, }, { - pattern: "/api/v1/:date/:regex", + pattern: `/api/v1/:date/:regex`, testCases: []routeTestCase{ {url: "/api/v1/2005-11-01/a", params: nil, match: false}, {url: "/api/v1/2005-1101/paach", params: nil, match: false}, diff --git a/prefork.go b/prefork.go index 5674124a14..966b8bc199 100644 --- a/prefork.go +++ b/prefork.go @@ -13,8 +13,9 @@ import ( "sync/atomic" "time" - "github.com/gofiber/fiber/v2/log" "github.com/valyala/fasthttp/reuseport" + + "github.com/gofiber/fiber/v3/log" ) const ( diff --git a/router.go b/router.go index 87429d6213..f25e98c565 100644 --- a/router.go +++ b/router.go @@ -6,6 +6,7 @@ package fiber import ( "fmt" + "html" "sort" "strconv" "strings" @@ -43,7 +44,7 @@ type Router interface { // Route is a struct that holds all metadata for each registered handler. type Route struct { - // always keep in sync with the copy method "app.copyRoute" + // ### important: always keep in sync with the copy method "app.copyRoute" ### // Data for routing pos uint32 // Position in stack -> important for the sort of the matched routes use bool // USE matches path prefixes @@ -192,7 +193,7 @@ func (app *App) next(c *DefaultCtx) (bool, error) { } // If c.Next() does not match, return 404 - err := NewError(StatusNotFound, "Cannot "+c.method+" "+c.pathOriginal) + err := NewError(StatusNotFound, "Cannot "+c.method+" "+html.EscapeString(c.pathOriginal)) if !c.matched && app.methodExist(c) { // If no match, scan stack again if other methods match the request // Moved from app.handler because middleware may break the route chain @@ -277,13 +278,14 @@ func (*App) copyRoute(route *Route) *Route { // Path data path: route.path, routeParser: route.routeParser, - Params: route.Params, // misc pos: route.pos, // Public data Path: route.Path, + Params: route.Params, + Name: route.Name, Method: route.Method, Handlers: route.Handlers, } diff --git a/router_test.go b/router_test.go index 4c28cc841d..eb1a1cb233 100644 --- a/router_test.go +++ b/router_test.go @@ -456,6 +456,40 @@ func Test_Route_Static_HasPrefix(t *testing.T) { require.True(t, strings.Contains(app.getString(body), "color")) } +func Test_Router_NotFound(t *testing.T) { + app := New() + app.Use(func(c Ctx) error { + return c.Next() + }) + appHandler := app.Handler() + c := &fasthttp.RequestCtx{} + + c.Request.Header.SetMethod("DELETE") + c.URI().SetPath("/this/route/does/not/exist") + + appHandler(c) + + require.Equal(t, 404, c.Response.StatusCode()) + require.Equal(t, "Cannot DELETE /this/route/does/not/exist", string(c.Response.Body())) +} + +func Test_Router_NotFound_HTML_Inject(t *testing.T) { + app := New() + app.Use(func(c Ctx) error { + return c.Next() + }) + appHandler := app.Handler() + c := &fasthttp.RequestCtx{} + + c.Request.Header.SetMethod("DELETE") + c.URI().SetPath("/does/not/exist") + + appHandler(c) + + require.Equal(t, 404, c.Response.StatusCode()) + require.Equal(t, "Cannot DELETE /does/not/exist<script>alert('foo');</script>", string(c.Response.Body())) +} + ////////////////////////////////////////////// ///////////////// BENCHMARKS ///////////////// ////////////////////////////////////////////// diff --git a/utils/assertions.go b/utils/assertions.go new file mode 100644 index 0000000000..3682d56542 --- /dev/null +++ b/utils/assertions.go @@ -0,0 +1,68 @@ +// ⚡️ 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 interface{}, description ...string) { //nolint:thelper // TODO: Verify if tb can be nil + 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 + const pad = 5 + w := tabwriter.NewWriter(&buf, 0, 0, pad, ' ', 0) + _, _ = fmt.Fprintf(w, "\nTest:\t%s", testName) + _, _ = fmt.Fprintf(w, "\nTrace:\t%s:%d", filepath.Base(file), line) + if len(description) > 0 { + _, _ = fmt.Fprintf(w, "\nDescription:\t%s", description[0]) + } + _, _ = fmt.Fprintf(w, "\nExpect:\t%v\t(%s)", expected, aType) + _, _ = fmt.Fprintf(w, "\nResult:\t%v\t(%s)", actual, bType) + + var result string + if err := w.Flush(); err != nil { + result = err.Error() + } else { + result = buf.String() + } + + if tb != nil { + tb.Fatal(result) + } else { + log.Fatal(result) //nolint:revive // tb might be nil, so we need a fallback + } +} diff --git a/utils/convert_b2s_new.go b/utils/convert_b2s_new.go new file mode 100644 index 0000000000..3fcf7d5afa --- /dev/null +++ b/utils/convert_b2s_new.go @@ -0,0 +1,12 @@ +//go:build go1.20 + +package utils + +import ( + "unsafe" +) + +// UnsafeString returns a string pointer without allocation +func UnsafeString(b []byte) string { + return unsafe.String(unsafe.SliceData(b), len(b)) +} diff --git a/utils/convert_b2s_old.go b/utils/convert_b2s_old.go new file mode 100644 index 0000000000..36cbe30967 --- /dev/null +++ b/utils/convert_b2s_old.go @@ -0,0 +1,14 @@ +//go:build !go1.20 + +package utils + +import ( + "unsafe" +) + +// UnsafeString returns a string pointer without allocation +// +//nolint:gosec // unsafe is used for better performance here +func UnsafeString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} diff --git a/utils/convert_s2b_new.go b/utils/convert_s2b_new.go new file mode 100644 index 0000000000..5da5c81a61 --- /dev/null +++ b/utils/convert_s2b_new.go @@ -0,0 +1,12 @@ +//go:build go1.20 + +package utils + +import ( + "unsafe" +) + +// UnsafeBytes returns a byte pointer without allocation. +func UnsafeBytes(s string) []byte { + return unsafe.Slice(unsafe.StringData(s), len(s)) +} diff --git a/utils/convert_s2b_old.go b/utils/convert_s2b_old.go new file mode 100644 index 0000000000..c9435bd484 --- /dev/null +++ b/utils/convert_s2b_old.go @@ -0,0 +1,24 @@ +//go:build !go1.20 + +package utils + +import ( + "reflect" + "unsafe" +) + +const MaxStringLen = 0x7fff0000 // Maximum string length for UnsafeBytes. (decimal: 2147418112) + +// UnsafeBytes returns a byte pointer without allocation. +// String length shouldn't be more than 2147418112. +// +//nolint:gosec // unsafe is used for better performance here +func UnsafeBytes(s string) []byte { + if s == "" { + return nil + } + + return (*[MaxStringLen]byte)(unsafe.Pointer( + (*reflect.StringHeader)(unsafe.Pointer(&s)).Data), + )[:len(s):len(s)] +}