From 24a6170323e45e5c16ee14de3853b2d763509c2f Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Mon, 31 Oct 2022 22:12:43 +0800 Subject: [PATCH 01/32] :recycle: Leverage runtime/debug to print the full stack trace info (#2183) --- middleware/recover/config.go | 2 -- middleware/recover/recover.go | 8 +++----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/middleware/recover/config.go b/middleware/recover/config.go index fb3ffe73..8e372a5a 100644 --- a/middleware/recover/config.go +++ b/middleware/recover/config.go @@ -22,8 +22,6 @@ type Config struct { StackTraceHandler func(c *fiber.Ctx, e interface{}) } -var defaultStackTraceBufLen = 1024 - // ConfigDefault is the default config var ConfigDefault = Config{ Next: nil, diff --git a/middleware/recover/recover.go b/middleware/recover/recover.go index 8cc80233..5d77d6d0 100644 --- a/middleware/recover/recover.go +++ b/middleware/recover/recover.go @@ -3,15 +3,13 @@ package recover import ( "fmt" "os" - "runtime" + "runtime/debug" "github.com/gofiber/fiber/v2" ) -func defaultStackTraceHandler(c *fiber.Ctx, e interface{}) { - buf := make([]byte, defaultStackTraceBufLen) - buf = buf[:runtime.Stack(buf, false)] - _, _ = os.Stderr.WriteString(fmt.Sprintf("panic: %v\n%s\n", e, buf)) +func defaultStackTraceHandler(_ *fiber.Ctx, e interface{}) { + _, _ = os.Stderr.WriteString(fmt.Sprintf("panic: %v\n%s\n", e, debug.Stack())) } // New creates a new middleware handler From c63a569a929514e08334c54780ac175aae2e5a61 Mon Sep 17 00:00:00 2001 From: James R T Date: Tue, 1 Nov 2022 15:49:44 +0800 Subject: [PATCH 02/32] =?UTF-8?q?=F0=9F=9A=80=20[Feature]:=20Allow=20optio?= =?UTF-8?q?nal=20params=20with=20route=20constraints=20(#2179)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: James R T Signed-off-by: James R T --- path.go | 10 ++++++---- path_test.go | 12 ++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/path.go b/path.go index 927a97aa..6ea02951 100644 --- a/path.go +++ b/path.go @@ -437,10 +437,12 @@ func (routeParser *routeParser) getMatch(detectionPath, path string, params *[ma // take over the params positions params[paramsIterator] = path[:i] - // check constraint - for _, c := range segment.Constraints { - if matched := c.CheckConstraint(params[paramsIterator]); !matched { - return false + if !(segment.IsOptional && i == 0) { + // check constraint + for _, c := range segment.Constraints { + if matched := c.CheckConstraint(params[paramsIterator]); !matched { + return false + } } } diff --git a/path_test.go b/path_test.go index 5e89e1a8..a702eaa2 100644 --- a/path_test.go +++ b/path_test.go @@ -597,6 +597,12 @@ func Test_Path_matchParams(t *testing.T) { {url: "/api/v1/2005-1101/paach", params: nil, match: false}, {url: "/api/v1/2005-11-01/peach", params: []string{"2005-11-01", "peach"}, match: true}, }) + testCase("/api/v1/:param?", []testparams{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/true", params: nil, match: false}, + {url: "/api/v1/", params: []string{""}, match: true}, + }) } func Test_Utils_GetTrimmedParam(t *testing.T) { @@ -851,4 +857,10 @@ func Benchmark_Path_matchParams(t *testing.B) { {url: "/api/v1/2005-1101/paach", params: nil, match: false}, {url: "/api/v1/2005-11-01/peach", params: []string{"2005-11-01", "peach"}, match: true}, }) + benchCase("/api/v1/:param?", []testparams{ + {url: "/api/v1/entity", params: nil, match: false}, + {url: "/api/v1/8728382", params: []string{"8728382"}, match: true}, + {url: "/api/v1/true", params: nil, match: false}, + {url: "/api/v1/", params: []string{""}, match: true}, + }) } From 87cedc2022cc3ebfe3ca4ec5621154feb9a313e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Tue, 1 Nov 2022 10:50:33 +0300 Subject: [PATCH 03/32] :construction_worker: ci: replace snyk with govulncheck (#2178) --- .github/workflows/snyk.yml | 11 ----------- .github/workflows/vulncheck.yml | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 11 deletions(-) delete mode 100644 .github/workflows/snyk.yml create mode 100644 .github/workflows/vulncheck.yml diff --git a/.github/workflows/snyk.yml b/.github/workflows/snyk.yml deleted file mode 100644 index f3b872ec..00000000 --- a/.github/workflows/snyk.yml +++ /dev/null @@ -1,11 +0,0 @@ -on: [push, pull_request_target] -name: Snyk security -jobs: - security: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Run Snyk to check for vulnerabilities - uses: snyk/actions/golang@master - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/vulncheck.yml b/.github/workflows/vulncheck.yml new file mode 100644 index 00000000..bbf7c155 --- /dev/null +++ b/.github/workflows/vulncheck.yml @@ -0,0 +1,20 @@ +on: [push, pull_request_target] +name: Vulnerability Check +jobs: + Security: + runs-on: ubuntu-latest + steps: + - name: Install Go + uses: actions/setup-go@v3 + with: + go-version: 1.19.x + - name: Fetch Repository + uses: actions/checkout@v3 + - name: Install Govulncheck + run: | + export GO111MODULE=on + export PATH=${PATH}:`go env GOPATH`/bin + go install golang.org/x/vuln/cmd/govulncheck@latest + - name: Run Govulncheck + run: "`go env GOPATH`/bin/govulncheck ./..." + From 486f3709ed96861643cdb575988777f711e51dce Mon Sep 17 00:00:00 2001 From: kinggo Date: Tue, 8 Nov 2022 21:20:14 +0800 Subject: [PATCH 04/32] ci: add check-latest param in vulncheck.yml (#2197) ci: add check-latest param --- .github/workflows/vulncheck.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/vulncheck.yml b/.github/workflows/vulncheck.yml index bbf7c155..061ccd56 100644 --- a/.github/workflows/vulncheck.yml +++ b/.github/workflows/vulncheck.yml @@ -7,7 +7,8 @@ jobs: - name: Install Go uses: actions/setup-go@v3 with: - go-version: 1.19.x + go-version: 1.19 + check-latest: true - name: Fetch Repository uses: actions/checkout@v3 - name: Install Govulncheck From 55fcddda6f153cbc4c00ed1d50f5a497ee62ab26 Mon Sep 17 00:00:00 2001 From: skyenought <70408571+Skyenought@users.noreply.github.com> Date: Wed, 9 Nov 2022 14:56:58 +0800 Subject: [PATCH 05/32] =?UTF-8?q?=F0=9F=93=9D=20Update=20README=5Fzh-CN.md?= =?UTF-8?q?=20(#2186)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/README_zh-CN.md | 123 +++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 58 deletions(-) diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index 7697ae6e..81b74840 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -79,7 +79,9 @@

- Fiber是一个受到Express启发的Web框架,基于使用Go语言编写的最快的HTTP引擎Fasthttp构建。旨在通过零内存分配高性能服务,使快速开发更加简便。 + Fiber是一个受到 Express 启发的Web框架,基于使用 + Go 语言编写的最快的 HTTP 引擎 + Fasthttp 构建。旨在通过零内存分配高性能服务,使快速开发更加简便。

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

@@ -111,7 +113,7 @@ func main() { ## ⚙️ 安装 -确保已安装`1.14`或更高版本的 Go([下载](https://go.dev/dl/))。 +确保已安装 `1.14` 或更高版本的 Go ([下载](https://go.dev/dl/))。 通过创建文件夹并在文件夹内运行 `go mod init github.com/your/repo` ([了解更多](https://go.dev/blog/using-go-modules)) 来初始化项目,然后使用 [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) 命令安装 Fiber: @@ -126,30 +128,33 @@ go get -u github.com/gofiber/fiber/v2 - 极致[性能](https://docs.gofiber.io/extra/benchmarks) - [低内存占用](https://docs.gofiber.io/extra/benchmarks) - [API 接口](https://docs.gofiber.io/api/ctx) -- [中间件](https://docs.gofiber.io/middleware)和[Next](https://docs.gofiber.io/api/ctx#next)支持 -- [快速](https://dev.to/koddr/welcome-to-fiber-an-express-js-styled-fastest-web-framework-written-with-on-golang-497)服务器端编程 +- 支持[中间件](https://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) +- [支持 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/) +- [被翻译成 18 种语言](https://docs.gofiber.io/) - 更多请[探索文档](https://docs.gofiber.io/) ## 💡 哲学 -从[Node.js](https://nodejs.org/en/about/)切换到[Go](https://go.dev/doc/)的新`gopher`在开始构建`Web`应用程序或微服务之前需要经历一段艰难的学习过程。 而`Fiber`,一个遵循**极简主义**和**UNIX 方式**创建的**Web 框架**,使新的`gopher`可以在热烈和可信赖的欢迎中迅速进入`Go`的世界。 +从 [Node.js](https://nodejs.org/en/about/) 切换到 [Go](https://go.dev/doc/) 的新 `gopher` 在开始构建 `Web` +应用程序或微服务之前需要经历一段艰难的学习过程。 而 `Fiber`,一个基于**极简主义**并且遵循 **UNIX 方式**创建的 **Web 框架**, +使新的 `gopher` 可以在热烈和可信赖的欢迎中迅速进入 `Go` 的世界。 `Fiber`受到了互联网上最流行的`Web`框架`Express`的**启发** 。我们结合了`Express`的**易用性**和`Go`的**原始性能** 。如果您曾经使用`Node.js`构建`Web`应用程序(_使用 Express 或类似框架_),那么许多方法和原理对您来说应该**非常易懂**。 -我们会**倾听**用户在[issues](https://github.com/gofiber/fiber/issues)和 Discord [channel](https://gofiber.io/discord)和在互联网上的所有诉求,为了创建一个能让有着任何技术栈的开发者都能在deadline前完成任务的**迅速**,**灵活**以及**友好**的`Go web`框架,就像`Express`在`JavaScript`世界中一样。 +我们会**倾听**用户在 [issues](https://github.com/gofiber/fiber/issues),Discord [channel](https://gofiber.io/discord) +以及在互联网上的所有诉求,为了创建一个能让有着任何技术栈的开发者都能在 deadline 前完成任务的**迅速**,**灵活**以及**友好**的 `Go web` 框架,就像 `Express` 在 `JavaScript` 世界中一样。 ## ⚠️ 限制 * 由于 Fiber 使用了 unsafe 特性,导致其可能与最新的 Go 版本不兼容。Fiber 2.40.0 已经在 Go 1.16 到 1.19 上测试过。 -* Fiber 与 net/http 接口不兼容。也就是说你无法使用 gqlen,go-swagger 或者任何其他属于 net/http 生态的项目。 +* Fiber 与 net/http 接口不兼容。也就是说你无法直接使用例如 gqlen,go-swagger 或者任何其他属于 net/http 生态的项目。 ## 👀 示例 -下面列出了一些常见示例。如果您想查看更多代码示例,请访问我们的[Recipes](https://github.com/gofiber/recipes)代码库或[API 文档](https://docs.gofiber.io) 。 +下面列出了一些常见示例。如果您想查看更多代码示例,请访问我们的 [Recipes](https://github.com/gofiber/recipes) 代码库或 [API 文档](https://docs.gofiber.io) 。 #### 📖 [**基础路由**](https://docs.gofiber.io/#basic-routing) @@ -244,7 +249,7 @@ func main() { ``` -#### 📖 [**中间件**](https://docs.gofiber.io/middleware)和[**Next**](https://docs.gofiber.io/api/ctx#next) +#### 📖 [**中间件**](https://docs.gofiber.io/middleware)和 [**Next**](https://docs.gofiber.io/api/ctx#next) ```go func main() { @@ -282,11 +287,12 @@ func main() { 📖 [模版引擎](https://github.com/gofiber/template) 📖 [渲染](https://docs.gofiber.io/api/ctx#render) -如果未设置模版引擎,则`Fiber`默认使用[html/template](https://pkg.go.dev/html/template/)。 +如果未设置模版引擎,则`Fiber`默认使用 [html/template](https://pkg.go.dev/html/template/)。 -如果您要执行部分模版或使用其他引擎,例如[amber](https://github.com/eknkc/amber),[handlebars](https://github.com/aymerick/raymond),[mustache](https://github.com/cbroglie/mustache)或者[pug](https://github.com/Joker/jade)等等... +如果您要执行部分模版或使用其他引擎,例如[amber](https://github.com/eknkc/amber),[handlebars](https://github.com/aymerick/raymond), +[mustache](https://github.com/cbroglie/mustache) 或者 [pug](https://github.com/Joker/jade)等 -请查看我们的[Template](https://github.com/gofiber/template)包,该包支持多个模版引擎。 +请查看我们的 [Template](https://github.com/gofiber/template) 包,该包支持多个模版引擎。 ```go package main @@ -397,7 +403,7 @@ func main() { } ``` -通过在请求头中设置`Origin`传递任何域来检查 CORS: +通过在请求头中设置 `Origin` 传递任何域来检查 CORS : ```bash curl -H "Origin: http://example.com" --verbose http://localhost:3000 @@ -461,7 +467,7 @@ func main() { } ``` -### 升级到 WebSocket +### 使用 WebSocket 中间件 📖 [Websocket](https://github.com/gofiber/websocket) @@ -565,45 +571,45 @@ func main() { 以下为包含在Fiber框架中的中间件列表. -| 中间件 | 描述 | -| :------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | 基本身份验证中间件提供HTTP基本身份验证。 它为有效凭证调用下一个处理程序,为丢失或无效凭证调用401 Unauthorized. | -| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | 拦截和缓存响应. | -| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Fiber的压缩中间件,默认支持' deflate ', ' gzip '和' brotli '. | -| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | 使用各种选项启用跨源资源共享\(CORS\). | -| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | 保护来自CSRF的漏洞. | -| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | 加密 cookie 值的加密中间件. | -| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | 通过提供可选配置来公开环境变量。. | -| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | 让缓存更加高效并且节省带宽,让web服务不再需要重新响应整个响应体如果响应内容未变更. | -| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | 通过其HTTP服务器运行时间提供JSON格式的暴露变体. | -| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | 如果提供了文件路径,则忽略日志中的图标或从内存中服务. | -| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Fiber文件系统中间件,特别感谢 Alireza Salary. | -| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | 用于Fiber的限速中间件。 用于限制对公共api和/或端点的重复请求,如密码重置. | -| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP请求/响应日志. | -| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | 报告服务器指标,受Express-status-monitor启发. | -| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | 特别感谢 Matthew Lee \(@mthli\) | -| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | 允许您将请求proxy到多个服务器 | -| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | 恢复中间件从堆栈链中的任何位置的恐慌中恢复,并将控制处理到集中式. [ ErrorHandler](https://docs.gofiber.io/guide/error-handling). | -| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | 为每个请求添加一个requesttid. | -| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session 中间件. 注意: 此中间件使用了我们的存储包. | -| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip middleware that skips a wrapped handler is a predicate is true. | -| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | 添加请求的最大时间,如果超过了,则发送给ErrorHandler. | +| 中间件 | 描述 | +|:---------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------| +| [basicauth](https://github.com/gofiber/fiber/tree/master/middleware/basicauth) | 基本身份验证中间件提供 HTTP 基本身份验证。 它为有效凭证调用下一个处理程序,为丢失或无效凭证调用 401 Unauthorized | +| [cache](https://github.com/gofiber/fiber/tree/master/middleware/cache) | 用于拦截和缓存响应 | +| [compress](https://github.com/gofiber/fiber/tree/master/middleware/compress) | Fiber 的压缩中间件,默认支持 `deflate`,`gzip` 和 `brotli` | +| [cors](https://github.com/gofiber/fiber/tree/master/middleware/cors) | 使用各种选项启用跨源资源共享\(CORS\) | +| [csrf](https://github.com/gofiber/fiber/tree/master/middleware/csrf) | 保护来自 CSRF 的漏洞 | +| [encryptcookie](https://github.com/gofiber/fiber/tree/master/middleware/encryptcookie) | 加密 cookie 值的加密中间件 | +| [envvar](https://github.com/gofiber/fiber/tree/master/middleware/envvar) | 通过提供可选配置来公开环境变量 | +| [etag](https://github.com/gofiber/fiber/tree/master/middleware/etag) | 让缓存更加高效并且节省带宽, 让 web 服务在响应内容未变更的情况下不再需要重发送整个响应体 | +| [expvar](https://github.com/gofiber/fiber/tree/master/middleware/expvar) | 通过其 HTTP 服务器运行时间提供 JSON 格式的暴露变体 | +| [favicon](https://github.com/gofiber/fiber/tree/master/middleware/favicon) | 如果提供了文件路径,则忽略日志中的图标或从内存中服务 | +| [filesystem](https://github.com/gofiber/fiber/tree/master/middleware/filesystem) | Fiber 文件系统中间件,特别感谢 Alireza Salary | +| [limiter](https://github.com/gofiber/fiber/tree/master/middleware/limiter) | 用于 Fiber 的限速中间件。 用于限制对公共 api 或对端点的重复请求,如密码重置 | +| [logger](https://github.com/gofiber/fiber/tree/master/middleware/logger) | HTTP 请求/响应日志 | +| [monitor](https://github.com/gofiber/fiber/tree/master/middleware/monitor) | 用于报告服务器指标,受 Express-status-monitor 启发 | +| [pprof](https://github.com/gofiber/fiber/tree/master/middleware/pprof) | 特别感谢 Matthew Lee \(@mthli\) | +| [proxy](https://github.com/gofiber/fiber/tree/master/middleware/proxy) | 允许您将请求proxy到多个服务器 | +| [recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) | Recover 中间件将可以堆栈链中的任何位置将 panic 恢复,并将处理集中到 [ErrorHandler](https://docs.gofiber.io/guide/error-handling) | +| [requestid](https://github.com/gofiber/fiber/tree/master/middleware/requestid) | 为每个请求添加一个 requestid. | +| [session](https://github.com/gofiber/fiber/tree/master/middleware/session) | Session 中间件. 注意: 此中间件使用了我们的存储包. | +| [skip](https://github.com/gofiber/fiber/tree/master/middleware/skip) | Skip 中间件会在判断条为 true 时忽略此次请求 | +| [timeout](https://github.com/gofiber/fiber/tree/master/middleware/timeout) | 添加请求的最大时间,如果超时则发送给ErrorHandler 进行处理. | ## 🧬 外部中间件 -以下为外部托管的中间件列表,由[Fiber团队](https://github.com/orgs/gofiber/people)维护。 +以下为外部托管的中间件列表,由 [Fiber团队](https://github.com/orgs/gofiber/people) 维护。 -| 中间件 | 描述 | -| :------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [adaptor](https://github.com/gofiber/adaptor) | net/http处理程序与Fiber请求处理程序之间的转换器,特别感谢 @arsmn! | -| [helmet](https://github.com/gofiber/helmet) | 通过设置各种HTTP头帮助保护您的应用程序。 | -| [jwt](https://github.com/gofiber/jwt) | JWT返回一个JSON Web Token\(JWT\)身份验证中间件。 | -| [keyauth](https://github.com/gofiber/keyauth) | Key auth中间件提供基于密钥的身份验证。 | -| [redirect](https://github.com/gofiber/redirect) | Redirect middleware | -| [rewrite](https://github.com/gofiber/rewrite) | Rewrite中间件根据提供的规则重写URL路径。它有助于向后兼容或者创建更清晰、更具描述性的链接。 | -| [storage](https://github.com/gofiber/storage) | Premade storage drivers that implement the Storage interface, designed to be used with various Fiber middlewares. | -| [template](https://github.com/gofiber/template) | 该软件包包含8个模板引擎,可与Fiber `v1.10.x` Go 1.13或更高版本一起使用。 | -| [websocket](https://github.com/gofiber/websocket) | 基于 Fasthttp WebSocket for Fiber与Locals支持! | +| 中间件 | 描述 | +|:--------------------------------------------------|:-------------------------------------------------------------------------------------------| +| [adaptor](https://github.com/gofiber/adaptor) | net/http 处理程序与 Fiber 请求处理程序之间的转换器,特别感谢 @arsmn! | +| [helmet](https://github.com/gofiber/helmet) | 通过设置各种 HTTP 头帮助保护您的应用程序 | +| [jwt](https://github.com/gofiber/jwt) | JWT 返回一个 JSON Web Token\(JWT\) 身份验证中间件 | +| [keyauth](https://github.com/gofiber/keyauth) | Key auth 中间件提供基于密钥的身份验证 | +| [redirect](https://github.com/gofiber/redirect) | 用于重定向请求的中间件 | +| [rewrite](https://github.com/gofiber/rewrite) | Rewrite 中间件根据提供的规则重写URL路径。它有助于向后兼容或者创建更清晰、更具描述性的链接 | +| [storage](https://github.com/gofiber/storage) | 包含实现 Storage 接口的数据库驱动,它的设计旨在配合 fiber 的其他中间件来进行使用 | +| [template](https://github.com/gofiber/template) | 该中间件包含 8 个模板引擎,可与 Fiber `v1.10.x` Go 1.13或更高版本一起使用 | +| [websocket](https://github.com/gofiber/websocket) | 基于 Fasthttp WebSocket for Fiber 实现,支持使用 [Locals](https://docs.gofiber.io/api/ctx#locals) ! | ## 🕶️ Awesome List @@ -611,16 +617,17 @@ For more articles, middlewares, examples or tools check our [awesome list](https ## 👍 贡献 -如果想**感谢**我们或支持`Fiber`的积极发展: +如果想**感谢**我们或支持 `Fiber` 的积极发展: -1. 为`Fiber`[GitHub Star](https://github.com/gofiber/fiber/stargazers)点个 ⭐ 星星。 -2. 在[Twitter](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber)上发布有关项目的[推文](https://twitter.com/intent/tweet?text=Fiber%20is%20an%20Express%20inspired%20%23web%20%23framework%20built%20on%20top%20of%20Fasthttp%2C%20the%20fastest%20HTTP%20engine%20for%20%23Go.%20Designed%20to%20ease%20things%20up%20for%20%23fast%20development%20with%20zero%20memory%20allocation%20and%20%23performance%20in%20mind%20%F0%9F%9A%80%20https%3A%2F%2Fgithub.com%2Fgofiber%2Ffiber)。 -3. 在[Medium](https://medium.com/),[Dev.to](https://dev.to/)或个人博客上写评论或教程。 +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)。 +3. 在 [Medium](https://medium.com/),[Dev.to](https://dev.to/) 或个人博客上写评论或教程。 4. 通过捐赠[一杯咖啡](https://buymeacoff.ee/fenny)来支持本项目。 ## ☕ 支持者 -`Fiber`是一个开源项目,依靠捐赠来支付账单,例如我们的域名,`gitbook`,`netlify`和无服务器托管。如果要支持`Fiber`,可以 ☕ [**在这里买一杯咖啡**](https://buymeacoff.ee/fenny) +`Fiber`是一个开源项目,依靠捐赠来支付账单,例如我们的域名,`gitbook`,`netlify` 和无服务器托管。如果要支持 `Fiber`, +可以 ☕ [**在这里买一杯咖啡**](https://buymeacoff.ee/fenny) | | User | Donation | | :--------------------------------------------------------- | :----------------------------------------------- | :------- | @@ -647,7 +654,7 @@ For more articles, middlewares, examples or tools check our [awesome list](https Code Contributors -## ⭐️ 星星数增长情况 +## ⭐️ Star 数增长情况 Stargazers over time From 13247206ab63de182748c23d277675b574ff7c95 Mon Sep 17 00:00:00 2001 From: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> Date: Tue, 8 Nov 2022 22:59:33 -0800 Subject: [PATCH 06/32] Add URL prefix to pprof middleware (#2194) * Add URL prefix to pprof middleware Signed-off-by: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> * Minor tweak Signed-off-by: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> Signed-off-by: Glenn Lewis <6598971+gmlewis@users.noreply.github.com> --- middleware/pprof/README.md | 19 ++++++- middleware/pprof/config.go | 7 +++ middleware/pprof/pprof.go | 26 ++++----- middleware/pprof/pprof_test.go | 96 ++++++++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 14 deletions(-) diff --git a/middleware/pprof/README.md b/middleware/pprof/README.md index 5f266438..2a5bbc2f 100644 --- a/middleware/pprof/README.md +++ b/middleware/pprof/README.md @@ -24,6 +24,16 @@ After you initiate your Fiber app, you can use the following possibilities: app.Use(pprof.New()) ``` +In systems where you have multiple ingress endpoints, it is common to add a URL prefix, like so: + +```go +// Default middleware +app.Use(pprof.New(pprof.Config{Prefix: "/endpoint-prefix"})) +``` + +This prefix will be added to the default path of "/debug/pprof/", for a resulting URL of: +"/endpoint-prefix/debug/pprof/". + ## Config ```go @@ -33,6 +43,13 @@ type Config struct { // // Optional. Default: nil Next func(c *fiber.Ctx) bool + + // Prefix defines a URL prefix added before "/debug/pprof". + // Note that it should start with (but not end with) a slash. + // Example: "/federated-fiber" + // + // Optional. Default: "" + Prefix string } ``` @@ -42,4 +59,4 @@ type Config struct { var ConfigDefault = Config{ Next: nil, } -``` \ No newline at end of file +``` diff --git a/middleware/pprof/config.go b/middleware/pprof/config.go index 93f58fc9..e69ffd70 100644 --- a/middleware/pprof/config.go +++ b/middleware/pprof/config.go @@ -8,6 +8,13 @@ type Config struct { // // Optional. Default: nil Next func(c *fiber.Ctx) bool + + // Prefix defines a URL prefix added before "/debug/pprof". + // Note that it should start with (but not end with) a slash. + // Example: "/federated-fiber" + // + // Optional. Default: "" + Prefix string } var ConfigDefault = Config{ diff --git a/middleware/pprof/pprof.go b/middleware/pprof/pprof.go index b4c7ba33..8ad85c9a 100644 --- a/middleware/pprof/pprof.go +++ b/middleware/pprof/pprof.go @@ -37,39 +37,39 @@ func New(config ...Config) fiber.Handler { path := c.Path() // We are only interested in /debug/pprof routes - if len(path) < 12 || !strings.HasPrefix(path, "/debug/pprof") { + if len(path) < 12 || !strings.HasPrefix(path, cfg.Prefix+"/debug/pprof") { return c.Next() } // Switch to original path without stripped slashes switch path { - case "/debug/pprof/": + case cfg.Prefix + "/debug/pprof/": pprofIndex(c.Context()) - case "/debug/pprof/cmdline": + case cfg.Prefix + "/debug/pprof/cmdline": pprofCmdline(c.Context()) - case "/debug/pprof/profile": + case cfg.Prefix + "/debug/pprof/profile": pprofProfile(c.Context()) - case "/debug/pprof/symbol": + case cfg.Prefix + "/debug/pprof/symbol": pprofSymbol(c.Context()) - case "/debug/pprof/trace": + case cfg.Prefix + "/debug/pprof/trace": pprofTrace(c.Context()) - case "/debug/pprof/allocs": + case cfg.Prefix + "/debug/pprof/allocs": pprofAllocs(c.Context()) - case "/debug/pprof/block": + case cfg.Prefix + "/debug/pprof/block": pprofBlock(c.Context()) - case "/debug/pprof/goroutine": + case cfg.Prefix + "/debug/pprof/goroutine": pprofGoroutine(c.Context()) - case "/debug/pprof/heap": + case cfg.Prefix + "/debug/pprof/heap": pprofHeap(c.Context()) - case "/debug/pprof/mutex": + case cfg.Prefix + "/debug/pprof/mutex": pprofMutex(c.Context()) - case "/debug/pprof/threadcreate": + case cfg.Prefix + "/debug/pprof/threadcreate": pprofThreadcreate(c.Context()) default: // pprof index only works with trailing slash if strings.HasSuffix(path, "/") { path = strings.TrimRight(path, "/") } else { - path = "/debug/pprof/" + path = cfg.Prefix + "/debug/pprof/" } return c.Redirect(path, fiber.StatusFound) diff --git a/middleware/pprof/pprof_test.go b/middleware/pprof/pprof_test.go index ed0a41b5..09d7206d 100644 --- a/middleware/pprof/pprof_test.go +++ b/middleware/pprof/pprof_test.go @@ -28,6 +28,24 @@ func Test_Non_Pprof_Path(t *testing.T) { utils.AssertEqual(t, "escaped", string(b)) } +func Test_Non_Pprof_Path_WithPrefix(t *testing.T) { + app := fiber.New(fiber.Config{DisableStartupMessage: true}) + + app.Use(New(Config{Prefix: "/federated-fiber"})) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("escaped") + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + + b, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "escaped", string(b)) +} + func Test_Pprof_Index(t *testing.T) { app := fiber.New(fiber.Config{DisableStartupMessage: true}) @@ -47,6 +65,25 @@ func Test_Pprof_Index(t *testing.T) { utils.AssertEqual(t, true, bytes.Contains(b, []byte("/debug/pprof/"))) } +func Test_Pprof_Index_WithPrefix(t *testing.T) { + app := fiber.New(fiber.Config{DisableStartupMessage: true}) + + app.Use(New(Config{Prefix: "/federated-fiber"})) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("escaped") + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/federated-fiber/debug/pprof/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + utils.AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, resp.Header.Get(fiber.HeaderContentType)) + + b, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, true, bytes.Contains(b, []byte("/debug/pprof/"))) +} + func Test_Pprof_Subs(t *testing.T) { app := fiber.New(fiber.Config{DisableStartupMessage: true}) @@ -74,6 +111,33 @@ func Test_Pprof_Subs(t *testing.T) { } } +func Test_Pprof_Subs_WithPrefix(t *testing.T) { + app := fiber.New(fiber.Config{DisableStartupMessage: true}) + + app.Use(New(Config{Prefix: "/federated-fiber"})) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("escaped") + }) + + subs := []string{ + "cmdline", "profile", "symbol", "trace", "allocs", "block", + "goroutine", "heap", "mutex", "threadcreate", + } + + for _, sub := range subs { + t.Run(sub, func(t *testing.T) { + target := "/federated-fiber/debug/pprof/" + sub + if sub == "profile" { + target += "?seconds=1" + } + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, target, nil), 5000) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 200, resp.StatusCode) + }) + } +} + func Test_Pprof_Other(t *testing.T) { app := fiber.New(fiber.Config{DisableStartupMessage: true}) @@ -88,6 +152,20 @@ func Test_Pprof_Other(t *testing.T) { utils.AssertEqual(t, 302, resp.StatusCode) } +func Test_Pprof_Other_WithPrefix(t *testing.T) { + app := fiber.New(fiber.Config{DisableStartupMessage: true}) + + app.Use(New(Config{Prefix: "/federated-fiber"})) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("escaped") + }) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/federated-fiber/debug/pprof/302", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 302, resp.StatusCode) +} + // go test -run Test_Pprof_Next func Test_Pprof_Next(t *testing.T) { t.Parallel() @@ -104,3 +182,21 @@ func Test_Pprof_Next(t *testing.T) { utils.AssertEqual(t, nil, err) utils.AssertEqual(t, 404, resp.StatusCode) } + +// go test -run Test_Pprof_Next_WithPrefix +func Test_Pprof_Next_WithPrefix(t *testing.T) { + t.Parallel() + + app := fiber.New() + + app.Use(New(Config{ + Next: func(_ *fiber.Ctx) bool { + return true + }, + Prefix: "/federated-fiber", + })) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/federated-fiber/debug/pprof/", nil)) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, 404, resp.StatusCode) +} From 581af0052dd4534b3291b45f40f31d920f6f9084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Wed, 9 Nov 2022 18:03:16 +0300 Subject: [PATCH 07/32] :bug: bug: fix naming of routes inside groups (#2199) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :bug: bug: fix naming of routes inside groups * fix tests * bug: fix naming of routes inside groups Co-authored-by: René Werner --- app.go | 23 +++++++++++------------ app_test.go | 5 +++-- group.go | 35 +++++++++++++++++++++-------------- router.go | 6 +++++- router_test.go | 2 +- 5 files changed, 41 insertions(+), 30 deletions(-) diff --git a/app.go b/app.go index f725424e..fcad05bf 100644 --- a/app.go +++ b/app.go @@ -9,6 +9,8 @@ package fiber import ( "bufio" + "encoding/json" + "encoding/xml" "errors" "fmt" "net" @@ -20,9 +22,6 @@ import ( "sync" "time" - "encoding/json" - "encoding/xml" - "github.com/gofiber/fiber/v2/utils" "github.com/valyala/fasthttp" ) @@ -109,7 +108,6 @@ type App struct { hooks *Hooks // Latest route & group latestRoute *Route - latestGroup *Group // TLS handler tlsHandler *TLSHandler // Mount fields @@ -485,7 +483,6 @@ func New(config ...Config) *App { getBytes: utils.UnsafeBytes, getString: utils.UnsafeString, latestRoute: &Route{}, - latestGroup: &Group{}, } // Define hooks @@ -583,8 +580,10 @@ func (app *App) SetTLSHandler(tlsHandler *TLSHandler) { // Name Assign name to specific route. func (app *App) Name(name string) Router { app.mutex.Lock() - if strings.HasPrefix(app.latestRoute.path, app.latestGroup.Prefix) { - app.latestRoute.Name = app.latestGroup.name + name + + latestGroup := app.latestRoute.group + if latestGroup != nil { + app.latestRoute.Name = latestGroup.name + name } else { app.latestRoute.Name = name } @@ -656,7 +655,7 @@ func (app *App) Use(args ...interface{}) Router { panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg))) } } - app.register(methodUse, prefix, handlers...) + app.register(methodUse, prefix, nil, handlers...) return app } @@ -715,7 +714,7 @@ func (app *App) Patch(path string, handlers ...Handler) Router { // Add allows you to specify a HTTP method to register a route func (app *App) Add(method, path string, handlers ...Handler) Router { - return app.register(method, path, handlers...) + return app.register(method, path, nil, handlers...) } // Static will create a file server serving static files @@ -736,10 +735,10 @@ func (app *App) All(path string, handlers ...Handler) Router { // api := app.Group("/api") // api.Get("/users", handler) func (app *App) Group(prefix string, handlers ...Handler) Router { - if len(handlers) > 0 { - app.register(methodUse, prefix, handlers...) - } grp := &Group{Prefix: prefix, app: app} + if len(handlers) > 0 { + app.register(methodUse, prefix, grp, handlers...) + } if err := app.hooks.executeOnGroupHooks(*grp); err != nil { panic(err) } diff --git a/app_test.go b/app_test.go index 2bef6a47..2e87114b 100644 --- a/app_test.go +++ b/app_test.go @@ -587,15 +587,16 @@ func Test_App_Route_Naming(t *testing.T) { app.Name("doe") jane := app.Group("/jane").Name("jane.") + group := app.Group("/group") + subGroup := jane.Group("/sub-group").Name("sub.") + jane.Get("/test", handler).Name("test") jane.Trace("/trace", handler).Name("trace") - group := app.Group("/group") group.Get("/test", handler).Name("test") app.Post("/post", handler).Name("post") - subGroup := jane.Group("/sub-group").Name("sub.") subGroup.Get("/done", handler).Name("done") utils.AssertEqual(t, "post", app.GetRoute("post").Name) diff --git a/group.go b/group.go index a59eaed8..93826807 100644 --- a/group.go +++ b/group.go @@ -7,13 +7,13 @@ package fiber import ( "fmt" "reflect" - "strings" ) // Group struct type Group struct { - app *App - name string + app *App + parentGroup *Group + name string Prefix string } @@ -21,15 +21,14 @@ type Group struct { // Name Assign name to specific route. func (grp *Group) Name(name string) Router { grp.app.mutex.Lock() - if strings.HasPrefix(grp.Prefix, grp.app.latestGroup.Prefix) { - grp.name = grp.app.latestGroup.name + name + + if grp.parentGroup != nil { + grp.name = grp.parentGroup.name + name } else { grp.name = name } - grp.app.latestGroup = grp - - if err := grp.app.hooks.executeOnGroupNameHooks(*grp.app.latestGroup); err != nil { + if err := grp.app.hooks.executeOnGroupNameHooks(*grp); err != nil { panic(err) } grp.app.mutex.Unlock() @@ -64,15 +63,15 @@ func (grp *Group) Use(args ...interface{}) Router { panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg))) } } - grp.app.register(methodUse, getGroupPath(grp.Prefix, prefix), handlers...) + grp.app.register(methodUse, getGroupPath(grp.Prefix, prefix), grp, handlers...) return grp } // Get registers a route for GET methods that requests a representation // of the specified resource. Requests using GET should only retrieve data. func (grp *Group) Get(path string, handlers ...Handler) Router { - path = getGroupPath(grp.Prefix, path) - return grp.app.Add(MethodHead, path, handlers...).Add(MethodGet, path, handlers...) + grp.Add(MethodHead, path, handlers...) + return grp.Add(MethodGet, path, handlers...) } // Head registers a route for HEAD methods that asks for a response identical @@ -124,7 +123,7 @@ func (grp *Group) Patch(path string, handlers ...Handler) Router { // Add allows you to specify a HTTP method to register a route func (grp *Group) Add(method, path string, handlers ...Handler) Router { - return grp.app.register(method, getGroupPath(grp.Prefix, path), handlers...) + return grp.app.register(method, getGroupPath(grp.Prefix, path), grp, handlers...) } // Static will create a file server serving static files @@ -147,9 +146,17 @@ func (grp *Group) All(path string, handlers ...Handler) Router { func (grp *Group) Group(prefix string, handlers ...Handler) Router { prefix = getGroupPath(grp.Prefix, prefix) if len(handlers) > 0 { - _ = grp.app.register(methodUse, prefix, handlers...) + _ = grp.app.register(methodUse, prefix, grp, handlers...) } - return grp.app.Group(prefix) + + // Create new group + newGrp := &Group{Prefix: prefix, app: grp.app, parentGroup: grp} + if err := grp.app.hooks.executeOnGroupHooks(*newGrp); err != nil { + panic(err) + } + + return newGrp + } // Route is used to define routes with a common prefix inside the common function. diff --git a/router.go b/router.go index 642149d5..d83c1868 100644 --- a/router.go +++ b/router.go @@ -52,6 +52,7 @@ type Route struct { root bool // Path equals '/' path string // Prettified path routeParser routeParser // Parameter parser + group *Group // Group instance. used for routes in groups // Public fields Method string `json:"method"` // HTTP method @@ -211,7 +212,7 @@ func (app *App) copyRoute(route *Route) *Route { } } -func (app *App) register(method, pathRaw string, handlers ...Handler) Router { +func (app *App) register(method, pathRaw string, group *Group, handlers ...Handler) Router { // Uppercase HTTP methods method = utils.ToUpper(method) // Check if the HTTP method is valid unless it's USE @@ -262,6 +263,9 @@ func (app *App) register(method, pathRaw string, handlers ...Handler) Router { routeParser: parsedPretty, Params: parsedRaw.params, + // Group data + group: group, + // Public data Path: pathRaw, Method: method, diff --git a/router_test.go b/router_test.go index eceae1fd..fbd82e4b 100644 --- a/router_test.go +++ b/router_test.go @@ -280,7 +280,7 @@ func Test_Router_Register_Missing_Handler(t *testing.T) { utils.AssertEqual(t, "missing handler in route: /doe\n", fmt.Sprintf("%v", err)) } }() - app.register("USE", "/doe") + app.register("USE", "/doe", nil) } func Test_Ensure_Router_Interface_Implementation(t *testing.T) { From 878c9549d87af6f8ed75b0f54a0081f2ff1ccc9e Mon Sep 17 00:00:00 2001 From: Rafi Muhammad <69524060+rafimuhammad01@users.noreply.github.com> Date: Fri, 11 Nov 2022 14:23:30 +0700 Subject: [PATCH 08/32] Feat: Register custom methods (#2107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implementing register custom methods * Return timout time to 1000 * Update app_test.go * Change update stack to add custom request method * Feat: Register custom methods #2107 * Feat: Register custom methods #2107 * update logic * optimization. * fix Co-authored-by: Rafi Muhammad Co-authored-by: RW Co-authored-by: Muhammed Efe Çetin --- app.go | 34 +++++++++++++++++++++--- app_test.go | 62 +++++++++++++++++++++++++++++++++----------- ctx.go | 4 +-- group.go | 2 +- helpers.go | 74 ++++++++++++++++++++++++++--------------------------- router.go | 12 ++++----- 6 files changed, 123 insertions(+), 65 deletions(-) diff --git a/app.go b/app.go index fcad05bf..a5763ed2 100644 --- a/app.go +++ b/app.go @@ -110,6 +110,8 @@ type App struct { latestRoute *Route // TLS handler tlsHandler *TLSHandler + // custom method check + customMethod bool // Mount fields mountFields *mountFields } @@ -380,6 +382,11 @@ type Config struct { // // Optional. Default: DefaultColors ColorScheme Colors `json:"color_scheme"` + + // RequestMethods provides customizibility for HTTP methods. You can add/remove methods as you wish. + // + // Optional. Defaukt: DefaultMethods + RequestMethods []string } // Static defines configuration options when defining static assets. @@ -445,6 +452,19 @@ const ( DefaultCompressedFileSuffix = ".fiber.gz" ) +// HTTP methods enabled by default +var DefaultMethods = []string{ + MethodGet, + MethodHead, + MethodPost, + MethodPut, + MethodDelete, + MethodConnect, + MethodOptions, + MethodTrace, + MethodPatch, +} + // DefaultErrorHandler that process return errors from handlers var DefaultErrorHandler = func(c *Ctx, err error) error { code := StatusInternalServerError @@ -469,9 +489,6 @@ var DefaultErrorHandler = func(c *Ctx, err error) error { func New(config ...Config) *App { // Create a new app app := &App{ - // Create router stack - stack: make([][]*Route, len(intMethod)), - treeStack: make([]map[string][]*Route, len(intMethod)), // Create Ctx pool pool: sync.Pool{ New: func() interface{} { @@ -538,12 +555,21 @@ func New(config ...Config) *App { if app.config.Network == "" { app.config.Network = NetworkTCP4 } + if len(app.config.RequestMethods) == 0 { + app.config.RequestMethods = DefaultMethods + } else { + app.customMethod = true + } app.config.trustedProxiesMap = make(map[string]struct{}, len(app.config.TrustedProxies)) for _, ipAddress := range app.config.TrustedProxies { app.handleTrustedProxy(ipAddress) } + // Create router stack + app.stack = make([][]*Route, len(app.config.RequestMethods)) + app.treeStack = make([]map[string][]*Route, len(app.config.RequestMethods)) + // Override colors app.config.ColorScheme = defaultColors(app.config.ColorScheme) @@ -724,7 +750,7 @@ func (app *App) Static(prefix, root string, config ...Static) Router { // All will register the handler on all HTTP methods func (app *App) All(path string, handlers ...Handler) Router { - for _, method := range intMethod { + for _, method := range app.config.RequestMethods { _ = app.Add(method, path, handlers...) } return app diff --git a/app_test.go b/app_test.go index 2e87114b..c3b65657 100644 --- a/app_test.go +++ b/app_test.go @@ -435,13 +435,32 @@ func Test_App_Use_StrictRouting(t *testing.T) { } func Test_App_Add_Method_Test(t *testing.T) { - app := New() defer func() { if err := recover(); err != nil { - utils.AssertEqual(t, "add: invalid http method JOHN\n", fmt.Sprintf("%v", err)) + utils.AssertEqual(t, "add: invalid http method JANE\n", fmt.Sprintf("%v", err)) } }() + + methods := append(DefaultMethods, "JOHN") + app := New(Config{ + RequestMethods: methods, + }) + app.Add("JOHN", "/doe", testEmptyHandler) + + resp, err := app.Test(httptest.NewRequest("JOHN", "/doe", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + resp, err = app.Test(httptest.NewRequest(MethodGet, "/doe", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusMethodNotAllowed, resp.StatusCode, "Status code") + + resp, err = app.Test(httptest.NewRequest("UNKNOWN", "/doe", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusBadRequest, resp.StatusCode, "Status code") + + app.Add("JANE", "/doe", testEmptyHandler) } // go test -run Test_App_GETOnly @@ -487,7 +506,7 @@ func Test_App_Chaining(t *testing.T) { return c.SendStatus(202) }) // check handler count for registered HEAD route - utils.AssertEqual(t, 5, len(app.stack[methodInt(MethodHead)][0].Handlers), "app.Test(req)") + utils.AssertEqual(t, 5, len(app.stack[app.methodInt(MethodHead)][0].Handlers), "app.Test(req)") req := httptest.NewRequest(MethodPost, "/john", nil) @@ -1250,16 +1269,17 @@ func Test_App_Stack(t *testing.T) { app.Post("/path3", testEmptyHandler) stack := app.Stack() - utils.AssertEqual(t, 9, len(stack)) - utils.AssertEqual(t, 3, len(stack[methodInt(MethodGet)])) - utils.AssertEqual(t, 3, len(stack[methodInt(MethodHead)])) - utils.AssertEqual(t, 2, len(stack[methodInt(MethodPost)])) - utils.AssertEqual(t, 1, len(stack[methodInt(MethodPut)])) - utils.AssertEqual(t, 1, len(stack[methodInt(MethodPatch)])) - utils.AssertEqual(t, 1, len(stack[methodInt(MethodDelete)])) - utils.AssertEqual(t, 1, len(stack[methodInt(MethodConnect)])) - utils.AssertEqual(t, 1, len(stack[methodInt(MethodOptions)])) - utils.AssertEqual(t, 1, len(stack[methodInt(MethodTrace)])) + methodList := app.config.RequestMethods + utils.AssertEqual(t, len(methodList), len(stack)) + utils.AssertEqual(t, 3, len(stack[app.methodInt(MethodGet)])) + utils.AssertEqual(t, 3, len(stack[app.methodInt(MethodHead)])) + utils.AssertEqual(t, 2, len(stack[app.methodInt(MethodPost)])) + utils.AssertEqual(t, 1, len(stack[app.methodInt(MethodPut)])) + utils.AssertEqual(t, 1, len(stack[app.methodInt(MethodPatch)])) + utils.AssertEqual(t, 1, len(stack[app.methodInt(MethodDelete)])) + utils.AssertEqual(t, 1, len(stack[app.methodInt(MethodConnect)])) + utils.AssertEqual(t, 1, len(stack[app.methodInt(MethodOptions)])) + utils.AssertEqual(t, 1, len(stack[app.methodInt(MethodTrace)])) } // go test -run Test_App_HandlersCount @@ -1513,6 +1533,19 @@ func Test_App_SetTLSHandler(t *testing.T) { utils.AssertEqual(t, "example.golang", c.ClientHelloInfo().ServerName) } +func Test_App_AddCustomRequestMethod(t *testing.T) { + methods := append(DefaultMethods, "TEST") + app := New(Config{ + RequestMethods: methods, + }) + appMethods := app.config.RequestMethods + + // method name is always uppercase - https://datatracker.ietf.org/doc/html/rfc7231#section-4.1 + utils.AssertEqual(t, len(app.stack), len(appMethods)) + utils.AssertEqual(t, len(app.stack), len(appMethods)) + utils.AssertEqual(t, "TEST", appMethods[len(appMethods)-1]) +} + func TestApp_GetRoutes(t *testing.T) { app := New() app.Use(func(c *Ctx) error { @@ -1524,7 +1557,7 @@ func TestApp_GetRoutes(t *testing.T) { app.Delete("/delete", handler).Name("delete") app.Post("/post", handler).Name("post") routes := app.GetRoutes(false) - utils.AssertEqual(t, 11, len(routes)) + utils.AssertEqual(t, 2+len(app.config.RequestMethods), len(routes)) methodMap := map[string]string{"/delete": "delete", "/post": "post"} for _, route := range routes { name, ok := methodMap[route.Path] @@ -1540,5 +1573,4 @@ func TestApp_GetRoutes(t *testing.T) { utils.AssertEqual(t, true, ok) utils.AssertEqual(t, name, route.Name) } - } diff --git a/ctx.go b/ctx.go index dcbe7125..ac2e2585 100644 --- a/ctx.go +++ b/ctx.go @@ -163,7 +163,7 @@ func (app *App) AcquireCtx(fctx *fasthttp.RequestCtx) *Ctx { c.pathOriginal = app.getString(fctx.URI().PathOriginal()) // Set method c.method = app.getString(fctx.Request.Header.Method()) - c.methodINT = methodInt(c.method) + c.methodINT = app.methodInt(c.method) // Attach *fasthttp.RequestCtx to ctx c.fasthttp = fctx // reset base uri @@ -906,7 +906,7 @@ func (c *Ctx) Location(path string) { func (c *Ctx) Method(override ...string) string { if len(override) > 0 { method := utils.ToUpper(override[0]) - mINT := methodInt(method) + mINT := c.app.methodInt(method) if mINT == -1 { return c.method } diff --git a/group.go b/group.go index 93826807..a312fc55 100644 --- a/group.go +++ b/group.go @@ -133,7 +133,7 @@ func (grp *Group) Static(prefix, root string, config ...Static) Router { // All will register the handler on all HTTP methods func (grp *Group) All(path string, handlers ...Handler) Router { - for _, method := range intMethod { + for _, method := range grp.app.config.RequestMethods { _ = grp.Add(method, path, handlers...) } return grp diff --git a/helpers.go b/helpers.go index c611b917..aa9511da 100644 --- a/helpers.go +++ b/helpers.go @@ -78,8 +78,9 @@ func (app *App) quoteString(raw string) string { } // Scan stack if other methods match the request -func methodExist(ctx *Ctx) (exist bool) { - for i := 0; i < len(intMethod); i++ { +func (app *App) methodExist(ctx *Ctx) (exist bool) { + methods := app.config.RequestMethods + for i := 0; i < len(methods); i++ { // Skip original method if ctx.methodINT == i { continue @@ -109,7 +110,7 @@ func methodExist(ctx *Ctx) (exist bool) { // We matched exist = true // Add method to Allow header - ctx.Append(HeaderAllow, intMethod[i]) + ctx.Append(HeaderAllow, methods[i]) // Break stack loop break } @@ -331,42 +332,41 @@ var getBytesImmutable = func(s string) (b []byte) { } // HTTP methods and their unique INTs -func methodInt(s string) int { - switch s { - case MethodGet: - return 0 - case MethodHead: - return 1 - case MethodPost: - return 2 - case MethodPut: - return 3 - case MethodDelete: - return 4 - case MethodConnect: - return 5 - case MethodOptions: - return 6 - case MethodTrace: - return 7 - case MethodPatch: - return 8 - default: - return -1 +func (app *App) methodInt(s string) int { + // For better performance + if !app.customMethod { + switch s { + case MethodGet: + return 0 + case MethodHead: + return 1 + case MethodPost: + return 2 + case MethodPut: + return 3 + case MethodDelete: + return 4 + case MethodConnect: + return 5 + case MethodOptions: + return 6 + case MethodTrace: + return 7 + case MethodPatch: + return 8 + default: + return -1 + } } -} -// HTTP methods slice -var intMethod = []string{ - MethodGet, - MethodHead, - MethodPost, - MethodPut, - MethodDelete, - MethodConnect, - MethodOptions, - MethodTrace, - MethodPatch, + // For method customization + for i, v := range app.config.RequestMethods { + if s == v { + return i + } + } + + return -1 } // HTTP methods were copied from net/http. diff --git a/router.go b/router.go index d83c1868..84ff6064 100644 --- a/router.go +++ b/router.go @@ -139,7 +139,7 @@ func (app *App) next(c *Ctx) (match bool, err error) { // If no match, scan stack again if other methods match the request // Moved from app.handler because middleware may break the route chain - if !c.matched && methodExist(c) { + if !c.matched && app.methodExist(c) { err = ErrMethodNotAllowed } return @@ -216,7 +216,7 @@ func (app *App) register(method, pathRaw string, group *Group, handlers ...Handl // Uppercase HTTP methods method = utils.ToUpper(method) // Check if the HTTP method is valid unless it's USE - if method != methodUse && methodInt(method) == -1 { + if method != methodUse && app.methodInt(method) == -1 { panic(fmt.Sprintf("add: invalid http method %s\n", method)) } // A route requires atleast one ctx handler @@ -277,7 +277,7 @@ func (app *App) register(method, pathRaw string, group *Group, handlers ...Handl // Middleware route matches all HTTP methods if isUse { // Add route to all HTTP methods stack - for _, m := range intMethod { + for _, m := range app.config.RequestMethods { // Create a route copy to avoid duplicates during compression r := route app.addRoute(m, &r) @@ -435,7 +435,7 @@ func (app *App) addRoute(method string, route *Route, isMounted ...bool) { } // Get unique HTTP method identifier - m := methodInt(method) + m := app.methodInt(method) // prevent identically route registration l := len(app.stack[m]) @@ -469,7 +469,7 @@ func (app *App) buildTree() *App { } // loop all the methods and stacks and create the prefix tree - for m := range intMethod { + for m := range app.config.RequestMethods { tsMap := make(map[string][]*Route) for _, route := range app.stack[m] { treePath := "" @@ -483,7 +483,7 @@ func (app *App) buildTree() *App { } // loop the methods and tree stacks and add global stack and sort everything - for m := range intMethod { + for m := range app.config.RequestMethods { tsMap := app.treeStack[m] for treePart := range tsMap { if treePart != "" { From e388e0edb3c6d4bdbfa41e8d533e1f4bd734404d Mon Sep 17 00:00:00 2001 From: leonklingele Date: Fri, 11 Nov 2022 08:40:37 +0100 Subject: [PATCH 09/32] Update HTTP status codes (#2203) * helpers: add HTTP status code 306 * helpers: show numeric HTTP status code instead of status code RFC next to errors * utils: add HTTP status code 425 * helpers,utils: update list of HTTP status codes --- helpers.go | 205 ++++++++++++++++++++++++++------------------------ utils/http.go | 131 ++++++++++++++++---------------- 2 files changed, 175 insertions(+), 161 deletions(-) diff --git a/helpers.go b/helpers.go index aa9511da..11364e73 100644 --- a/helpers.go +++ b/helpers.go @@ -407,65 +407,73 @@ const ( MIMEApplicationJavaScriptCharsetUTF8 = "application/javascript; charset=utf-8" ) -// HTTP status codes were copied from net/http. +// HTTP status codes were copied from https://github.com/nginx/nginx/blob/67d2a9541826ecd5db97d604f23460210fd3e517/conf/mime.types with the following updates: +// - Rename StatusNonAuthoritativeInfo to StatusNonAuthoritativeInformation +// - Add StatusSwitchProxy (306) +// NOTE: Keep this list in sync with statusMessage const ( - StatusContinue = 100 // RFC 7231, 6.2.1 - StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2 - StatusProcessing = 102 // RFC 2518, 10.1 - StatusEarlyHints = 103 // RFC 8297 - StatusOK = 200 // RFC 7231, 6.3.1 - StatusCreated = 201 // RFC 7231, 6.3.2 - StatusAccepted = 202 // RFC 7231, 6.3.3 - StatusNonAuthoritativeInformation = 203 // RFC 7231, 6.3.4 - StatusNoContent = 204 // RFC 7231, 6.3.5 - StatusResetContent = 205 // RFC 7231, 6.3.6 - StatusPartialContent = 206 // RFC 7233, 4.1 - StatusMultiStatus = 207 // RFC 4918, 11.1 - StatusAlreadyReported = 208 // RFC 5842, 7.1 - StatusIMUsed = 226 // RFC 3229, 10.4.1 - StatusMultipleChoices = 300 // RFC 7231, 6.4.1 - StatusMovedPermanently = 301 // RFC 7231, 6.4.2 - StatusFound = 302 // RFC 7231, 6.4.3 - StatusSeeOther = 303 // RFC 7231, 6.4.4 - StatusNotModified = 304 // RFC 7232, 4.1 - StatusUseProxy = 305 // RFC 7231, 6.4.5 - StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7 - StatusPermanentRedirect = 308 // RFC 7538, 3 - StatusBadRequest = 400 // RFC 7231, 6.5.1 - StatusUnauthorized = 401 // RFC 7235, 3.1 - StatusPaymentRequired = 402 // RFC 7231, 6.5.2 - StatusForbidden = 403 // RFC 7231, 6.5.3 - StatusNotFound = 404 // RFC 7231, 6.5.4 - StatusMethodNotAllowed = 405 // RFC 7231, 6.5.5 - StatusNotAcceptable = 406 // RFC 7231, 6.5.6 - StatusProxyAuthRequired = 407 // RFC 7235, 3.2 - StatusRequestTimeout = 408 // RFC 7231, 6.5.7 - StatusConflict = 409 // RFC 7231, 6.5.8 - StatusGone = 410 // RFC 7231, 6.5.9 - StatusLengthRequired = 411 // RFC 7231, 6.5.10 - StatusPreconditionFailed = 412 // RFC 7232, 4.2 - StatusRequestEntityTooLarge = 413 // RFC 7231, 6.5.11 - StatusRequestURITooLong = 414 // RFC 7231, 6.5.12 - StatusUnsupportedMediaType = 415 // RFC 7231, 6.5.13 - StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4 - StatusExpectationFailed = 417 // RFC 7231, 6.5.14 - StatusTeapot = 418 // RFC 7168, 2.3.3 - StatusMisdirectedRequest = 421 // RFC 7540, 9.1.2 - StatusUnprocessableEntity = 422 // RFC 4918, 11.2 - StatusLocked = 423 // RFC 4918, 11.3 - StatusFailedDependency = 424 // RFC 4918, 11.4 - StatusTooEarly = 425 // RFC 8470, 5.2. - StatusUpgradeRequired = 426 // RFC 7231, 6.5.15 - StatusPreconditionRequired = 428 // RFC 6585, 3 - StatusTooManyRequests = 429 // RFC 6585, 4 - StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5 - StatusUnavailableForLegalReasons = 451 // RFC 7725, 3 - StatusInternalServerError = 500 // RFC 7231, 6.6.1 - StatusNotImplemented = 501 // RFC 7231, 6.6.2 - StatusBadGateway = 502 // RFC 7231, 6.6.3 - StatusServiceUnavailable = 503 // RFC 7231, 6.6.4 - StatusGatewayTimeout = 504 // RFC 7231, 6.6.5 - StatusHTTPVersionNotSupported = 505 // RFC 7231, 6.6.6 + StatusContinue = 100 // RFC 9110, 15.2.1 + StatusSwitchingProtocols = 101 // RFC 9110, 15.2.2 + StatusProcessing = 102 // RFC 2518, 10.1 + StatusEarlyHints = 103 // RFC 8297 + + StatusOK = 200 // RFC 9110, 15.3.1 + StatusCreated = 201 // RFC 9110, 15.3.2 + StatusAccepted = 202 // RFC 9110, 15.3.3 + StatusNonAuthoritativeInformation = 203 // RFC 9110, 15.3.4 + StatusNoContent = 204 // RFC 9110, 15.3.5 + StatusResetContent = 205 // RFC 9110, 15.3.6 + StatusPartialContent = 206 // RFC 9110, 15.3.7 + StatusMultiStatus = 207 // RFC 4918, 11.1 + StatusAlreadyReported = 208 // RFC 5842, 7.1 + StatusIMUsed = 226 // RFC 3229, 10.4.1 + + StatusMultipleChoices = 300 // RFC 9110, 15.4.1 + StatusMovedPermanently = 301 // RFC 9110, 15.4.2 + StatusFound = 302 // RFC 9110, 15.4.3 + StatusSeeOther = 303 // RFC 9110, 15.4.4 + StatusNotModified = 304 // RFC 9110, 15.4.5 + StatusUseProxy = 305 // RFC 9110, 15.4.6 + StatusSwitchProxy = 306 // RFC 9110, 15.4.7 (Unused) + StatusTemporaryRedirect = 307 // RFC 9110, 15.4.8 + StatusPermanentRedirect = 308 // RFC 9110, 15.4.9 + + StatusBadRequest = 400 // RFC 9110, 15.5.1 + StatusUnauthorized = 401 // RFC 9110, 15.5.2 + StatusPaymentRequired = 402 // RFC 9110, 15.5.3 + StatusForbidden = 403 // RFC 9110, 15.5.4 + StatusNotFound = 404 // RFC 9110, 15.5.5 + StatusMethodNotAllowed = 405 // RFC 9110, 15.5.6 + StatusNotAcceptable = 406 // RFC 9110, 15.5.7 + StatusProxyAuthRequired = 407 // RFC 9110, 15.5.8 + StatusRequestTimeout = 408 // RFC 9110, 15.5.9 + StatusConflict = 409 // RFC 9110, 15.5.10 + StatusGone = 410 // RFC 9110, 15.5.11 + StatusLengthRequired = 411 // RFC 9110, 15.5.12 + StatusPreconditionFailed = 412 // RFC 9110, 15.5.13 + StatusRequestEntityTooLarge = 413 // RFC 9110, 15.5.14 + StatusRequestURITooLong = 414 // RFC 9110, 15.5.15 + StatusUnsupportedMediaType = 415 // RFC 9110, 15.5.16 + StatusRequestedRangeNotSatisfiable = 416 // RFC 9110, 15.5.17 + StatusExpectationFailed = 417 // RFC 9110, 15.5.18 + StatusTeapot = 418 // RFC 9110, 15.5.19 (Unused) + StatusMisdirectedRequest = 421 // RFC 9110, 15.5.20 + StatusUnprocessableEntity = 422 // RFC 9110, 15.5.21 + StatusLocked = 423 // RFC 4918, 11.3 + StatusFailedDependency = 424 // RFC 4918, 11.4 + StatusTooEarly = 425 // RFC 8470, 5.2. + StatusUpgradeRequired = 426 // RFC 9110, 15.5.22 + StatusPreconditionRequired = 428 // RFC 6585, 3 + StatusTooManyRequests = 429 // RFC 6585, 4 + StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5 + StatusUnavailableForLegalReasons = 451 // RFC 7725, 3 + + StatusInternalServerError = 500 // RFC 9110, 15.6.1 + StatusNotImplemented = 501 // RFC 9110, 15.6.2 + StatusBadGateway = 502 // RFC 9110, 15.6.3 + StatusServiceUnavailable = 503 // RFC 9110, 15.6.4 + StatusGatewayTimeout = 504 // RFC 9110, 15.6.5 + StatusHTTPVersionNotSupported = 505 // RFC 9110, 15.6.6 StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1 StatusInsufficientStorage = 507 // RFC 4918, 11.5 StatusLoopDetected = 508 // RFC 5842, 7.2 @@ -475,46 +483,47 @@ const ( // Errors var ( - ErrBadRequest = NewError(StatusBadRequest) // RFC 7231, 6.5.1 - ErrUnauthorized = NewError(StatusUnauthorized) // RFC 7235, 3.1 - ErrPaymentRequired = NewError(StatusPaymentRequired) // RFC 7231, 6.5.2 - ErrForbidden = NewError(StatusForbidden) // RFC 7231, 6.5.3 - ErrNotFound = NewError(StatusNotFound) // RFC 7231, 6.5.4 - ErrMethodNotAllowed = NewError(StatusMethodNotAllowed) // RFC 7231, 6.5.5 - ErrNotAcceptable = NewError(StatusNotAcceptable) // RFC 7231, 6.5.6 - ErrProxyAuthRequired = NewError(StatusProxyAuthRequired) // RFC 7235, 3.2 - ErrRequestTimeout = NewError(StatusRequestTimeout) // RFC 7231, 6.5.7 - ErrConflict = NewError(StatusConflict) // RFC 7231, 6.5.8 - ErrGone = NewError(StatusGone) // RFC 7231, 6.5.9 - ErrLengthRequired = NewError(StatusLengthRequired) // RFC 7231, 6.5.10 - ErrPreconditionFailed = NewError(StatusPreconditionFailed) // RFC 7232, 4.2 - ErrRequestEntityTooLarge = NewError(StatusRequestEntityTooLarge) // RFC 7231, 6.5.11 - ErrRequestURITooLong = NewError(StatusRequestURITooLong) // RFC 7231, 6.5.12 - ErrUnsupportedMediaType = NewError(StatusUnsupportedMediaType) // RFC 7231, 6.5.13 - ErrRequestedRangeNotSatisfiable = NewError(StatusRequestedRangeNotSatisfiable) // RFC 7233, 4.4 - ErrExpectationFailed = NewError(StatusExpectationFailed) // RFC 7231, 6.5.14 - ErrTeapot = NewError(StatusTeapot) // RFC 7168, 2.3.3 - ErrMisdirectedRequest = NewError(StatusMisdirectedRequest) // RFC 7540, 9.1.2 - ErrUnprocessableEntity = NewError(StatusUnprocessableEntity) // RFC 4918, 11.2 - ErrLocked = NewError(StatusLocked) // RFC 4918, 11.3 - ErrFailedDependency = NewError(StatusFailedDependency) // RFC 4918, 11.4 - ErrTooEarly = NewError(StatusTooEarly) // RFC 8470, 5.2. - ErrUpgradeRequired = NewError(StatusUpgradeRequired) // RFC 7231, 6.5.15 - ErrPreconditionRequired = NewError(StatusPreconditionRequired) // RFC 6585, 3 - ErrTooManyRequests = NewError(StatusTooManyRequests) // RFC 6585, 4 - ErrRequestHeaderFieldsTooLarge = NewError(StatusRequestHeaderFieldsTooLarge) // RFC 6585, 5 - ErrUnavailableForLegalReasons = NewError(StatusUnavailableForLegalReasons) // RFC 7725, 3 - ErrInternalServerError = NewError(StatusInternalServerError) // RFC 7231, 6.6.1 - ErrNotImplemented = NewError(StatusNotImplemented) // RFC 7231, 6.6.2 - ErrBadGateway = NewError(StatusBadGateway) // RFC 7231, 6.6.3 - ErrServiceUnavailable = NewError(StatusServiceUnavailable) // RFC 7231, 6.6.4 - ErrGatewayTimeout = NewError(StatusGatewayTimeout) // RFC 7231, 6.6.5 - ErrHTTPVersionNotSupported = NewError(StatusHTTPVersionNotSupported) // RFC 7231, 6.6.6 - ErrVariantAlsoNegotiates = NewError(StatusVariantAlsoNegotiates) // RFC 2295, 8.1 - ErrInsufficientStorage = NewError(StatusInsufficientStorage) // RFC 4918, 11.5 - ErrLoopDetected = NewError(StatusLoopDetected) // RFC 5842, 7.2 - ErrNotExtended = NewError(StatusNotExtended) // RFC 2774, 7 - ErrNetworkAuthenticationRequired = NewError(StatusNetworkAuthenticationRequired) // RFC 6585, 6 + ErrBadRequest = NewError(StatusBadRequest) // 400 + ErrUnauthorized = NewError(StatusUnauthorized) // 401 + ErrPaymentRequired = NewError(StatusPaymentRequired) // 402 + ErrForbidden = NewError(StatusForbidden) // 403 + ErrNotFound = NewError(StatusNotFound) // 404 + ErrMethodNotAllowed = NewError(StatusMethodNotAllowed) // 405 + ErrNotAcceptable = NewError(StatusNotAcceptable) // 406 + ErrProxyAuthRequired = NewError(StatusProxyAuthRequired) // 407 + ErrRequestTimeout = NewError(StatusRequestTimeout) // 408 + ErrConflict = NewError(StatusConflict) // 409 + ErrGone = NewError(StatusGone) // 410 + ErrLengthRequired = NewError(StatusLengthRequired) // 411 + ErrPreconditionFailed = NewError(StatusPreconditionFailed) // 412 + ErrRequestEntityTooLarge = NewError(StatusRequestEntityTooLarge) // 413 + ErrRequestURITooLong = NewError(StatusRequestURITooLong) // 414 + ErrUnsupportedMediaType = NewError(StatusUnsupportedMediaType) // 415 + ErrRequestedRangeNotSatisfiable = NewError(StatusRequestedRangeNotSatisfiable) // 416 + ErrExpectationFailed = NewError(StatusExpectationFailed) // 417 + ErrTeapot = NewError(StatusTeapot) // 418 + ErrMisdirectedRequest = NewError(StatusMisdirectedRequest) // 421 + ErrUnprocessableEntity = NewError(StatusUnprocessableEntity) // 422 + ErrLocked = NewError(StatusLocked) // 423 + ErrFailedDependency = NewError(StatusFailedDependency) // 424 + ErrTooEarly = NewError(StatusTooEarly) // 425 + ErrUpgradeRequired = NewError(StatusUpgradeRequired) // 426 + ErrPreconditionRequired = NewError(StatusPreconditionRequired) // 428 + ErrTooManyRequests = NewError(StatusTooManyRequests) // 429 + ErrRequestHeaderFieldsTooLarge = NewError(StatusRequestHeaderFieldsTooLarge) // 431 + ErrUnavailableForLegalReasons = NewError(StatusUnavailableForLegalReasons) // 451 + + ErrInternalServerError = NewError(StatusInternalServerError) // 500 + ErrNotImplemented = NewError(StatusNotImplemented) // 501 + ErrBadGateway = NewError(StatusBadGateway) // 502 + ErrServiceUnavailable = NewError(StatusServiceUnavailable) // 503 + ErrGatewayTimeout = NewError(StatusGatewayTimeout) // 504 + ErrHTTPVersionNotSupported = NewError(StatusHTTPVersionNotSupported) // 505 + ErrVariantAlsoNegotiates = NewError(StatusVariantAlsoNegotiates) // 506 + ErrInsufficientStorage = NewError(StatusInsufficientStorage) // 507 + ErrLoopDetected = NewError(StatusLoopDetected) // 508 + ErrNotExtended = NewError(StatusNotExtended) // 510 + ErrNetworkAuthenticationRequired = NewError(StatusNetworkAuthenticationRequired) // 511 ) // HTTP Headers were copied from net/http. diff --git a/utils/http.go b/utils/http.go index fc524023..bdcd834c 100644 --- a/utils/http.go +++ b/utils/http.go @@ -66,70 +66,75 @@ func StatusMessage(status int) string { return statusMessage[status] } -// HTTP status codes were copied from net/http. +// NOTE: Keep this in sync with the status code list var statusMessage = []string{ - 100: "Continue", - 101: "Switching Protocols", - 102: "Processing", - 103: "Early Hints", - 200: "OK", - 201: "Created", - 202: "Accepted", - 203: "Non-Authoritative Information", - 204: "No Content", - 205: "Reset Content", - 206: "Partial Content", - 207: "Multi-Status", - 208: "Already Reported", - 226: "IM Used", - 300: "Multiple Choices", - 301: "Moved Permanently", - 302: "Found", - 303: "See Other", - 304: "Not Modified", - 305: "Use Proxy", - 306: "Switch Proxy", - 307: "Temporary Redirect", - 308: "Permanent Redirect", - 400: "Bad Request", - 401: "Unauthorized", - 402: "Payment Required", - 403: "Forbidden", - 404: "Not Found", - 405: "Method Not Allowed", - 406: "Not Acceptable", - 407: "Proxy Authentication Required", - 408: "Request Timeout", - 409: "Conflict", - 410: "Gone", - 411: "Length Required", - 412: "Precondition Failed", - 413: "Request Entity Too Large", - 414: "Request URI Too Long", - 415: "Unsupported Media Type", - 416: "Requested Range Not Satisfiable", - 417: "Expectation Failed", - 418: "I'm a teapot", - 421: "Misdirected Request", - 422: "Unprocessable Entity", - 423: "Locked", - 424: "Failed Dependency", - 426: "Upgrade Required", - 428: "Precondition Required", - 429: "Too Many Requests", - 431: "Request Header Fields Too Large", - 451: "Unavailable For Legal Reasons", - 500: "Internal Server Error", - 501: "Not Implemented", - 502: "Bad Gateway", - 503: "Service Unavailable", - 504: "Gateway Timeout", - 505: "HTTP Version Not Supported", - 506: "Variant Also Negotiates", - 507: "Insufficient Storage", - 508: "Loop Detected", - 510: "Not Extended", - 511: "Network Authentication Required", + 100: "Continue", // StatusContinue + 101: "Switching Protocols", // StatusSwitchingProtocols + 102: "Processing", // StatusProcessing + 103: "Early Hints", // StatusEarlyHints + + 200: "OK", // StatusOK + 201: "Created", // StatusCreated + 202: "Accepted", // StatusAccepted + 203: "Non-Authoritative Information", // StatusNonAuthoritativeInformation + 204: "No Content", // StatusNoContent + 205: "Reset Content", // StatusResetContent + 206: "Partial Content", // StatusPartialContent + 207: "Multi-Status", // StatusMultiStatus + 208: "Already Reported", // StatusAlreadyReported + 226: "IM Used", // StatusIMUsed + + 300: "Multiple Choices", // StatusMultipleChoices + 301: "Moved Permanently", // StatusMovedPermanently + 302: "Found", // StatusFound + 303: "See Other", // StatusSeeOther + 304: "Not Modified", // StatusNotModified + 305: "Use Proxy", // StatusUseProxy + 306: "Switch Proxy", // StatusSwitchProxy + 307: "Temporary Redirect", // StatusTemporaryRedirect + 308: "Permanent Redirect", // StatusPermanentRedirect + + 400: "Bad Request", // StatusBadRequest + 401: "Unauthorized", // StatusUnauthorized + 402: "Payment Required", // StatusPaymentRequired + 403: "Forbidden", // StatusForbidden + 404: "Not Found", // StatusNotFound + 405: "Method Not Allowed", // StatusMethodNotAllowed + 406: "Not Acceptable", // StatusNotAcceptable + 407: "Proxy Authentication Required", // StatusProxyAuthRequired + 408: "Request Timeout", // StatusRequestTimeout + 409: "Conflict", // StatusConflict + 410: "Gone", // StatusGone + 411: "Length Required", // StatusLengthRequired + 412: "Precondition Failed", // StatusPreconditionFailed + 413: "Request Entity Too Large", // StatusRequestEntityTooLarge + 414: "Request URI Too Long", // StatusRequestURITooLong + 415: "Unsupported Media Type", // StatusUnsupportedMediaType + 416: "Requested Range Not Satisfiable", // StatusRequestedRangeNotSatisfiable + 417: "Expectation Failed", // StatusExpectationFailed + 418: "I'm a teapot", // StatusTeapot + 421: "Misdirected Request", // StatusMisdirectedRequest + 422: "Unprocessable Entity", // StatusUnprocessableEntity + 423: "Locked", // StatusLocked + 424: "Failed Dependency", // StatusFailedDependency + 425: "Too Early", // StatusTooEarly + 426: "Upgrade Required", // StatusUpgradeRequired + 428: "Precondition Required", // StatusPreconditionRequired + 429: "Too Many Requests", // StatusTooManyRequests + 431: "Request Header Fields Too Large", // StatusRequestHeaderFieldsTooLarge + 451: "Unavailable For Legal Reasons", // StatusUnavailableForLegalReasons + + 500: "Internal Server Error", // StatusInternalServerError + 501: "Not Implemented", // StatusNotImplemented + 502: "Bad Gateway", // StatusBadGateway + 503: "Service Unavailable", // StatusServiceUnavailable + 504: "Gateway Timeout", // StatusGatewayTimeout + 505: "HTTP Version Not Supported", // StatusHTTPVersionNotSupported + 506: "Variant Also Negotiates", // StatusVariantAlsoNegotiates + 507: "Insufficient Storage", // StatusInsufficientStorage + 508: "Loop Detected", // StatusLoopDetected + 510: "Not Extended", // StatusNotExtended + 511: "Network Authentication Required", // StatusNetworkAuthenticationRequired } // MIME types were copied from https://github.com/nginx/nginx/blob/67d2a9541826ecd5db97d604f23460210fd3e517/conf/mime.types with the following updates: From 4d562923aa3a80a7ee0b4b9a2c3a404edc54a705 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Sat, 12 Nov 2022 07:10:07 +0100 Subject: [PATCH 10/32] README: update list of third-party library licenses (#2211) --- .github/README.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/README.md b/.github/README.md index 45c10be6..43f675f1 100644 --- a/.github/README.md +++ b/.github/README.md @@ -686,13 +686,17 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( **Third-party library licenses** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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) From 938e0fcea4f44ba4cd965a65c905ad3dab200538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Sat, 12 Nov 2022 09:26:47 +0300 Subject: [PATCH 11/32] :recycle: sync other langs with README.md --- .github/README_ckb.md | 18 +++++++++++------- .github/README_de.md | 18 +++++++++++------- .github/README_es.md | 18 +++++++++++------- .github/README_fa.md | 18 +++++++++++------- .github/README_fr.md | 18 +++++++++++------- .github/README_he.md | 18 +++++++++++------- .github/README_id.md | 18 +++++++++++------- .github/README_it.md | 18 +++++++++++------- .github/README_ja.md | 18 +++++++++++------- .github/README_ko.md | 18 +++++++++++------- .github/README_nl.md | 18 +++++++++++------- .github/README_pt.md | 18 +++++++++++------- .github/README_ru.md | 18 +++++++++++------- .github/README_sa.md | 18 +++++++++++------- .github/README_tr.md | 18 +++++++++++------- .github/README_zh-CN.md | 18 +++++++++++------- .github/README_zh-TW.md | 18 +++++++++++------- 17 files changed, 187 insertions(+), 119 deletions(-) diff --git a/.github/README_ckb.md b/.github/README_ckb.md index 84d8ae91..c62c5d12 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -685,13 +685,17 @@ For more articles, middlewares, examples or tools check our [awesome list](https **مۆڵەتەکانی دیکە** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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_de.md b/.github/README_de.md index 6fa4a2b1..aca4a2fa 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -655,13 +655,17 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( **Third-party MIT licenses** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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 cf524f62..0d3d36a2 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -655,14 +655,18 @@ Copyright (c) 2019-presente [Fenny](https://github.com/fenny) y [contribuyentes] **Third-party library licenses** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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_fa.md b/.github/README_fa.md index 30afaa5f..7fe0475f 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -807,13 +807,17 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( **مجوزهای کتابخانه شخص ثالث** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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_fr.md b/.github/README_fr.md index f0ad7525..8ced8bc2 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -657,13 +657,17 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( **Third-party library licenses** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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_he.md b/.github/README_he.md index aacf2777..3a8a7c67 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -832,14 +832,18 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( **רישיונות של ספריות צד שלישי** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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_id.md b/.github/README_id.md index b16b32d6..4d4fc3f7 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -658,14 +658,18 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( **Lisensi perpustakaan pihak-ketiga** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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_it.md b/.github/README_it.md index 5651c3eb..b3df35e5 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -681,13 +681,17 @@ Copyright (c) 2019-ora [Fenny](https://github.com/fenny) e [Contributors](https: **Licenze di Terze parti ** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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_ja.md b/.github/README_ja.md index f15e5247..29e3f3a1 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -660,13 +660,17 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( **Third-party library licenses** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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_ko.md b/.github/README_ko.md index 6c14afc5..2f71cb55 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -661,13 +661,17 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( **Third-party library licenses** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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_nl.md b/.github/README_nl.md index 2bf40e93..854e360c 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -661,13 +661,17 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( **Third-party library licenses** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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 f4bfaa04..52df5a58 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -657,13 +657,17 @@ O logo oficial foi criado por [Vic Shóstak](https://github.com/koddr) e distrib **Licença das bibliotecas de terceiros** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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_ru.md b/.github/README_ru.md index 05063c44..dad365ad 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -664,13 +664,17 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( **Third-party library licenses** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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_sa.md b/.github/README_sa.md index 50a0b632..efc80c4b 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -726,13 +726,17 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( **Third-party library licenses** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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_tr.md b/.github/README_tr.md index f775218c..c425d40c 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -654,13 +654,17 @@ Telif (c) 2019-günümüz [Fenny](https://github.com/fenny) ve [katkıda bulunan **Üçüncü Parti Kütüphane Lisansları** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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 81b74840..2a64bd04 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -664,13 +664,17 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( **Third-party library licenses** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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-TW.md b/.github/README_zh-TW.md index 61d012e2..c1999289 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -657,13 +657,17 @@ Fiber 是一個以贊助維生的開源專案,像是: 網域、gitbook、netli **Third-party library licenses** -- [schema](https://github.com/gorilla/schema/blob/master/LICENSE) -- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) -- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [colorable](https://github.com/mattn/go-colorable/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) +- [isatty](https://github.com/mattn/go-isatty/blob/master/LICENSE) +- [runewidth](https://github.com/mattn/go-runewidth/blob/master/LICENSE) +- [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) +- [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) +- [fasttemplate](https://github.com/valyala/fasttemplate/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) -- [go-ole](https://github.com/go-ole/go-ole) -- [wmi](https://github.com/StackExchange/wmi) -- [dictpool](https://github.com/savsgio/dictpool) +- [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) From 5cac575ad038f47c1d0cea540232d55017316982 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Sat, 12 Nov 2022 08:27:57 +0100 Subject: [PATCH 12/32] utils: reduce diff to external utils package (#2206) https://github.com/gofiber/utils/blob/f33fd2c5f7f02d398bc996ddcc53d6b6a567e4d9/bytes.go --- utils/bytes.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/bytes.go b/utils/bytes.go index 49f55b2d..bd2c87be 100644 --- a/utils/bytes.go +++ b/utils/bytes.go @@ -4,7 +4,7 @@ package utils -// ToLowerBytes converts ascii slice to lower-case +// ToLowerBytes converts ascii slice to lower-case in-place. func ToLowerBytes(b []byte) []byte { for i := 0; i < len(b); i++ { b[i] = toLowerTable[b[i]] @@ -12,7 +12,7 @@ func ToLowerBytes(b []byte) []byte { return b } -// ToUpperBytes converts ascii slice to upper-case +// ToUpperBytes converts ascii slice to upper-case in-place. func ToUpperBytes(b []byte) []byte { for i := 0; i < len(b); i++ { b[i] = toUpperTable[b[i]] From 20109922aa7a0df5d99d99d12c8933b61d779fc3 Mon Sep 17 00:00:00 2001 From: hs son Date: Sun, 13 Nov 2022 21:39:15 +0900 Subject: [PATCH 13/32] Add korean translate in Installation section (#2213) --- .github/README_ko.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README_ko.md b/.github/README_ko.md index 2f71cb55..c2479bae 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -113,7 +113,7 @@ func main() { Go가 설치되어 있는 것을 확인해 주세요 ([download](https://go.dev/dl/)). 버전 1.14 또는 그 이상이어야 합니다. -Initialize your project by creating a folder and then running `go mod init github.com/your/repo` ([learn more](https://go.dev/blog/using-go-modules)) inside the folder. Then install Fiber with the [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) command: +폴더를 생성하여 당신의 프로젝트를 초기화하고, 폴더 안에서 `go mod init github.com/your/repo` ([learn more](https://go.dev/blog/using-go-modules)) 를 실행하세요. 그리고 [`go get`](https://pkg.go.dev/cmd/go/#hdr-Add_dependencies_to_current_module_and_install_them) 명령어로 Fiber를 설치하세요: ```bash go get -u github.com/gofiber/fiber/v2 From 8aeb147c7599b07c5aa30741c07dee7704fb6e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Sun, 13 Nov 2022 18:56:34 +0300 Subject: [PATCH 14/32] :sparkles: feature: add multiple-prefix support to app.Use() and group.Use() (#2205) --- app.go | 13 ++++++++++++- app_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ group.go | 16 ++++++++++++++-- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/app.go b/app.go index a5763ed2..3b45b307 100644 --- a/app.go +++ b/app.go @@ -669,19 +669,30 @@ func (app *App) GetRoutes(filterUseOption ...bool) []Route { // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... func (app *App) Use(args ...interface{}) Router { var prefix string + var prefixes []string var handlers []Handler for i := 0; i < len(args); i++ { switch arg := args[i].(type) { case string: prefix = arg + case []string: + prefixes = arg case Handler: handlers = append(handlers, arg) default: panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg))) } } - app.register(methodUse, prefix, nil, handlers...) + + if len(prefixes) == 0 { + prefixes = append(prefixes, prefix) + } + + for _, prefix := range prefixes { + app.register(methodUse, prefix, nil, handlers...) + } + return app } diff --git a/app_test.go b/app_test.go index c3b65657..7d532f31 100644 --- a/app_test.go +++ b/app_test.go @@ -401,6 +401,52 @@ func Test_App_Not_Use_StrictRouting(t *testing.T) { utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") } +func Test_App_Use_MultiplePrefix(t *testing.T) { + app := New() + + app.Use([]string{"/john", "/doe"}, func(c *Ctx) error { + return c.SendString(c.Path()) + }) + + g := app.Group("/test") + g.Use([]string{"/john", "/doe"}, func(c *Ctx) error { + return c.SendString(c.Path()) + }) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/john", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + body, err := io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "/john", string(body)) + + resp, err = app.Test(httptest.NewRequest(MethodGet, "/doe", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + body, err = io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "/doe", string(body)) + + resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/john", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + body, err = io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "/test/john", string(body)) + + resp, err = app.Test(httptest.NewRequest(MethodGet, "/test/doe", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") + + body, err = io.ReadAll(resp.Body) + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, "/test/doe", string(body)) + +} + func Test_App_Use_StrictRouting(t *testing.T) { app := New(Config{StrictRouting: true}) diff --git a/group.go b/group.go index a312fc55..ce1e1b2d 100644 --- a/group.go +++ b/group.go @@ -51,19 +51,31 @@ func (grp *Group) Name(name string) Router { // // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... func (grp *Group) Use(args ...interface{}) Router { - prefix := "" + var prefix string + var prefixes []string var handlers []Handler + for i := 0; i < len(args); i++ { switch arg := args[i].(type) { case string: prefix = arg + case []string: + prefixes = arg case Handler: handlers = append(handlers, arg) default: panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg))) } } - grp.app.register(methodUse, getGroupPath(grp.Prefix, prefix), grp, handlers...) + + if len(prefixes) == 0 { + prefixes = append(prefixes, prefix) + } + + for _, prefix := range prefixes { + grp.app.register(methodUse, getGroupPath(grp.Prefix, prefix), grp, handlers...) + } + return grp } From e5ae76413901c46e0e786eff67625bcdf8a8bfbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Sun, 13 Nov 2022 18:58:42 +0300 Subject: [PATCH 15/32] :broom: update: replace UnsafeBytes util with suggested way (#2204) --- utils/convert.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/utils/convert.go b/utils/convert.go index 32b04b7c..d1d3f520 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -13,6 +13,8 @@ import ( "unsafe" ) +const MaxStringLen = 0x7fff0000 // Maximum string length for UnsafeBytes. (decimal: 2147418112) + // #nosec G103 // UnsafeString returns a string pointer without allocation func UnsafeString(b []byte) string { @@ -20,14 +22,16 @@ func UnsafeString(b []byte) string { } // #nosec G103 -// UnsafeBytes returns a byte pointer without allocation -func UnsafeBytes(s string) (bs []byte) { - sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) - bh := (*reflect.SliceHeader)(unsafe.Pointer(&bs)) - bh.Data = sh.Data - bh.Len = sh.Len - bh.Cap = sh.Len - return +// UnsafeBytes returns a byte pointer without allocation. +// String length shouldn't be more than 2147418112. +func UnsafeBytes(s string) []byte { + if s == "" { + return nil + } + + return (*[MaxStringLen]byte)(unsafe.Pointer( + (*reflect.StringHeader)(unsafe.Pointer(&s)).Data), + )[:len(s):len(s)] } // CopyString copies a string to make it immutable From a0645af1ed365a7fbfd605f3ad880ee9336ad2e3 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Mon, 14 Nov 2022 08:21:03 +0100 Subject: [PATCH 16/32] Fix and optimize memory storage (#2207) * internal/memory: cache timestamp * internal/memory: ensure to never delete non-expired items This fixes a TOCTOU problem between a mutex rlock and a mutex lock. * internal/memory: move costly operations outside of locked area * internal/storage: cache timestamp * internal/storage: ensure to never delete non-expired items This fixes a TOCTOU problem between a mutex rlock and a mutex lock. * internal/storage: move costly operations outside of locked area --- internal/memory/memory.go | 16 ++++++++++++---- internal/storage/memory/memory.go | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/internal/memory/memory.go b/internal/memory/memory.go index 0c37aa9c..ff220284 100644 --- a/internal/memory/memory.go +++ b/internal/memory/memory.go @@ -47,8 +47,9 @@ func (s *Storage) Set(key string, val interface{}, ttl time.Duration) { if ttl > 0 { exp = uint32(ttl.Seconds()) + atomic.LoadUint32(&utils.Timestamp) } + i := item{exp, val} s.Lock() - s.data[key] = item{exp, val} + s.data[key] = i s.Unlock() } @@ -61,8 +62,9 @@ func (s *Storage) Delete(key string) { // Reset all keys func (s *Storage) Reset() { + nd := make(map[string]item) s.Lock() - s.data = make(map[string]item) + s.data = nd s.Unlock() } @@ -74,17 +76,23 @@ func (s *Storage) gc(sleep time.Duration) { for { select { case <-ticker.C: + ts := atomic.LoadUint32(&utils.Timestamp) expired = expired[:0] s.RLock() for key, v := range s.data { - if v.e != 0 && v.e <= atomic.LoadUint32(&utils.Timestamp) { + if v.e != 0 && v.e <= ts { expired = append(expired, key) } } s.RUnlock() s.Lock() + // Double-checked locking. + // We might have replaced the item in the meantime. for i := range expired { - delete(s.data, expired[i]) + v := s.data[expired[i]] + if v.e != 0 && v.e <= ts { + delete(s.data, expired[i]) + } } s.Unlock() } diff --git a/internal/storage/memory/memory.go b/internal/storage/memory/memory.go index ff43c305..1a561061 100644 --- a/internal/storage/memory/memory.go +++ b/internal/storage/memory/memory.go @@ -70,8 +70,9 @@ func (s *Storage) Set(key string, val []byte, exp time.Duration) error { expire = uint32(exp.Seconds()) + atomic.LoadUint32(&utils.Timestamp) } + e := entry{val, expire} s.mux.Lock() - s.db[key] = entry{val, expire} + s.db[key] = e s.mux.Unlock() return nil } @@ -90,8 +91,9 @@ func (s *Storage) Delete(key string) error { // Reset all keys func (s *Storage) Reset() error { + ndb := make(map[string]entry) s.mux.Lock() - s.db = make(map[string]entry) + s.db = ndb s.mux.Unlock() return nil } @@ -112,17 +114,23 @@ func (s *Storage) gc() { case <-s.done: return case <-ticker.C: + ts := atomic.LoadUint32(&utils.Timestamp) expired = expired[:0] s.mux.RLock() for id, v := range s.db { - if v.expiry != 0 && v.expiry < atomic.LoadUint32(&utils.Timestamp) { + if v.expiry != 0 && v.expiry <= ts { expired = append(expired, id) } } s.mux.RUnlock() s.mux.Lock() + // Double-checked locking. + // We might have replaced the item in the meantime. for i := range expired { - delete(s.db, expired[i]) + v := s.db[expired[i]] + if v.expiry != 0 && v.expiry <= ts { + delete(s.db, expired[i]) + } } s.mux.Unlock() } From b288a9f54e6454543b7a12754ab55bfc982d7a70 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Mon, 14 Nov 2022 08:22:35 +0100 Subject: [PATCH 17/32] ctx: make Secure() also report whether a secure connection was established to a trusted proxy (#2215) We had a discussion about this in https://github.com/gofiber/helmet/pull/74. --- ctx.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctx.go b/ctx.go index ac2e2585..4fb70947 100644 --- a/ctx.go +++ b/ctx.go @@ -1496,9 +1496,9 @@ func (c *Ctx) SaveFileToStorage(fileheader *multipart.FileHeader, path string, s return storage.Set(path, content, 0) } -// Secure returns a boolean property, that is true, if a TLS connection is established. +// Secure returns whether a secure connection was established. func (c *Ctx) Secure() bool { - return c.fasthttp.IsTLS() + return c.Protocol() == "https" } // Send sets the HTTP response body without copying it. From 235cd9df826849d51d47528026ffdb9ca88f8df4 Mon Sep 17 00:00:00 2001 From: leonklingele Date: Mon, 14 Nov 2022 08:32:48 +0100 Subject: [PATCH 18/32] ctx: simplify Protocol() (#2217) * ctx: simplify Protocol() * ctx: also mention "X-Url-Scheme" header in Protocol() * ctx: use the same warning comment about enabling Config.EnableTrustedProxyCheck everywhere --- ctx.go | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/ctx.go b/ctx.go index 4fb70947..9fb8404a 100644 --- a/ctx.go +++ b/ctx.go @@ -1040,28 +1040,25 @@ func (c *Ctx) Path(override ...string) string { } // Protocol contains the request protocol string: http or https for TLS requests. -// Use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. +// Please use Config.EnableTrustedProxyCheck to prevent header spoofing, in case when your app is behind the proxy. func (c *Ctx) Protocol() string { if c.fasthttp.IsTLS() { return "https" } - scheme := "http" if !c.IsProxyTrusted() { - return scheme + return "http" } + + scheme := "http" c.fasthttp.Request.Header.VisitAll(func(key, val []byte) { if len(key) < 12 { - return // X-Forwarded- - } else if bytes.HasPrefix(key, []byte("X-Forwarded-")) { - v := c.app.getString(val) - if bytes.Equal(key, []byte(HeaderXForwardedProto)) { - commaPos := strings.Index(v, ",") - if commaPos != -1 { - scheme = v[:commaPos] - } else { - scheme = v - } - } else if bytes.Equal(key, []byte(HeaderXForwardedProtocol)) { + return // Neither "X-Forwarded-" nor "X-Url-Scheme" + } + switch { + case bytes.HasPrefix(key, []byte("X-Forwarded-")): + if bytes.Equal(key, []byte(HeaderXForwardedProto)) || + bytes.Equal(key, []byte(HeaderXForwardedProtocol)) { + v := c.app.getString(val) commaPos := strings.Index(v, ",") if commaPos != -1 { scheme = v[:commaPos] @@ -1071,7 +1068,8 @@ func (c *Ctx) Protocol() string { } else if bytes.Equal(key, []byte(HeaderXForwardedSsl)) && bytes.Equal(val, []byte("on")) { scheme = "https" } - } else if bytes.Equal(key, []byte(HeaderXUrlScheme)) { + + case bytes.Equal(key, []byte(HeaderXUrlScheme)): scheme = c.app.getString(val) } }) From 61b449606716cb1da1a78f9ffaf81d5395ca61b2 Mon Sep 17 00:00:00 2001 From: Caleb Case Date: Tue, 15 Nov 2022 06:13:11 -0500 Subject: [PATCH 19/32] Track Configured Values (#2221) * WIP: Use Parent Error Handler on Mount * Add suggested boolean guard * Move flag to App * Move to copy of config as configured * Apply the same trick to customMethod --- app.go | 14 +++++++++----- helpers.go | 2 +- mount_test.go | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/app.go b/app.go index 3b45b307..55896ae7 100644 --- a/app.go +++ b/app.go @@ -110,10 +110,10 @@ type App struct { latestRoute *Route // TLS handler tlsHandler *TLSHandler - // custom method check - customMethod bool // Mount fields mountFields *mountFields + // Indicates if the value was explicitly configured + configured Config } // Config is a struct holding the server settings. @@ -513,6 +513,9 @@ func New(config ...Config) *App { app.config = config[0] } + // Initialize configured before defaults are set + app.configured = app.config + if app.config.ETag { if !IsChild() { fmt.Println("[Warning] Config.ETag is deprecated since v2.0.6, please use 'middleware/etag'.") @@ -557,8 +560,6 @@ func New(config ...Config) *App { } if len(app.config.RequestMethods) == 0 { app.config.RequestMethods = DefaultMethods - } else { - app.customMethod = true } app.config.trustedProxiesMap = make(map[string]struct{}, len(app.config.TrustedProxies)) @@ -999,7 +1000,10 @@ func (app *App) ErrorHandler(ctx *Ctx, err error) error { if prefix != "" && strings.HasPrefix(ctx.path, prefix) { parts := len(strings.Split(prefix, "/")) if mountedPrefixParts <= parts { - mountedErrHandler = subApp.config.ErrorHandler + if subApp.configured.ErrorHandler != nil { + mountedErrHandler = subApp.config.ErrorHandler + } + mountedPrefixParts = parts } } diff --git a/helpers.go b/helpers.go index 11364e73..34b9350b 100644 --- a/helpers.go +++ b/helpers.go @@ -334,7 +334,7 @@ var getBytesImmutable = func(s string) (b []byte) { // HTTP methods and their unique INTs func (app *App) methodInt(s string) int { // For better performance - if !app.customMethod { + if len(app.configured.RequestMethods) == 0 { switch s { case MethodGet: return 0 diff --git a/mount_test.go b/mount_test.go index deee3ad4..1e944d09 100644 --- a/mount_test.go +++ b/mount_test.go @@ -139,6 +139,24 @@ func Test_App_Group_Mount(t *testing.T) { utils.AssertEqual(t, uint32(2), app.handlersCount) } +func Test_App_UseParentErrorHandler(t *testing.T) { + app := New(Config{ + ErrorHandler: func(ctx *Ctx, err error) error { + return ctx.Status(500).SendString("hi, i'm a custom error") + }, + }) + + fiber := New() + fiber.Get("/", func(c *Ctx) error { + return errors.New("something happened") + }) + + app.Mount("/api", fiber) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) + testErrorResponse(t, err, resp, "hi, i'm a custom error") +} + func Test_App_UseMountedErrorHandler(t *testing.T) { app := New() From 3157fb5f1c45616173da443866e778d8e7c9a3ca Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Tue, 15 Nov 2022 19:14:32 +0800 Subject: [PATCH 20/32] :sparkles: Add callback function for middleware/logger (#2219) * :sparkles: Add callback function for middleware/logger * Refine test code --- middleware/logger/config.go | 10 ++++++++++ middleware/logger/logger.go | 12 +++++++++--- middleware/logger/logger_test.go | 25 ++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 08c5ad51..0e8aaa5c 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -16,6 +16,12 @@ type Config struct { // Optional. Default: nil Next func(c *fiber.Ctx) bool + // Done is a function that is called after the log string for a request is written to Output, + // and pass the log string as parameter. + // + // Optional. Default: a function that does nothing. + Done func(c *fiber.Ctx, logString []byte) + // Format defines the logging tags // // Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n @@ -49,6 +55,7 @@ type Config struct { // ConfigDefault is the default config var ConfigDefault = Config{ Next: nil, + Done: func(c *fiber.Ctx, logString []byte) {}, Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", TimeFormat: "15:04:05", TimeZone: "Local", @@ -90,6 +97,9 @@ func configDefault(config ...Config) Config { if cfg.Next == nil { cfg.Next = ConfigDefault.Next } + if cfg.Done == nil { + cfg.Done = ConfigDefault.Done + } if cfg.Format == "" { cfg.Format = ConfigDefault.Format } diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 566b685d..dc348bfe 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -10,12 +10,13 @@ import ( "sync/atomic" "time" - "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/internal/bytebufferpool" - "github.com/gofiber/fiber/v2/internal/fasttemplate" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/valyala/fasthttp" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/internal/bytebufferpool" + "github.com/gofiber/fiber/v2/internal/fasttemplate" ) // Logger variables @@ -186,6 +187,8 @@ func New(config ...Config) fiber.Handler { // Write buffer to output _, _ = cfg.Output.Write(buf.Bytes()) + cfg.Done(c, buf.Bytes()) + // Put buffer back to pool bytebufferpool.Put(buf) @@ -315,6 +318,9 @@ func New(config ...Config) fiber.Handler { } } mu.Unlock() + + cfg.Done(c, buf.Bytes()) + // Put buffer back to pool bytebufferpool.Put(buf) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index 43522fa3..ba566d2c 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -1,6 +1,7 @@ package logger import ( + "bytes" "errors" "fmt" "io" @@ -10,11 +11,12 @@ import ( "sync" "testing" + "github.com/valyala/fasthttp" + "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/bytebufferpool" "github.com/gofiber/fiber/v2/middleware/requestid" "github.com/gofiber/fiber/v2/utils" - "github.com/valyala/fasthttp" ) // go test -run Test_Logger @@ -99,6 +101,27 @@ func Test_Logger_Next(t *testing.T) { utils.AssertEqual(t, fiber.StatusNotFound, resp.StatusCode) } +// go test -run Test_Logger_Done +func Test_Logger_Done(t *testing.T) { + buf := bytes.NewBuffer(nil) + app := fiber.New() + app.Use(New(Config{ + Done: func(c *fiber.Ctx, logString []byte) { + if c.Response().StatusCode() == fiber.StatusOK { + buf.Write(logString) + } + }, + })).Get("/logging", func(ctx *fiber.Ctx) error { + return ctx.SendStatus(fiber.StatusOK) + }) + + resp, err := app.Test(httptest.NewRequest("GET", "/logging", nil)) + + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + utils.AssertEqual(t, true, buf.Len() > 0) +} + // go test -run Test_Logger_ErrorTimeZone func Test_Logger_ErrorTimeZone(t *testing.T) { app := fiber.New() From e8f8cb647b6400260e2c70ad380f97cc8f4f99c8 Mon Sep 17 00:00:00 2001 From: skyenought <70408571+Skyenought@users.noreply.github.com> Date: Fri, 18 Nov 2022 20:10:43 +0800 Subject: [PATCH 21/32] :sparkles: Add customTags in logger middleware Config (#2188) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * :sparkles: Add customTags in logger middleware Config * improve tags with parameter * improve logger performance * improve logger performance * improve logger performance * improve logger performance Co-authored-by: René Werner --- .github/README.md | 1 - .github/README_ckb.md | 1 - .github/README_de.md | 1 - .github/README_es.md | 1 - .github/README_fa.md | 1 - .github/README_fr.md | 1 - .github/README_he.md | 1 - .github/README_id.md | 1 - .github/README_it.md | 1 - .github/README_ja.md | 1 - .github/README_ko.md | 1 - .github/README_nl.md | 1 - .github/README_pt.md | 1 - .github/README_ru.md | 1 - .github/README_sa.md | 1 - .github/README_tr.md | 1 - .github/README_zh-CN.md | 1 - .github/README_zh-TW.md | 1 - internal/fasttemplate/LICENSE | 22 -- internal/fasttemplate/template.go | 437 ---------------------------- internal/fasttemplate/unsafe.go | 22 -- internal/fasttemplate/unsafe_gae.go | 12 - middleware/logger/README.md | 18 +- middleware/logger/config.go | 31 +- middleware/logger/data.go | 19 ++ middleware/logger/logger.go | 201 +++---------- middleware/logger/logger_test.go | 94 ++++-- middleware/logger/tags.go | 208 +++++++++++++ middleware/logger/template_chain.go | 67 +++++ 29 files changed, 447 insertions(+), 702 deletions(-) delete mode 100644 internal/fasttemplate/LICENSE delete mode 100644 internal/fasttemplate/template.go delete mode 100644 internal/fasttemplate/unsafe.go delete mode 100644 internal/fasttemplate/unsafe_gae.go create mode 100644 middleware/logger/data.go create mode 100644 middleware/logger/tags.go create mode 100644 middleware/logger/template_chain.go diff --git a/.github/README.md b/.github/README.md index 43f675f1..acced730 100644 --- a/.github/README.md +++ b/.github/README.md @@ -692,7 +692,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_ckb.md b/.github/README_ckb.md index c62c5d12..9c584134 100644 --- a/.github/README_ckb.md +++ b/.github/README_ckb.md @@ -691,7 +691,6 @@ For more articles, middlewares, examples or tools check our [awesome list](https - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_de.md b/.github/README_de.md index aca4a2fa..c3c33942 100644 --- a/.github/README_de.md +++ b/.github/README_de.md @@ -661,7 +661,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_es.md b/.github/README_es.md index 0d3d36a2..918207b5 100644 --- a/.github/README_es.md +++ b/.github/README_es.md @@ -661,7 +661,6 @@ Copyright (c) 2019-presente [Fenny](https://github.com/fenny) y [contribuyentes] - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_fa.md b/.github/README_fa.md index 7fe0475f..5cbaba18 100644 --- a/.github/README_fa.md +++ b/.github/README_fa.md @@ -813,7 +813,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_fr.md b/.github/README_fr.md index 8ced8bc2..4ecf6da8 100644 --- a/.github/README_fr.md +++ b/.github/README_fr.md @@ -663,7 +663,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_he.md b/.github/README_he.md index 3a8a7c67..5457e55e 100644 --- a/.github/README_he.md +++ b/.github/README_he.md @@ -838,7 +838,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_id.md b/.github/README_id.md index 4d4fc3f7..98f5fdf4 100644 --- a/.github/README_id.md +++ b/.github/README_id.md @@ -664,7 +664,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_it.md b/.github/README_it.md index b3df35e5..5f8b06c0 100644 --- a/.github/README_it.md +++ b/.github/README_it.md @@ -687,7 +687,6 @@ Copyright (c) 2019-ora [Fenny](https://github.com/fenny) e [Contributors](https: - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_ja.md b/.github/README_ja.md index 29e3f3a1..4264f837 100644 --- a/.github/README_ja.md +++ b/.github/README_ja.md @@ -666,7 +666,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_ko.md b/.github/README_ko.md index c2479bae..ca2008d5 100644 --- a/.github/README_ko.md +++ b/.github/README_ko.md @@ -667,7 +667,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_nl.md b/.github/README_nl.md index 854e360c..85a7888f 100644 --- a/.github/README_nl.md +++ b/.github/README_nl.md @@ -667,7 +667,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_pt.md b/.github/README_pt.md index 52df5a58..4568b145 100644 --- a/.github/README_pt.md +++ b/.github/README_pt.md @@ -663,7 +663,6 @@ O logo oficial foi criado por [Vic Shóstak](https://github.com/koddr) e distrib - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_ru.md b/.github/README_ru.md index dad365ad..a87dcb94 100644 --- a/.github/README_ru.md +++ b/.github/README_ru.md @@ -670,7 +670,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_sa.md b/.github/README_sa.md index efc80c4b..740c754e 100644 --- a/.github/README_sa.md +++ b/.github/README_sa.md @@ -732,7 +732,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_tr.md b/.github/README_tr.md index c425d40c..4e4864c7 100644 --- a/.github/README_tr.md +++ b/.github/README_tr.md @@ -660,7 +660,6 @@ Telif (c) 2019-günümüz [Fenny](https://github.com/fenny) ve [katkıda bulunan - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_zh-CN.md b/.github/README_zh-CN.md index 2a64bd04..0936f09f 100644 --- a/.github/README_zh-CN.md +++ b/.github/README_zh-CN.md @@ -670,7 +670,6 @@ Copyright (c) 2019-present [Fenny](https://github.com/fenny) and [Contributors]( - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/.github/README_zh-TW.md b/.github/README_zh-TW.md index c1999289..68d16bdd 100644 --- a/.github/README_zh-TW.md +++ b/.github/README_zh-TW.md @@ -663,7 +663,6 @@ Fiber 是一個以贊助維生的開源專案,像是: 網域、gitbook、netli - [fasthttp](https://github.com/valyala/fasthttp/blob/master/LICENSE) - [bytebufferpool](https://github.com/valyala/bytebufferpool/blob/master/LICENSE) - [dictpool](https://github.com/savsgio/dictpool/blob/master/LICENSE) -- [fasttemplate](https://github.com/valyala/fasttemplate/blob/master/LICENSE) - [fwd](https://github.com/philhofer/fwd/blob/master/LICENSE.md) - [go-ole](https://github.com/go-ole/go-ole/blob/master/LICENSE) - [gopsutil](https://github.com/shirou/gopsutil/blob/master/LICENSE) diff --git a/internal/fasttemplate/LICENSE b/internal/fasttemplate/LICENSE deleted file mode 100644 index 7125a63c..00000000 --- a/internal/fasttemplate/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Aliaksandr Valialkin - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/internal/fasttemplate/template.go b/internal/fasttemplate/template.go deleted file mode 100644 index d4559fee..00000000 --- a/internal/fasttemplate/template.go +++ /dev/null @@ -1,437 +0,0 @@ -// Package fasttemplate implements simple and fast template library. -// -// Fasttemplate is faster than text/template, strings.Replace -// and strings.Replacer. -// -// Fasttemplate ideally fits for fast and simple placeholders' substitutions. -package fasttemplate - -import ( - "bytes" - "fmt" - "io" - - "github.com/gofiber/fiber/v2/internal/bytebufferpool" -) - -// ExecuteFunc calls f on each template tag (placeholder) occurrence. -// -// Returns the number of bytes written to w. -// -// This function is optimized for constantly changing templates. -// Use Template.ExecuteFunc for frozen templates. -func ExecuteFunc(template, startTag, endTag string, w io.Writer, f TagFunc) (int64, error) { - s := unsafeString2Bytes(template) - a := unsafeString2Bytes(startTag) - b := unsafeString2Bytes(endTag) - - var nn int64 - var ni int - var err error - for { - n := bytes.Index(s, a) - if n < 0 { - break - } - ni, err = w.Write(s[:n]) - nn += int64(ni) - if err != nil { - return nn, err - } - - s = s[n+len(a):] - n = bytes.Index(s, b) - if n < 0 { - // cannot find end tag - just write it to the output. - ni, _ = w.Write(a) - nn += int64(ni) - break - } - - ni, err = f(w, unsafeBytes2String(s[:n])) - nn += int64(ni) - if err != nil { - return nn, err - } - s = s[n+len(b):] - } - ni, err = w.Write(s) - nn += int64(ni) - - return nn, err -} - -// Execute substitutes template tags (placeholders) with the corresponding -// values from the map m and writes the result to the given writer w. -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// Returns the number of bytes written to w. -// -// This function is optimized for constantly changing templates. -// Use Template.Execute for frozen templates. -func Execute(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) { - return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) }) -} - -// ExecuteStd works the same way as Execute, but keeps the unknown placeholders. -// This can be used as a drop-in replacement for strings.Replacer -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// Returns the number of bytes written to w. -// -// This function is optimized for constantly changing templates. -// Use Template.ExecuteStd for frozen templates. -func ExecuteStd(template, startTag, endTag string, w io.Writer, m map[string]interface{}) (int64, error) { - return ExecuteFunc(template, startTag, endTag, w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, tag, m) }) -} - -// ExecuteFuncString calls f on each template tag (placeholder) occurrence -// and substitutes it with the data written to TagFunc's w. -// -// Returns the resulting string. -// -// This function is optimized for constantly changing templates. -// Use Template.ExecuteFuncString for frozen templates. -func ExecuteFuncString(template, startTag, endTag string, f TagFunc) string { - s, err := ExecuteFuncStringWithErr(template, startTag, endTag, f) - if err != nil { - panic(fmt.Sprintf("unexpected error: %s", err)) - } - return s -} - -// ExecuteFuncStringWithErr is nearly the same as ExecuteFuncString -// but when f returns an error, ExecuteFuncStringWithErr won't panic like ExecuteFuncString -// it just returns an empty string and the error f returned -func ExecuteFuncStringWithErr(template, startTag, endTag string, f TagFunc) (string, error) { - tagsCount := bytes.Count(unsafeString2Bytes(template), unsafeString2Bytes(startTag)) - if tagsCount == 0 { - return template, nil - } - - bb := byteBufferPool.Get() - if _, err := ExecuteFunc(template, startTag, endTag, bb, f); err != nil { - bb.Reset() - byteBufferPool.Put(bb) - return "", err - } - s := string(bb.B) - bb.Reset() - byteBufferPool.Put(bb) - return s, nil -} - -var byteBufferPool bytebufferpool.Pool - -// ExecuteString substitutes template tags (placeholders) with the corresponding -// values from the map m and returns the result. -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// This function is optimized for constantly changing templates. -// Use Template.ExecuteString for frozen templates. -func ExecuteString(template, startTag, endTag string, m map[string]interface{}) string { - return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) }) -} - -// ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders. -// This can be used as a drop-in replacement for strings.Replacer -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// This function is optimized for constantly changing templates. -// Use Template.ExecuteStringStd for frozen templates. -func ExecuteStringStd(template, startTag, endTag string, m map[string]interface{}) string { - return ExecuteFuncString(template, startTag, endTag, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, startTag, endTag, tag, m) }) -} - -// Template implements simple template engine, which can be used for fast -// tags' (aka placeholders) substitution. -type Template struct { - template string - startTag string - endTag string - - texts [][]byte - tags []string - byteBufferPool bytebufferpool.Pool -} - -// New parses the given template using the given startTag and endTag -// as tag start and tag end. -// -// The returned template can be executed by concurrently running goroutines -// using Execute* methods. -// -// New panics if the given template cannot be parsed. Use NewTemplate instead -// if template may contain errors. -func New(template, startTag, endTag string) *Template { - t, err := NewTemplate(template, startTag, endTag) - if err != nil { - panic(err) - } - return t -} - -// NewTemplate parses the given template using the given startTag and endTag -// as tag start and tag end. -// -// The returned template can be executed by concurrently running goroutines -// using Execute* methods. -func NewTemplate(template, startTag, endTag string) (*Template, error) { - var t Template - err := t.Reset(template, startTag, endTag) - if err != nil { - return nil, err - } - return &t, nil -} - -// TagFunc can be used as a substitution value in the map passed to Execute*. -// Execute* functions pass tag (placeholder) name in 'tag' argument. -// -// TagFunc must be safe to call from concurrently running goroutines. -// -// TagFunc must write contents to w and return the number of bytes written. -type TagFunc func(w io.Writer, tag string) (int, error) - -// Reset resets the template t to new one defined by -// template, startTag and endTag. -// -// Reset allows Template object re-use. -// -// Reset may be called only if no other goroutines call t methods at the moment. -func (t *Template) Reset(template, startTag, endTag string) error { - // Keep these vars in t, so GC won't collect them and won't break - // vars derived via unsafe* - t.template = template - t.startTag = startTag - t.endTag = endTag - t.texts = t.texts[:0] - t.tags = t.tags[:0] - - if len(startTag) == 0 { - panic("startTag cannot be empty") - } - if len(endTag) == 0 { - panic("endTag cannot be empty") - } - - s := unsafeString2Bytes(template) - a := unsafeString2Bytes(startTag) - b := unsafeString2Bytes(endTag) - - tagsCount := bytes.Count(s, a) - if tagsCount == 0 { - return nil - } - - if tagsCount+1 > cap(t.texts) { - t.texts = make([][]byte, 0, tagsCount+1) - } - if tagsCount > cap(t.tags) { - t.tags = make([]string, 0, tagsCount) - } - - for { - n := bytes.Index(s, a) - if n < 0 { - t.texts = append(t.texts, s) - break - } - t.texts = append(t.texts, s[:n]) - - s = s[n+len(a):] - n = bytes.Index(s, b) - if n < 0 { - return fmt.Errorf("cannot find end tag=%q in the template=%q starting from %q", endTag, template, s) - } - - t.tags = append(t.tags, unsafeBytes2String(s[:n])) - s = s[n+len(b):] - } - - return nil -} - -// ExecuteFunc calls f on each template tag (placeholder) occurrence. -// -// Returns the number of bytes written to w. -// -// This function is optimized for frozen templates. -// Use ExecuteFunc for constantly changing templates. -func (t *Template) ExecuteFunc(w io.Writer, f TagFunc) (int64, error) { - var nn int64 - - n := len(t.texts) - 1 - if n == -1 { - ni, err := w.Write(unsafeString2Bytes(t.template)) - return int64(ni), err - } - - for i := 0; i < n; i++ { - ni, err := w.Write(t.texts[i]) - nn += int64(ni) - if err != nil { - return nn, err - } - - ni, err = f(w, t.tags[i]) - nn += int64(ni) - if err != nil { - return nn, err - } - } - ni, err := w.Write(t.texts[n]) - nn += int64(ni) - return nn, err -} - -// Execute substitutes template tags (placeholders) with the corresponding -// values from the map m and writes the result to the given writer w. -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// Returns the number of bytes written to w. -func (t *Template) Execute(w io.Writer, m map[string]interface{}) (int64, error) { - return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) }) -} - -// ExecuteStd works the same way as Execute, but keeps the unknown placeholders. -// This can be used as a drop-in replacement for strings.Replacer -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// Returns the number of bytes written to w. -func (t *Template) ExecuteStd(w io.Writer, m map[string]interface{}) (int64, error) { - return t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, tag, m) }) -} - -// ExecuteFuncString calls f on each template tag (placeholder) occurrence -// and substitutes it with the data written to TagFunc's w. -// -// Returns the resulting string. -// -// This function is optimized for frozen templates. -// Use ExecuteFuncString for constantly changing templates. -func (t *Template) ExecuteFuncString(f TagFunc) string { - s, err := t.ExecuteFuncStringWithErr(f) - if err != nil { - panic(fmt.Sprintf("unexpected error: %s", err)) - } - return s -} - -// ExecuteFuncStringWithErr calls f on each template tag (placeholder) occurrence -// and substitutes it with the data written to TagFunc's w. -// -// Returns the resulting string. -// -// This function is optimized for frozen templates. -// Use ExecuteFuncString for constantly changing templates. -func (t *Template) ExecuteFuncStringWithErr(f TagFunc) (string, error) { - bb := t.byteBufferPool.Get() - if _, err := t.ExecuteFunc(bb, f); err != nil { - bb.Reset() - t.byteBufferPool.Put(bb) - return "", err - } - s := string(bb.Bytes()) - bb.Reset() - t.byteBufferPool.Put(bb) - return s, nil -} - -// ExecuteString substitutes template tags (placeholders) with the corresponding -// values from the map m and returns the result. -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// This function is optimized for frozen templates. -// Use ExecuteString for constantly changing templates. -func (t *Template) ExecuteString(m map[string]interface{}) string { - return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return stdTagFunc(w, tag, m) }) -} - -// ExecuteStringStd works the same way as ExecuteString, but keeps the unknown placeholders. -// This can be used as a drop-in replacement for strings.Replacer -// -// Substitution map m may contain values with the following types: -// - []byte - the fastest value type -// - string - convenient value type -// - TagFunc - flexible value type -// -// This function is optimized for frozen templates. -// Use ExecuteStringStd for constantly changing templates. -func (t *Template) ExecuteStringStd(m map[string]interface{}) string { - return t.ExecuteFuncString(func(w io.Writer, tag string) (int, error) { return keepUnknownTagFunc(w, t.startTag, t.endTag, tag, m) }) -} - -func stdTagFunc(w io.Writer, tag string, m map[string]interface{}) (int, error) { - v := m[tag] - if v == nil { - return 0, nil - } - switch value := v.(type) { - case []byte: - return w.Write(value) - case string: - return w.Write([]byte(value)) - case TagFunc: - return value(w, tag) - default: - panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v)) - } -} - -func keepUnknownTagFunc(w io.Writer, startTag, endTag, tag string, m map[string]interface{}) (int, error) { - v, ok := m[tag] - if !ok { - if _, err := w.Write(unsafeString2Bytes(startTag)); err != nil { - return 0, err - } - if _, err := w.Write(unsafeString2Bytes(tag)); err != nil { - return 0, err - } - if _, err := w.Write(unsafeString2Bytes(endTag)); err != nil { - return 0, err - } - return len(startTag) + len(tag) + len(endTag), nil - } - if v == nil { - return 0, nil - } - switch value := v.(type) { - case []byte: - return w.Write(value) - case string: - return w.Write([]byte(value)) - case TagFunc: - return value(w, tag) - default: - panic(fmt.Sprintf("tag=%q contains unexpected value type=%#v. Expected []byte, string or TagFunc", tag, v)) - } -} diff --git a/internal/fasttemplate/unsafe.go b/internal/fasttemplate/unsafe.go deleted file mode 100644 index 1d0bc9e8..00000000 --- a/internal/fasttemplate/unsafe.go +++ /dev/null @@ -1,22 +0,0 @@ -//go:build !appengine -// +build !appengine - -package fasttemplate - -import ( - "reflect" - "unsafe" -) - -func unsafeBytes2String(b []byte) string { - return *(*string)(unsafe.Pointer(&b)) -} - -func unsafeString2Bytes(s string) (b []byte) { - sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) - bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) - bh.Data = sh.Data - bh.Cap = sh.Len - bh.Len = sh.Len - return b -} diff --git a/internal/fasttemplate/unsafe_gae.go b/internal/fasttemplate/unsafe_gae.go deleted file mode 100644 index dbfed3ba..00000000 --- a/internal/fasttemplate/unsafe_gae.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build appengine -// +build appengine - -package fasttemplate - -func unsafeBytes2String(b []byte) string { - return string(b) -} - -func unsafeString2Bytes(s string) []byte { - return []byte(s) -} diff --git a/middleware/logger/README.md b/middleware/logger/README.md index 63c1530f..f6a70117 100644 --- a/middleware/logger/README.md +++ b/middleware/logger/README.md @@ -11,6 +11,7 @@ Logger middleware for [Fiber](https://github.com/gofiber/fiber) that logs HTTP r - [Logging Request ID](#logging-request-id) - [Changing TimeZone & TimeFormat](#changing-timezone--timeformat) - [Custom File Writer](#custom-file-writer) + - [Add Custom Tags](#add-custom-tags) - [Config](#config) - [Default Config](#default-config-1) - [Constants](#constants) @@ -75,6 +76,16 @@ app.Use(logger.New(logger.Config{ Output: file, })) ``` +### Add Custom Tags +```go +app.Use(logger.New(logger.Config{ + CustomTags: map[string]logger.LogFunc{ + "custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString("it is a custom tag") + }, + }, +})) +``` ## Config ```go @@ -85,11 +96,16 @@ type Config struct { // Optional. Default: nil Next func(c *fiber.Ctx) bool + // CustomTags defines the custom tag action + // + // Optional. Default: map[string]LogFunc{} + CustomTags map[string]LogFunc + // Format defines the logging tags // // Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n Format string - + // TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html // // Optional. Default: 15:04:05 diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 0e8aaa5c..685ce618 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -7,6 +7,7 @@ import ( "time" "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/internal/bytebufferpool" ) // Config defines the config for middleware. @@ -22,6 +23,11 @@ type Config struct { // Optional. Default: a function that does nothing. Done func(c *fiber.Ctx, logString []byte) + // tagFunctions defines the custom tag action + // + // Optional. Default: map[string]LogFunc + CustomTags map[string]LogFunc + // Format defines the logging tags // // Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n @@ -52,6 +58,14 @@ type Config struct { timeZoneLocation *time.Location } +const ( + startTag = "${" + endTag = "}" + paramSeparator = ":" +) + +type LogFunc func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) + // ConfigDefault is the default config var ConfigDefault = Config{ Next: nil, @@ -65,11 +79,8 @@ var ConfigDefault = Config{ } // Function to check if the logger format is compatible for coloring -func validCustomFormat(format string) bool { +func checkColorEnable(format string) bool { validTemplates := []string{"${status}", "${method}"} - if format == "" { - return true - } for _, template := range validTemplates { if strings.Contains(format, template) { return true @@ -88,11 +99,6 @@ func configDefault(config ...Config) Config { // Override default config cfg := config[0] - // Enable colors if no custom format or output is given - if validCustomFormat(cfg.Format) && cfg.Output == nil { - cfg.enableColors = true - } - // Set default values if cfg.Next == nil { cfg.Next = ConfigDefault.Next @@ -103,6 +109,7 @@ func configDefault(config ...Config) Config { if cfg.Format == "" { cfg.Format = ConfigDefault.Format } + if cfg.TimeZone == "" { cfg.TimeZone = ConfigDefault.TimeZone } @@ -115,5 +122,11 @@ func configDefault(config ...Config) Config { if cfg.Output == nil { cfg.Output = ConfigDefault.Output } + + // Enable colors if no custom format or output is given + if cfg.Output == nil && checkColorEnable(cfg.Format) { + cfg.enableColors = true + } + return cfg } diff --git a/middleware/logger/data.go b/middleware/logger/data.go new file mode 100644 index 00000000..611a0aee --- /dev/null +++ b/middleware/logger/data.go @@ -0,0 +1,19 @@ +package logger + +import ( + "sync" + "sync/atomic" + "time" +) + +var DataPool = sync.Pool{New: func() interface{} { return new(Data) }} + +// Data is a struct to define some variables to use in custom logger function. +type Data struct { + Pid string + ErrPaddingStr string + ChainErr error + Start time.Time + Stop time.Time + Timestamp atomic.Value +} diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index dc348bfe..41d2564a 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -2,7 +2,6 @@ package logger import ( "fmt" - "io" "os" "strconv" "strings" @@ -10,56 +9,13 @@ import ( "sync/atomic" "time" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" "github.com/valyala/fasthttp" - "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/internal/bytebufferpool" - "github.com/gofiber/fiber/v2/internal/fasttemplate" -) - -// Logger variables -const ( - TagPid = "pid" - TagTime = "time" - TagReferer = "referer" - TagProtocol = "protocol" - TagPort = "port" - TagIP = "ip" - TagIPs = "ips" - TagHost = "host" - TagMethod = "method" - TagPath = "path" - TagURL = "url" - TagUA = "ua" - TagLatency = "latency" - TagStatus = "status" - TagResBody = "resBody" - TagReqHeaders = "reqHeaders" - TagQueryStringParams = "queryParams" - TagBody = "body" - TagBytesSent = "bytesSent" - TagBytesReceived = "bytesReceived" - TagRoute = "route" - TagError = "error" - // DEPRECATED: Use TagReqHeader instead - TagHeader = "header:" - TagReqHeader = "reqHeader:" - TagRespHeader = "respHeader:" - TagLocals = "locals:" - TagQuery = "query:" - TagForm = "form:" - TagCookie = "cookie:" - TagBlack = "black" - TagRed = "red" - TagGreen = "green" - TagYellow = "yellow" - TagBlue = "blue" - TagMagenta = "magenta" - TagCyan = "cyan" - TagWhite = "white" - TagReset = "reset" ) // New creates a new middleware handler @@ -76,17 +32,14 @@ func New(config ...Config) fiber.Handler { } // Check if format contains latency - cfg.enableLatency = strings.Contains(cfg.Format, "${latency}") + cfg.enableLatency = strings.Contains(cfg.Format, "${"+TagLatency+"}") - // Create template parser - tmpl := fasttemplate.New(cfg.Format, "${", "}") - - // Create correct timeformat var timestamp atomic.Value + // Create correct timeformat timestamp.Store(time.Now().In(cfg.timeZoneLocation).Format(cfg.TimeFormat)) // Update date/time every 500 milliseconds in a separate go routine - if strings.Contains(cfg.Format, "${time}") { + if strings.Contains(cfg.Format, "${"+TagTime+"}") { go func() { for { time.Sleep(cfg.TimeInterval) @@ -114,6 +67,14 @@ func New(config ...Config) fiber.Handler { } errPadding := 15 errPaddingStr := strconv.Itoa(errPadding) + + // instead of analyzing the template inside(handler) each time, this is done once before + // and we create several slices of the same length with the functions to be executed and fixed parts. + templateChain, logFunChain, err := buildLogFuncChain(&cfg, createTagMap(&cfg)) + if err != nil { + panic(err) + } + // Return new handler return func(c *fiber.Ctx) (err error) { // Don't execute middleware if Next returns true @@ -140,16 +101,24 @@ func New(config ...Config) fiber.Handler { errHandler = c.App().ErrorHandler }) - var start, stop time.Time + // Logger data + data := DataPool.Get().(*Data) + // no need for a reset, as long as we always override everything + data.Pid = pid + data.ErrPaddingStr = errPaddingStr + data.Timestamp = timestamp + // put data back in the pool + defer DataPool.Put(data) // Set latency start time if cfg.enableLatency { - start = time.Now() + data.Start = time.Now() } // Handle request, store err for logging chainErr := c.Next() + data.ChainErr = chainErr // Manually call error handler if chainErr != nil { if err := errHandler(c, chainErr); err != nil { @@ -159,7 +128,7 @@ func New(config ...Config) fiber.Handler { // Set latency stop time if cfg.enableLatency { - stop = time.Now() + data.Stop = time.Now() } // Get new buffer @@ -177,7 +146,7 @@ func New(config ...Config) fiber.Handler { _, _ = buf.WriteString(fmt.Sprintf("%s |%s %3d %s| %7v | %15s |%s %-7s %s| %-"+errPaddingStr+"s %s\n", timestamp.Load().(string), statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset, - stop.Sub(start).Round(time.Millisecond), + data.Stop.Sub(data.Start).Round(time.Millisecond), c.IP(), methodColor(c.Method(), colors), c.Method(), colors.Reset, c.Path(), @@ -196,114 +165,20 @@ func New(config ...Config) fiber.Handler { return nil } - // Loop over template tags to replace it with the correct value - _, err = tmpl.ExecuteFunc(buf, func(w io.Writer, tag string) (int, error) { - switch tag { - case TagTime: - return buf.WriteString(timestamp.Load().(string)) - case TagReferer: - return buf.WriteString(c.Get(fiber.HeaderReferer)) - case TagProtocol: - return buf.WriteString(c.Protocol()) - case TagPid: - return buf.WriteString(pid) - case TagPort: - return buf.WriteString(c.Port()) - case TagIP: - return buf.WriteString(c.IP()) - case TagIPs: - return buf.WriteString(c.Get(fiber.HeaderXForwardedFor)) - case TagHost: - return buf.WriteString(c.Hostname()) - case TagPath: - return buf.WriteString(c.Path()) - case TagURL: - return buf.WriteString(c.OriginalURL()) - case TagUA: - return buf.WriteString(c.Get(fiber.HeaderUserAgent)) - case TagLatency: - return buf.WriteString(fmt.Sprintf("%7v", stop.Sub(start).Round(time.Millisecond))) - case TagBody: - return buf.Write(c.Body()) - case TagBytesReceived: - return appendInt(buf, len(c.Request().Body())) - case TagBytesSent: - return appendInt(buf, len(c.Response().Body())) - case TagRoute: - return buf.WriteString(c.Route().Path) - case TagStatus: - if cfg.enableColors { - return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset)) - } - return appendInt(buf, c.Response().StatusCode()) - case TagResBody: - return buf.Write(c.Response().Body()) - case TagReqHeaders: - reqHeaders := make([]string, 0) - for k, v := range c.GetReqHeaders() { - reqHeaders = append(reqHeaders, k+"="+v) - } - return buf.Write([]byte(strings.Join(reqHeaders, "&"))) - case TagQueryStringParams: - return buf.WriteString(c.Request().URI().QueryArgs().String()) - case TagMethod: - if cfg.enableColors { - return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) - } - return buf.WriteString(c.Method()) - case TagBlack: - return buf.WriteString(colors.Black) - case TagRed: - return buf.WriteString(colors.Red) - case TagGreen: - return buf.WriteString(colors.Green) - case TagYellow: - return buf.WriteString(colors.Yellow) - case TagBlue: - return buf.WriteString(colors.Blue) - case TagMagenta: - return buf.WriteString(colors.Magenta) - case TagCyan: - return buf.WriteString(colors.Cyan) - case TagWhite: - return buf.WriteString(colors.White) - case TagReset: - return buf.WriteString(colors.Reset) - case TagError: - if chainErr != nil { - return buf.WriteString(chainErr.Error()) - } - return buf.WriteString("-") - default: - // Check if we have a value tag i.e.: "reqHeader:x-key" - switch { - case strings.HasPrefix(tag, TagReqHeader): - return buf.WriteString(c.Get(tag[10:])) - case strings.HasPrefix(tag, TagHeader): - return buf.WriteString(c.Get(tag[7:])) - case strings.HasPrefix(tag, TagRespHeader): - return buf.WriteString(c.GetRespHeader(tag[11:])) - case strings.HasPrefix(tag, TagQuery): - return buf.WriteString(c.Query(tag[6:])) - case strings.HasPrefix(tag, TagForm): - return buf.WriteString(c.FormValue(tag[5:])) - case strings.HasPrefix(tag, TagCookie): - return buf.WriteString(c.Cookies(tag[7:])) - case strings.HasPrefix(tag, TagLocals): - switch v := c.Locals(tag[7:]).(type) { - case []byte: - return buf.Write(v) - case string: - return buf.WriteString(v) - case nil: - return 0, nil - default: - return buf.WriteString(fmt.Sprintf("%v", v)) - } - } + // Loop over template parts execute dynamic parts and add fixed parts to the buffer + for i, logFunc := range logFunChain { + if logFunc == nil { + _, _ = buf.Write(templateChain[i]) + } else if templateChain[i] == nil { + _, err = logFunc(buf, c, data, "") + } else { + _, err = logFunc(buf, c, data, utils.UnsafeString(templateChain[i])) } - return 0, nil - }) + if err != nil { + break + } + } + // Also write errors to the buffer if err != nil { _, _ = buf.WriteString(err.Error()) diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index ba566d2c..eae71bb3 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -287,30 +287,59 @@ func Test_Logger_Data_Race(t *testing.T) { // go test -v -run=^$ -bench=Benchmark_Logger -benchmem -count=4 func Benchmark_Logger(b *testing.B) { - app := fiber.New() + benchSetup := func(bb *testing.B, app *fiber.App) { + h := app.Handler() - app.Use(New(Config{ - Format: "${bytesReceived} ${bytesSent} ${status}", - Output: io.Discard, - })) - app.Get("/", func(c *fiber.Ctx) error { - return c.SendString("Hello, World!") - }) + fctx := &fasthttp.RequestCtx{} + fctx.Request.Header.SetMethod("GET") + fctx.Request.SetRequestURI("/") - h := app.Handler() + bb.ReportAllocs() + bb.ResetTimer() - fctx := &fasthttp.RequestCtx{} - fctx.Request.Header.SetMethod("GET") - fctx.Request.SetRequestURI("/") + for n := 0; n < bb.N; n++ { + h(fctx) + } - b.ReportAllocs() - b.ResetTimer() - - for n := 0; n < b.N; n++ { - h(fctx) + utils.AssertEqual(bb, 200, fctx.Response.Header.StatusCode()) } - utils.AssertEqual(b, 200, fctx.Response.Header.StatusCode()) + b.Run("Base", func(bb *testing.B) { + app := fiber.New() + app.Use(New(Config{ + Format: "${bytesReceived} ${bytesSent} ${status}", + Output: io.Discard, + })) + app.Get("/", func(c *fiber.Ctx) error { + c.Set("test", "test") + return c.SendString("Hello, World!") + }) + benchSetup(bb, app) + }) + + b.Run("DefaultFormat", func(bb *testing.B) { + app := fiber.New() + app.Use(New(Config{ + Output: io.Discard, + })) + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + benchSetup(bb, app) + }) + + b.Run("WithTagParameter", func(bb *testing.B) { + app := fiber.New() + app.Use(New(Config{ + Format: "${bytesReceived} ${bytesSent} ${status} ${reqHeader:test}", + Output: io.Discard, + })) + app.Get("/", func(c *fiber.Ctx) error { + c.Set("test", "test") + return c.SendString("Hello, World!") + }) + benchSetup(bb, app) + }) } // go test -run Test_Response_Header @@ -383,3 +412,32 @@ func Test_ReqHeader_Header(t *testing.T) { utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) utils.AssertEqual(t, "Hello fiber!", buf.String()) } + +// go test -run Test_CustomTags +func Test_CustomTags(t *testing.T) { + customTag := "it is a custom tag" + + buf := bytebufferpool.Get() + defer bytebufferpool.Put(buf) + + app := fiber.New() + app.Use(New(Config{ + Format: "${custom_tag}", + CustomTags: map[string]LogFunc{ + "custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(customTag) + }, + }, + Output: buf, + })) + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello fiber!") + }) + reqHeaderReq := httptest.NewRequest("GET", "/", nil) + reqHeaderReq.Header.Add("test", "Hello fiber!") + resp, err := app.Test(reqHeaderReq) + + utils.AssertEqual(t, nil, err) + utils.AssertEqual(t, fiber.StatusOK, resp.StatusCode) + utils.AssertEqual(t, customTag, buf.String()) +} diff --git a/middleware/logger/tags.go b/middleware/logger/tags.go new file mode 100644 index 00000000..db72c43f --- /dev/null +++ b/middleware/logger/tags.go @@ -0,0 +1,208 @@ +package logger + +import ( + "fmt" + "strings" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/internal/bytebufferpool" +) + +// Logger variables +const ( + TagPid = "pid" + TagTime = "time" + TagReferer = "referer" + TagProtocol = "protocol" + TagPort = "port" + TagIP = "ip" + TagIPs = "ips" + TagHost = "host" + TagMethod = "method" + TagPath = "path" + TagURL = "url" + TagUA = "ua" + TagLatency = "latency" + TagStatus = "status" + TagResBody = "resBody" + TagReqHeaders = "reqHeaders" + TagQueryStringParams = "queryParams" + TagBody = "body" + TagBytesSent = "bytesSent" + TagBytesReceived = "bytesReceived" + TagRoute = "route" + TagError = "error" + // DEPRECATED: Use TagReqHeader instead + TagHeader = "header:" + TagReqHeader = "reqHeader:" + TagRespHeader = "respHeader:" + TagLocals = "locals:" + TagQuery = "query:" + TagForm = "form:" + TagCookie = "cookie:" + TagBlack = "black" + TagRed = "red" + TagGreen = "green" + TagYellow = "yellow" + TagBlue = "blue" + TagMagenta = "magenta" + TagCyan = "cyan" + TagWhite = "white" + TagReset = "reset" +) + +// createTagMap function merged the default with the custom tags +func createTagMap(cfg *Config) map[string]LogFunc { + // Set default tags + tagFunctions := map[string]LogFunc{ + TagReferer: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Get(fiber.HeaderReferer)) + }, + TagProtocol: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Protocol()) + }, + TagPort: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Port()) + }, + TagIP: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.IP()) + }, + TagIPs: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Get(fiber.HeaderXForwardedFor)) + }, + TagHost: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Hostname()) + }, + TagPath: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Path()) + }, + TagURL: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.OriginalURL()) + }, + TagUA: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Get(fiber.HeaderUserAgent)) + }, + TagBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.Write(c.Body()) + }, + TagBytesReceived: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return appendInt(buf, len(c.Request().Body())) + }, + TagBytesSent: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return appendInt(buf, len(c.Response().Body())) + }, + TagRoute: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Route().Path) + }, + TagResBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.Write(c.Response().Body()) + }, + TagReqHeaders: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + reqHeaders := make([]string, 0) + for k, v := range c.GetReqHeaders() { + reqHeaders = append(reqHeaders, k+"="+v) + } + return buf.Write([]byte(strings.Join(reqHeaders, "&"))) + }, + TagQueryStringParams: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Request().URI().QueryArgs().String()) + }, + + TagBlack: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Black) + }, + TagRed: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Red) + }, + TagGreen: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Green) + }, + TagYellow: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Yellow) + }, + TagBlue: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Blue) + }, + TagMagenta: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Magenta) + }, + TagCyan: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Cyan) + }, + TagWhite: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.White) + }, + TagReset: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.App().Config().ColorScheme.Reset) + }, + TagError: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + if data.ChainErr != nil { + return buf.WriteString(data.ChainErr.Error()) + } + return buf.WriteString("-") + }, + TagReqHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Get(extraParam)) + }, + TagHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Get(extraParam)) + }, + TagRespHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.GetRespHeader(extraParam)) + }, + TagQuery: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Query(extraParam)) + }, + TagForm: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.FormValue(extraParam)) + }, + TagCookie: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(c.Cookies(extraParam)) + }, + TagLocals: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + switch v := c.Locals(extraParam).(type) { + case []byte: + return buf.Write(v) + case string: + return buf.WriteString(v) + case nil: + return 0, nil + default: + return buf.WriteString(fmt.Sprintf("%v", v)) + } + }, + TagStatus: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + if cfg.enableColors { + colors := c.App().Config().ColorScheme + return buf.WriteString(fmt.Sprintf("%s %3d %s", statusColor(c.Response().StatusCode(), colors), c.Response().StatusCode(), colors.Reset)) + } + return appendInt(buf, c.Response().StatusCode()) + }, + TagMethod: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + if cfg.enableColors { + colors := c.App().Config().ColorScheme + return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) + } + return buf.WriteString(c.Method()) + }, + TagPid: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(data.Pid) + }, + TagLatency: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + latency := data.Stop.Sub(data.Start).Round(time.Millisecond) + return buf.WriteString(fmt.Sprintf("%7v", latency)) + }, + TagTime: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return buf.WriteString(data.Timestamp.Load().(string)) + }, + } + // merge with custom tags from user + if cfg.CustomTags != nil { + for k, v := range cfg.CustomTags { + tagFunctions[k] = v + } + } + + return tagFunctions +} diff --git a/middleware/logger/template_chain.go b/middleware/logger/template_chain.go new file mode 100644 index 00000000..ceb31a33 --- /dev/null +++ b/middleware/logger/template_chain.go @@ -0,0 +1,67 @@ +package logger + +import ( + "bytes" + "errors" + + "github.com/gofiber/fiber/v2/utils" +) + +// buildLogFuncChain analyzes the template and creates slices with the functions for execution and +// slices with the fixed parts of the template and the parameters +// +// fixParts contains the fixed parts of the template or parameters if a function is stored in the funcChain at this position +// funcChain contains for the parts which exist the functions for the dynamic parts +// funcChain and fixParts always have the same length and contain nil for the parts where no data is required in the chain, +// if a function exists for the part, a parameter for it can also exist in the fixParts slice +func buildLogFuncChain(cfg *Config, tagFunctions map[string]LogFunc) (fixParts [][]byte, funcChain []LogFunc, err error) { + // process flow is copied from the fasttemplate flow https://github.com/valyala/fasttemplate/blob/2a2d1afadadf9715bfa19683cdaeac8347e5d9f9/template.go#L23-L62 + templateB := utils.UnsafeBytes(cfg.Format) + startTagB := utils.UnsafeBytes(startTag) + endTagB := utils.UnsafeBytes(endTag) + paramSeparatorB := utils.UnsafeBytes(paramSeparator) + + for { + currentPos := bytes.Index(templateB, startTagB) + if currentPos < 0 { + // no starting tag found in the existing template part + break + } + // add fixed part + funcChain = append(funcChain, nil) + fixParts = append(fixParts, templateB[:currentPos]) + + templateB = templateB[currentPos+len(startTagB):] + currentPos = bytes.Index(templateB, endTagB) + if currentPos < 0 { + // cannot find end tag - just write it to the output. + funcChain = append(funcChain, nil) + fixParts = append(fixParts, startTagB) + break + } + // ## function block ## + // first check for tags with parameters + if index := bytes.Index(templateB[:currentPos], paramSeparatorB); index != -1 { + if logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:index+1])]; ok { + funcChain = append(funcChain, logFunc) + // add param to the fixParts + fixParts = append(fixParts, templateB[index+1:currentPos]) + } else { + return nil, nil, errors.New("No parameter found in \"" + utils.UnsafeString(templateB[:currentPos]) + "\"") + } + } else if logFunc, ok := tagFunctions[utils.UnsafeString(templateB[:currentPos])]; ok { + // add functions without parameter + funcChain = append(funcChain, logFunc) + fixParts = append(fixParts, nil) + } + // ## function block end ## + + // reduce the template string + templateB = templateB[currentPos+len(endTagB):] + } + // set the rest + funcChain = append(funcChain, nil) + fixParts = append(fixParts, templateB) + + return +} From 3d39b82aad8be450cfbdc5352cdb36f5a9d76bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Werner?= Date: Fri, 18 Nov 2022 13:23:57 +0100 Subject: [PATCH 22/32] logger: adjustment for the new Done function - use nil instead of empty func --- middleware/logger/config.go | 4 ++-- middleware/logger/logger.go | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 685ce618..2839a376 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -20,7 +20,7 @@ type Config struct { // Done is a function that is called after the log string for a request is written to Output, // and pass the log string as parameter. // - // Optional. Default: a function that does nothing. + // Optional. Default: nil Done func(c *fiber.Ctx, logString []byte) // tagFunctions defines the custom tag action @@ -69,7 +69,7 @@ type LogFunc func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extr // ConfigDefault is the default config var ConfigDefault = Config{ Next: nil, - Done: func(c *fiber.Ctx, logString []byte) {}, + Done: nil, Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", TimeFormat: "15:04:05", TimeZone: "Local", diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 41d2564a..824d706b 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -156,7 +156,9 @@ func New(config ...Config) fiber.Handler { // Write buffer to output _, _ = cfg.Output.Write(buf.Bytes()) - cfg.Done(c, buf.Bytes()) + if cfg.Done != nil { + cfg.Done(c, buf.Bytes()) + } // Put buffer back to pool bytebufferpool.Put(buf) @@ -194,7 +196,9 @@ func New(config ...Config) fiber.Handler { } mu.Unlock() - cfg.Done(c, buf.Bytes()) + if cfg.Done != nil { + cfg.Done(c, buf.Bytes()) + } // Put buffer back to pool bytebufferpool.Put(buf) From 92ce4aac7749485b3ae64a0e37828d73e191559a Mon Sep 17 00:00:00 2001 From: Andy Pan Date: Fri, 18 Nov 2022 21:29:53 +0800 Subject: [PATCH 23/32] :memo: Update middleware/logger docs (#2224) * :memo: Update middleware/logger docs Update #2219 * Update middleware/logger/README.md Co-authored-by: RW --- middleware/logger/README.md | 47 ++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/middleware/logger/README.md b/middleware/logger/README.md index f6a70117..bc3c70ad 100644 --- a/middleware/logger/README.md +++ b/middleware/logger/README.md @@ -87,6 +87,20 @@ app.Use(logger.New(logger.Config{ })) ``` +### Callback after log is written + +```go +app.Use(logger.New(logger.Config{ + TimeFormat: time.RFC3339Nano, + TimeZone: "Asia/Shanghai", + Done: func(c *fiber.Ctx, logString []byte) { + if c.Response().StatusCode() != fiber.StatusOK { + reporter.SendToSlack(logString) + } + }, +})) +``` + ## Config ```go // Config defines the config for middleware. @@ -96,16 +110,22 @@ type Config struct { // Optional. Default: nil Next func(c *fiber.Ctx) bool - // CustomTags defines the custom tag action + // Done is a function that is called after the log string for a request is written to Output, + // and pass the log string as parameter. // - // Optional. Default: map[string]LogFunc{} + // Optional. Default: nil + Done func(c *fiber.Ctx, logString []byte) + + // tagFunctions defines the custom tag action + // + // Optional. Default: map[string]LogFunc CustomTags map[string]LogFunc - + // Format defines the logging tags // // Optional. Default: [${time}] ${status} - ${latency} ${method} ${path}\n Format string - + // TimeFormat https://programming.guide/go/format-parse-string-time-date-example.html // // Optional. Default: 15:04:05 @@ -123,20 +143,25 @@ type Config struct { // Output is a writer where logs are written // - // Default: os.Stderr + // Default: os.Stdout Output io.Writer + + enableColors bool + enableLatency bool + timeZoneLocation *time.Location } ``` ## Default Config ```go var ConfigDefault = Config{ - Next: nil, - Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", - TimeFormat: "15:04:05", - TimeZone: "Local", - TimeInterval: 500 * time.Millisecond, - Output: os.Stderr, + Next: nil, + Done: nil, + Format: "[${time}] ${status} - ${latency} ${method} ${path}\n", + TimeFormat: "15:04:05", + TimeZone: "Local", + TimeInterval: 500 * time.Millisecond, + Output: os.Stdout, } ``` From e4b3b5c70899d62db42d82b318beb583a4fefe91 Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 18 Nov 2022 15:32:56 +0100 Subject: [PATCH 24/32] Improve interface for custom logger func (#2225) * - logger: fix custom tag - use real bytebufferpool dependency instead of the internal * - logger: fix custom tag - use real bytebufferpool dependency instead of the internal * - logger: fix custom tag - use real bytebufferpool dependency instead of the internal * - logger: fix custom tag - use real bytebufferpool dependency instead of the internal --- ctx.go | 2 +- ctx_test.go | 2 +- go.mod | 2 +- helpers.go | 2 +- hooks_test.go | 2 +- internal/bytebufferpool/LICENSE | 22 ---- internal/bytebufferpool/bytebuffer.go | 111 ------------------ internal/bytebufferpool/pool.go | 151 ------------------------ middleware/etag/etag.go | 2 +- middleware/logger/README.md | 12 +- middleware/logger/config.go | 16 ++- middleware/logger/logger.go | 11 +- middleware/logger/logger_test.go | 6 +- middleware/logger/tags.go | 163 +++++++++++++------------- 14 files changed, 114 insertions(+), 390 deletions(-) delete mode 100644 internal/bytebufferpool/LICENSE delete mode 100644 internal/bytebufferpool/bytebuffer.go delete mode 100644 internal/bytebufferpool/pool.go diff --git a/ctx.go b/ctx.go index 9fb8404a..e07ce30d 100644 --- a/ctx.go +++ b/ctx.go @@ -24,10 +24,10 @@ import ( "text/template" "time" - "github.com/gofiber/fiber/v2/internal/bytebufferpool" "github.com/gofiber/fiber/v2/internal/dictpool" "github.com/gofiber/fiber/v2/internal/schema" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) diff --git a/ctx_test.go b/ctx_test.go index 5d27c34b..0d208980 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -29,9 +29,9 @@ import ( "text/template" "time" - "github.com/gofiber/fiber/v2/internal/bytebufferpool" "github.com/gofiber/fiber/v2/internal/storage/memory" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) diff --git a/go.mod b/go.mod index d9239e49..64bcbc40 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 + github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.41.0 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab ) @@ -14,6 +15,5 @@ require ( github.com/andybalholm/brotli v1.0.4 // indirect github.com/klauspost/compress v1.15.9 // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect ) diff --git a/helpers.go b/helpers.go index 34b9350b..8df13878 100644 --- a/helpers.go +++ b/helpers.go @@ -19,8 +19,8 @@ import ( "time" "unsafe" - "github.com/gofiber/fiber/v2/internal/bytebufferpool" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" ) diff --git a/hooks_test.go b/hooks_test.go index e39d269c..626a1a4b 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - "github.com/gofiber/fiber/v2/internal/bytebufferpool" "github.com/gofiber/fiber/v2/utils" + "github.com/valyala/bytebufferpool" ) var testSimpleHandler = func(c *Ctx) error { diff --git a/internal/bytebufferpool/LICENSE b/internal/bytebufferpool/LICENSE deleted file mode 100644 index f7c935c2..00000000 --- a/internal/bytebufferpool/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Aliaksandr Valialkin, VertaMedia - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/internal/bytebufferpool/bytebuffer.go b/internal/bytebufferpool/bytebuffer.go deleted file mode 100644 index 07a055a2..00000000 --- a/internal/bytebufferpool/bytebuffer.go +++ /dev/null @@ -1,111 +0,0 @@ -package bytebufferpool - -import "io" - -// ByteBuffer provides byte buffer, which can be used for minimizing -// memory allocations. -// -// ByteBuffer may be used with functions appending data to the given []byte -// slice. See example code for details. -// -// Use Get for obtaining an empty byte buffer. -type ByteBuffer struct { - - // B is a byte buffer to use in append-like workloads. - // See example code for details. - B []byte -} - -// Len returns the size of the byte buffer. -func (b *ByteBuffer) Len() int { - return len(b.B) -} - -// ReadFrom implements io.ReaderFrom. -// -// The function appends all the data read from r to b. -func (b *ByteBuffer) ReadFrom(r io.Reader) (int64, error) { - p := b.B - nStart := int64(len(p)) - nMax := int64(cap(p)) - n := nStart - if nMax == 0 { - nMax = 64 - p = make([]byte, nMax) - } else { - p = p[:nMax] - } - for { - if n == nMax { - nMax *= 2 - bNew := make([]byte, nMax) - copy(bNew, p) - p = bNew - } - nn, err := r.Read(p[n:]) - n += int64(nn) - if err != nil { - b.B = p[:n] - n -= nStart - if err == io.EOF { - return n, nil - } - return n, err - } - } -} - -// WriteTo implements io.WriterTo. -func (b *ByteBuffer) WriteTo(w io.Writer) (int64, error) { - n, err := w.Write(b.B) - return int64(n), err -} - -// Bytes returns b.B, i.e. all the bytes accumulated in the buffer. -// -// The purpose of this function is bytes.Buffer compatibility. -func (b *ByteBuffer) Bytes() []byte { - return b.B -} - -// Write implements io.Writer - it appends p to ByteBuffer.B -func (b *ByteBuffer) Write(p []byte) (int, error) { - b.B = append(b.B, p...) - return len(p), nil -} - -// WriteByte appends the byte c to the buffer. -// -// The purpose of this function is bytes.Buffer compatibility. -// -// The function always returns nil. -func (b *ByteBuffer) WriteByte(c byte) error { - b.B = append(b.B, c) - return nil -} - -// WriteString appends s to ByteBuffer.B. -func (b *ByteBuffer) WriteString(s string) (int, error) { - b.B = append(b.B, s...) - return len(s), nil -} - -// Set sets ByteBuffer.B to p. -func (b *ByteBuffer) Set(p []byte) { - b.B = append(b.B[:0], p...) -} - -// SetString sets ByteBuffer.B to s. -func (b *ByteBuffer) SetString(s string) { - b.B = append(b.B[:0], s...) -} - -// String returns string representation of ByteBuffer.B. -func (b *ByteBuffer) String() string { - return string(b.B) -} - -// Reset makes ByteBuffer.B empty. -func (b *ByteBuffer) Reset() { - b.B = b.B[:0] -} diff --git a/internal/bytebufferpool/pool.go b/internal/bytebufferpool/pool.go deleted file mode 100644 index 8bb4134d..00000000 --- a/internal/bytebufferpool/pool.go +++ /dev/null @@ -1,151 +0,0 @@ -package bytebufferpool - -import ( - "sort" - "sync" - "sync/atomic" -) - -const ( - minBitSize = 6 // 2**6=64 is a CPU cache line size - steps = 20 - - minSize = 1 << minBitSize - maxSize = 1 << (minBitSize + steps - 1) - - calibrateCallsThreshold = 42000 - maxPercentile = 0.95 -) - -// Pool represents byte buffer pool. -// -// Distinct pools may be used for distinct types of byte buffers. -// Properly determined byte buffer types with their own pools may help reducing -// memory waste. -type Pool struct { - calls [steps]uint64 - calibrating uint64 - - defaultSize uint64 - maxSize uint64 - - pool sync.Pool -} - -var defaultPool Pool - -// Get returns an empty byte buffer from the pool. -// -// Got byte buffer may be returned to the pool via Put call. -// This reduces the number of memory allocations required for byte buffer -// management. -func Get() *ByteBuffer { return defaultPool.Get() } - -// Get returns new byte buffer with zero length. -// -// The byte buffer may be returned to the pool via Put after the use -// in order to minimize GC overhead. -func (p *Pool) Get() *ByteBuffer { - v := p.pool.Get() - if v != nil { - return v.(*ByteBuffer) - } - return &ByteBuffer{ - B: make([]byte, 0, atomic.LoadUint64(&p.defaultSize)), - } -} - -// Put returns byte buffer to the pool. -// -// ByteBuffer.B mustn't be touched after returning it to the pool. -// Otherwise data races will occur. -func Put(b *ByteBuffer) { defaultPool.Put(b) } - -// Put releases byte buffer obtained via Get to the pool. -// -// The buffer mustn't be accessed after returning to the pool. -func (p *Pool) Put(b *ByteBuffer) { - idx := index(len(b.B)) - - if atomic.AddUint64(&p.calls[idx], 1) > calibrateCallsThreshold { - p.calibrate() - } - - maxSize := int(atomic.LoadUint64(&p.maxSize)) - if maxSize == 0 || cap(b.B) <= maxSize { - b.Reset() - p.pool.Put(b) - } -} - -func (p *Pool) calibrate() { - if !atomic.CompareAndSwapUint64(&p.calibrating, 0, 1) { - return - } - - a := make(callSizes, 0, steps) - var callsSum uint64 - for i := uint64(0); i < steps; i++ { - calls := atomic.SwapUint64(&p.calls[i], 0) - callsSum += calls - a = append(a, callSize{ - calls: calls, - size: minSize << i, - }) - } - sort.Sort(a) - - defaultSize := a[0].size - maxSize := defaultSize - - maxSum := uint64(float64(callsSum) * maxPercentile) - callsSum = 0 - for i := 0; i < steps; i++ { - if callsSum > maxSum { - break - } - callsSum += a[i].calls - size := a[i].size - if size > maxSize { - maxSize = size - } - } - - atomic.StoreUint64(&p.defaultSize, defaultSize) - atomic.StoreUint64(&p.maxSize, maxSize) - - atomic.StoreUint64(&p.calibrating, 0) -} - -type callSize struct { - calls uint64 - size uint64 -} - -type callSizes []callSize - -func (ci callSizes) Len() int { - return len(ci) -} - -func (ci callSizes) Less(i, j int) bool { - return ci[i].calls > ci[j].calls -} - -func (ci callSizes) Swap(i, j int) { - ci[i], ci[j] = ci[j], ci[i] -} - -func index(n int) int { - n-- - n >>= minBitSize - idx := 0 - for n > 0 { - n >>= 1 - idx++ - } - if idx >= steps { - idx = steps - 1 - } - return idx -} diff --git a/middleware/etag/etag.go b/middleware/etag/etag.go index 153f84d8..72ad71ba 100644 --- a/middleware/etag/etag.go +++ b/middleware/etag/etag.go @@ -5,7 +5,7 @@ import ( "hash/crc32" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/internal/bytebufferpool" + "github.com/valyala/bytebufferpool" ) var ( diff --git a/middleware/logger/README.md b/middleware/logger/README.md index bc3c70ad..60bb2eca 100644 --- a/middleware/logger/README.md +++ b/middleware/logger/README.md @@ -40,7 +40,7 @@ app.Use(logger.New()) ```go app.Use(logger.New(logger.Config{ - Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", + Format: "[${ip}]:${port} ${status} - ${method} ${path}\n", })) ``` @@ -80,8 +80,8 @@ app.Use(logger.New(logger.Config{ ```go app.Use(logger.New(logger.Config{ CustomTags: map[string]logger.LogFunc{ - "custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString("it is a custom tag") + "custom_tag": func(output logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) { + return output.WriteString("it is a custom tag") }, }, })) @@ -145,11 +145,9 @@ type Config struct { // // Default: os.Stdout Output io.Writer - - enableColors bool - enableLatency bool - timeZoneLocation *time.Location } + +type LogFunc func(buf logger.Buffer, c *fiber.Ctx, data *logger.Data, extraParam string) (int, error) ``` ## Default Config diff --git a/middleware/logger/config.go b/middleware/logger/config.go index 2839a376..e9766db6 100644 --- a/middleware/logger/config.go +++ b/middleware/logger/config.go @@ -7,7 +7,6 @@ import ( "time" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/internal/bytebufferpool" ) // Config defines the config for middleware. @@ -64,7 +63,20 @@ const ( paramSeparator = ":" ) -type LogFunc func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) +type Buffer interface { + Len() int + ReadFrom(r io.Reader) (int64, error) + WriteTo(w io.Writer) (int64, error) + Bytes() []byte + Write(p []byte) (int, error) + WriteByte(c byte) error + WriteString(s string) (int, error) + Set(p []byte) + SetString(s string) + String() string +} + +type LogFunc func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) // ConfigDefault is the default config var ConfigDefault = Config{ diff --git a/middleware/logger/logger.go b/middleware/logger/logger.go index 824d706b..1fa07439 100644 --- a/middleware/logger/logger.go +++ b/middleware/logger/logger.go @@ -13,9 +13,8 @@ import ( "github.com/gofiber/fiber/v2/utils" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" - - "github.com/gofiber/fiber/v2/internal/bytebufferpool" ) // New creates a new middleware handler @@ -207,8 +206,8 @@ func New(config ...Config) fiber.Handler { } } -func appendInt(buf *bytebufferpool.ByteBuffer, v int) (int, error) { - old := len(buf.B) - buf.B = fasthttp.AppendUint(buf.B, v) - return len(buf.B) - old, nil +func appendInt(output Buffer, v int) (int, error) { + old := output.Len() + output.Set(fasthttp.AppendUint(output.Bytes(), v)) + return output.Len() - old, nil } diff --git a/middleware/logger/logger_test.go b/middleware/logger/logger_test.go index eae71bb3..3ca82c9f 100644 --- a/middleware/logger/logger_test.go +++ b/middleware/logger/logger_test.go @@ -11,10 +11,10 @@ import ( "sync" "testing" + "github.com/valyala/bytebufferpool" "github.com/valyala/fasthttp" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/internal/bytebufferpool" "github.com/gofiber/fiber/v2/middleware/requestid" "github.com/gofiber/fiber/v2/utils" ) @@ -424,8 +424,8 @@ func Test_CustomTags(t *testing.T) { app.Use(New(Config{ Format: "${custom_tag}", CustomTags: map[string]LogFunc{ - "custom_tag": func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(customTag) + "custom_tag": func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(customTag) }, }, Output: buf, diff --git a/middleware/logger/tags.go b/middleware/logger/tags.go index db72c43f..854f22e2 100644 --- a/middleware/logger/tags.go +++ b/middleware/logger/tags.go @@ -6,7 +6,6 @@ import ( "time" "github.com/gofiber/fiber/v2" - "github.com/gofiber/fiber/v2/internal/bytebufferpool" ) // Logger variables @@ -56,145 +55,145 @@ const ( func createTagMap(cfg *Config) map[string]LogFunc { // Set default tags tagFunctions := map[string]LogFunc{ - TagReferer: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Get(fiber.HeaderReferer)) + TagReferer: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Get(fiber.HeaderReferer)) }, - TagProtocol: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Protocol()) + TagProtocol: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Protocol()) }, - TagPort: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Port()) + TagPort: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Port()) }, - TagIP: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.IP()) + TagIP: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.IP()) }, - TagIPs: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Get(fiber.HeaderXForwardedFor)) + TagIPs: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Get(fiber.HeaderXForwardedFor)) }, - TagHost: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Hostname()) + TagHost: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Hostname()) }, - TagPath: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Path()) + TagPath: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Path()) }, - TagURL: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.OriginalURL()) + TagURL: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.OriginalURL()) }, - TagUA: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Get(fiber.HeaderUserAgent)) + TagUA: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Get(fiber.HeaderUserAgent)) }, - TagBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.Write(c.Body()) + TagBody: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.Write(c.Body()) }, - TagBytesReceived: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return appendInt(buf, len(c.Request().Body())) + TagBytesReceived: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return appendInt(output, len(c.Request().Body())) }, - TagBytesSent: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return appendInt(buf, len(c.Response().Body())) + TagBytesSent: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return appendInt(output, len(c.Response().Body())) }, - TagRoute: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Route().Path) + TagRoute: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Route().Path) }, - TagResBody: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.Write(c.Response().Body()) + TagResBody: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.Write(c.Response().Body()) }, - TagReqHeaders: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + TagReqHeaders: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { reqHeaders := make([]string, 0) for k, v := range c.GetReqHeaders() { reqHeaders = append(reqHeaders, k+"="+v) } - return buf.Write([]byte(strings.Join(reqHeaders, "&"))) + return output.Write([]byte(strings.Join(reqHeaders, "&"))) }, - TagQueryStringParams: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Request().URI().QueryArgs().String()) + TagQueryStringParams: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Request().URI().QueryArgs().String()) }, - TagBlack: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.App().Config().ColorScheme.Black) + TagBlack: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.App().Config().ColorScheme.Black) }, - TagRed: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.App().Config().ColorScheme.Red) + TagRed: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.App().Config().ColorScheme.Red) }, - TagGreen: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.App().Config().ColorScheme.Green) + TagGreen: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.App().Config().ColorScheme.Green) }, - TagYellow: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.App().Config().ColorScheme.Yellow) + TagYellow: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.App().Config().ColorScheme.Yellow) }, - TagBlue: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.App().Config().ColorScheme.Blue) + TagBlue: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.App().Config().ColorScheme.Blue) }, - TagMagenta: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.App().Config().ColorScheme.Magenta) + TagMagenta: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.App().Config().ColorScheme.Magenta) }, - TagCyan: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.App().Config().ColorScheme.Cyan) + TagCyan: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.App().Config().ColorScheme.Cyan) }, - TagWhite: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.App().Config().ColorScheme.White) + TagWhite: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.App().Config().ColorScheme.White) }, - TagReset: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.App().Config().ColorScheme.Reset) + TagReset: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.App().Config().ColorScheme.Reset) }, - TagError: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + TagError: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { if data.ChainErr != nil { - return buf.WriteString(data.ChainErr.Error()) + return output.WriteString(data.ChainErr.Error()) } - return buf.WriteString("-") + return output.WriteString("-") }, - TagReqHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Get(extraParam)) + TagReqHeader: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Get(extraParam)) }, - TagHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Get(extraParam)) + TagHeader: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Get(extraParam)) }, - TagRespHeader: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.GetRespHeader(extraParam)) + TagRespHeader: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.GetRespHeader(extraParam)) }, - TagQuery: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Query(extraParam)) + TagQuery: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Query(extraParam)) }, - TagForm: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.FormValue(extraParam)) + TagForm: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.FormValue(extraParam)) }, - TagCookie: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(c.Cookies(extraParam)) + TagCookie: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(c.Cookies(extraParam)) }, - TagLocals: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + TagLocals: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { switch v := c.Locals(extraParam).(type) { case []byte: - return buf.Write(v) + return output.Write(v) case string: - return buf.WriteString(v) + return output.WriteString(v) case nil: return 0, nil default: - return buf.WriteString(fmt.Sprintf("%v", v)) + return output.WriteString(fmt.Sprintf("%v", v)) } }, - TagStatus: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + TagStatus: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { if cfg.enableColors { colors := c.App().Config().ColorScheme - return buf.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(buf, c.Response().StatusCode()) + return appendInt(output, c.Response().StatusCode()) }, - TagMethod: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + TagMethod: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { if cfg.enableColors { colors := c.App().Config().ColorScheme - return buf.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) + return output.WriteString(fmt.Sprintf("%s %-7s %s", methodColor(c.Method(), colors), c.Method(), colors.Reset)) } - return buf.WriteString(c.Method()) + return output.WriteString(c.Method()) }, - TagPid: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(data.Pid) + TagPid: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(data.Pid) }, - TagLatency: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + TagLatency: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { latency := data.Stop.Sub(data.Start).Round(time.Millisecond) - return buf.WriteString(fmt.Sprintf("%7v", latency)) + return output.WriteString(fmt.Sprintf("%7v", latency)) }, - TagTime: func(buf *bytebufferpool.ByteBuffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { - return buf.WriteString(data.Timestamp.Load().(string)) + TagTime: func(output Buffer, c *fiber.Ctx, data *Data, extraParam string) (int, error) { + return output.WriteString(data.Timestamp.Load().(string)) }, } // merge with custom tags from user From c8baa613aebe3d11cd3850f4f632cb8c6b69779a Mon Sep 17 00:00:00 2001 From: RW Date: Fri, 18 Nov 2022 16:21:47 +0100 Subject: [PATCH 25/32] prepare release v2.40.0 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 55896ae7..0fee4083 100644 --- a/app.go +++ b/app.go @@ -27,7 +27,7 @@ import ( ) // Version of current fiber package -const Version = "2.39.0" +const Version = "2.40.0" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From 70403aa120a3d084753fb05d301a85ecb154daab Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 20 Nov 2022 14:04:46 +0100 Subject: [PATCH 26/32] Update security.yml --- .github/workflows/security.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 854a1d57..e7d8bade 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -1,4 +1,9 @@ -on: [push, pull_request] +on: + push: + branches: + - master + - main + pull_request: name: Security jobs: Gosec: @@ -9,4 +14,4 @@ jobs: - name: Run Gosec uses: securego/gosec@master with: - args: -exclude-dir=internal/*/ ./... \ No newline at end of file + args: -exclude-dir=internal/*/ ./... From fc35dbf9272f94fd81dd9c648f90d151354217fc Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 20 Nov 2022 14:05:03 +0100 Subject: [PATCH 27/32] Update linter.yml --- .github/workflows/linter.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 26492c59..4397113a 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -1,4 +1,9 @@ -on: [push, pull_request] +on: + push: + branches: + - master + - main + pull_request: name: Linter jobs: Golint: From 82db2963be7f1c9bf627ae473d6f37bab0cb65e0 Mon Sep 17 00:00:00 2001 From: RW Date: Sun, 20 Nov 2022 14:26:32 +0100 Subject: [PATCH 28/32] Update vulncheck.yml --- .github/workflows/vulncheck.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/vulncheck.yml b/.github/workflows/vulncheck.yml index 061ccd56..bddf020f 100644 --- a/.github/workflows/vulncheck.yml +++ b/.github/workflows/vulncheck.yml @@ -1,4 +1,9 @@ -on: [push, pull_request_target] +on: + push: + branches: + - master + - main + pull_request: name: Vulnerability Check jobs: Security: From 291fb6e444e8c6d058fd7d27a7c96e4b869d6b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Sun, 20 Nov 2022 16:26:42 +0300 Subject: [PATCH 29/32] :bug: bug: fix mounting when mount prefix is `/` (#2227) --- mount.go | 16 ++++++++++------ mount_test.go | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/mount.go b/mount.go index f7df3a97..abb4a739 100644 --- a/mount.go +++ b/mount.go @@ -44,8 +44,10 @@ func (app *App) Mount(prefix string, fiber *App) Router { // Support for configs of mounted-apps and sub-mounted-apps for mountedPrefixes, subApp := range fiber.mountFields.appList { - subApp.mountFields.mountPath = prefix + mountedPrefixes - app.mountFields.appList[prefix+mountedPrefixes] = subApp + path := getGroupPath(prefix, mountedPrefixes) + + subApp.mountFields.mountPath = path + app.mountFields.appList[path] = subApp } // Execute onMount hooks @@ -68,8 +70,10 @@ func (grp *Group) Mount(prefix string, fiber *App) Router { // Support for configs of mounted-apps and sub-mounted-apps for mountedPrefixes, subApp := range fiber.mountFields.appList { - subApp.mountFields.mountPath = groupPath + mountedPrefixes - grp.app.mountFields.appList[groupPath+mountedPrefixes] = subApp + path := getGroupPath(groupPath, mountedPrefixes) + + subApp.mountFields.mountPath = path + grp.app.mountFields.appList[path] = subApp } // Execute onMount hooks @@ -105,7 +109,7 @@ func (app *App) appendSubAppLists(appList map[string]*App, parent ...string) { } if len(parent) > 0 { - prefix = parent[0] + prefix + prefix = getGroupPath(parent[0], prefix) } if _, ok := app.mountFields.appList[prefix]; !ok { @@ -129,7 +133,7 @@ func (app *App) addSubAppsRoutes(appList map[string]*App, parent ...string) { } if len(parent) > 0 { - prefix = parent[0] + prefix + prefix = getGroupPath(parent[0], prefix) } // add routes diff --git a/mount_test.go b/mount_test.go index 1e944d09..d53ea062 100644 --- a/mount_test.go +++ b/mount_test.go @@ -29,6 +29,25 @@ func Test_App_Mount(t *testing.T) { utils.AssertEqual(t, uint32(2), app.handlersCount) } +func Test_App_Mount_RootPath_Nested(t *testing.T) { + app := New() + dynamic := New() + apiserver := New() + + apiroutes := apiserver.Group("/v1") + apiroutes.Get("/home", func(c *Ctx) error { + return c.SendString("home") + }) + + dynamic.Mount("/api", apiserver) + app.Mount("/", dynamic) + + resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/v1/home", nil)) + utils.AssertEqual(t, nil, err, "app.Test(req)") + utils.AssertEqual(t, 200, resp.StatusCode, "Status code") + utils.AssertEqual(t, uint32(2), app.handlersCount) +} + // go test -run Test_App_Mount_Nested func Test_App_Mount_Nested(t *testing.T) { app := New() From 0d038cba461950018635de7c032536191affc90f Mon Sep 17 00:00:00 2001 From: RW Date: Mon, 21 Nov 2022 08:54:34 +0100 Subject: [PATCH 30/32] fix typo --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 0fee4083..178c9872 100644 --- a/app.go +++ b/app.go @@ -385,7 +385,7 @@ type Config struct { // RequestMethods provides customizibility for HTTP methods. You can add/remove methods as you wish. // - // Optional. Defaukt: DefaultMethods + // Optional. Default: DefaultMethods RequestMethods []string } From 8952d170fe8bcaae9273b78455b6d7dfee4930d9 Mon Sep 17 00:00:00 2001 From: RW Date: Wed, 23 Nov 2022 09:05:31 +0100 Subject: [PATCH 31/32] prepare release v2.40.1 --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index 178c9872..0c48cebb 100644 --- a/app.go +++ b/app.go @@ -27,7 +27,7 @@ import ( ) // Version of current fiber package -const Version = "2.40.0" +const Version = "2.40.1" // Handler defines a function to serve HTTP requests. type Handler = func(*Ctx) error From f8457f2eea45ffb20ff040142f85590e5a429e6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Nov 2022 16:32:17 +0100 Subject: [PATCH 32/32] Bump github.com/valyala/fasthttp from 1.41.0 to 1.42.0 (#2237) Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.41.0 to 1.42.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.41.0...v1.42.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 64bcbc40..634a4674 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/mattn/go-isatty v0.0.16 github.com/mattn/go-runewidth v0.0.14 github.com/valyala/bytebufferpool v1.0.0 - github.com/valyala/fasthttp v1.41.0 + github.com/valyala/fasthttp v1.42.0 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab ) diff --git a/go.sum b/go.sum index b84535bd..546eef52 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY= -github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= +github.com/valyala/fasthttp v1.42.0 h1:LBMyqvJR8DEBgN79oI8dGbkuj5Lm9jbHESxH131TTN8= +github.com/valyala/fasthttp v1.42.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=