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
-## ⭐️ 星星数增长情况
+## ⭐️ Star 数增长情况
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=