From 70a06c5887992c24f9a89624abde66e0b6ed46b6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:50:34 +0300 Subject: [PATCH 1/9] build(deps): bump github.com/tinylib/msgp from 1.2.4 to 1.2.5 (#3240) Bumps [github.com/tinylib/msgp](https://github.com/tinylib/msgp) from 1.2.4 to 1.2.5. - [Release notes](https://github.com/tinylib/msgp/releases) - [Commits](https://github.com/tinylib/msgp/compare/v1.2.4...v1.2.5) --- updated-dependencies: - dependency-name: github.com/tinylib/msgp dependency-type: direct:production update-type: version-update:semver-patch ... 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 f8fa9006..a17d0b1a 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/mattn/go-colorable v0.1.13 github.com/mattn/go-isatty v0.0.20 github.com/stretchr/testify v1.10.0 - github.com/tinylib/msgp v1.2.4 + github.com/tinylib/msgp v1.2.5 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.57.0 ) diff --git a/go.sum b/go.sum index f3fda291..293e2d6c 100644 --- a/go.sum +++ b/go.sum @@ -23,8 +23,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tinylib/msgp v1.2.4 h1:yLFeUGostXXSGW5vxfT5dXG/qzkn4schv2I7at5+hVU= -github.com/tinylib/msgp v1.2.4/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= +github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po= +github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= 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.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg= From 27cfd3c8cd2e596d7bca5f2f2efe9ad92f2a1a4d Mon Sep 17 00:00:00 2001 From: Jinquan Wang <35188480+wangjq4214@users.noreply.github.com> Date: Tue, 10 Dec 2024 17:12:33 +0800 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=94=A5=20feat:=20Add=20support=20for?= =?UTF-8?q?=20AutoTLS=20/=20ACME=20(#3201)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add a simple support for app.Listen * fix: fix the nil access error * chore: add test case for simple tls * fix: align the struct * chore: change the test case can't passed and not chack the file yet * fix: use TLS1.2 min * Fix lint issues * Fix call to os.MkdirTemp * Fix test check order * Update unit-tests for ACME * Update docs * Fix identation of whats_new examples * More updates to docs * Remove ACME tests. Add check for tlsConfig * Add ACME section to whats_new docs * Update docs/whats_new.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Update fiber.md * Update whats_new.md --------- Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Co-authored-by: Juan Calderon-Perez Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- docs/api/fiber.md | 22 ++- docs/whats_new.md | 430 ++++++++++++++++++++++++---------------------- go.mod | 1 + go.sum | 2 + listen.go | 16 +- 5 files changed, 262 insertions(+), 209 deletions(-) diff --git a/docs/api/fiber.md b/docs/api/fiber.md index 2c50339d..a34a5d94 100644 --- a/docs/api/fiber.md +++ b/docs/api/fiber.md @@ -114,8 +114,9 @@ app.Listen(":8080", fiber.ListenConfig{ | ListenerAddrFunc | `func(addr net.Addr)` | Allows accessing and customizing `net.Listener`. | `nil` | | ListenerNetwork | `string` | Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only). WARNING: When prefork is set to true, only "tcp4" and "tcp6" can be chosen. | `tcp4` | | OnShutdownError | `func(err error)` | Allows to customize error behavior when gracefully shutting down the server by given signal. Prints error with `log.Fatalf()` | `nil` | -| OnShutdownSuccess | `func()` | Allows customizing success behavior when gracefully shutting down the server by given signal. | `nil` | +| OnShutdownSuccess | `func()` | Allows customizing success behavior when gracefully shutting down the server by given signal. | `nil` | | TLSConfigFunc | `func(tlsConfig *tls.Config)` | Allows customizing `tls.Config` as you want. | `nil` | +| AutoCertManager | `func(tlsConfig *tls.Config)` | Manages TLS certificates automatically using the ACME protocol. Enables integration with Let's Encrypt or other ACME-compatible providers. | `nil` | ### Listen @@ -166,6 +167,25 @@ app.Listen(":443", fiber.ListenConfig{CertClientFile: "./ca-chain-cert.pem"}) app.Listen(":443", fiber.ListenConfig{CertFile: "./cert.pem", CertKeyFile: "./cert.key", CertClientFile: "./ca-chain-cert.pem"}) ``` +#### TLS AutoCert support (ACME / Let's Encrypt) + +Provides automatic access to certificates management from Let's Encrypt and any other ACME-based providers. + +```go title="Examples" +// Certificate manager +certManager := &autocert.Manager{ + Prompt: autocert.AcceptTOS, + // Replace with your domain name + HostPolicy: autocert.HostWhitelist("example.com"), + // Folder to store the certificates + Cache: autocert.DirCache("./certs"), +} + +app.Listen(":444", fiber.ListenConfig{ + AutoCertManager: certManager, +}) +``` + ### Listener You can pass your own [`net.Listener`](https://pkg.go.dev/net/#Listener) using the `Listener` method. This method can be used to enable **TLS/HTTPS** with a custom tls.Config. diff --git a/docs/whats_new.md b/docs/whats_new.md index 3221f5dd..bfc6f25c 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -130,6 +130,25 @@ In this example, a custom context `CustomCtx` is created with an additional meth +#### TLS AutoCert support (ACME / Let's Encrypt) + +We have added native support for automatic certificates management from Let's Encrypt and any other ACME-based providers. + +```go +// Certificate manager +certManager := &autocert.Manager{ + Prompt: autocert.AcceptTOS, + // Replace with your domain name + HostPolicy: autocert.HostWhitelist("example.com"), + // Folder to store the certificates + Cache: autocert.DirCache("./certs"), +} + +app.Listen(":444", fiber.ListenConfig{ + AutoCertManager: certManager, +}) +``` + ## 🗺 Router We have slightly adapted our router interface @@ -175,22 +194,22 @@ The route method is now like [`Express`](https://expressjs.com/de/api.html#app.r ```go app.Route("/api").Route("/user/:id?") - .Get(func(c fiber.Ctx) error { - // Get user - return c.JSON(fiber.Map{"message": "Get user", "id": c.Params("id")}) - }) - .Post(func(c fiber.Ctx) error { - // Create user - return c.JSON(fiber.Map{"message": "User created"}) - }) - .Put(func(c fiber.Ctx) error { - // Update user - return c.JSON(fiber.Map{"message": "User updated", "id": c.Params("id")}) - }) - .Delete(func(c fiber.Ctx) error { - // Delete user - return c.JSON(fiber.Map{"message": "User deleted", "id": c.Params("id")}) - }) + .Get(func(c fiber.Ctx) error { + // Get user + return c.JSON(fiber.Map{"message": "Get user", "id": c.Params("id")}) + }) + .Post(func(c fiber.Ctx) error { + // Create user + return c.JSON(fiber.Map{"message": "User created"}) + }) + .Put(func(c fiber.Ctx) error { + // Update user + return c.JSON(fiber.Map{"message": "User updated", "id": c.Params("id")}) + }) + .Delete(func(c fiber.Ctx) error { + // Delete user + return c.JSON(fiber.Map{"message": "User deleted", "id": c.Params("id")}) + }) ``` @@ -209,14 +228,14 @@ Registering a subapp is now also possible via the [`Use`](./api/app#use) method ```go // register mulitple prefixes app.Use(["/v1", "/v2"], func(c fiber.Ctx) error { - // Middleware for /v1 and /v2 - return c.Next() + // Middleware for /v1 and /v2 + return c.Next() }) // define subapp api := fiber.New() api.Get("/user", func(c fiber.Ctx) error { - return c.SendString("User") + return c.SendString("User") }) // register subapp app.Use("/api", api) @@ -242,14 +261,14 @@ The `app.Test()` method now allows users to customize their test configurations: // Create a test app with a handler to test app := fiber.New() app.Get("/", func(c fiber.Ctx) { - return c.SendString("hello world") + return c.SendString("hello world") }) // Define the HTTP request and custom TestConfig to test the handler req := httptest.NewRequest(MethodGet, "/", nil) testConfig := fiber.TestConfig{ - Timeout: 0, - FailOnTimeout: false, + Timeout: 0, + FailOnTimeout: false, } // Test the handler using the request and testConfig @@ -277,8 +296,8 @@ If a custom `TestConfig` isn't provided, then the following will be used: ```go testConfig := fiber.TestConfig{ - Timeout: time.Second, - FailOnTimeout: true, + Timeout: time.Second, + FailOnTimeout: true, } ``` @@ -288,8 +307,8 @@ An empty `TestConfig` is the equivalent of: ```go testConfig := fiber.TestConfig{ - Timeout: 0, - FailOnTimeout: false, + Timeout: 0, + FailOnTimeout: false, } ``` @@ -340,7 +359,7 @@ testConfig := fiber.TestConfig{ ### SendStreamWriter -In v3, we added support for buffered streaming by providing the new method `SendStreamWriter()`. +In v3, we introduced support for buffered streaming with the addition of the `SendStreamWriter` method: ```go func (c Ctx) SendStreamWriter(streamWriter func(w *bufio.Writer)) @@ -354,22 +373,22 @@ With this new method, you can implement: ```go app.Get("/sse", func(c fiber.Ctx) { - c.Set("Content-Type", "text/event-stream") - c.Set("Cache-Control", "no-cache") - c.Set("Connection", "keep-alive") - c.Set("Transfer-Encoding", "chunked") + c.Set("Content-Type", "text/event-stream") + c.Set("Cache-Control", "no-cache") + c.Set("Connection", "keep-alive") + c.Set("Transfer-Encoding", "chunked") - return c.SendStreamWriter(func(w *bufio.Writer) { - for { - fmt.Fprintf(w, "event: my-event\n") - fmt.Fprintf(w, "data: Hello SSE\n\n") + return c.SendStreamWriter(func(w *bufio.Writer) { + for { + fmt.Fprintf(w, "event: my-event\n") + fmt.Fprintf(w, "data: Hello SSE\n\n") - if err := w.Flush(); err != nil { - log.Print("Client disconnected!") - return - } - } - }) + if err := w.Flush(); err != nil { + log.Print("Client disconnected!") + return + } + } + }) }) ``` @@ -397,17 +416,17 @@ Fiber v3 introduces a new binding mechanism that simplifies the process of bindi ```go type User struct { - ID int `params:"id"` - Name string `json:"name"` - Email string `json:"email"` + ID int `params:"id"` + Name string `json:"name"` + Email string `json:"email"` } app.Post("/user/:id", func(c fiber.Ctx) error { - var user User - if err := c.Bind().Body(&user); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(user) + var user User + if err := c.Bind().Body(&user); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(user) }) ``` @@ -430,11 +449,11 @@ Fiber v3 enhances the redirect functionality by introducing new methods and impr ```go app.Get("/old", func(c fiber.Ctx) error { - return c.Redirect().To("/new") + return c.Redirect().To("/new") }) app.Get("/new", func(c fiber.Ctx) error { - return c.SendString("Welcome to the new route!") + return c.SendString("Welcome to the new route!") }) ``` @@ -461,22 +480,22 @@ Fiber v3 introduces new generic functions that provide additional utility and fl package main import ( - "strconv" - "github.com/gofiber/fiber/v3" + "strconv" + "github.com/gofiber/fiber/v3" ) func main() { - app := fiber.New() + app := fiber.New() - app.Get("/convert", func(c fiber.Ctx) error { - value, err := Convert[string](c.Query("value"), strconv.Atoi, 0) - if err != nil { - return c.Status(fiber.StatusBadRequest).SendString(err.Error()) - } - return c.JSON(value) - }) + app.Get("/convert", func(c fiber.Ctx) error { + value, err := Convert[string](c.Query("value"), strconv.Atoi, 0) + if err != nil { + return c.Status(fiber.StatusBadRequest).SendString(err.Error()) + } + return c.JSON(value) + }) - app.Listen(":3000") + app.Listen(":3000") } ``` @@ -540,20 +559,19 @@ curl "http://localhost:3000/user/5" package main import ( - "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3" ) func main() { - app := fiber.New() + app := fiber.New() - app.Get("/params/:id", func(c fiber.Ctx) error { - id := Params[int](c, "id", 0) - return c.JSON(id) - }) + app.Get("/params/:id", func(c fiber.Ctx) error { + id := Params[int](c, "id", 0) + return c.JSON(id) + }) - app.Listen(":3000") + app.Listen(":3000") } - ``` ```sh @@ -573,24 +591,23 @@ curl "http://localhost:3000/params/abc" package main import ( - "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3" ) func main() { - app := fiber.New() + app := fiber.New() - app.Get("/query", func(c fiber.Ctx) error { - age := Query[int](c, "age", 0) - return c.JSON(age) - }) + app.Get("/query", func(c fiber.Ctx) error { + age := Query[int](c, "age", 0) + return c.JSON(age) + }) - app.Listen(":3000") + app.Listen(":3000") } ``` ```sh - curl "http://localhost:3000/query?age=25" # Output: 25 @@ -607,18 +624,18 @@ curl "http://localhost:3000/query?age=abc" package main import ( - "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3" ) func main() { - app := fiber.New() + app := fiber.New() - app.Get("/header", func(c fiber.Ctx) error { - userAgent := GetReqHeader[string](c, "User-Agent", "Unknown") - return c.JSON(userAgent) - }) + app.Get("/header", func(c fiber.Ctx) error { + userAgent := GetReqHeader[string](c, "User-Agent", "Unknown") + return c.JSON(userAgent) + }) - app.Listen(":3000") + app.Listen(":3000") } ``` @@ -805,7 +822,7 @@ Since we've removed `app.Static()`, you need to move methods to static middlewar app.Static("/", "./public") app.Static("/prefix", "./public") app.Static("/prefix", "./public", Static{ - Index: "index.htm", + Index: "index.htm", }) app.Static("*", "./public/index.html") ``` @@ -815,7 +832,7 @@ app.Static("*", "./public/index.html") app.Get("/*", static.New("./public")) app.Get("/prefix*", static.New("./public")) app.Get("/prefix*", static.New("./public", static.Config{ - IndexNames: []string{"index.htm", "index.html"}, + IndexNames: []string{"index.htm", "index.html"}, })) app.Get("*", static.New("./public/index.html")) ``` @@ -831,25 +848,25 @@ We've renamed `EnableTrustedProxyCheck` to `TrustProxy` and moved `TrustedProxie ```go // Before app := fiber.New(fiber.Config{ - // EnableTrustedProxyCheck enables the trusted proxy check. - EnableTrustedProxyCheck: true, - // TrustedProxies is a list of trusted proxy IP ranges/addresses. - TrustedProxies: []string{"0.8.0.0", "127.0.0.0/8", "::1/128"}, + // EnableTrustedProxyCheck enables the trusted proxy check. + EnableTrustedProxyCheck: true, + // TrustedProxies is a list of trusted proxy IP ranges/addresses. + TrustedProxies: []string{"0.8.0.0", "127.0.0.0/8", "::1/128"}, }) ``` ```go // After app := fiber.New(fiber.Config{ - // TrustProxy enables the trusted proxy check - TrustProxy: true, - // TrustProxyConfig allows for configuring trusted proxies. - TrustProxyConfig: fiber.TrustProxyConfig{ - // Proxies is a list of trusted proxy IP ranges/addresses. - Proxies: []string{"0.8.0.0"}, - // Trust all loop-back IP addresses (127.0.0.0/8, ::1/128) - Loopback: true, - } + // TrustProxy enables the trusted proxy check + TrustProxy: true, + // TrustProxyConfig allows for configuring trusted proxies. + TrustProxyConfig: fiber.TrustProxyConfig{ + // Proxies is a list of trusted proxy IP ranges/addresses. + Proxies: []string{"0.8.0.0"}, + // Trust all loop-back IP addresses (127.0.0.0/8, ::1/128) + Loopback: true, + } }) ``` @@ -890,21 +907,21 @@ app.Route("/api", func(apiGrp Router) { ```go // After app.Route("/api").Route("/user/:id?") - .Get(func(c fiber.Ctx) error { - // Get user - return c.JSON(fiber.Map{"message": "Get user", "id": c.Params("id")}) - }) - .Post(func(c fiber.Ctx) error { - // Create user - return c.JSON(fiber.Map{"message": "User created"}) - }); + .Get(func(c fiber.Ctx) error { + // Get user + return c.JSON(fiber.Map{"message": "Get user", "id": c.Params("id")}) + }) + .Post(func(c fiber.Ctx) error { + // Create user + return c.JSON(fiber.Map{"message": "User created"}) + }); ``` ### 🗺 RebuildTree -We have added a new method that allows the route tree stack to be rebuilt in runtime, with it, you can add a route while your application is running and rebuild the route tree stack to make it registered and available for calls. +We introduced a new method that enables rebuilding the route tree stack at runtime. This allows you to add routes dynamically while your application is running and update the route tree to make the new routes available for use. -You can find more reference on it in the [app](./api/app.md#rebuildtree): +For more details, refer to the [app documentation](./api/app.md#rebuildtree): #### Example Usage @@ -920,10 +937,9 @@ app.Get("/define", func(c Ctx) error { // Define a new route dynamically }) ``` -In this example, a new route is defined and then `RebuildTree()` is called to make sure the new route is registered and available. +In this example, a new route is defined, and `RebuildTree()` is called to ensure the new route is registered and available. -**Note:** Use this method with caution. It is **not** thread-safe and calling it can be very performance-intensive, so it should be used sparingly and only in -development mode. Avoid using it concurrently. +Note: Use this method with caution. It is **not** thread-safe and can be very performance-intensive. Therefore, it should be used sparingly and primarily in development mode. It should not be invoke concurrently. ### 🧠 Context @@ -946,18 +962,18 @@ In Fiber v3, the `Ctx` parameter in handlers is now an interface, which means th package main import ( - "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2" ) func main() { - app := fiber.New() + app := fiber.New() - // Route Handler with *fiber.Ctx - app.Get("/", func(c *fiber.Ctx) error { - return c.SendString("Hello, World!") - }) + // Route Handler with *fiber.Ctx + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) - app.Listen(":3000") + app.Listen(":3000") } ``` @@ -967,18 +983,18 @@ func main() { package main import ( - "github.com/gofiber/fiber/v3" + "github.com/gofiber/fiber/v3" ) func main() { - app := fiber.New() + app := fiber.New() - // Route Handler without *fiber.Ctx - app.Get("/", func(c fiber.Ctx) error { - return c.SendString("Hello, World!") - }) + // Route Handler without *fiber.Ctx + app.Get("/", func(c fiber.Ctx) error { + return c.SendString("Hello, World!") + }) - app.Listen(":3000") + app.Listen(":3000") } ``` @@ -1002,22 +1018,22 @@ The `Parser` section in Fiber v3 has undergone significant changes to improve fu ```go // Before app.Post("/user", func(c *fiber.Ctx) error { - var user User - if err := c.BodyParser(&user); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(user) + var user User + if err := c.BodyParser(&user); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(user) }) ``` ```go // After app.Post("/user", func(c fiber.Ctx) error { - var user User - if err := c.Bind().Body(&user); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(user) + var user User + if err := c.Bind().Body(&user); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(user) }) ``` @@ -1031,22 +1047,22 @@ The `Parser` section in Fiber v3 has undergone significant changes to improve fu ```go // Before app.Get("/user/:id", func(c *fiber.Ctx) error { - var params Params - if err := c.ParamsParser(¶ms); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(params) + var params Params + if err := c.ParamsParser(¶ms); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(params) }) ``` ```go // After app.Get("/user/:id", func(c fiber.Ctx) error { - var params Params - if err := c.Bind().URL(¶ms); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(params) + var params Params + if err := c.Bind().URL(¶ms); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(params) }) ``` @@ -1060,22 +1076,22 @@ The `Parser` section in Fiber v3 has undergone significant changes to improve fu ```go // Before app.Get("/search", func(c *fiber.Ctx) error { - var query Query - if err := c.QueryParser(&query); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(query) + var query Query + if err := c.QueryParser(&query); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(query) }) ``` ```go // After app.Get("/search", func(c fiber.Ctx) error { - var query Query - if err := c.Bind().Query(&query); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(query) + var query Query + if err := c.Bind().Query(&query); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(query) }) ``` @@ -1089,22 +1105,22 @@ The `Parser` section in Fiber v3 has undergone significant changes to improve fu ```go // Before app.Get("/cookie", func(c *fiber.Ctx) error { - var cookie Cookie - if err := c.CookieParser(&cookie); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(cookie) + var cookie Cookie + if err := c.CookieParser(&cookie); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(cookie) }) ``` ```go // After app.Get("/cookie", func(c fiber.Ctx) error { - var cookie Cookie - if err := c.Bind().Cookie(&cookie); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(cookie) + var cookie Cookie + if err := c.Bind().Cookie(&cookie); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(cookie) }) ``` @@ -1124,14 +1140,14 @@ Fiber v3 enhances the redirect functionality by introducing new methods and impr ```go // Before app.Get("/old", func(c *fiber.Ctx) error { - return c.RedirectToRoute("newRoute") + return c.RedirectToRoute("newRoute") }) ``` ```go // After app.Get("/old", func(c fiber.Ctx) error { - return c.Redirect().Route("newRoute") + return c.Redirect().Route("newRoute") }) ``` @@ -1145,14 +1161,14 @@ Fiber v3 enhances the redirect functionality by introducing new methods and impr ```go // Before app.Get("/back", func(c *fiber.Ctx) error { - return c.RedirectBack() + return c.RedirectBack() }) ``` ```go // After app.Get("/back", func(c fiber.Ctx) error { - return c.Redirect().Back() + return c.Redirect().Back() }) ``` @@ -1166,14 +1182,14 @@ Fiber v3 enhances the redirect functionality by introducing new methods and impr ```go // Before app.Get("/old", func(c *fiber.Ctx) error { - return c.Redirect("/new") + return c.Redirect("/new") }) ``` ```go // After app.Get("/old", func(c fiber.Ctx) error { - return c.Redirect().To("/new") + return c.Redirect().To("/new") }) ``` @@ -1226,18 +1242,18 @@ The CORS middleware has been updated to use slices instead of strings for the `A ```go // Before app.Use(cors.New(cors.Config{ - AllowOrigins: "https://example.com,https://example2.com", - AllowMethods: strings.Join([]string{fiber.MethodGet, fiber.MethodPost}, ","), - AllowHeaders: "Content-Type", - ExposeHeaders: "Content-Length", + AllowOrigins: "https://example.com,https://example2.com", + AllowMethods: strings.Join([]string{fiber.MethodGet, fiber.MethodPost}, ","), + AllowHeaders: "Content-Type", + ExposeHeaders: "Content-Length", })) // After app.Use(cors.New(cors.Config{ - AllowOrigins: []string{"https://example.com", "https://example2.com"}, - AllowMethods: []string{fiber.MethodGet, fiber.MethodPost}, - AllowHeaders: []string{"Content-Type"}, - ExposeHeaders: []string{"Content-Length"}, + AllowOrigins: []string{"https://example.com", "https://example2.com"}, + AllowMethods: []string{fiber.MethodGet, fiber.MethodPost}, + AllowHeaders: []string{"Content-Type"}, + ExposeHeaders: []string{"Content-Length"}, })) ``` @@ -1248,12 +1264,12 @@ app.Use(cors.New(cors.Config{ ```go // Before app.Use(csrf.New(csrf.Config{ - Expiration: 10 * time.Minute, + Expiration: 10 * time.Minute, })) // After app.Use(csrf.New(csrf.Config{ - IdleTimeout: 10 * time.Minute, + IdleTimeout: 10 * time.Minute, })) ``` @@ -1266,28 +1282,28 @@ You need to move filesystem middleware to static middleware due to it has been r ```go // Before app.Use(filesystem.New(filesystem.Config{ - Root: http.Dir("./assets"), + Root: http.Dir("./assets"), })) app.Use(filesystem.New(filesystem.Config{ - Root: http.Dir("./assets"), - Browse: true, - Index: "index.html", - MaxAge: 3600, + Root: http.Dir("./assets"), + Browse: true, + Index: "index.html", + MaxAge: 3600, })) ``` ```go // After app.Use(static.New("", static.Config{ - FS: os.DirFS("./assets"), + FS: os.DirFS("./assets"), })) app.Use(static.New("", static.Config{ - FS: os.DirFS("./assets"), - Browse: true, - IndexNames: []string{"index.html"}, - MaxAge: 3600, + FS: os.DirFS("./assets"), + Browse: true, + IndexNames: []string{"index.html"}, + MaxAge: 3600, })) ``` @@ -1298,14 +1314,14 @@ Previously, the Healthcheck middleware was configured with a combined setup for ```go //before app.Use(healthcheck.New(healthcheck.Config{ - LivenessProbe: func(c fiber.Ctx) bool { - return true - }, - LivenessEndpoint: "/live", - ReadinessProbe: func(c fiber.Ctx) bool { - return serviceA.Ready() && serviceB.Ready() && ... - }, - ReadinessEndpoint: "/ready", + LivenessProbe: func(c fiber.Ctx) bool { + return true + }, + LivenessEndpoint: "/live", + ReadinessProbe: func(c fiber.Ctx) bool { + return serviceA.Ready() && serviceB.Ready() && ... + }, + ReadinessEndpoint: "/ready", })) ``` @@ -1316,9 +1332,9 @@ With the new version, each health check endpoint is configured separately, allow // Default liveness endpoint configuration app.Get(healthcheck.DefaultLivenessEndpoint, healthcheck.NewHealthChecker(healthcheck.Config{ - Probe: func(c fiber.Ctx) bool { - return true - }, + Probe: func(c fiber.Ctx) bool { + return true + }, })) // Default readiness endpoint configuration @@ -1327,9 +1343,9 @@ app.Get(healthcheck.DefaultReadinessEndpoint, healthcheck.NewHealthChecker()) // New default startup endpoint configuration // Default endpoint is /startupz app.Get(healthcheck.DefaultStartupEndpoint, healthcheck.NewHealthChecker(healthcheck.Config{ - Probe: func(c fiber.Ctx) bool { - return serviceA.Ready() && serviceB.Ready() && ... - }, + Probe: func(c fiber.Ctx) bool { + return serviceA.Ready() && serviceB.Ready() && ... + }, })) // Custom liveness endpoint configuration diff --git a/go.mod b/go.mod index a17d0b1a..a979f183 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/tinylib/msgp v1.2.5 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.57.0 + golang.org/x/crypto v0.28.0 ) require ( diff --git a/go.sum b/go.sum index 293e2d6c..3601863b 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,8 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/listen.go b/listen.go index 624b2539..e0c55369 100644 --- a/listen.go +++ b/listen.go @@ -23,6 +23,7 @@ import ( "github.com/gofiber/fiber/v3/log" "github.com/mattn/go-colorable" "github.com/mattn/go-isatty" + "golang.org/x/crypto/acme/autocert" ) // Figlet text to show Fiber ASCII art on startup message @@ -69,6 +70,13 @@ type ListenConfig struct { // // Default: nil OnShutdownSuccess func() + + // AutoCertManager manages TLS certificates automatically using the ACME protocol, + // Enables integration with Let's Encrypt or other ACME-compatible providers. + // + // Default: nil + AutoCertManager *autocert.Manager `json:"auto_cert_manager"` + // Known networks are "tcp", "tcp4" (IPv4-only), "tcp6" (IPv6-only) // WARNING: When prefork is set to true, only "tcp4" and "tcp6" can be chosen. // @@ -183,9 +191,15 @@ func (app *App) Listen(addr string, config ...ListenConfig) error { // Attach the tlsHandler to the config app.SetTLSHandler(tlsHandler) + } else if cfg.AutoCertManager != nil { + tlsConfig = &tls.Config{ + MinVersion: tls.VersionTLS12, + GetCertificate: cfg.AutoCertManager.GetCertificate, + NextProtos: []string{"http/1.1", "acme-tls/1"}, + } } - if cfg.TLSConfigFunc != nil { + if tlsConfig != nil && cfg.TLSConfigFunc != nil { cfg.TLSConfigFunc(tlsConfig) } From e9849b758d0afaaf700f52ef76372bc41d373412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=2E=20Efe=20=C3=87etin?= Date: Tue, 10 Dec 2024 12:39:23 +0300 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=94=A5=20feat:=20Add=20support=20for?= =?UTF-8?q?=20iterator=20methods=20to=20Fiber=20client=20(#3228)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: simplify parserRequestBodyFile logic * client: add support for go1.23 iterators * Apply suggestions from code review Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix linter * fix tests * correct benchmark * fix linter * create docs * update * rename FormDatas -> AllFormData * add examples for maps.Collect() * change request/response markdown examples --------- Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: René --- Makefile | 2 +- client/hooks.go | 25 ++--- client/request.go | 133 +++++++++++++++++++++++++ client/request_test.go | 213 ++++++++++++++++++++++++++++++++++++++++ client/response.go | 27 +++++ client/response_test.go | 79 +++++++++++++++ docs/client/request.md | 184 +++++++++++++++++++++++++++------- docs/client/response.md | 75 ++++++++++++-- 8 files changed, 673 insertions(+), 65 deletions(-) diff --git a/Makefile b/Makefile index 87d4b50d..4b348cd5 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ coverage: format: go run mvdan.cc/gofumpt@latest -w -l . -## markdown: 🎨 Find markdown format issues (Requires markdownlint-cli) +## markdown: 🎨 Find markdown format issues (Requires markdownlint-cli2) .PHONY: markdown markdown: markdownlint-cli2 "**/*.md" "#vendor" diff --git a/client/hooks.go b/client/hooks.go index ec398793..4c36145f 100644 --- a/client/hooks.go +++ b/client/hooks.go @@ -1,7 +1,6 @@ package client import ( - "errors" "fmt" "io" "math/rand" @@ -241,8 +240,8 @@ func parserRequestBodyFile(req *Request) error { return fmt.Errorf("write formdata error: %w", err) } - // add file - b := make([]byte, 512) + // add files + fileBuf := make([]byte, 1<<20) // Allocate 1MB buffer for i, v := range req.files { if v.name == "" && v.path == "" { return ErrFileNoName @@ -273,24 +272,12 @@ func parserRequestBodyFile(req *Request) error { return fmt.Errorf("create file error: %w", err) } - for { - n, err := v.reader.Read(b) - if err != nil && !errors.Is(err, io.EOF) { - return fmt.Errorf("read file error: %w", err) - } - - if errors.Is(err, io.EOF) { - break - } - - _, err = w.Write(b[:n]) - if err != nil { - return fmt.Errorf("write file error: %w", err) - } + // Copy the file from reader to multipart writer + if _, err := io.CopyBuffer(w, v.reader, fileBuf); err != nil { + return fmt.Errorf("failed to copy file data: %w", err) } - err = v.reader.Close() - if err != nil { + if err := v.reader.Close(); err != nil { return fmt.Errorf("close file error: %w", err) } } diff --git a/client/request.go b/client/request.go index a86d927c..f303fb74 100644 --- a/client/request.go +++ b/client/request.go @@ -5,8 +5,10 @@ import ( "context" "errors" "io" + "iter" "path/filepath" "reflect" + "slices" "strconv" "sync" "time" @@ -129,6 +131,31 @@ func (r *Request) Header(key string) []string { return r.header.PeekMultiple(key) } +// Headers returns all headers in the request using an iterator. +// You can use maps.Collect() to collect all headers into a map. +// +// The returned value is valid until the request object is released. +// Any future calls to Headers method will return the modified value. Do not store references to returned value. Make copies instead. +func (r *Request) Headers() iter.Seq2[string, []string] { + return func(yield func(string, []string) bool) { + peekKeys := r.header.PeekKeys() + keys := make([][]byte, len(peekKeys)) + copy(keys, peekKeys) // It is necessary to have immutable byte slice. + + for _, key := range keys { + vals := r.header.PeekAll(utils.UnsafeString(key)) + valsStr := make([]string, len(vals)) + for i, v := range vals { + valsStr[i] = utils.UnsafeString(v) + } + + if !yield(utils.UnsafeString(key), valsStr) { + return + } + } + } +} + // AddHeader method adds a single header field and its value in the request instance. func (r *Request) AddHeader(key, val string) *Request { r.header.Add(key, val) @@ -168,6 +195,33 @@ func (r *Request) Param(key string) []string { return res } +// Params returns all params in the request using an iterator. +// You can use maps.Collect() to collect all params into a map. +// +// The returned value is valid until the request object is released. +// Any future calls to Params method will return the modified value. Do not store references to returned value. Make copies instead. +func (r *Request) Params() iter.Seq2[string, []string] { + return func(yield func(string, []string) bool) { + keys := r.params.Keys() + + for _, key := range keys { + if key == "" { + continue + } + + vals := r.params.PeekMulti(key) + valsStr := make([]string, len(vals)) + for i, v := range vals { + valsStr[i] = utils.UnsafeString(v) + } + + if !yield(key, valsStr) { + return + } + } + } +} + // AddParam method adds a single param field and its value in the request instance. func (r *Request) AddParam(key, val string) *Request { r.params.Add(key, val) @@ -254,6 +308,18 @@ func (r *Request) Cookie(key string) string { return "" } +// Cookies returns all cookies in the cookies using an iterator. +// You can use maps.Collect() to collect all cookies into a map. +func (r *Request) Cookies() iter.Seq2[string, string] { + return func(yield func(string, string) bool) { + r.cookies.VisitAll(func(key, val string) { + if !yield(key, val) { + return + } + }) + } +} + // SetCookie method sets a single cookie field and its value in the request instance. // It will override cookie which set in client instance. func (r *Request) SetCookie(key, val string) *Request { @@ -291,6 +357,18 @@ func (r *Request) PathParam(key string) string { return "" } +// PathParams returns all path params in request instance. +// You can use maps.Collect() to collect all cookies into a map. +func (r *Request) PathParams() iter.Seq2[string, string] { + return func(yield func(string, string) bool) { + r.path.VisitAll(func(key, val string) { + if !yield(key, val) { + return + } + }) + } +} + // SetPathParam method sets a single path param field and its value in the request instance. // It will override path param which set in client instance. func (r *Request) SetPathParam(key, val string) *Request { @@ -376,6 +454,33 @@ func (r *Request) FormData(key string) []string { return res } +// AllFormData method returns all form datas in request instance. +// You can use maps.Collect() to collect all cookies into a map. +// +// The returned value is valid until the request object is released. +// Any future calls to FormDatas method will return the modified value. Do not store references to returned value. Make copies instead. +func (r *Request) AllFormData() iter.Seq2[string, []string] { + return func(yield func(string, []string) bool) { + keys := r.formData.Keys() + + for _, key := range keys { + if key == "" { + continue + } + + vals := r.formData.PeekMulti(key) + valsStr := make([]string, len(vals)) + for i, v := range vals { + valsStr[i] = utils.UnsafeString(v) + } + + if !yield(key, valsStr) { + return + } + } + } +} + // AddFormData method adds a single form data field and its value in the request instance. func (r *Request) AddFormData(key, val string) *Request { r.formData.AddData(key, val) @@ -435,6 +540,14 @@ func (r *Request) File(name string) *File { return nil } +// Files method returns all files in request instance. +// +// The returned value is valid until the request object is released. +// Any future calls to Files method will return the modified value. Do not store references to returned value. Make copies instead. +func (r *Request) Files() []*File { + return r.files +} + // FileByPath returns file ptr store in request obj by path. func (r *Request) FileByPath(path string) *File { for _, v := range r.files { @@ -617,6 +730,16 @@ type QueryParam struct { *fasthttp.Args } +// Keys method returns all keys in the query params. +func (p *QueryParam) Keys() []string { + keys := make([]string, 0, p.Len()) + p.VisitAll(func(key, _ []byte) { + keys = append(keys, utils.UnsafeString(key)) + }) + + return slices.Compact(keys) +} + // AddParams receive a map and add each value to param. func (p *QueryParam) AddParams(r map[string][]string) { for k, v := range r { @@ -747,6 +870,16 @@ type FormData struct { *fasthttp.Args } +// Keys method returns all keys in the form data. +func (f *FormData) Keys() []string { + keys := make([]string, 0, f.Len()) + f.VisitAll(func(key, _ []byte) { + keys = append(keys, utils.UnsafeString(key)) + }) + + return slices.Compact(keys) +} + // AddData method is a wrapper of Args's Add method. func (f *FormData) AddData(key, val string) { f.Add(key, val) diff --git a/client/request_test.go b/client/request_test.go index f62865a3..e663f2fe 100644 --- a/client/request_test.go +++ b/client/request_test.go @@ -5,6 +5,7 @@ import ( "context" "errors" "io" + "maps" "mime/multipart" "net" "os" @@ -157,6 +158,42 @@ func Test_Request_Header(t *testing.T) { }) } +func Test_Request_Headers(t *testing.T) { + t.Parallel() + + req := AcquireRequest() + req.AddHeaders(map[string][]string{ + "foo": {"bar", "fiber"}, + "bar": {"foo"}, + }) + + headers := maps.Collect(req.Headers()) + + require.Contains(t, headers["Foo"], "fiber") + require.Contains(t, headers["Foo"], "bar") + require.Contains(t, headers["Bar"], "foo") + + require.Len(t, headers, 2) +} + +func Benchmark_Request_Headers(b *testing.B) { + req := AcquireRequest() + req.AddHeaders(map[string][]string{ + "foo": {"bar", "fiber"}, + "bar": {"foo"}, + }) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + for k, v := range req.Headers() { + _ = k + _ = v + } + } +} + func Test_Request_QueryParam(t *testing.T) { t.Parallel() @@ -282,6 +319,42 @@ func Test_Request_QueryParam(t *testing.T) { }) } +func Test_Request_Params(t *testing.T) { + t.Parallel() + + req := AcquireRequest() + req.AddParams(map[string][]string{ + "foo": {"bar", "fiber"}, + "bar": {"foo"}, + }) + + pathParams := maps.Collect(req.Params()) + + require.Contains(t, pathParams["foo"], "bar") + require.Contains(t, pathParams["foo"], "fiber") + require.Contains(t, pathParams["bar"], "foo") + + require.Len(t, pathParams, 2) +} + +func Benchmark_Request_Params(b *testing.B) { + req := AcquireRequest() + req.AddParams(map[string][]string{ + "foo": {"bar", "fiber"}, + "bar": {"foo"}, + }) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + for k, v := range req.Params() { + _ = k + _ = v + } + } +} + func Test_Request_UA(t *testing.T) { t.Parallel() @@ -364,6 +437,41 @@ func Test_Request_Cookie(t *testing.T) { }) } +func Test_Request_Cookies(t *testing.T) { + t.Parallel() + + req := AcquireRequest() + req.SetCookies(map[string]string{ + "foo": "bar", + "bar": "foo", + }) + + cookies := maps.Collect(req.Cookies()) + + require.Equal(t, "bar", cookies["foo"]) + require.Equal(t, "foo", cookies["bar"]) + + require.Len(t, cookies, 2) +} + +func Benchmark_Request_Cookies(b *testing.B) { + req := AcquireRequest() + req.SetCookies(map[string]string{ + "foo": "bar", + "bar": "foo", + }) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + for k, v := range req.Cookies() { + _ = k + _ = v + } + } +} + func Test_Request_PathParam(t *testing.T) { t.Parallel() @@ -441,6 +549,41 @@ func Test_Request_PathParam(t *testing.T) { }) } +func Test_Request_PathParams(t *testing.T) { + t.Parallel() + + req := AcquireRequest() + req.SetPathParams(map[string]string{ + "foo": "bar", + "bar": "foo", + }) + + pathParams := maps.Collect(req.PathParams()) + + require.Equal(t, "bar", pathParams["foo"]) + require.Equal(t, "foo", pathParams["bar"]) + + require.Len(t, pathParams, 2) +} + +func Benchmark_Request_PathParams(b *testing.B) { + req := AcquireRequest() + req.SetPathParams(map[string]string{ + "foo": "bar", + "bar": "foo", + }) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + for k, v := range req.PathParams() { + _ = k + _ = v + } + } +} + func Test_Request_FormData(t *testing.T) { t.Parallel() @@ -610,6 +753,40 @@ func Test_Request_File(t *testing.T) { }) } +func Test_Request_Files(t *testing.T) { + t.Parallel() + + req := AcquireRequest() + req.AddFile("../.github/index.html") + req.AddFiles(AcquireFile(SetFileName("tmp.txt"))) + + files := req.Files() + + require.Equal(t, "../.github/index.html", files[0].path) + require.Nil(t, files[0].reader) + + require.Equal(t, "tmp.txt", files[1].name) + require.Nil(t, files[1].reader) + + require.Len(t, files, 2) +} + +func Benchmark_Request_Files(b *testing.B) { + req := AcquireRequest() + req.AddFile("../.github/index.html") + req.AddFiles(AcquireFile(SetFileName("tmp.txt"))) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + for k, v := range req.Files() { + _ = k + _ = v + } + } +} + func Test_Request_Timeout(t *testing.T) { t.Parallel() @@ -1181,6 +1358,42 @@ func Test_Request_Body_With_Server(t *testing.T) { }) } +func Test_Request_AllFormData(t *testing.T) { + t.Parallel() + + req := AcquireRequest() + req.AddFormDatas(map[string][]string{ + "foo": {"bar", "fiber"}, + "bar": {"foo"}, + }) + + pathParams := maps.Collect(req.AllFormData()) + + require.Contains(t, pathParams["foo"], "bar") + require.Contains(t, pathParams["foo"], "fiber") + require.Contains(t, pathParams["bar"], "foo") + + require.Len(t, pathParams, 2) +} + +func Benchmark_Request_AllFormData(b *testing.B) { + req := AcquireRequest() + req.AddFormDatas(map[string][]string{ + "foo": {"bar", "fiber"}, + "bar": {"foo"}, + }) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + for k, v := range req.AllFormData() { + _ = k + _ = v + } + } +} + func Test_Request_Error_Body_With_Server(t *testing.T) { t.Parallel() t.Run("json error", func(t *testing.T) { diff --git a/client/response.go b/client/response.go index e60c6bd0..8d213297 100644 --- a/client/response.go +++ b/client/response.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "io/fs" + "iter" "os" "path/filepath" "sync" @@ -55,7 +56,33 @@ func (r *Response) Header(key string) string { return utils.UnsafeString(r.RawResponse.Header.Peek(key)) } +// Headers returns all headers in the response using an iterator. +// You can use maps.Collect() to collect all headers into a map. +// +// The returned value is valid until the response object is released. +// Any future calls to Headers method will return the modified value. Do not store references to returned value. Make copies instead. +func (r *Response) Headers() iter.Seq2[string, []string] { + return func(yield func(string, []string) bool) { + keys := r.RawResponse.Header.PeekKeys() + + for _, key := range keys { + vals := r.RawResponse.Header.PeekAll(utils.UnsafeString(key)) + valsStr := make([]string, len(vals)) + for i, v := range vals { + valsStr[i] = utils.UnsafeString(v) + } + + if !yield(utils.UnsafeString(key), valsStr) { + return + } + } + } +} + // Cookies method to access all the response cookies. +// +// The returned value is valid until the response object is released. +// Any future calls to Cookies method will return the modified value. Do not store references to returned value. Make copies instead. func (r *Response) Cookies() []*fasthttp.Cookie { return r.cookie } diff --git a/client/response_test.go b/client/response_test.go index bf12e751..60b87bd1 100644 --- a/client/response_test.go +++ b/client/response_test.go @@ -199,6 +199,85 @@ func Test_Response_Header(t *testing.T) { resp.Close() } +func Test_Response_Headers(t *testing.T) { + t.Parallel() + + server := startTestServer(t, func(app *fiber.App) { + app.Get("/", func(c fiber.Ctx) error { + c.Response().Header.Add("foo", "bar") + c.Response().Header.Add("foo", "bar2") + c.Response().Header.Add("foo2", "bar") + + return c.SendString("hello world") + }) + }) + defer server.stop() + + client := New().SetDial(server.dial()) + + resp, err := AcquireRequest(). + SetClient(client). + Get("http://example.com") + + require.NoError(t, err) + + headers := make(map[string][]string) + for k, v := range resp.Headers() { + headers[k] = append(headers[k], v...) + } + + require.Equal(t, "hello world", resp.String()) + + require.Contains(t, headers["Foo"], "bar") + require.Contains(t, headers["Foo"], "bar2") + require.Contains(t, headers["Foo2"], "bar") + + require.Len(t, headers, 3) // Foo + Foo2 + Date + + resp.Close() +} + +func Benchmark_Headers(b *testing.B) { + server := startTestServer( + b, + func(app *fiber.App) { + app.Get("/", func(c fiber.Ctx) error { + c.Response().Header.Add("foo", "bar") + c.Response().Header.Add("foo", "bar2") + c.Response().Header.Add("foo", "bar3") + + c.Response().Header.Add("foo2", "bar") + c.Response().Header.Add("foo2", "bar2") + c.Response().Header.Add("foo2", "bar3") + + return c.SendString("helo world") + }) + }, + ) + + client := New().SetDial(server.dial()) + + resp, err := AcquireRequest(). + SetClient(client). + Get("http://example.com") + require.NoError(b, err) + + b.Cleanup(func() { + resp.Close() + server.stop() + }) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + for k, v := range resp.Headers() { + _ = k + _ = v + } + } +} + func Test_Response_Cookie(t *testing.T) { t.Parallel() diff --git a/docs/client/request.md b/docs/client/request.md index 08a04e65..9789ea0d 100644 --- a/docs/client/request.md +++ b/docs/client/request.md @@ -211,6 +211,60 @@ Header method returns header value via key, this method will visit all field in func (r *Request) Header(key string) []string ``` +### Headers + +Headers returns all headers in the request using an iterator. You can use `maps.Collect()` to collect all headers into a map. +The returned value is valid until the request object is released. Any future calls to Headers method will return the modified value. Do not store references to returned value. Make copies instead. + +```go title="Signature" +func (r *Request) Headers() iter.Seq2[string, []string] +``` + +
+Example + +```go title="Example" +req := client.AcquireRequest() + +req.AddHeader("Golang", "Fiber") +req.AddHeader("Test", "123456") +req.AddHeader("Test", "654321") + +for k, v := range req.Headers() { + fmt.Printf("Header Key: %s, Header Value: %v\n", k, v) +} +``` + +```sh +Header Key: Golang, Header Value: [Fiber] +Header Key: Test, Header Value: [123456 654321] +``` + +
+ +
+Example with maps.Collect() + +```go title="Example with maps.Collect()" +req := client.AcquireRequest() + +req.AddHeader("Golang", "Fiber") +req.AddHeader("Test", "123456") +req.AddHeader("Test", "654321") + +headers := maps.Collect(req.Headers()) // Collect all headers into a map +for k, v := range headers { + fmt.Printf("Header Key: %s, Header Value: %v\n", k, v) +} +``` + +```sh +Header Key: Golang, Header Value: [Fiber] +Header Key: Test, Header Value: [123456 654321] +``` + +
+ ### AddHeader AddHeader method adds a single header field and its value in the request instance. @@ -219,6 +273,9 @@ AddHeader method adds a single header field and its value in the request instanc func (r *Request) AddHeader(key, val string) *Request ``` +
+Example + ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) @@ -235,9 +292,6 @@ if err != nil { fmt.Println(resp.String()) ``` -
-Click here to see the result - ```json { "headers": { @@ -262,6 +316,9 @@ It will override the header which has been set in the client instance. func (r *Request) SetHeader(key, val string) *Request ``` +
+Example + ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) @@ -277,9 +334,6 @@ if err != nil { fmt.Println(resp.String()) ``` -
-Click here to see the result - ```json { "headers": { @@ -320,6 +374,15 @@ Param method returns params value via key, this method will visit all field in t func (r *Request) Param(key string) []string ``` +### Params + +Params returns all params in the request using an iterator. You can use `maps.Collect()` to collect all params into a map. +The returned value is valid until the request object is released. Any future calls to Params method will return the modified value. Do not store references to returned value. Make copies instead. + +```go title="Signature" +func (r *Request) Params() iter.Seq2[string, []string] +``` + ### AddParam AddParam method adds a single param field and its value in the request instance. @@ -328,6 +391,9 @@ AddParam method adds a single param field and its value in the request instance. func (r *Request) AddParam(key, val string) *Request ``` +
+Example + ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) @@ -344,9 +410,6 @@ if err != nil { fmt.Println(string(resp.Body())) ``` -
-Click here to see the result - ```json { "Content-Length": "145", @@ -396,6 +459,9 @@ It will override param, which has been set in client instance. func (r *Request) SetParamsWithStruct(v any) *Request ``` +
+Example + ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) @@ -419,9 +485,6 @@ if err != nil { fmt.Println(string(resp.Body())) ``` -
-Click here to see the result - ```json { "Content-Length": "147", @@ -502,6 +565,14 @@ Cookie returns the cookie set in the request instance. If the cookie doesn't exi func (r *Request) Cookie(key string) string ``` +### Cookies + +Cookies returns all cookies in the request using an iterator. You can use `maps.Collect()` to collect all cookies into a map. + +```go title="Signature" +func (r *Request) Cookies() iter.Seq2[string, string] +``` + ### SetCookie SetCookie method sets a single cookie field and its value in the request instance. @@ -520,6 +591,9 @@ It will override the cookie which is set in the client instance. func (r *Request) SetCookies(m map[string]string) *Request ``` +
+Example + ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) @@ -537,9 +611,6 @@ if err != nil { fmt.Println(string(resp.Body())) ``` -
-Click here to see the result - ```json { "cookies": { @@ -575,6 +646,14 @@ PathParam returns the path param set in the request instance. If the path param func (r *Request) PathParam(key string) string ``` +### PathParams + +PathParams returns all path params in the request using an iterator. You can use `maps.Collect()` to collect all path params into a map. + +```go title="Signature" +func (r *Request) PathParams() iter.Seq2[string, string] +``` + ### SetPathParam SetPathParam method sets a single path param field and its value in the request instance. @@ -584,6 +663,9 @@ It will override path param which set in client instance. func (r *Request) SetPathParam(key, val string) *Request ``` +
+Example + ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) @@ -598,9 +680,6 @@ if err != nil { fmt.Println(string(resp.Body())) ``` -
-Click here to see the result - ```plaintext Gofiber ``` @@ -682,6 +761,14 @@ FormData method returns form data value via key, this method will visit all fiel func (r *Request) FormData(key string) []string ``` +### AllFormData + +AllFormData returns all form data in the request using an iterator. You can use `maps.Collect()` to collect all form data into a map. + +```go title="Signature" +func (r *Request) AllFormData() iter.Seq2[string, []string] +``` + ### AddFormData AddFormData method adds a single form data field and its value in the request instance. @@ -690,6 +777,9 @@ AddFormData method adds a single form data field and its value in the request in func (r *Request) AddFormData(key, val string) *Request ``` +
+Example + ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) @@ -706,9 +796,6 @@ if err != nil { fmt.Println(string(resp.Body())) ``` -
-Click here to see the result - ```json { "args": {}, @@ -735,6 +822,9 @@ SetFormData method sets a single form data field and its value in the request in func (r *Request) SetFormData(key, val string) *Request ``` +
+Example + ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) @@ -750,9 +840,6 @@ if err != nil { fmt.Println(string(resp.Body())) ``` -
-Click here to see the result - ```json { "args": {}, @@ -817,6 +904,15 @@ If the name field is empty, it will try to match path. func (r *Request) File(name string) *File ``` +### Files + +Files method returns all files in request instance. +The returned value is valid until the request object is released. Any future calls to Files method will return the modified value. Do not store references to returned value. Make copies instead. + +```go title="Signature" +func (r *Request) Files() []*File +``` + ### FileByPath FileByPath returns file ptr store in request obj by path. @@ -833,6 +929,9 @@ AddFile method adds a single file field and its value in the request instance vi func (r *Request) AddFile(path string) *Request ``` +
+Example + ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) @@ -847,9 +946,6 @@ if err != nil { fmt.Println(string(resp.Body())) ``` -
-Click here to see the result - ```json { "args": {}, @@ -872,6 +968,9 @@ AddFileWithReader method adds a single field and its value in the request instan func (r *Request) AddFileWithReader(name string, reader io.ReadCloser) *Request ``` +
+Example + ```go title="Example" req := client.AcquireRequest() defer client.ReleaseRequest(req) @@ -887,9 +986,6 @@ if err != nil { fmt.Println(string(resp.Body())) ``` -
-Click here to see the result - ```json { "args": {}, @@ -929,6 +1025,9 @@ It will override timeout which set in client instance. func (r *Request) SetTimeout(t time.Duration) *Request ``` +
+Example 1 + ```go title="Example 1" req := client.AcquireRequest() defer client.ReleaseRequest(req) @@ -943,9 +1042,6 @@ if err != nil { fmt.Println(string(resp.Body())) ``` -
-Click here to see the result - ```json { "args": {}, @@ -958,6 +1054,9 @@ fmt.Println(string(resp.Body()))
+
+Example 2 + ```go title="Example 2" req := client.AcquireRequest() defer client.ReleaseRequest(req) @@ -972,9 +1071,6 @@ if err != nil { fmt.Println(string(resp.Body())) ``` -
-Click here to see the result - ```shell panic: timeout or cancel @@ -1063,6 +1159,14 @@ type QueryParam struct { } ``` +### Keys + +Keys method returns all keys in the query params. + +```go title="Signature" +func (p *QueryParam) Keys() []string +``` + ### AddParams AddParams receive a map and add each value to param. @@ -1242,6 +1346,14 @@ type FormData struct { } ``` +### Keys + +Keys method returns all keys in the form data. + +```go title="Signature" +func (f *FormData) Keys() []string +``` + ### AddData AddData method is a wrapper of Args's Add method. diff --git a/docs/client/response.md b/docs/client/response.md index 0dba3c6b..13597006 100644 --- a/docs/client/response.md +++ b/docs/client/response.md @@ -68,6 +68,9 @@ Protocol method returns the HTTP response protocol used for the request. func (r *Response) Protocol() string ``` +
+Example + ```go title="Example" resp, err := client.Get("https://httpbin.org/get") if err != nil { @@ -77,9 +80,6 @@ if err != nil { fmt.Println(resp.Protocol()) ``` -
-Click here to see the result - ```text HTTP/1.1 ``` @@ -94,14 +94,74 @@ Header method returns the response headers. func (r *Response) Header(key string) string ``` +## Headers + +Headers returns all headers in the response using an iterator. You can use `maps.Collect()` to collect all headers into a map. +The returned value is valid until the response object is released. Any future calls to Headers method will return the modified value. Do not store references to returned value. Make copies instead. + +```go title="Signature" +func (r *Response) Headers() iter.Seq2[string, []string] +``` + +
+Example + +```go title="Example" +resp, err := client.Get("https://httpbin.org/get") +if err != nil { + panic(err) +} + +for key, values := range resp.Headers() { + fmt.Printf("%s => %s\n", key, strings.Join(values, ", ")) +} +``` + +```text +Date => Wed, 04 Dec 2024 15:28:29 GMT +Connection => keep-alive +Access-Control-Allow-Origin => * +Access-Control-Allow-Credentials => true +``` + +
+ +
+Example with maps.Collect() + +```go title="Example with maps.Collect()" +resp, err := client.Get("https://httpbin.org/get") +if err != nil { + panic(err) +} + +headers := maps.Collect(resp.Headers()) // Collect all headers into a map +for key, values := range headers { + fmt.Printf("%s => %s\n", key, strings.Join(values, ", ")) +} +``` + +```text +Date => Wed, 04 Dec 2024 15:28:29 GMT +Connection => keep-alive +Access-Control-Allow-Origin => * +Access-Control-Allow-Credentials => true +``` + +
+ ## Cookies Cookies method to access all the response cookies. +The returned value is valid until the response object is released. Any future calls to Cookies method will return the modified value. Do not store references to returned value. Make copies instead. ```go title="Signature" func (r *Response) Cookies() []*fasthttp.Cookie ``` +
+Example + ```go title="Example" resp, err := client.Get("https://httpbin.org/cookies/set/go/fiber") if err != nil { @@ -114,9 +174,6 @@ for _, cookie := range cookies { } ``` -
-Click here to see the result - ```text go => fiber ``` @@ -147,6 +204,9 @@ JSON method will unmarshal body to json. func (r *Response) JSON(v any) error ``` +
+Example + ```go title="Example" type Body struct { Slideshow struct { @@ -170,9 +230,6 @@ if err != nil { fmt.Printf("%+v\n", out) ``` -
-Click here to see the result - ```text {Slideshow:{Author:Yours Truly Date:date of publication Title:Sample Slide Show}} ``` From b513a00f4495636453523168dcf4eae9a5d38099 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 12:16:06 +0000 Subject: [PATCH 4/9] build(deps): bump golang.org/x/crypto from 0.28.0 to 0.30.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.28.0 to 0.30.0. - [Commits](https://github.com/golang/crypto/compare/v0.28.0...v0.30.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index a979f183..4b1f3698 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tinylib/msgp v1.2.5 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.57.0 - golang.org/x/crypto v0.28.0 + golang.org/x/crypto v0.30.0 ) require ( @@ -25,7 +25,7 @@ require ( github.com/valyala/tcplisten v1.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect golang.org/x/net v0.30.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 3601863b..40860c3c 100644 --- a/go.sum +++ b/go.sum @@ -35,16 +35,16 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= From f94ac3c6091b835a8b670b9845ad3a34c7750a2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:40:39 +0000 Subject: [PATCH 5/9] build(deps): bump golang.org/x/crypto from 0.30.0 to 0.31.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.30.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.30.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4b1f3698..af46fd9f 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/tinylib/msgp v1.2.5 github.com/valyala/bytebufferpool v1.0.0 github.com/valyala/fasthttp v1.57.0 - golang.org/x/crypto v0.30.0 + golang.org/x/crypto v0.31.0 ) require ( diff --git a/go.sum b/go.sum index 40860c3c..b6af6a6c 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 1134e1f408b7008c9711e9eb53af9ac855e13ff0 Mon Sep 17 00:00:00 2001 From: Kashiwa <13825170+ksw2000@users.noreply.github.com> Date: Fri, 13 Dec 2024 18:14:11 +0800 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=A9=B9=20fix:=20make=20SetValWithStru?= =?UTF-8?q?ct=20set=20zero=20values=20and=20support=20more=20types=20#3167?= =?UTF-8?q?=20(#3227)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🩹 fix: make SetValWithStruct set zero values and support more types * 🚨 test: check zero in int_slice * fix: SetValWithStruct does not exist in fiber v2 * 🩹fix: restrict supported types in SetValWithStruct * 🩹fix: golangci-lint --------- Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> --- client/request.go | 30 +++++++++------ client/request_test.go | 83 ++++++++++++++++++------------------------ 2 files changed, 54 insertions(+), 59 deletions(-) diff --git a/client/request.go b/client/request.go index f303fb74..2834a1ad 100644 --- a/client/request.go +++ b/client/request.go @@ -1062,11 +1062,14 @@ func ReleaseFile(f *File) { filePool.Put(f) } -// SetValWithStruct Set some values using structs. -// `p` is a structure that implements the WithStruct interface, -// The field name can be specified by `tagName`. -// `v` is a struct include some data. -// Note: This method only supports simple types and nested structs are not currently supported. +// SetValWithStruct stores the fields of `v` into `p`. +// `tagName` specifies the key used to store into `p`. If not specified, +// the field name is used by default. +// `v` is a struct or a pointer to a struct containing some data. +// Fields in `v` should be string, int, int8, int16, int32, int64, uint, +// uint8, uint16, uint32, uint64, float32, float64, complex64, +// complex128 or bool. Arrays or slices are inserted sequentially with the +// same key. Other types are ignored. func SetValWithStruct(p WithStruct, tagName string, v any) { valueOfV := reflect.ValueOf(v) typeOfV := reflect.TypeOf(v) @@ -1080,25 +1083,31 @@ func SetValWithStruct(p WithStruct, tagName string, v any) { } // Boring type judge. - // TODO: cover more types and complex data structure. - var setVal func(name string, value reflect.Value) + var setVal func(name string, val reflect.Value) setVal = func(name string, val reflect.Value) { switch val.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: p.Add(name, strconv.Itoa(int(val.Int()))) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + p.Add(name, strconv.FormatUint(val.Uint(), 10)) + case reflect.Float32, reflect.Float64: + p.Add(name, strconv.FormatFloat(val.Float(), 'f', -1, 64)) + case reflect.Complex64, reflect.Complex128: + p.Add(name, strconv.FormatComplex(val.Complex(), 'f', -1, 128)) case reflect.Bool: if val.Bool() { p.Add(name, "true") + } else { + p.Add(name, "false") } case reflect.String: p.Add(name, val.String()) - case reflect.Float32, reflect.Float64: - p.Add(name, strconv.FormatFloat(val.Float(), 'f', -1, 64)) case reflect.Slice, reflect.Array: for i := 0; i < val.Len(); i++ { setVal(name, val.Index(i)) } default: + return } } @@ -1113,9 +1122,6 @@ func SetValWithStruct(p WithStruct, tagName string, v any) { name = field.Name } val := valueOfV.Field(i) - if val.IsZero() { - continue - } // To cover slice and array, we delete the val then add it. p.Del(name) setVal(name, val) diff --git a/client/request_test.go b/client/request_test.go index e663f2fe..c00cf337 100644 --- a/client/request_test.go +++ b/client/request_test.go @@ -1530,14 +1530,16 @@ func Test_Request_MaxRedirects(t *testing.T) { func Test_SetValWithStruct(t *testing.T) { t.Parallel() - // test SetValWithStruct vai QueryParam struct. + // test SetValWithStruct via QueryParam struct. type args struct { TString string TSlice []string TIntSlice []int `param:"int_slice"` unexport int TInt int + TUint uint TFloat float64 + TComplex complex128 TBool bool } @@ -1550,18 +1552,22 @@ func Test_SetValWithStruct(t *testing.T) { SetValWithStruct(p, "param", args{ unexport: 5, TInt: 5, + TUint: 5, TString: "string", TFloat: 3.1, + TComplex: 3 + 4i, TBool: false, TSlice: []string{"foo", "bar"}, - TIntSlice: []int{1, 2}, + TIntSlice: []int{0, 1, 2}, }) require.Equal(t, "", string(p.Peek("unexport"))) require.Equal(t, []byte("5"), p.Peek("TInt")) + require.Equal(t, []byte("5"), p.Peek("TUint")) require.Equal(t, []byte("string"), p.Peek("TString")) require.Equal(t, []byte("3.1"), p.Peek("TFloat")) - require.Equal(t, "", string(p.Peek("TBool"))) + require.Equal(t, []byte("(3+4i)"), p.Peek("TComplex")) + require.Equal(t, []byte("false"), p.Peek("TBool")) require.True(t, func() bool { for _, v := range p.PeekMulti("TSlice") { if string(v) == "foo" { @@ -1580,6 +1586,15 @@ func Test_SetValWithStruct(t *testing.T) { return false }()) + require.True(t, func() bool { + for _, v := range p.PeekMulti("int_slice") { + if string(v) == "0" { + return true + } + } + return false + }()) + require.True(t, func() bool { for _, v := range p.PeekMulti("int_slice") { if string(v) == "1" { @@ -1655,24 +1670,6 @@ func Test_SetValWithStruct(t *testing.T) { }()) }) - t.Run("the zero val should be ignore", func(t *testing.T) { - t.Parallel() - p := &QueryParam{ - Args: fasthttp.AcquireArgs(), - } - SetValWithStruct(p, "param", &args{ - TInt: 0, - TString: "", - TFloat: 0.0, - }) - - require.Equal(t, "", string(p.Peek("TInt"))) - require.Equal(t, "", string(p.Peek("TString"))) - require.Equal(t, "", string(p.Peek("TFloat"))) - require.Empty(t, p.PeekMulti("TSlice")) - require.Empty(t, p.PeekMulti("int_slice")) - }) - t.Run("error type should ignore", func(t *testing.T) { t.Parallel() p := &QueryParam{ @@ -1684,14 +1681,16 @@ func Test_SetValWithStruct(t *testing.T) { } func Benchmark_SetValWithStruct(b *testing.B) { - // test SetValWithStruct vai QueryParam struct. + // test SetValWithStruct via QueryParam struct. type args struct { TString string TSlice []string TIntSlice []int `param:"int_slice"` unexport int TInt int + TUint uint TFloat float64 + TComplex complex128 TBool bool } @@ -1707,19 +1706,23 @@ func Benchmark_SetValWithStruct(b *testing.B) { SetValWithStruct(p, "param", args{ unexport: 5, TInt: 5, + TUint: 5, TString: "string", TFloat: 3.1, + TComplex: 3 + 4i, TBool: false, TSlice: []string{"foo", "bar"}, - TIntSlice: []int{1, 2}, + TIntSlice: []int{0, 1, 2}, }) } require.Equal(b, "", string(p.Peek("unexport"))) require.Equal(b, []byte("5"), p.Peek("TInt")) + require.Equal(b, []byte("5"), p.Peek("TUint")) require.Equal(b, []byte("string"), p.Peek("TString")) require.Equal(b, []byte("3.1"), p.Peek("TFloat")) - require.Equal(b, "", string(p.Peek("TBool"))) + require.Equal(b, []byte("(3+4i)"), p.Peek("TComplex")) + require.Equal(b, []byte("false"), p.Peek("TBool")) require.True(b, func() bool { for _, v := range p.PeekMulti("TSlice") { if string(v) == "foo" { @@ -1738,6 +1741,15 @@ func Benchmark_SetValWithStruct(b *testing.B) { return false }()) + require.True(b, func() bool { + for _, v := range p.PeekMulti("int_slice") { + if string(v) == "0" { + return true + } + } + return false + }()) + require.True(b, func() bool { for _, v := range p.PeekMulti("int_slice") { if string(v) == "1" { @@ -1817,29 +1829,6 @@ func Benchmark_SetValWithStruct(b *testing.B) { }()) }) - b.Run("the zero val should be ignore", func(b *testing.B) { - p := &QueryParam{ - Args: fasthttp.AcquireArgs(), - } - - b.ReportAllocs() - b.StartTimer() - - for i := 0; i < b.N; i++ { - SetValWithStruct(p, "param", &args{ - TInt: 0, - TString: "", - TFloat: 0.0, - }) - } - - require.Empty(b, string(p.Peek("TInt"))) - require.Empty(b, string(p.Peek("TString"))) - require.Empty(b, string(p.Peek("TFloat"))) - require.Empty(b, p.PeekMulti("TSlice")) - require.Empty(b, p.PeekMulti("int_slice")) - }) - b.Run("error type should ignore", func(b *testing.B) { p := &QueryParam{ Args: fasthttp.AcquireArgs(), From 02999352cdcb14576634c5777d2be8c286808c6e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 09:03:57 +0100 Subject: [PATCH 7/9] build(deps): bump github.com/valyala/fasthttp from 1.57.0 to 1.58.0 (#3244) * build(deps): bump github.com/valyala/fasthttp from 1.57.0 to 1.58.0 Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.57.0 to 1.58.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.57.0...v1.58.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Cookies should expire immediately as per RFC6265 --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Juan Calderon-Perez Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- middleware/session/middleware_test.go | 20 ++------------------ 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index af46fd9f..ebdc9080 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/stretchr/testify v1.10.0 github.com/tinylib/msgp v1.2.5 github.com/valyala/bytebufferpool v1.0.0 - github.com/valyala/fasthttp v1.57.0 + github.com/valyala/fasthttp v1.58.0 golang.org/x/crypto v0.31.0 ) @@ -24,7 +24,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect - golang.org/x/net v0.30.0 // indirect + golang.org/x/net v0.31.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index b6af6a6c..5b8204d1 100644 --- a/go.sum +++ b/go.sum @@ -27,8 +27,8 @@ github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po= github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= 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.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg= -github.com/valyala/fasthttp v1.57.0/go.mod h1:h6ZBaPRlzpZ6O3H5t2gEk1Qi33+TmLvfwgLLp0t9CpE= +github.com/valyala/fasthttp v1.58.0 h1:GGB2dWxSbEprU9j0iMJHgdKYJVDyjrOwF9RE59PbRuE= +github.com/valyala/fasthttp v1.58.0/go.mod h1:SYXvHHaFp7QZHGKSHmoMipInhrI5StHrhDTYVEjK/Kw= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -37,8 +37,8 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= diff --git a/middleware/session/middleware_test.go b/middleware/session/middleware_test.go index 579d61c4..9cbb8cd5 100644 --- a/middleware/session/middleware_test.go +++ b/middleware/session/middleware_test.go @@ -166,25 +166,9 @@ func Test_Session_Middleware(t *testing.T) { h(ctx) require.Equal(t, fiber.StatusOK, ctx.Response.StatusCode()) - // Verify the session cookie is set to expire + // Verify the session cookie has expired setCookieHeader := string(ctx.Response.Header.Peek(fiber.HeaderSetCookie)) - require.Contains(t, setCookieHeader, "expires=") - cookieParts := strings.Split(setCookieHeader, ";") - expired := false - for _, part := range cookieParts { - if strings.Contains(part, "expires=") { - part = strings.TrimSpace(part) - expiryDateStr := strings.TrimPrefix(part, "expires=") - // Correctly parse the date with "GMT" timezone - expiryDate, err := time.Parse(time.RFC1123, strings.TrimSpace(expiryDateStr)) - require.NoError(t, err) - if expiryDate.Before(time.Now()) { - expired = true - break - } - } - } - require.True(t, expired, "Session cookie should be expired") + require.Contains(t, setCookieHeader, "max-age=0") // Sleep so that the session expires time.Sleep(1 * time.Second) From 154c74d5785aa3f6db2f9f51c206e4891c5477e7 Mon Sep 17 00:00:00 2001 From: Iliya Date: Mon, 16 Dec 2024 11:37:41 +0330 Subject: [PATCH 8/9] =?UTF-8?q?=F0=9F=94=A5=20feat:=20Add=20support=20for?= =?UTF-8?q?=20configuring=20TLS=20Min=20Version=20(#3248)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make tls.Config MinVersion configurable This commit will resolve #3239 For more info: https://github.com/gofiber/fiber/issues/3239 * Add documents about tls minimum version configurable * Add if statement for don't allow to use TLS1.0 and TLS1.1 * Fix lint issues, add test for panic() * Update docs * Add test with valid TLS version --------- Co-authored-by: Juan Calderon-Perez --- docs/api/fiber.md | 3 ++- docs/whats_new.md | 8 ++++++++ listen.go | 23 +++++++++++++++++++---- listen_test.go | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/docs/api/fiber.md b/docs/api/fiber.md index a34a5d94..17cf3896 100644 --- a/docs/api/fiber.md +++ b/docs/api/fiber.md @@ -116,7 +116,8 @@ app.Listen(":8080", fiber.ListenConfig{ | OnShutdownError | `func(err error)` | Allows to customize error behavior when gracefully shutting down the server by given signal. Prints error with `log.Fatalf()` | `nil` | | OnShutdownSuccess | `func()` | Allows customizing success behavior when gracefully shutting down the server by given signal. | `nil` | | TLSConfigFunc | `func(tlsConfig *tls.Config)` | Allows customizing `tls.Config` as you want. | `nil` | -| AutoCertManager | `func(tlsConfig *tls.Config)` | Manages TLS certificates automatically using the ACME protocol. Enables integration with Let's Encrypt or other ACME-compatible providers. | `nil` | +| AutoCertManager | `*autocert.Manager` | Manages TLS certificates automatically using the ACME protocol. Enables integration with Let's Encrypt or other ACME-compatible providers. | `nil` | +| TLSMinVersion | `uint16` | Allows customizing the TLS minimum version. | `tls.VersionTLS12` | ### Listen diff --git a/docs/whats_new.md b/docs/whats_new.md index bfc6f25c..eadc1afa 100644 --- a/docs/whats_new.md +++ b/docs/whats_new.md @@ -130,6 +130,14 @@ In this example, a custom context `CustomCtx` is created with an additional meth
+### Configurable TLS Minimum Version + +We have added support for configuring the TLS minimum version. This field allows you to set the TLS minimum version for TLSAutoCert and the server listener. + +```go +app.Listen(":444", fiber.ListenConfig{TLSMinVersion: tls.VersionTLS12}) +``` + #### TLS AutoCert support (ACME / Let's Encrypt) We have added native support for automatic certificates management from Let's Encrypt and any other ACME-based providers. diff --git a/listen.go b/listen.go index e0c55369..793d36d2 100644 --- a/listen.go +++ b/listen.go @@ -108,6 +108,12 @@ type ListenConfig struct { // Default: 10 * time.Second ShutdownTimeout time.Duration `json:"shutdown_timeout"` + // TLSMinVersion allows to set TLS minimum version. + // + // Default: tls.VersionTLS12 + // WARNING: TLS1.0 and TLS1.1 versions are not supported. + TLSMinVersion uint16 `json:"tls_min_version"` + // When set to true, it will not print out the «Fiber» ASCII art and listening address. // // Default: false @@ -128,6 +134,7 @@ type ListenConfig struct { func listenConfigDefault(config ...ListenConfig) ListenConfig { if len(config) < 1 { return ListenConfig{ + TLSMinVersion: tls.VersionTLS12, ListenerNetwork: NetworkTCP4, OnShutdownError: func(err error) { log.Fatalf("shutdown: %v", err) //nolint:revive // It's an option @@ -147,6 +154,14 @@ func listenConfigDefault(config ...ListenConfig) ListenConfig { } } + if cfg.TLSMinVersion == 0 { + cfg.TLSMinVersion = tls.VersionTLS12 + } + + if cfg.TLSMinVersion != tls.VersionTLS12 && cfg.TLSMinVersion != tls.VersionTLS13 { + panic("unsupported TLS version, please use tls.VersionTLS12 or tls.VersionTLS13") + } + return cfg } @@ -168,8 +183,8 @@ func (app *App) Listen(addr string, config ...ListenConfig) error { } tlsHandler := &TLSHandler{} - tlsConfig = &tls.Config{ - MinVersion: tls.VersionTLS12, + tlsConfig = &tls.Config{ //nolint:gosec // This is a user input + MinVersion: cfg.TLSMinVersion, Certificates: []tls.Certificate{ cert, }, @@ -192,8 +207,8 @@ func (app *App) Listen(addr string, config ...ListenConfig) error { // Attach the tlsHandler to the config app.SetTLSHandler(tlsHandler) } else if cfg.AutoCertManager != nil { - tlsConfig = &tls.Config{ - MinVersion: tls.VersionTLS12, + tlsConfig = &tls.Config{ //nolint:gosec // This is a user input + MinVersion: cfg.TLSMinVersion, GetCertificate: cfg.AutoCertManager.GetCertificate, NextProtos: []string{"http/1.1", "acme-tls/1"}, } diff --git a/listen_test.go b/listen_test.go index 123cf2b3..032f7d32 100644 --- a/listen_test.go +++ b/listen_test.go @@ -236,6 +236,43 @@ func Test_Listen_Prefork(t *testing.T) { require.NoError(t, app.Listen(":99999", ListenConfig{DisableStartupMessage: true, EnablePrefork: true})) } +// go test -run Test_Listen_TLSMinVersion +func Test_Listen_TLSMinVersion(t *testing.T) { + testPreforkMaster = true + + app := New() + + // Invalid TLSMinVersion + require.Panics(t, func() { + _ = app.Listen(":443", ListenConfig{TLSMinVersion: tls.VersionTLS10}) //nolint:errcheck // ignore error + }) + require.Panics(t, func() { + _ = app.Listen(":443", ListenConfig{TLSMinVersion: tls.VersionTLS11}) //nolint:errcheck // ignore error + }) + + // Prefork + require.Panics(t, func() { + _ = app.Listen(":443", ListenConfig{DisableStartupMessage: true, EnablePrefork: true, TLSMinVersion: tls.VersionTLS10}) //nolint:errcheck // ignore error + }) + require.Panics(t, func() { + _ = app.Listen(":443", ListenConfig{DisableStartupMessage: true, EnablePrefork: true, TLSMinVersion: tls.VersionTLS11}) //nolint:errcheck // ignore error + }) + + // Valid TLSMinVersion + go func() { + time.Sleep(1000 * time.Millisecond) + assert.NoError(t, app.Shutdown()) + }() + require.NoError(t, app.Listen(":0", ListenConfig{TLSMinVersion: tls.VersionTLS13})) + + // Valid TLSMinVersion with Prefork + go func() { + time.Sleep(1000 * time.Millisecond) + assert.NoError(t, app.Shutdown()) + }() + require.NoError(t, app.Listen(":99999", ListenConfig{DisableStartupMessage: true, EnablePrefork: true, TLSMinVersion: tls.VersionTLS13})) +} + // go test -run Test_Listen_TLS func Test_Listen_TLS(t *testing.T) { app := New() From c2b557c5ace2998842ca1cec67f4ad953a8eadb9 Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Mon, 16 Dec 2024 08:25:15 -0500 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=A7=B9=20chore:=20Update=20documentat?= =?UTF-8?q?ion=20for=20Fiber=20client=20(#3249)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Update client package inline documentation * Update inline docs * Update Client Request docs * Fix formatting issues * Update docs * Update docs/client/response.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * Add missing space * Remove return statement * Update request.go * Update docs/client/response.md * Update docs/client/hooks.md --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: RW --- client/client.go | 294 ++++++++++++------------------- client/cookiejar.go | 86 +++++---- client/core.go | 102 +++++------ client/hooks.go | 87 +++++---- client/request.go | 382 ++++++++++++++++++---------------------- client/response.go | 73 ++++---- docs/client/hooks.md | 72 +++++--- docs/client/request.md | 302 +++++++++++++------------------ docs/client/response.md | 77 ++++---- docs/client/rest.md | 347 ++++++++++++++++++------------------ 10 files changed, 827 insertions(+), 995 deletions(-) diff --git a/client/client.go b/client/client.go index 2c4086ae..d42af366 100644 --- a/client/client.go +++ b/client/client.go @@ -24,16 +24,13 @@ import ( var ErrFailedToAppendCert = errors.New("failed to append certificate") -// The Client is used to create a Fiber Client with -// client-level settings that apply to all requests -// raise from the client. +// Client is used to create a Fiber client with client-level settings that +// apply to all requests made by the client. // -// Fiber Client also provides an option to override -// or merge most of the client settings at the request. +// The Fiber client also provides an option to override or merge most of the +// client settings at the request level. type Client struct { - // logger - logger log.CommonLogger - + logger log.CommonLogger fasthttp *fasthttp.Client header *Header @@ -48,45 +45,32 @@ type Client struct { cborMarshal utils.CBORMarshal cborUnmarshal utils.CBORUnmarshal - cookieJar *CookieJar - - // retry - retryConfig *RetryConfig - - baseURL string - userAgent string - referer string - - // user defined request hooks - userRequestHooks []RequestHook - - // client package defined request hooks - builtinRequestHooks []RequestHook - - // user defined response hooks - userResponseHooks []ResponseHook - - // client package defined response hooks + cookieJar *CookieJar + retryConfig *RetryConfig + baseURL string + userAgent string + referer string + userRequestHooks []RequestHook + builtinRequestHooks []RequestHook + userResponseHooks []ResponseHook builtinResponseHooks []ResponseHook timeout time.Duration - - mu sync.RWMutex - - debug bool + mu sync.RWMutex + debug bool } -// R raise a request from the client. +// R creates a new Request associated with the client. func (c *Client) R() *Request { return AcquireRequest().SetClient(c) } -// RequestHook Request returns user-defined request hooks. +// RequestHook returns the user-defined request hooks. func (c *Client) RequestHook() []RequestHook { return c.userRequestHooks } -// AddRequestHook Add user-defined request hooks. +// AddRequestHook adds user-defined request hooks. func (c *Client) AddRequestHook(h ...RequestHook) *Client { c.mu.Lock() defer c.mu.Unlock() @@ -95,12 +79,12 @@ func (c *Client) AddRequestHook(h ...RequestHook) *Client { return c } -// ResponseHook return user-define response hooks. +// ResponseHook returns the user-defined response hooks. func (c *Client) ResponseHook() []ResponseHook { return c.userResponseHooks } -// AddResponseHook Add user-defined response hooks. +// AddResponseHook adds user-defined response hooks. func (c *Client) AddResponseHook(h ...ResponseHook) *Client { c.mu.Lock() defer c.mu.Unlock() @@ -109,74 +93,74 @@ func (c *Client) AddResponseHook(h ...ResponseHook) *Client { return c } -// JSONMarshal returns json marshal function in Core. +// JSONMarshal returns the JSON marshal function used by the client. func (c *Client) JSONMarshal() utils.JSONMarshal { return c.jsonMarshal } -// SetJSONMarshal sets the JSON encoder. +// SetJSONMarshal sets the JSON marshal function to use. func (c *Client) SetJSONMarshal(f utils.JSONMarshal) *Client { c.jsonMarshal = f return c } -// JSONUnmarshal returns json unmarshal function in Core. +// JSONUnmarshal returns the JSON unmarshal function used by the client. func (c *Client) JSONUnmarshal() utils.JSONUnmarshal { return c.jsonUnmarshal } -// Set json decoder. +// SetJSONUnmarshal sets the JSON unmarshal function to use. func (c *Client) SetJSONUnmarshal(f utils.JSONUnmarshal) *Client { c.jsonUnmarshal = f return c } -// XMLMarshal returns xml marshal function in Core. +// XMLMarshal returns the XML marshal function used by the client. func (c *Client) XMLMarshal() utils.XMLMarshal { return c.xmlMarshal } -// SetXMLMarshal Set xml encoder. +// SetXMLMarshal sets the XML marshal function to use. func (c *Client) SetXMLMarshal(f utils.XMLMarshal) *Client { c.xmlMarshal = f return c } -// XMLUnmarshal returns xml unmarshal function in Core. +// XMLUnmarshal returns the XML unmarshal function used by the client. func (c *Client) XMLUnmarshal() utils.XMLUnmarshal { return c.xmlUnmarshal } -// SetXMLUnmarshal Set xml decoder. +// SetXMLUnmarshal sets the XML unmarshal function to use. func (c *Client) SetXMLUnmarshal(f utils.XMLUnmarshal) *Client { c.xmlUnmarshal = f return c } -// CBORMarshal returns CBOR marshal function in Core. +// CBORMarshal returns the CBOR marshal function used by the client. func (c *Client) CBORMarshal() utils.CBORMarshal { return c.cborMarshal } -// SetCBORMarshal sets CBOR encoder. +// SetCBORMarshal sets the CBOR marshal function to use. func (c *Client) SetCBORMarshal(f utils.CBORMarshal) *Client { c.cborMarshal = f return c } -// CBORUnmarshal returns CBOR unmarshal function in Core. +// CBORUnmarshal returns the CBOR unmarshal function used by the client. func (c *Client) CBORUnmarshal() utils.CBORUnmarshal { return c.cborUnmarshal } -// SetCBORUnmarshal sets CBOR decoder. +// SetCBORUnmarshal sets the CBOR unmarshal function to use. func (c *Client) SetCBORUnmarshal(f utils.CBORUnmarshal) *Client { c.cborUnmarshal = f return c } -// TLSConfig returns tlsConfig in client. -// If client don't have tlsConfig, this function will init it. +// TLSConfig returns the client's TLS configuration. +// If none is set, it initializes a new one. func (c *Client) TLSConfig() *tls.Config { if c.fasthttp.TLSConfig == nil { c.fasthttp.TLSConfig = &tls.Config{ @@ -187,20 +171,20 @@ func (c *Client) TLSConfig() *tls.Config { return c.fasthttp.TLSConfig } -// SetTLSConfig sets tlsConfig in client. +// SetTLSConfig sets the TLS configuration for the client. func (c *Client) SetTLSConfig(config *tls.Config) *Client { c.fasthttp.TLSConfig = config return c } -// SetCertificates method sets client certificates into client. +// SetCertificates adds certificates to the client's TLS configuration. func (c *Client) SetCertificates(certs ...tls.Certificate) *Client { config := c.TLSConfig() config.Certificates = append(config.Certificates, certs...) return c } -// SetRootCertificate adds one or more root certificates into client. +// SetRootCertificate adds one or more root certificates to the client's TLS configuration. func (c *Client) SetRootCertificate(path string) *Client { cleanPath := filepath.Clean(path) file, err := os.Open(cleanPath) @@ -230,7 +214,7 @@ func (c *Client) SetRootCertificate(path string) *Client { return c } -// SetRootCertificateFromString method adds one or more root certificates into client. +// SetRootCertificateFromString adds one or more root certificates from a string to the client's TLS configuration. func (c *Client) SetRootCertificateFromString(pem string) *Client { config := c.TLSConfig() @@ -245,19 +229,18 @@ func (c *Client) SetRootCertificateFromString(pem string) *Client { return c } -// SetProxyURL sets proxy url in client. It will apply via core to hostclient. +// SetProxyURL sets the proxy URL for the client. This affects all subsequent requests. func (c *Client) SetProxyURL(proxyURL string) error { c.fasthttp.Dial = fasthttpproxy.FasthttpHTTPDialer(proxyURL) - return nil } -// RetryConfig returns retry config in client. +// RetryConfig returns the current retry configuration. func (c *Client) RetryConfig() *RetryConfig { return c.retryConfig } -// SetRetryConfig sets retry config in client which is impl by addon/retry package. +// SetRetryConfig sets the retry configuration for the client. func (c *Client) SetRetryConfig(config *RetryConfig) *Client { c.mu.Lock() defer c.mu.Unlock() @@ -266,58 +249,49 @@ func (c *Client) SetRetryConfig(config *RetryConfig) *Client { return c } -// BaseURL returns baseurl in Client instance. +// BaseURL returns the client's base URL. func (c *Client) BaseURL() string { return c.baseURL } -// SetBaseURL Set baseUrl which is prefix of real url. +// SetBaseURL sets the base URL prefix for all requests made by the client. func (c *Client) SetBaseURL(url string) *Client { c.baseURL = url return c } -// Header method returns header value via key, -// this method will visit all field in the header, -// then sort them. +// Header returns all header values associated with the provided key. func (c *Client) Header(key string) []string { return c.header.PeekMultiple(key) } -// AddHeader method adds a single header field and its value in the client instance. -// These headers will be applied to all requests raised from this client instance. -// Also, it can be overridden at request level header options. +// AddHeader adds a single header field and its value to the client. These headers apply to all requests. func (c *Client) AddHeader(key, val string) *Client { c.header.Add(key, val) return c } -// SetHeader method sets a single header field and its value in the client instance. -// These headers will be applied to all requests raised from this client instance. -// Also, it can be overridden at request level header options. +// SetHeader sets a single header field and its value in the client. func (c *Client) SetHeader(key, val string) *Client { c.header.Set(key, val) return c } -// AddHeaders method adds multiple headers field and its values at one go in the client instance. -// These headers will be applied to all requests raised from this client instance. Also it can be -// overridden at request level headers options. +// AddHeaders adds multiple header fields and their values to the client. func (c *Client) AddHeaders(h map[string][]string) *Client { c.header.AddHeaders(h) return c } // SetHeaders method sets multiple headers field and its values at one go in the client instance. -// These headers will be applied to all requests raised from this client instance. Also it can be +// These headers will be applied to all requests created from this client instance. Also it can be // overridden at request level headers options. func (c *Client) SetHeaders(h map[string]string) *Client { c.header.SetHeaders(h) return c } -// Param method returns params value via key, -// this method will visit all field in the query param. +// Param returns all values of the specified query parameter. func (c *Client) Param(key string) []string { res := []string{} tmp := c.params.PeekMulti(key) @@ -328,47 +302,38 @@ func (c *Client) Param(key string) []string { return res } -// AddParam method adds a single query param field and its value in the client instance. -// These params will be applied to all requests raised from this client instance. -// Also, it can be overridden at request level param options. +// AddParam adds a single query parameter and its value to the client. +// These params will be applied to all requests created from this client instance. func (c *Client) AddParam(key, val string) *Client { c.params.Add(key, val) return c } -// SetParam method sets a single query param field and its value in the client instance. -// These params will be applied to all requests raised from this client instance. -// Also, it can be overridden at request level param options. +// SetParam sets a single query parameter and its value in the client. func (c *Client) SetParam(key, val string) *Client { c.params.Set(key, val) return c } -// AddParams method adds multiple query params field and its values at one go in the client instance. -// These params will be applied to all requests raised from this client instance. Also it can be -// overridden at request level params options. +// AddParams adds multiple query parameters and their values to the client. func (c *Client) AddParams(m map[string][]string) *Client { c.params.AddParams(m) return c } -// SetParams method sets multiple params field and its values at one go in the client instance. -// These params will be applied to all requests raised from this client instance. Also it can be -// overridden at request level params options. +// SetParams sets multiple query parameters and their values in the client. func (c *Client) SetParams(m map[string]string) *Client { c.params.SetParams(m) return c } -// SetParamsWithStruct method sets multiple params field and its values at one go in the client instance. -// These params will be applied to all requests raised from this client instance. Also it can be -// overridden at request level params options. +// SetParamsWithStruct sets multiple query parameters and their values using a struct. func (c *Client) SetParamsWithStruct(v any) *Client { c.params.SetParamsWithStruct(v) return c } -// DelParams method deletes single or multiple params field and its values in client. +// DelParams deletes one or more query parameters and their values from the client. func (c *Client) DelParams(key ...string) *Client { for _, v := range key { c.params.Del(v) @@ -376,64 +341,51 @@ func (c *Client) DelParams(key ...string) *Client { return c } -// SetUserAgent method sets userAgent field and its value in the client instance. -// This ua will be applied to all requests raised from this client instance. -// Also it can be overridden at request level ua options. +// SetUserAgent sets the User-Agent header for the client. func (c *Client) SetUserAgent(ua string) *Client { c.userAgent = ua return c } -// SetReferer method sets referer field and its value in the client instance. -// This referer will be applied to all requests raised from this client instance. -// Also it can be overridden at request level referer options. +// SetReferer sets the Referer header for the client. func (c *Client) SetReferer(r string) *Client { c.referer = r return c } -// PathParam returns the path param be set in request instance. -// if path param doesn't exist, return empty string. +// PathParam returns the value of the specified path parameter. Returns an empty string if it does not exist. func (c *Client) PathParam(key string) string { if val, ok := (*c.path)[key]; ok { return val } - return "" } -// SetPathParam method sets a single path param field and its value in the client instance. -// These path params will be applied to all requests raised from this client instance. -// Also it can be overridden at request level path params options. +// SetPathParam sets a single path parameter and its value in the client. func (c *Client) SetPathParam(key, val string) *Client { c.path.SetParam(key, val) return c } -// SetPathParams method sets multiple path params field and its values at one go in the client instance. -// These path params will be applied to all requests raised from this client instance. Also it can be -// overridden at request level path params options. +// SetPathParams sets multiple path parameters and their values in the client. func (c *Client) SetPathParams(m map[string]string) *Client { c.path.SetParams(m) return c } -// SetPathParamsWithStruct method sets multiple path params field and its values at one go in the client instance. -// These path params will be applied to all requests raised from this client instance. Also it can be -// overridden at request level path params options. +// SetPathParamsWithStruct sets multiple path parameters and their values using a struct. func (c *Client) SetPathParamsWithStruct(v any) *Client { c.path.SetParamsWithStruct(v) return c } -// DelPathParams method deletes single or multiple path params field and its values in client. +// DelPathParams deletes one or more path parameters and their values from the client. func (c *Client) DelPathParams(key ...string) *Client { c.path.DelParams(key...) return c } -// Cookie returns the cookie be set in request instance. -// if cookie doesn't exist, return empty string. +// Cookie returns the value of the specified cookie. Returns an empty string if it does not exist. func (c *Client) Cookie(key string) string { if val, ok := (*c.cookies)[key]; ok { return val @@ -441,127 +393,111 @@ func (c *Client) Cookie(key string) string { return "" } -// SetCookie method sets a single cookie field and its value in the client instance. -// These cookies will be applied to all requests raised from this client instance. -// Also it can be overridden at request level cookie options. +// SetCookie sets a single cookie and its value in the client. func (c *Client) SetCookie(key, val string) *Client { c.cookies.SetCookie(key, val) return c } -// SetCookies method sets multiple cookies field and its values at one go in the client instance. -// These cookies will be applied to all requests raised from this client instance. Also it can be -// overridden at request level cookie options. +// SetCookies sets multiple cookies and their values in the client. func (c *Client) SetCookies(m map[string]string) *Client { c.cookies.SetCookies(m) return c } -// SetCookiesWithStruct method sets multiple cookies field and its values at one go in the client instance. -// These cookies will be applied to all requests raised from this client instance. Also it can be -// overridden at request level cookies options. +// SetCookiesWithStruct sets multiple cookies and their values using a struct. func (c *Client) SetCookiesWithStruct(v any) *Client { c.cookies.SetCookiesWithStruct(v) return c } -// DelCookies method deletes single or multiple cookies field and its values in client. +// DelCookies deletes one or more cookies and their values from the client. func (c *Client) DelCookies(key ...string) *Client { c.cookies.DelCookies(key...) return c } -// SetTimeout method sets timeout val in client instance. -// This value will be applied to all requests raised from this client instance. -// Also, it can be overridden at request level timeout options. +// SetTimeout sets the timeout value for the client. This applies to all requests unless overridden at the request level. func (c *Client) SetTimeout(t time.Duration) *Client { c.timeout = t return c } -// Debug enable log debug level output. +// Debug enables debug-level logging output. func (c *Client) Debug() *Client { c.debug = true return c } -// DisableDebug disables log debug level output. +// DisableDebug disables debug-level logging output. func (c *Client) DisableDebug() *Client { c.debug = false return c } -// SetCookieJar sets cookie jar in client instance. +// SetCookieJar sets the cookie jar for the client. func (c *Client) SetCookieJar(cookieJar *CookieJar) *Client { c.cookieJar = cookieJar return c } -// Get provide an API like axios which send get request. +// Get sends a GET request to the specified URL, similar to axios. func (c *Client) Get(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Get(url) } -// Post provide an API like axios which send post request. +// Post sends a POST request to the specified URL, similar to axios. func (c *Client) Post(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Post(url) } -// Head provide a API like axios which send head request. +// Head sends a HEAD request to the specified URL, similar to axios. func (c *Client) Head(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Head(url) } -// Put provide an API like axios which send put request. +// Put sends a PUT request to the specified URL, similar to axios. func (c *Client) Put(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Put(url) } -// Delete provide an API like axios which send delete request. +// Delete sends a DELETE request to the specified URL, similar to axios. func (c *Client) Delete(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Delete(url) } -// Options provide an API like axios which send options request. +// Options sends an OPTIONS request to the specified URL, similar to axios. func (c *Client) Options(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Options(url) } -// Patch provide an API like axios which send patch request. +// Patch sends a PATCH request to the specified URL, similar to axios. func (c *Client) Patch(url string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Patch(url) } -// Custom provide an API like axios which send custom request. +// Custom sends a request with a custom method to the specified URL, similar to axios. func (c *Client) Custom(url, method string, cfg ...Config) (*Response, error) { req := AcquireRequest().SetClient(c) setConfigToRequest(req, cfg...) - return req.Custom(url, method) } -// SetDial sets dial function in client. +// SetDial sets the custom dial function for the client. func (c *Client) SetDial(dial fasthttp.DialFunc) *Client { c.mu.Lock() defer c.mu.Unlock() @@ -570,7 +506,7 @@ func (c *Client) SetDial(dial fasthttp.DialFunc) *Client { return c } -// SetLogger sets logger instance in client. +// SetLogger sets the logger instance used by the client. func (c *Client) SetLogger(logger log.CommonLogger) *Client { c.mu.Lock() defer c.mu.Unlock() @@ -579,12 +515,12 @@ func (c *Client) SetLogger(logger log.CommonLogger) *Client { return c } -// Logger returns logger instance of client. +// Logger returns the logger instance used by the client. func (c *Client) Logger() log.CommonLogger { return c.logger } -// Reset clears the Client object +// Reset resets the client to its default state, clearing most configurations. func (c *Client) Reset() { c.fasthttp = &fasthttp.Client{} c.baseURL = "" @@ -605,31 +541,25 @@ func (c *Client) Reset() { c.params.Reset() } -// Config for easy to set the request parameters, it should be -// noted that when setting the request body will use JSON as -// the default serialization mechanism, while the priority of -// Body is higher than FormData, and the priority of FormData -// is higher than File. +// Config is used to easily set request parameters. Note that when setting a request body, +// JSON is used as the default serialization mechanism. The priority is: +// Body > FormData > File. type Config struct { - Ctx context.Context //nolint:containedctx // It's needed to be stored in the config. - - Body any - Header map[string]string - Param map[string]string - Cookie map[string]string - PathParam map[string]string - - FormData map[string]string - - UserAgent string - Referer string - File []*File - + Ctx context.Context //nolint:containedctx // It's needed to be stored in the config. + Body any + Header map[string]string + Param map[string]string + Cookie map[string]string + PathParam map[string]string + FormData map[string]string + UserAgent string + Referer string + File []*File Timeout time.Duration MaxRedirects int } -// setConfigToRequest Set the parameters passed via Config to Request. +// setConfigToRequest sets the parameters passed via Config to the Request. func setConfigToRequest(req *Request, config ...Config) { if len(config) == 0 { return @@ -694,21 +624,19 @@ var ( defaultUserAgent = "fiber" ) -// init acquire a default client. func init() { defaultClient = New() } // New creates and returns a new Client object. func New() *Client { - // FOllOW-UP performance optimization - // trie to use a pool to reduce the cost of memory allocation - // for the fiber client and the fasthttp client - // if possible also for other structs -> request header, cookie, query param, path param... + // Follow-up performance optimizations: + // Try to use a pool to reduce the memory allocation cost for the Fiber client and the fasthttp client. + // If possible, also consider pooling other structs (e.g., request headers, cookies, query parameters, path parameters). return NewWithClient(&fasthttp.Client{}) } -// NewWithClient creates and returns a new Client object from an existing client. +// NewWithClient creates and returns a new Client object from an existing fasthttp.Client. func NewWithClient(c *fasthttp.Client) *Client { if c == nil { panic("fasthttp.Client must not be nil") @@ -738,12 +666,12 @@ func NewWithClient(c *fasthttp.Client) *Client { } } -// C get default client. +// C returns the default client. func C() *Client { return defaultClient } -// Replace the defaultClient, the returned function can undo. +// Replace replaces the defaultClient with a new one, returning a function to restore the old client. func Replace(c *Client) func() { replaceMu.Lock() defer replaceMu.Unlock() @@ -759,37 +687,37 @@ func Replace(c *Client) func() { } } -// Get send a get request use defaultClient, a convenient method. +// Get sends a GET request using the default client. func Get(url string, cfg ...Config) (*Response, error) { return C().Get(url, cfg...) } -// Post send a post request use defaultClient, a convenient method. +// Post sends a POST request using the default client. func Post(url string, cfg ...Config) (*Response, error) { return C().Post(url, cfg...) } -// Head send a head request use defaultClient, a convenient method. +// Head sends a HEAD request using the default client. func Head(url string, cfg ...Config) (*Response, error) { return C().Head(url, cfg...) } -// Put send a put request use defaultClient, a convenient method. +// Put sends a PUT request using the default client. func Put(url string, cfg ...Config) (*Response, error) { return C().Put(url, cfg...) } -// Delete send a delete request use defaultClient, a convenient method. +// Delete sends a DELETE request using the default client. func Delete(url string, cfg ...Config) (*Response, error) { return C().Delete(url, cfg...) } -// Options send a options request use defaultClient, a convenient method. +// Options sends an OPTIONS request using the default client. func Options(url string, cfg ...Config) (*Response, error) { return C().Options(url, cfg...) } -// Patch send a patch request use defaultClient, a convenient method. +// Patch sends a PATCH request using the default client. func Patch(url string, cfg ...Config) (*Response, error) { return C().Patch(url, cfg...) } diff --git a/client/cookiejar.go b/client/cookiejar.go index 834357fb..92669b70 100644 --- a/client/cookiejar.go +++ b/client/cookiejar.go @@ -1,4 +1,4 @@ -// The code has been taken from https://github.com/valyala/fasthttp/pull/526 originally. +// The code was originally taken from https://github.com/valyala/fasthttp/pull/526. package client import ( @@ -18,7 +18,7 @@ var cookieJarPool = sync.Pool{ }, } -// AcquireCookieJar returns an empty CookieJar object from pool. +// AcquireCookieJar returns an empty CookieJar object from the pool. func AcquireCookieJar() *CookieJar { jar, ok := cookieJarPool.Get().(*CookieJar) if !ok { @@ -28,22 +28,23 @@ func AcquireCookieJar() *CookieJar { return jar } -// ReleaseCookieJar returns CookieJar to the pool. +// ReleaseCookieJar returns a CookieJar object to the pool. func ReleaseCookieJar(c *CookieJar) { c.Release() cookieJarPool.Put(c) } -// CookieJar manages cookie storage. It is used by the client to store cookies. +// CookieJar manages cookie storage for the client. It stores cookies keyed by host. type CookieJar struct { hostCookies map[string][]*fasthttp.Cookie mu sync.Mutex } -// Get returns the cookies stored from a specific domain. -// If there were no cookies related with host returned slice will be nil. +// Get returns all cookies stored for a given URI. If there are no cookies for the +// provided host, the returned slice will be nil. // -// CookieJar keeps a copy of the cookies, so the returned cookies can be released safely. +// The CookieJar keeps its own copies of cookies, so it is safe to release the returned +// cookies after use. func (cj *CookieJar) Get(uri *fasthttp.URI) []*fasthttp.Cookie { if uri == nil { return nil @@ -52,7 +53,7 @@ func (cj *CookieJar) Get(uri *fasthttp.URI) []*fasthttp.Cookie { return cj.getByHostAndPath(uri.Host(), uri.Path()) } -// get returns the cookies stored from a specific host and path. +// getByHostAndPath returns cookies stored for a specific host and path. func (cj *CookieJar) getByHostAndPath(host, path []byte) []*fasthttp.Cookie { if cj.hostCookies == nil { return nil @@ -84,8 +85,7 @@ func (cj *CookieJar) getByHostAndPath(host, path []byte) []*fasthttp.Cookie { return newCookies } -// getCookiesByHost returns the cookies stored from a specific host. -// If cookies are expired they will be deleted. +// getCookiesByHost returns cookies stored for a specific host, removing any that have expired. func (cj *CookieJar) getCookiesByHost(host string) []*fasthttp.Cookie { cj.mu.Lock() defer cj.mu.Unlock() @@ -95,7 +95,8 @@ func (cj *CookieJar) getCookiesByHost(host string) []*fasthttp.Cookie { for i := 0; i < len(cookies); i++ { c := cookies[i] - if !c.Expire().Equal(fasthttp.CookieExpireUnlimited) && c.Expire().Before(now) { // release cookie if expired + // Remove expired cookies. + if !c.Expire().Equal(fasthttp.CookieExpireUnlimited) && c.Expire().Before(now) { cookies = append(cookies[:i], cookies[i+1:]...) fasthttp.ReleaseCookie(c) i-- @@ -105,23 +106,21 @@ func (cj *CookieJar) getCookiesByHost(host string) []*fasthttp.Cookie { return cookies } -// Set sets cookies for a specific host. -// The host is get from uri.Host(). -// If the cookie key already exists it will be replaced by the new cookie value. +// Set stores the given cookies for the specified URI host. If a cookie key already exists, +// it will be replaced by the new cookie value. // -// CookieJar keeps a copy of the cookies, so the parsed cookies can be released safely. +// CookieJar stores copies of the provided cookies, so they may be safely released after use. func (cj *CookieJar) Set(uri *fasthttp.URI, cookies ...*fasthttp.Cookie) { if uri == nil { return } - cj.SetByHost(uri.Host(), cookies...) } -// SetByHost sets cookies for a specific host. -// If the cookie key already exists it will be replaced by the new cookie value. +// SetByHost stores the given cookies for the specified host. If a cookie key already exists, +// it will be replaced by the new cookie value. // -// CookieJar keeps a copy of the cookies, so the parsed cookies can be released safely. +// CookieJar stores copies of the provided cookies, so they may be safely released after use. func (cj *CookieJar) SetByHost(host []byte, cookies ...*fasthttp.Cookie) { hostStr := utils.UnsafeString(host) @@ -134,26 +133,25 @@ func (cj *CookieJar) SetByHost(host []byte, cookies ...*fasthttp.Cookie) { hostCookies, ok := cj.hostCookies[hostStr] if !ok { - // If the key does not exist in the map, then we must make a copy for the key to avoid unsafe usage. + // If the key does not exist in the map, make a copy to avoid unsafe usage. hostStr = string(host) } for _, cookie := range cookies { - c := searchCookieByKeyAndPath(cookie.Key(), cookie.Path(), hostCookies) - if c == nil { - // If the cookie does not exist in the slice, let's acquire new cookie and store it. - c = fasthttp.AcquireCookie() - hostCookies = append(hostCookies, c) + existing := searchCookieByKeyAndPath(cookie.Key(), cookie.Path(), hostCookies) + if existing == nil { + // If the cookie does not exist, acquire a new one. + existing = fasthttp.AcquireCookie() + hostCookies = append(hostCookies, existing) } - c.CopyTo(cookie) // override cookie properties + existing.CopyTo(cookie) // Override cookie properties. } cj.hostCookies[hostStr] = hostCookies } -// SetKeyValue sets a cookie by key and value for a specific host. +// SetKeyValue sets a cookie for the specified host with the given key and value. // -// This function prevents extra allocations by making repeated cookies -// not being duplicated. +// This function helps prevent extra allocations by avoiding duplication of repeated cookies. func (cj *CookieJar) SetKeyValue(host, key, value string) { c := fasthttp.AcquireCookie() c.SetKey(key) @@ -162,10 +160,9 @@ func (cj *CookieJar) SetKeyValue(host, key, value string) { cj.SetByHost(utils.UnsafeBytes(host), c) } -// SetKeyValueBytes sets a cookie by key and value for a specific host. +// SetKeyValueBytes sets a cookie for the specified host using byte slices for the key and value. // -// This function prevents extra allocations by making repeated cookies -// not being duplicated. +// This function helps prevent extra allocations by avoiding duplication of repeated cookies. func (cj *CookieJar) SetKeyValueBytes(host string, key, value []byte) { c := fasthttp.AcquireCookie() c.SetKeyBytes(key) @@ -174,17 +171,16 @@ func (cj *CookieJar) SetKeyValueBytes(host string, key, value []byte) { cj.SetByHost(utils.UnsafeBytes(host), c) } -// dumpCookiesToReq dumps the stored cookies to the request. +// dumpCookiesToReq writes the stored cookies to the given request. func (cj *CookieJar) dumpCookiesToReq(req *fasthttp.Request) { uri := req.URI() - cookies := cj.getByHostAndPath(uri.Host(), uri.Path()) for _, cookie := range cookies { req.Header.SetCookieBytesKV(cookie.Key(), cookie.Value()) } } -// parseCookiesFromResp parses the response cookies and stores them. +// parseCookiesFromResp parses the cookies from the response and stores them for the specified host and path. func (cj *CookieJar) parseCookiesFromResp(host, path []byte, resp *fasthttp.Response) { hostStr := utils.UnsafeString(host) @@ -194,35 +190,36 @@ func (cj *CookieJar) parseCookiesFromResp(host, path []byte, resp *fasthttp.Resp if cj.hostCookies == nil { cj.hostCookies = make(map[string][]*fasthttp.Cookie) } + cookies, ok := cj.hostCookies[hostStr] if !ok { - // If the key does not exist in the map then - // we must make a copy for the key to avoid unsafe usage. + // If the key does not exist in the map, make a copy to avoid unsafe usage. hostStr = string(host) } now := time.Now() resp.Header.VisitAllCookie(func(key, value []byte) { - isCreated := false + created := false c := searchCookieByKeyAndPath(key, path, cookies) if c == nil { - c, isCreated = fasthttp.AcquireCookie(), true + c, created = fasthttp.AcquireCookie(), true } _ = c.ParseBytes(value) //nolint:errcheck // ignore error if c.Expire().Equal(fasthttp.CookieExpireUnlimited) || c.Expire().After(now) { cookies = append(cookies, c) - } else if isCreated { + } else if created { fasthttp.ReleaseCookie(c) } }) cj.hostCookies[hostStr] = cookies } -// Release releases all cookie values. +// Release releases all stored cookies. After this, the CookieJar is empty. func (cj *CookieJar) Release() { - // FOllOW-UP performance optimization - // currently a race condition is found because the reset method modifies a value which is not a copy but a reference -> solution should be to make a copy + // FOLLOW-UP performance optimization: + // Currently, a race condition is found because the reset method modifies a value + // that is not a copy but a reference. A solution would be to make a copy. // for _, v := range cj.hostCookies { // for _, c := range v { // fasthttp.ReleaseCookie(c) @@ -231,7 +228,7 @@ func (cj *CookieJar) Release() { cj.hostCookies = nil } -// searchCookieByKeyAndPath searches for a cookie by key and path. +// searchCookieByKeyAndPath looks up a cookie by its key and path from the provided slice of cookies. func searchCookieByKeyAndPath(key, path []byte, cookies []*fasthttp.Cookie) *fasthttp.Cookie { for _, c := range cookies { if bytes.Equal(key, c.Key()) { @@ -240,6 +237,5 @@ func searchCookieByKeyAndPath(key, path []byte, cookies []*fasthttp.Cookie) *fas } } } - return nil } diff --git a/client/core.go b/client/core.go index 315d12d4..c669d3ac 100644 --- a/client/core.go +++ b/client/core.go @@ -16,23 +16,21 @@ import ( var boundary = "--FiberFormBoundary" -// RequestHook is a function that receives Agent and Request, -// it can change the data in Request and Agent. -// -// Called before a request is sent. +// RequestHook is a function invoked before the request is sent. +// It receives a Client and a Request, allowing you to modify the Request or Client data. type RequestHook func(*Client, *Request) error -// ResponseHook is a function that receives Agent, Response and Request, -// it can change the data is Response or deal with some effects. -// -// Called after a response has been received. +// ResponseHook is a function invoked after a response is received. +// It receives a Client, Response, and Request, allowing you to modify the Response data +// or perform actions based on the response. type ResponseHook func(*Client, *Response, *Request) error -// RetryConfig is an alias for config in the `addon/retry` package. +// RetryConfig is an alias for the `retry.Config` type from the `addon/retry` package. type RetryConfig = retry.Config -// addMissingPort will add the corresponding port number for host. -func addMissingPort(addr string, isTLS bool) string { //revive:disable-line:flag-parameter // Accepting a bool param named isTLS if fine here +// addMissingPort appends the appropriate port number to the given address if it doesn't have one. +// If isTLS is true, it uses port 443; otherwise, it uses port 80. +func addMissingPort(addr string, isTLS bool) string { //revive:disable-line:flag-parameter n := strings.Index(addr, ":") if n >= 0 { return addr @@ -44,15 +42,14 @@ func addMissingPort(addr string, isTLS bool) string { //revive:disable-line:flag return net.JoinHostPort(addr, strconv.Itoa(port)) } -// `core` stores middleware and plugin definitions, -// and defines the execution process +// core stores middleware and plugin definitions and defines the request execution process. type core struct { client *Client req *Request - ctx context.Context //nolint:containedctx // It's needed to be stored in the core. + ctx context.Context //nolint:containedctx // Context is needed here. } -// getRetryConfig returns the retry configuration of the client. +// getRetryConfig returns a copy of the client's retry configuration. func (c *core) getRetryConfig() *RetryConfig { c.client.mu.RLock() defer c.client.mu.RUnlock() @@ -70,19 +67,16 @@ func (c *core) getRetryConfig() *RetryConfig { } } -// execFunc is the core function of the client. -// It sends the request and receives the response. +// execFunc is the core logic to send the request and receive the response. +// It leverages the fasthttp client, optionally with retries or redirects. func (c *core) execFunc() (*Response, error) { resp := AcquireResponse() resp.setClient(c.client) resp.setRequest(c.req) - // To avoid memory allocation reuse of data structures such as errch. done := int32(0) errCh, reqv := acquireErrChan(), fasthttp.AcquireRequest() - defer func() { - releaseErrChan(errCh) - }() + defer releaseErrChan(errCh) c.req.RawRequest.CopyTo(reqv) cfg := c.getRetryConfig() @@ -96,11 +90,11 @@ func (c *core) execFunc() (*Response, error) { }() if cfg != nil { + // Use an exponential backoff retry strategy. err = retry.NewExponentialBackoff(*cfg).Retry(func() error { if c.req.maxRedirects > 0 && (string(reqv.Header.Method()) == fiber.MethodGet || string(reqv.Header.Method()) == fiber.MethodHead) { return c.client.fasthttp.DoRedirects(reqv, respv, c.req.maxRedirects) } - return c.client.fasthttp.Do(reqv, respv) }) } else { @@ -124,7 +118,7 @@ func (c *core) execFunc() (*Response, error) { select { case err := <-errCh: if err != nil { - // When get error should release Response + // Release the response if an error occurs. ReleaseResponse(resp) return nil, err } @@ -136,21 +130,19 @@ func (c *core) execFunc() (*Response, error) { } } -// preHooks Exec request hook +// preHooks runs all request hooks before sending the request. func (c *core) preHooks() error { c.client.mu.Lock() defer c.client.mu.Unlock() for _, f := range c.client.userRequestHooks { - err := f(c.client, c.req) - if err != nil { + if err := f(c.client, c.req); err != nil { return err } } for _, f := range c.client.builtinRequestHooks { - err := f(c.client, c.req) - if err != nil { + if err := f(c.client, c.req); err != nil { return err } } @@ -158,21 +150,19 @@ func (c *core) preHooks() error { return nil } -// afterHooks Exec response hooks +// afterHooks runs all response hooks after receiving the response. func (c *core) afterHooks(resp *Response) error { c.client.mu.Lock() defer c.client.mu.Unlock() for _, f := range c.client.builtinResponseHooks { - err := f(c.client, resp, c.req) - if err != nil { + if err := f(c.client, resp, c.req); err != nil { return err } } for _, f := range c.client.userResponseHooks { - err := f(c.client, resp, c.req) - if err != nil { + if err := f(c.client, resp, c.req); err != nil { return err } } @@ -180,7 +170,7 @@ func (c *core) afterHooks(resp *Response) error { return nil } -// timeout deals with timeout +// timeout applies the configured timeout to the request, if any. func (c *core) timeout() context.CancelFunc { var cancel context.CancelFunc @@ -193,35 +183,32 @@ func (c *core) timeout() context.CancelFunc { return cancel } -// execute will exec each hooks and plugins. +// execute runs all hooks, applies timeouts, sends the request, and runs response hooks. func (c *core) execute(ctx context.Context, client *Client, req *Request) (*Response, error) { - // keep a reference, because pass param is boring + // Store references locally. c.ctx = ctx c.client = client c.req = req - // The built-in hooks will be executed only - // after the user-defined hooks are executed. - err := c.preHooks() - if err != nil { + // Execute pre request hooks (user-defined and built-in). + if err := c.preHooks(); err != nil { return nil, err } + // Apply timeout if specified. cancel := c.timeout() if cancel != nil { defer cancel() } - // Do http request + // Perform the actual HTTP request. resp, err := c.execFunc() if err != nil { return nil, err } - // The built-in hooks will be executed only - // before the user-defined hooks are executed. - err = c.afterHooks(resp) - if err != nil { + // Execute after response hooks (built-in and then user-defined). + if err := c.afterHooks(resp); err != nil { resp.Close() return nil, err } @@ -235,38 +222,35 @@ var errChanPool = &sync.Pool{ }, } -// acquireErrChan returns an empty error chan from the pool. +// acquireErrChan returns an empty error channel from the pool. // -// The returned error chan may be returned to the pool with releaseErrChan when no longer needed. -// This allows reducing GC load. +// The returned channel may be returned to the pool with releaseErrChan when no longer needed, +// reducing GC load. func acquireErrChan() chan error { ch, ok := errChanPool.Get().(chan error) if !ok { panic(errors.New("failed to type-assert to chan error")) } - return ch } -// releaseErrChan returns the object acquired via acquireErrChan to the pool. +// releaseErrChan returns the error channel to the pool. // -// Do not access the released core object, otherwise data races may occur. +// Do not use the released channel afterward to avoid data races. func releaseErrChan(ch chan error) { errChanPool.Put(ch) } -// newCore returns an empty core object. +// newCore returns a new core object. func newCore() *core { - c := &core{} - - return c + return &core{} } var ( ErrTimeoutOrCancel = errors.New("timeout or cancel") - ErrURLFormat = errors.New("the url is a mistake") - ErrNotSupportSchema = errors.New("the protocol is not support, only http or https") - ErrFileNoName = errors.New("the file should have name") + ErrURLFormat = errors.New("the URL is incorrect") + ErrNotSupportSchema = errors.New("protocol not supported; only http or https are allowed") + ErrFileNoName = errors.New("the file should have a name") ErrBodyType = errors.New("the body type should be []byte") - ErrNotSupportSaveMethod = errors.New("file path and io.Writer are supported") + ErrNotSupportSaveMethod = errors.New("only file paths and io.Writer are supported") ) diff --git a/client/hooks.go b/client/hooks.go index 4c36145f..d804babb 100644 --- a/client/hooks.go +++ b/client/hooks.go @@ -30,10 +30,10 @@ var ( letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" letterIdxBits = 6 // 6 bits to represent a letter index letterIdxMask = 1<- - Hooks are used to manipulate request/response process of Fiber client. + Hooks are used to manipulate the request/response process of the Fiber client. sidebar_position: 4 --- -With hooks, you can manipulate the client on before request/after response stages or more complex logging/tracing cases. +Hooks allow you to intercept and modify the request or response flow of the Fiber client. They are particularly useful for: -There are 2 kinds of hooks: +- Changing request parameters (e.g., URL, headers) before sending the request. +- Logging request and response details. +- Integrating complex tracing or monitoring tools. +- Handling authentication, retries, or other custom logic. + +There are two kinds of hooks: ## Request Hooks -They are called before the HTTP request has been sent. You can use them make changes on Request object. +**Request hooks** are functions executed before the HTTP request is sent. They follow the signature: -You need to use `RequestHook func(*Client, *Request) error` function signature while creating the hooks. You can use request hooks to change host URL, log request properties etc. Here is an example about how to create request hooks: +```go +type RequestHook func(*Client, *Request) error +``` + +A request hook receives both the `Client` and the `Request` objects, allowing you to modify the request before it leaves your application. For example, you could: + +- Change the host URL. +- Log request details (method, URL, headers). +- Add or modify headers or query parameters. +- Intercept and apply custom authentication logic. + +**Example:** ```go type Repository struct { @@ -31,9 +47,9 @@ type Repository struct { func main() { cc := client.New() + // Add a request hook that modifies the request URL before sending. cc.AddRequestHook(func(c *client.Client, r *client.Request) error { r.SetURL("https://api.github.com/" + r.URL()) - return nil }) @@ -48,7 +64,6 @@ func main() { } fmt.Printf("Status code: %d\n", resp.StatusCode()) - fmt.Printf("Repository: %s\n", repo.FullName) fmt.Printf("Description: %s\n", repo.Description) fmt.Printf("Homepage: %s\n", repo.Homepage) @@ -73,18 +88,20 @@ Full Name: gofiber/fiber
-There are also some builtin request hooks provide some functionalities for Fiber client. Here is a list of them: +### Built-in Request Hooks -- [parserRequestURL](https://github.com/gofiber/fiber/blob/main/client/hooks.go#L62): parserRequestURL customizes the URL according to the path params and query params. It's necessary for `PathParam` and `QueryParam` methods. +Fiber provides some built-in request hooks: -- [parserRequestHeader](https://github.com/gofiber/fiber/blob/main/client/hooks.go#L113): parserRequestHeader sets request headers, cookies, body type, referer, user agent according to client and request properties. It's necessary to make request header and cookiejar methods functional. - -- [parserRequestBody](https://github.com/gofiber/fiber/blob/main/client/hooks.go#L178): parserRequestBody serializes the body automatically. It is useful for XML, JSON, form, file bodies. +- **parserRequestURL**: Normalizes and customizes the URL based on path and query parameters. Required for `PathParam` and `QueryParam` methods. +- **parserRequestHeader**: Sets request headers, cookies, content type, referer, and user agent based on client and request properties. +- **parserRequestBody**: Automatically serializes the request body (JSON, XML, form, file uploads, etc.). :::info -If any error returns from request hook execution, it will interrupt the request and return the error. +If any request hook returns an error, the request is interrupted and the error is returned immediately. ::: +**Example with Multiple Hooks:** + ```go func main() { cc := client.New() @@ -123,9 +140,15 @@ exit status 2 ## Response Hooks -They are called after the HTTP response has been completed. You can use them to get some information about response and request. +**Response hooks** are functions executed after the HTTP response is received. They follow the signature: -You need to use `ResponseHook func(*Client, *Response, *Request) error` function signature while creating the hooks. You can use response hook for logging, tracing etc. Here is an example about how to create response hooks: +```go +type ResponseHook func(*Client, *Response, *Request) error +``` + +A response hook receives the `Client`, `Response`, and `Request` objects, allowing you to inspect and modify the response or perform additional actions such as logging, tracing, or processing response data. + +**Example:** ```go func main() { @@ -173,16 +196,19 @@ X-Cache: HIT
-There are also some builtin request hooks provide some functionalities for Fiber client. Here is a list of them: +### Built-in Response Hooks -- [parserResponseCookie](https://github.com/gofiber/fiber/blob/main/client/hooks.go#L293): parserResponseCookie parses cookies and saves into the response objects and cookiejar if it's exists. +Fiber provides built-in response hooks: -- [logger](https://github.com/gofiber/fiber/blob/main/client/hooks.go#L319): logger prints some RawRequest and RawResponse information. It uses [log.CommonLogger](https://github.com/gofiber/fiber/blob/main/log/log.go#L49) interface for logging. +- **parserResponseCookie**: Parses cookies from the response and stores them in the response object and cookie jar if available. +- **logger**: Logs information about the raw request and response. It uses the `log.CommonLogger` interface. :::info -If any error is returned from executing the response hook, it will return the error without executing other response hooks. +If a response hook returns an error, it stops executing any further hooks and returns the error. ::: +**Example with Multiple Response Hooks:** + ```go func main() { cc := client.New() @@ -225,9 +251,11 @@ exit status 2
-:::info -Hooks work as FIFO (first-in-first-out). You need to check the order while adding the hooks. -::: +## Hook Execution Order + +Hooks run in FIFO order (First-In-First-Out). That means hooks are executed in the order they were added. Keep this in mind when adding multiple hooks, as the order can affect the outcome. + +**Example:** ```go func main() { diff --git a/docs/client/request.md b/docs/client/request.md index 9789ea0d..e411c40a 100644 --- a/docs/client/request.md +++ b/docs/client/request.md @@ -6,15 +6,15 @@ description: >- sidebar_position: 2 --- -The `Request` structure in Gofiber's HTTP client represents an HTTP request. It encapsulates all the necessary information required to send a request to a server. This includes: +The `Request` structure in Gofiber's HTTP client represents an HTTP request. It encapsulates all the necessary information needed to send a request to a server, including: -- **URL**: The URL to which the request is sent. -- **Method**: The HTTP method used (GET, POST, PUT, DELETE, etc.). -- **Headers**: HTTP headers that provide additional information about the request or the needed responses. -- **Body**: The data sent with the request, typically used with POST and PUT methods. -- **Query Parameters**: Parameters that are appended to the URL, used to modify the request or to provide additional information. +- **URL**: The endpoint to which the request is sent. +- **Method**: The HTTP method (GET, POST, PUT, DELETE, etc.). +- **Headers**: Key-value pairs that provide additional information about the request or guide how the response should be processed. +- **Body**: The data sent with the request, commonly used with methods like POST and PUT. +- **Query Parameters**: Parameters appended to the URL to pass additional data or modify the request's behavior. -This structure is designed to be flexible and efficient, allowing users to easily construct and modify HTTP requests according to their needs. +This structure is designed to be both flexible and efficient, allowing you to easily build and modify HTTP requests as needed. ```go type Request struct { @@ -47,8 +47,7 @@ type Request struct { ### Get -Get sends the GET request. -It sets the URL and HTTP method, and then it sends the request. +**Get** sends a GET request to the specified URL. It sets the URL and HTTP method, then dispatches the request to the server. ```go title="Signature" func (r *Request) Get(url string) (*Response, error) @@ -56,8 +55,7 @@ func (r *Request) Get(url string) (*Response, error) ### Post -Post sends the POST request. -It sets the URL and HTTP method, and then it sends the request. +**Post** sends a POST request. It sets the URL and method to POST, then sends the request. ```go title="Signature" func (r *Request) Post(url string) (*Response, error) @@ -65,8 +63,7 @@ func (r *Request) Post(url string) (*Response, error) ### Put -Put sends the PUT request. -It sets the URL and HTTP method, and then it sends the request. +**Put** sends a PUT request. It sets the URL and method to PUT, then sends the request. ```go title="Signature" func (r *Request) Put(url string) (*Response, error) @@ -74,8 +71,7 @@ func (r *Request) Put(url string) (*Response, error) ### Patch -Patch sends the PATCH request. -It sets the URL and HTTP method, and then it sends the request. +**Patch** sends a PATCH request. It sets the URL and method to PATCH, then sends the request. ```go title="Signature" func (r *Request) Patch(url string) (*Response, error) @@ -83,8 +79,7 @@ func (r *Request) Patch(url string) (*Response, error) ### Delete -Delete sends the DELETE request. -It sets the URL and HTTP method, and then it sends the request. +**Delete** sends a DELETE request. It sets the URL and method to DELETE, then sends the request. ```go title="Signature" func (r *Request) Delete(url string) (*Response, error) @@ -92,8 +87,7 @@ func (r *Request) Delete(url string) (*Response, error) ### Head -Head sends the HEAD request. -It sets the URL and HTTP method, and then it sends the request. +**Head** sends a HEAD request. It sets the URL and method to HEAD, then sends the request. ```go title="Signature" func (r *Request) Head(url string) (*Response, error) @@ -101,8 +95,7 @@ func (r *Request) Head(url string) (*Response, error) ### Options -Options sends the OPTIONS request. -It sets the URL and HTTP method, and then it sends the request. +**Options** sends an OPTIONS request. It sets the URL and method to OPTIONS, then sends the request. ```go title="Signature" func (r *Request) Options(url string) (*Response, error) @@ -110,9 +103,7 @@ func (r *Request) Options(url string) (*Response, error) ### Custom -Custom sends a request with custom HTTP method. -It sets the URL and HTTP method, and then it sends the request. -You can use Custom to send requests with methods like TRACE, CONNECT. +**Custom** sends a request using a custom HTTP method. For example, you can use this to send a TRACE or CONNECT request. ```go title="Signature" func (r *Request) Custom(url, method string) (*Response, error) @@ -120,9 +111,7 @@ func (r *Request) Custom(url, method string) (*Response, error) ## AcquireRequest -AcquireRequest returns an empty request object from the pool. -The returned request may be returned to the pool with ReleaseRequest when no longer needed. -This allows reducing GC load. +**AcquireRequest** returns a new (pooled) `Request` object. When you are done with the request, call `ReleaseRequest` to return it to the pool and reduce GC load. ```go title="Signature" func AcquireRequest() *Request @@ -130,8 +119,7 @@ func AcquireRequest() *Request ## ReleaseRequest -ReleaseRequest returns the object acquired via AcquireRequest to the pool. -Do not access the released Request object; otherwise, data races may occur. +**ReleaseRequest** returns the `Request` object back to the pool. Do not use the request after releasing it, as this may cause data races. ```go title="Signature" func ReleaseRequest(req *Request) @@ -139,7 +127,7 @@ func ReleaseRequest(req *Request) ## Method -Method returns HTTP method in request. +**Method** returns the current HTTP method set for the request. ```go title="Signature" func (r *Request) Method() string @@ -147,7 +135,7 @@ func (r *Request) Method() string ## SetMethod -SetMethod will set method for Request object. The user should use request method to set method. +**SetMethod** sets the HTTP method for the `Request` object. Typically, you should use the specialized request methods (`Get`, `Post`, etc.) instead of calling `SetMethod` directly. ```go title="Signature" func (r *Request) SetMethod(method string) *Request @@ -155,7 +143,7 @@ func (r *Request) SetMethod(method string) *Request ## URL -URL returns request url in Request instance. +**URL** returns the current URL set in the `Request`. ```go title="Signature" func (r *Request) URL() string @@ -163,7 +151,7 @@ func (r *Request) URL() string ## SetURL -SetURL will set url for Request object. +**SetURL** sets the URL for the `Request` object. ```go title="Signature" func (r *Request) SetURL(url string) *Request @@ -171,7 +159,7 @@ func (r *Request) SetURL(url string) *Request ## Client -Client gets the Client instance of Request. +**Client** retrieves the `Client` instance associated with the `Request`. ```go title="Signature" func (r *Request) Client() *Client @@ -179,8 +167,7 @@ func (r *Request) Client() *Client ## SetClient -SetClient method sets client of request instance. -If the client given is null, it will panic. +**SetClient** assigns a `Client` to the `Request`. If the provided client is `nil`, it will panic. ```go title="Signature" func (r *Request) SetClient(c *Client) *Request @@ -188,7 +175,7 @@ func (r *Request) SetClient(c *Client) *Request ## Context -Context returns the Context if it's already set in the request; otherwise, it returns `context.Background()`. +**Context** returns the `context.Context` of the request, or `context.Background()` if none is set. ```go title="Signature" func (r *Request) Context() context.Context @@ -196,8 +183,7 @@ func (r *Request) Context() context.Context ## SetContext -SetContext sets the context.Context for current Request. It allows interruption of the request execution if the ctx.Done() channel is closed. -See [the article](https://blog.golang.org/context) and the [context](https://pkg.go.dev/context) package documentation. +**SetContext** sets the `context.Context` for the request, allowing you to cancel or time out the request. See the [Go blog](https://blog.golang.org/context) and [context](https://pkg.go.dev/context) docs for more details. ```go title="Signature" func (r *Request) SetContext(ctx context.Context) *Request @@ -205,7 +191,7 @@ func (r *Request) SetContext(ctx context.Context) *Request ## Header -Header method returns header value via key, this method will visit all field in the header. +**Header** returns all values for the specified header key. It searches all header fields stored in the request. ```go title="Signature" func (r *Request) Header(key string) []string @@ -213,8 +199,7 @@ func (r *Request) Header(key string) []string ### Headers -Headers returns all headers in the request using an iterator. You can use `maps.Collect()` to collect all headers into a map. -The returned value is valid until the request object is released. Any future calls to Headers method will return the modified value. Do not store references to returned value. Make copies instead. +**Headers** returns an iterator over all headers in the request. Use `maps.Collect()` to transform them into a map if needed. The returned values are valid only until the request is released. Make copies as required. ```go title="Signature" func (r *Request) Headers() iter.Seq2[string, []string] @@ -267,7 +252,7 @@ Header Key: Test, Header Value: [123456 654321] ### AddHeader -AddHeader method adds a single header field and its value in the request instance. +**AddHeader** adds a single header field and its value to the request. ```go title="Signature" func (r *Request) AddHeader(key, val string) *Request @@ -309,8 +294,7 @@ fmt.Println(resp.String()) ### SetHeader -SetHeader method sets a single header field and its value in the request instance. -It will override the header which has been set in the client instance. +**SetHeader** sets a single header field and its value, overriding any previously set header with the same key. ```go title="Signature" func (r *Request) SetHeader(key, val string) *Request @@ -351,7 +335,7 @@ fmt.Println(resp.String()) ### AddHeaders -AddHeaders method adds multiple header fields and its values at one go in the request instance. +**AddHeaders** adds multiple headers at once from a map of string slices. ```go title="Signature" func (r *Request) AddHeaders(h map[string][]string) *Request @@ -359,8 +343,7 @@ func (r *Request) AddHeaders(h map[string][]string) *Request ### SetHeaders -SetHeaders method sets multiple header fields and its values at one go in the request instance. -It will override the header which has been set in the client instance. +**SetHeaders** sets multiple headers at once from a map of strings, overriding any previously set headers. ```go title="Signature" func (r *Request) SetHeaders(h map[string]string) *Request @@ -368,7 +351,7 @@ func (r *Request) SetHeaders(h map[string]string) *Request ## Param -Param method returns params value via key, this method will visit all field in the query param. +**Param** returns all values associated with a given query parameter key. ```go title="Signature" func (r *Request) Param(key string) []string @@ -376,8 +359,7 @@ func (r *Request) Param(key string) []string ### Params -Params returns all params in the request using an iterator. You can use `maps.Collect()` to collect all params into a map. -The returned value is valid until the request object is released. Any future calls to Params method will return the modified value. Do not store references to returned value. Make copies instead. +**Params** returns an iterator over all query parameters. Use `maps.Collect()` if you need them in a map. The returned values are valid only until the request is released. ```go title="Signature" func (r *Request) Params() iter.Seq2[string, []string] @@ -385,7 +367,7 @@ func (r *Request) Params() iter.Seq2[string, []string] ### AddParam -AddParam method adds a single param field and its value in the request instance. +**AddParam** adds a single query parameter key-value pair. ```go title="Signature" func (r *Request) AddParam(key, val string) *Request @@ -426,8 +408,7 @@ fmt.Println(string(resp.Body())) ### SetParam -SetParam method sets a single param field and its value in the request instance. -It will override param, which has been set in client instance. +**SetParam** sets a single query parameter key-value pair, overriding any previously set values for that key. ```go title="Signature" func (r *Request) SetParam(key, val string) *Request @@ -435,7 +416,7 @@ func (r *Request) SetParam(key, val string) *Request ### AddParams -AddParams method adds multiple param fields and its values at one go in the request instance. +**AddParams** adds multiple query parameters from a map of string slices. ```go title="Signature" func (r *Request) AddParams(m map[string][]string) *Request @@ -443,8 +424,7 @@ func (r *Request) AddParams(m map[string][]string) *Request ### SetParams -SetParams method sets multiple param fields and its values at one go in the request instance. -It will override param, which has been set in client instance. +**SetParams** sets multiple query parameters from a map of strings, overriding previously set values. ```go title="Signature" func (r *Request) SetParams(m map[string]string) *Request @@ -452,8 +432,7 @@ func (r *Request) SetParams(m map[string]string) *Request ### SetParamsWithStruct -SetParamsWithStruct method sets multiple param fields and its values at one go in the request instance. -It will override param, which has been set in client instance. +**SetParamsWithStruct** sets multiple query parameters from a struct. Nested structs are not supported. ```go title="Signature" func (r *Request) SetParamsWithStruct(v any) *Request @@ -501,7 +480,7 @@ fmt.Println(string(resp.Body())) ### DelParams -DelParams method deletes single or multiple param fields and their values. +**DelParams** removes one or more query parameters by their keys. ```go title="Signature" func (r *Request) DelParams(key ...string) *Request @@ -509,7 +488,7 @@ func (r *Request) DelParams(key ...string) *Request ## UserAgent -UserAgent returns user agent in request instance. +**UserAgent** returns the user agent currently set in the request. ```go title="Signature" func (r *Request) UserAgent() string @@ -517,8 +496,7 @@ func (r *Request) UserAgent() string ## SetUserAgent -SetUserAgent method sets user agent in request. -It will override the user agent which has been set in the client instance. +**SetUserAgent** sets the user agent header for the request, overriding the one set at the client level if any. ```go title="Signature" func (r *Request) SetUserAgent(ua string) *Request @@ -526,7 +504,7 @@ func (r *Request) SetUserAgent(ua string) *Request ## Boundary -Boundary returns boundary in multipart boundary. +**Boundary** returns the multipart boundary used by the request. ```go title="Signature" func (r *Request) Boundary() string @@ -534,7 +512,7 @@ func (r *Request) Boundary() string ## SetBoundary -SetBoundary method sets multipart boundary. +**SetBoundary** sets the multipart boundary for file uploads. ```go title="Signature" func (r *Request) SetBoundary(b string) *Request @@ -542,7 +520,7 @@ func (r *Request) SetBoundary(b string) *Request ## Referer -Referer returns referer in request instance. +**Referer** returns the Referer header value currently set in the request. ```go title="Signature" func (r *Request) Referer() string @@ -550,8 +528,7 @@ func (r *Request) Referer() string ## SetReferer -SetReferer method sets referer in request. -It will override referer which set in client instance. +**SetReferer** sets the Referer header for the request, overriding the one set at the client level if any. ```go title="Signature" func (r *Request) SetReferer(referer string) *Request @@ -559,7 +536,7 @@ func (r *Request) SetReferer(referer string) *Request ## Cookie -Cookie returns the cookie set in the request instance. If the cookie doesn't exist, returns empty string. +**Cookie** returns the value of the specified cookie. If the cookie does not exist, it returns an empty string. ```go title="Signature" func (r *Request) Cookie(key string) string @@ -567,7 +544,7 @@ func (r *Request) Cookie(key string) string ### Cookies -Cookies returns all cookies in the request using an iterator. You can use `maps.Collect()` to collect all cookies into a map. +**Cookies** returns an iterator over all cookies set in the request. Use `maps.Collect()` to gather them into a map. ```go title="Signature" func (r *Request) Cookies() iter.Seq2[string, string] @@ -575,8 +552,7 @@ func (r *Request) Cookies() iter.Seq2[string, string] ### SetCookie -SetCookie method sets a single cookie field and its value in the request instance. -It will override the cookie which is set in the client instance. +**SetCookie** sets a single cookie key-value pair, overriding any previously set cookie with the same key. ```go title="Signature" func (r *Request) SetCookie(key, val string) *Request @@ -584,8 +560,7 @@ func (r *Request) SetCookie(key, val string) *Request ### SetCookies -SetCookies method sets multiple cookie fields and its values at one go in the request instance. -It will override the cookie which is set in the client instance. +**SetCookies** sets multiple cookies from a map, overriding previously set values. ```go title="Signature" func (r *Request) SetCookies(m map[string]string) *Request @@ -623,8 +598,7 @@ fmt.Println(string(resp.Body())) ### SetCookiesWithStruct -SetCookiesWithStruct method sets multiple cookie fields and its values at one go in the request instance. -It will override the cookie which is set in the client instance. +**SetCookiesWithStruct** sets multiple cookies from a struct. ```go title="Signature" func (r *Request) SetCookiesWithStruct(v any) *Request @@ -632,7 +606,7 @@ func (r *Request) SetCookiesWithStruct(v any) *Request ### DelCookies -DelCookies method deletes single or multiple cookie fields ant its values. +**DelCookies** removes one or more cookies by their keys. ```go title="Signature" func (r *Request) DelCookies(key ...string) *Request @@ -640,7 +614,7 @@ func (r *Request) DelCookies(key ...string) *Request ## PathParam -PathParam returns the path param set in the request instance. If the path param doesn't exist, return empty string. +**PathParam** returns the value of a named path parameter. If not found, returns an empty string. ```go title="Signature" func (r *Request) PathParam(key string) string @@ -648,7 +622,7 @@ func (r *Request) PathParam(key string) string ### PathParams -PathParams returns all path params in the request using an iterator. You can use `maps.Collect()` to collect all path params into a map. +**PathParams** returns an iterator over all path parameters in the request. Use `maps.Collect()` to convert them into a map. ```go title="Signature" func (r *Request) PathParams() iter.Seq2[string, string] @@ -656,8 +630,7 @@ func (r *Request) PathParams() iter.Seq2[string, string] ### SetPathParam -SetPathParam method sets a single path param field and its value in the request instance. -It will override path param which set in client instance. +**SetPathParam** sets a single path parameter key-value pair, overriding previously set values. ```go title="Signature" func (r *Request) SetPathParam(key, val string) *Request @@ -688,8 +661,7 @@ Gofiber ### SetPathParams -SetPathParams method sets multiple path param fields and its values at one go in the request instance. -It will override path param which set in client instance. +**SetPathParams** sets multiple path parameters at once, overriding previously set values. ```go title="Signature" func (r *Request) SetPathParams(m map[string]string) *Request @@ -697,8 +669,7 @@ func (r *Request) SetPathParams(m map[string]string) *Request ### SetPathParamsWithStruct -SetPathParamsWithStruct method sets multiple path param fields and its values at one go in the request instance. -It will override path param which set in client instance. +**SetPathParamsWithStruct** sets multiple path parameters from a struct. ```go title="Signature" func (r *Request) SetPathParamsWithStruct(v any) *Request @@ -706,7 +677,7 @@ func (r *Request) SetPathParamsWithStruct(v any) *Request ### DelPathParams -DelPathParams method deletes single or multiple path param fields ant its values. +**DelPathParams** deletes one or more path parameters by their keys. ```go title="Signature" func (r *Request) DelPathParams(key ...string) *Request @@ -714,7 +685,7 @@ func (r *Request) DelPathParams(key ...string) *Request ### ResetPathParams -ResetPathParams deletes all path params. +**ResetPathParams** deletes all path parameters. ```go title="Signature" func (r *Request) ResetPathParams() *Request @@ -722,7 +693,7 @@ func (r *Request) ResetPathParams() *Request ## SetJSON -SetJSON method sets JSON body in request. +**SetJSON** sets the request body to a JSON-encoded payload. ```go title="Signature" func (r *Request) SetJSON(v any) *Request @@ -730,7 +701,7 @@ func (r *Request) SetJSON(v any) *Request ## SetXML -SetXML method sets XML body in request. +**SetXML** sets the request body to an XML-encoded payload. ```go title="Signature" func (r *Request) SetXML(v any) *Request @@ -738,8 +709,7 @@ func (r *Request) SetXML(v any) *Request ## SetCBOR -SetCBOR method sets the request body using [CBOR](https://cbor.io/) encoding format. -It automatically sets the Content-Type header to `"application/cbor"`. +**SetCBOR** sets the request body to a CBOR-encoded payload. It automatically sets the `Content-Type` to `application/cbor`. ```go title="Signature" func (r *Request) SetCBOR(v any) *Request @@ -747,7 +717,7 @@ func (r *Request) SetCBOR(v any) *Request ## SetRawBody -SetRawBody method sets body with raw data in request. +**SetRawBody** sets the request body to raw bytes. ```go title="Signature" func (r *Request) SetRawBody(v []byte) *Request @@ -755,7 +725,7 @@ func (r *Request) SetRawBody(v []byte) *Request ## FormData -FormData method returns form data value via key, this method will visit all field in the form data. +**FormData** returns all values associated with the given form data field. ```go title="Signature" func (r *Request) FormData(key string) []string @@ -763,7 +733,7 @@ func (r *Request) FormData(key string) []string ### AllFormData -AllFormData returns all form data in the request using an iterator. You can use `maps.Collect()` to collect all form data into a map. +**AllFormData** returns an iterator over all form data fields. Use `maps.Collect()` if needed. ```go title="Signature" func (r *Request) AllFormData() iter.Seq2[string, []string] @@ -771,7 +741,7 @@ func (r *Request) AllFormData() iter.Seq2[string, []string] ### AddFormData -AddFormData method adds a single form data field and its value in the request instance. +**AddFormData** adds a single form data key-value pair. ```go title="Signature" func (r *Request) AddFormData(key, val string) *Request @@ -816,10 +786,10 @@ fmt.Println(string(resp.Body())) ### SetFormData -SetFormData method sets a single form data field and its value in the request instance. +**SetFormData** sets a single form data field, overriding any previously set values. ```go title="Signature" -func (r *Request) SetFormData(key, val string) *Request +func (r *Request) SetFormData(key, val string) *Request ```
@@ -857,7 +827,7 @@ fmt.Println(string(resp.Body())) ### AddFormDatas -AddFormDatas method adds multiple form data fields and its values in the request instance. +**AddFormDatas** adds multiple form data fields and values from a map of string slices. ```go title="Signature" func (r *Request) AddFormDatas(m map[string][]string) *Request @@ -865,15 +835,7 @@ func (r *Request) AddFormDatas(m map[string][]string) *Request ### SetFormDatas -SetFormDatas method sets multiple form data fields and its values in the request instance. - -```go title="Signature" -func (r *Request) SetFormDatas(m map[string]string) *Request -``` - -### SetFormDatas - -SetFormDatas method sets multiple form data fields and its values in the request instance. +**SetFormDatas** sets multiple form data fields from a map of strings. ```go title="Signature" func (r *Request) SetFormDatas(m map[string]string) *Request @@ -881,7 +843,7 @@ func (r *Request) SetFormDatas(m map[string]string) *Request ### SetFormDatasWithStruct -SetFormDatasWithStruct method sets multiple form data fields and its values in the request instance via struct. +**SetFormDatasWithStruct** sets multiple form data fields from a struct. ```go title="Signature" func (r *Request) SetFormDatasWithStruct(v any) *Request @@ -889,7 +851,7 @@ func (r *Request) SetFormDatasWithStruct(v any) *Request ### DelFormDatas -DelFormDatas method deletes multiple form data fields and its value in the request instance. +**DelFormDatas** deletes one or more form data fields by their keys. ```go title="Signature" func (r *Request) DelFormDatas(key ...string) *Request @@ -897,8 +859,7 @@ func (r *Request) DelFormDatas(key ...string) *Request ## File -File returns file ptr store in request obj by name. -If the name field is empty, it will try to match path. +**File** returns a file from the request by its name. If no name was provided, it attempts to match by path. ```go title="Signature" func (r *Request) File(name string) *File @@ -906,8 +867,7 @@ func (r *Request) File(name string) *File ### Files -Files method returns all files in request instance. -The returned value is valid until the request object is released. Any future calls to Files method will return the modified value. Do not store references to returned value. Make copies instead. +**Files** returns all files in the request as a slice. The returned slice is valid only until the request is released. ```go title="Signature" func (r *Request) Files() []*File @@ -915,7 +875,7 @@ func (r *Request) Files() []*File ### FileByPath -FileByPath returns file ptr store in request obj by path. +**FileByPath** returns a file from the request by its file path. ```go title="Signature" func (r *Request) FileByPath(path string) *File @@ -923,7 +883,7 @@ func (r *Request) FileByPath(path string) *File ### AddFile -AddFile method adds a single file field and its value in the request instance via file path. +**AddFile** adds a single file to the request from a file path. ```go title="Signature" func (r *Request) AddFile(path string) *Request @@ -962,7 +922,7 @@ fmt.Println(string(resp.Body())) ### AddFileWithReader -AddFileWithReader method adds a single field and its value in the request instance via reader. +**AddFileWithReader** adds a single file to the request from an `io.ReadCloser`. ```go title="Signature" func (r *Request) AddFileWithReader(name string, reader io.ReadCloser) *Request @@ -1002,7 +962,7 @@ fmt.Println(string(resp.Body())) ### AddFiles -AddFiles method adds multiple file fields and its value in the request instance via File instance. +**AddFiles** adds multiple files to the request at once. ```go title="Signature" func (r *Request) AddFiles(files ...*File) *Request @@ -1010,7 +970,7 @@ func (r *Request) AddFiles(files ...*File) *Request ## Timeout -Timeout returns the length of timeout in request. +**Timeout** returns the timeout duration set in the request. ```go title="Signature" func (r *Request) Timeout() time.Duration @@ -1018,8 +978,7 @@ func (r *Request) Timeout() time.Duration ## SetTimeout -SetTimeout method sets the timeout field and its values at one go in the request instance. -It will override timeout which set in client instance. +**SetTimeout** sets a timeout for the request, overriding any timeout set at the client level. ```go title="Signature" func (r *Request) SetTimeout(t time.Duration) *Request @@ -1084,7 +1043,7 @@ exit status 2 ## MaxRedirects -MaxRedirects returns the max redirects count in the request. +**MaxRedirects** returns the maximum number of redirects allowed for the request. ```go title="Signature" func (r *Request) MaxRedirects() int @@ -1092,8 +1051,7 @@ func (r *Request) MaxRedirects() int ## SetMaxRedirects -SetMaxRedirects method sets the maximum number of redirects at one go in the request instance. -It will override max redirect, which is set in the client instance. +**SetMaxRedirects** sets the maximum number of redirects for the request, overriding the client's setting. ```go title="Signature" func (r *Request) SetMaxRedirects(count int) *Request @@ -1101,7 +1059,7 @@ func (r *Request) SetMaxRedirects(count int) *Request ## Send -Send sends HTTP request. +**Send** executes the HTTP request and returns a `Response`. ```go title="Signature" func (r *Request) Send() (*Response, error) @@ -1109,7 +1067,7 @@ func (r *Request) Send() (*Response, error) ## Reset -Reset clears Request object, used by ReleaseRequest method. +**Reset** clears the `Request` object, making it ready for reuse. This is used by `ReleaseRequest`. ```go title="Signature" func (r *Request) Reset() @@ -1117,7 +1075,7 @@ func (r *Request) Reset() ## Header -Header is a wrapper which wrap http.Header, the header in client and request will store in it. +**Header** is a wrapper around `fasthttp.RequestHeader`, storing headers for both the client and request. ```go type Header struct { @@ -1127,7 +1085,7 @@ type Header struct { ### PeekMultiple -PeekMultiple methods returns multiple field in header with same key. +**PeekMultiple** returns multiple values associated with the same header key. ```go title="Signature" func (h *Header) PeekMultiple(key string) []string @@ -1135,7 +1093,7 @@ func (h *Header) PeekMultiple(key string) []string ### AddHeaders -AddHeaders receives a map and add each value to header. +**AddHeaders** adds multiple headers from a map of string slices. ```go title="Signature" func (h *Header) AddHeaders(r map[string][]string) @@ -1143,7 +1101,7 @@ func (h *Header) AddHeaders(r map[string][]string) ### SetHeaders -SetHeaders will override all headers. +**SetHeaders** sets multiple headers from a map of strings, overriding previously set headers. ```go title="Signature" func (h *Header) SetHeaders(r map[string]string) @@ -1151,7 +1109,7 @@ func (h *Header) SetHeaders(r map[string]string) ## QueryParam -QueryParam is a wrapper which wrap url.Values, the query string and formdata in client and request will store in it. +**QueryParam** is a wrapper around `fasthttp.Args`, storing query parameters. ```go type QueryParam struct { @@ -1161,7 +1119,7 @@ type QueryParam struct { ### Keys -Keys method returns all keys in the query params. +**Keys** returns all keys in the query parameters. ```go title="Signature" func (p *QueryParam) Keys() []string @@ -1169,7 +1127,7 @@ func (p *QueryParam) Keys() []string ### AddParams -AddParams receive a map and add each value to param. +**AddParams** adds multiple query parameters from a map of string slices. ```go title="Signature" func (p *QueryParam) AddParams(r map[string][]string) @@ -1177,7 +1135,7 @@ func (p *QueryParam) AddParams(r map[string][]string) ### SetParams -SetParams will override all params. +**SetParams** sets multiple query parameters from a map of strings, overriding previously set values. ```go title="Signature" func (p *QueryParam) SetParams(r map[string]string) @@ -1185,8 +1143,7 @@ func (p *QueryParam) SetParams(r map[string]string) ### SetParamsWithStruct -SetParamsWithStruct will override all params with struct or pointer of struct. -Nested structs are not currently supported. +**SetParamsWithStruct** sets multiple query parameters from a struct. Nested structs are not supported. ```go title="Signature" func (p *QueryParam) SetParamsWithStruct(v any) @@ -1194,7 +1151,7 @@ func (p *QueryParam) SetParamsWithStruct(v any) ## Cookie -Cookie is a map which to store the cookies. +**Cookie** is a map that stores cookies. ```go type Cookie map[string]string @@ -1202,7 +1159,7 @@ type Cookie map[string]string ### Add -Add method impl the method in WithStruct interface. +**Add** adds a cookie key-value pair. ```go title="Signature" func (c Cookie) Add(key, val string) @@ -1210,7 +1167,7 @@ func (c Cookie) Add(key, val string) ### Del -Del method impl the method in WithStruct interface. +**Del** removes a cookie by its key. ```go title="Signature" func (c Cookie) Del(key string) @@ -1218,7 +1175,7 @@ func (c Cookie) Del(key string) ### SetCookie -SetCookie method sets a single val in Cookie. +**SetCookie** sets a single cookie key-value pair, overriding previously set values. ```go title="Signature" func (c Cookie) SetCookie(key, val string) @@ -1226,7 +1183,7 @@ func (c Cookie) SetCookie(key, val string) ### SetCookies -SetCookies method sets multiple val in Cookie. +**SetCookies** sets multiple cookies from a map of strings. ```go title="Signature" func (c Cookie) SetCookies(m map[string]string) @@ -1234,7 +1191,7 @@ func (c Cookie) SetCookies(m map[string]string) ### SetCookiesWithStruct -SetCookiesWithStruct method sets multiple val in Cookie via a struct. +**SetCookiesWithStruct** sets multiple cookies from a struct. ```go title="Signature" func (c Cookie) SetCookiesWithStruct(v any) @@ -1242,7 +1199,7 @@ func (c Cookie) SetCookiesWithStruct(v any) ### DelCookies -DelCookies method deletes multiple val in Cookie. +**DelCookies** deletes one or more cookies by their keys. ```go title="Signature" func (c Cookie) DelCookies(key ...string) @@ -1250,7 +1207,7 @@ func (c Cookie) DelCookies(key ...string) ### VisitAll -VisitAll method receive a function which can travel the all val. +**VisitAll** iterates over all cookies and executes a given function. ```go title="Signature" func (c Cookie) VisitAll(f func(key, val string)) @@ -1258,7 +1215,7 @@ func (c Cookie) VisitAll(f func(key, val string)) ### Reset -Reset clears the Cookie object. +**Reset** clears all cookies. ```go title="Signature" func (c Cookie) Reset() @@ -1266,7 +1223,7 @@ func (c Cookie) Reset() ## PathParam -PathParam is a map which to store path params. +**PathParam** is a map that stores path parameters. ```go type PathParam map[string]string @@ -1274,7 +1231,7 @@ type PathParam map[string]string ### Add -Add method impl the method in WithStruct interface. +**Add** adds a path parameter key-value pair. ```go title="Signature" func (p PathParam) Add(key, val string) @@ -1282,7 +1239,7 @@ func (p PathParam) Add(key, val string) ### Del -Del method impl the method in WithStruct interface. +**Del** removes a path parameter by its key. ```go title="Signature" func (p PathParam) Del(key string) @@ -1290,7 +1247,7 @@ func (p PathParam) Del(key string) ### SetParam -SetParam method sets a single val in PathParam. +**SetParam** sets a single path parameter key-value pair, overriding previously set values. ```go title="Signature" func (p PathParam) SetParam(key, val string) @@ -1298,7 +1255,7 @@ func (p PathParam) SetParam(key, val string) ### SetParams -SetParams method sets multiple val in PathParam. +**SetParams** sets multiple path parameters from a map of strings. ```go title="Signature" func (p PathParam) SetParams(m map[string]string) @@ -1306,7 +1263,7 @@ func (p PathParam) SetParams(m map[string]string) ### SetParamsWithStruct -SetParamsWithStruct method sets multiple val in PathParam via a struct. +**SetParamsWithStruct** sets multiple path parameters from a struct. ```go title="Signature" func (p PathParam) SetParamsWithStruct(v any) @@ -1314,7 +1271,7 @@ func (p PathParam) SetParamsWithStruct(v any) ### DelParams -DelParams method deletes multiple val in PathParams. +**DelParams** deletes one or more path parameters by their keys. ```go title="Signature" func (p PathParam) DelParams(key ...string) @@ -1322,7 +1279,7 @@ func (p PathParam) DelParams(key ...string) ### VisitAll -VisitAll method receive a function which can travel the all val. +**VisitAll** iterates over all path parameters and executes the provided function. ```go title="Signature" func (p PathParam) VisitAll(f func(key, val string)) @@ -1330,7 +1287,7 @@ func (p PathParam) VisitAll(f func(key, val string)) ### Reset -Reset clears the PathParam object. +**Reset** clears all path parameters. ```go title="Signature" func (p PathParam) Reset() @@ -1338,7 +1295,7 @@ func (p PathParam) Reset() ## FormData -FormData is a wrapper of fasthttp.Args and it is used for url encode body and file body. +**FormData** is a wrapper around `fasthttp.Args`, used to handle URL-encoded and form-data (multipart) request bodies. ```go type FormData struct { @@ -1348,7 +1305,7 @@ type FormData struct { ### Keys -Keys method returns all keys in the form data. +**Keys** returns all form data keys. ```go title="Signature" func (f *FormData) Keys() []string @@ -1356,7 +1313,7 @@ func (f *FormData) Keys() []string ### AddData -AddData method is a wrapper of Args's Add method. +**AddData** adds a single form field key-value pair. ```go title="Signature" func (f *FormData) AddData(key, val string) @@ -1364,7 +1321,7 @@ func (f *FormData) AddData(key, val string) ### SetData -SetData method is a wrapper of Args's Set method. +**SetData** sets a single form field key-value pair, overriding any previously set values. ```go title="Signature" func (f *FormData) SetData(key, val string) @@ -1372,7 +1329,7 @@ func (f *FormData) SetData(key, val string) ### AddDatas -AddDatas method supports add multiple fields. +**AddDatas** adds multiple form fields from a map of string slices. ```go title="Signature" func (f *FormData) AddDatas(m map[string][]string) @@ -1380,7 +1337,7 @@ func (f *FormData) AddDatas(m map[string][]string) ### SetDatas -SetDatas method supports set multiple fields. +**SetDatas** sets multiple form fields from a map of strings. ```go title="Signature" func (f *FormData) SetDatas(m map[string]string) @@ -1388,7 +1345,7 @@ func (f *FormData) SetDatas(m map[string]string) ### SetDatasWithStruct -SetDatasWithStruct method supports set multiple fields via a struct. +**SetDatasWithStruct** sets multiple form fields from a struct. ```go title="Signature" func (f *FormData) SetDatasWithStruct(v any) @@ -1396,7 +1353,7 @@ func (f *FormData) SetDatasWithStruct(v any) ### DelDatas -DelDatas method deletes multiple fields. +**DelDatas** deletes one or more form fields by their keys. ```go title="Signature" func (f *FormData) DelDatas(key ...string) @@ -1404,7 +1361,7 @@ func (f *FormData) DelDatas(key ...string) ### Reset -Reset clear the FormData object. +**Reset** clears all form data fields. ```go title="Signature" func (f *FormData) Reset() @@ -1412,7 +1369,7 @@ func (f *FormData) Reset() ## File -File is a struct which support send files via request. +**File** represents a file to be uploaded. It can be specified by name, path, or an `io.ReadCloser`. ```go type File struct { @@ -1425,11 +1382,7 @@ type File struct { ### AcquireFile -AcquireFile returns a File object from the pool. -And you can set field in the File with SetFileFunc. - -The returned file may be returned to the pool with ReleaseFile when no longer needed. -This allows reducing GC load. +**AcquireFile** returns a `File` from the pool and applies any provided `SetFileFunc` functions to it. Release it with `ReleaseFile` when done. ```go title="Signature" func AcquireFile(setter ...SetFileFunc) *File @@ -1437,8 +1390,7 @@ func AcquireFile(setter ...SetFileFunc) *File ### ReleaseFile -ReleaseFile returns the object acquired via AcquireFile to the pool. -Do not access the released File object, otherwise data races may occur. +**ReleaseFile** returns the `File` to the pool. Do not use the file afterward. ```go title="Signature" func ReleaseFile(f *File) @@ -1446,7 +1398,7 @@ func ReleaseFile(f *File) ### SetName -SetName method sets file name. +**SetName** sets the file's name. ```go title="Signature" func (f *File) SetName(n string) @@ -1454,7 +1406,7 @@ func (f *File) SetName(n string) ### SetFieldName -SetFieldName method sets key of file in the body. +**SetFieldName** sets the field name of the file in the multipart form. ```go title="Signature" func (f *File) SetFieldName(n string) @@ -1462,7 +1414,7 @@ func (f *File) SetFieldName(n string) ### SetPath -SetPath method set file path. +**SetPath** sets the file's path. ```go title="Signature" func (f *File) SetPath(p string) @@ -1470,7 +1422,7 @@ func (f *File) SetPath(p string) ### SetReader -SetReader method can receive an io.ReadCloser which will be closed in parserBody hook. +**SetReader** sets the file's `io.ReadCloser`. The reader is closed automatically when the request body is parsed. ```go title="Signature" func (f *File) SetReader(r io.ReadCloser) @@ -1478,7 +1430,7 @@ func (f *File) SetReader(r io.ReadCloser) ### Reset -Reset clear the File object. +**Reset** clears the file's fields. ```go title="Signature" func (f *File) Reset() diff --git a/docs/client/response.md b/docs/client/response.md index 13597006..86a0b160 100644 --- a/docs/client/response.md +++ b/docs/client/response.md @@ -6,30 +6,27 @@ description: >- sidebar_position: 3 --- -The `Response` structure in Gofiber's HTTP client represents the server's response to an HTTP request. It contains all the necessary information received from the server. This includes: +The `Response` structure in Gofiber's HTTP client represents the server's response to an HTTP request. It includes: -- **Status Code**: The HTTP status code returned by the server (e.g., 200 OK, 404 Not Found). -- **Headers**: HTTP headers received from the server that provide additional information about the response. -- **Body**: The data received from the server, typically in the form of a JSON, XML, or plain text format. -- **Cookies**: Any cookies sent by the server along with the response. +- **Status Code**: The HTTP status code returned by the server (e.g., `200 OK`, `404 Not Found`). +- **Headers**: All HTTP headers returned by the server, providing additional response-related information. +- **Body**: The response body content, which can be JSON, XML, plain text, or other formats. +- **Cookies**: Any cookies the server sent along with the response. -This structure allows users to easily access and manage the data returned by the server, facilitating efficient handling of HTTP responses. +This structure makes it easy to inspect and handle the data sent back by the server. ```go type Response struct { - client *Client - request *Request - cookie []*fasthttp.Cookie - + client *Client + request *Request + cookie []*fasthttp.Cookie RawResponse *fasthttp.Response } ``` ## AcquireResponse -AcquireResponse returns an empty response object from the pool. -The returned response may be returned to the pool with ReleaseResponse when no longer needed. -This allows reducing GC load. +**AcquireResponse** returns a new (pooled) `Response` object. When finished, release it using `ReleaseResponse` to reduce GC overhead. ```go title="Signature" func AcquireResponse() *Response @@ -37,8 +34,7 @@ func AcquireResponse() *Response ## ReleaseResponse -ReleaseResponse returns the object acquired via AcquireResponse to the pool. -Do not access the released Response object; otherwise, data races may occur. +**ReleaseResponse** returns the `Response` object to the pool. Avoid using the response after releasing it to prevent data races. ```go title="Signature" func ReleaseResponse(resp *Response) @@ -46,7 +42,7 @@ func ReleaseResponse(resp *Response) ## Status -Status method returns the HTTP status string for the executed request. +**Status** returns the HTTP status message (e.g., `OK`, `Not Found`) associated with the response. ```go title="Signature" func (r *Response) Status() string @@ -54,7 +50,7 @@ func (r *Response) Status() string ## StatusCode -StatusCode method returns the HTTP status code for the executed request. +**StatusCode** returns the numeric HTTP status code of the response. ```go title="Signature" func (r *Response) StatusCode() int @@ -62,7 +58,7 @@ func (r *Response) StatusCode() int ## Protocol -Protocol method returns the HTTP response protocol used for the request. +**Protocol** returns the HTTP protocol used (e.g., `HTTP/1.1`, `HTTP/2`) for the response. ```go title="Signature" func (r *Response) Protocol() string @@ -80,6 +76,8 @@ if err != nil { fmt.Println(resp.Protocol()) ``` +**Output:** + ```text HTTP/1.1 ``` @@ -88,7 +86,7 @@ HTTP/1.1 ## Header -Header method returns the response headers. +**Header** retrieves the value of a specific response header by key. If multiple values exist for the same header, this returns the first one. ```go title="Signature" func (r *Response) Header(key string) string @@ -96,8 +94,7 @@ func (r *Response) Header(key string) string ## Headers -Headers returns all headers in the response using an iterator. You can use `maps.Collect()` to collect all headers into a map. -The returned value is valid until the response object is released. Any future calls to Headers method will return the modified value. Do not store references to returned value. Make copies instead. +**Headers** returns an iterator over all response headers. Use `maps.Collect()` to convert them into a map if desired. The returned values are only valid until the response is released, so make copies if needed. ```go title="Signature" func (r *Response) Headers() iter.Seq2[string, []string] @@ -117,6 +114,8 @@ for key, values := range resp.Headers() { } ``` +**Output:** + ```text Date => Wed, 04 Dec 2024 15:28:29 GMT Connection => keep-alive @@ -135,12 +134,14 @@ if err != nil { panic(err) } -headers := maps.Collect(resp.Headers()) // Collect all headers into a map +headers := maps.Collect(resp.Headers()) for key, values := range headers { fmt.Printf("%s => %s\n", key, strings.Join(values, ", ")) } ``` +**Output:** + ```text Date => Wed, 04 Dec 2024 15:28:29 GMT Connection => keep-alive @@ -152,8 +153,7 @@ Access-Control-Allow-Credentials => true ## Cookies -Cookies method to access all the response cookies. -The returned value is valid until the response object is released. Any future calls to Cookies method will return the modified value. Do not store references to returned value. Make copies instead. +**Cookies** returns a slice of all cookies set by the server in this response. The slice is only valid until the response is released. ```go title="Signature" func (r *Response) Cookies() []*fasthttp.Cookie @@ -174,6 +174,8 @@ for _, cookie := range cookies { } ``` +**Output:** + ```text go => fiber ``` @@ -182,7 +184,7 @@ go => fiber ## Body -Body method returns HTTP response as []byte array for the executed request. +**Body** returns the raw response body as a byte slice. ```go title="Signature" func (r *Response) Body() []byte @@ -190,7 +192,7 @@ func (r *Response) Body() []byte ## String -String method returns the body of the server response as String. +**String** returns the response body as a trimmed string. ```go title="Signature" func (r *Response) String() string @@ -198,7 +200,7 @@ func (r *Response) String() string ## JSON -JSON method will unmarshal body to json. +**JSON** unmarshals the response body into the provided variable `v` using JSON. `v` should be a pointer to a struct or a type compatible with JSON unmarshalling. ```go title="Signature" func (r *Response) JSON(v any) error @@ -222,14 +224,15 @@ if err != nil { panic(err) } -err = resp.JSON(&out) -if err != nil { +if err = resp.JSON(&out); err != nil { panic(err) } fmt.Printf("%+v\n", out) ``` +**Output:** + ```text {Slideshow:{Author:Yours Truly Date:date of publication Title:Sample Slide Show}} ``` @@ -238,7 +241,7 @@ fmt.Printf("%+v\n", out) ## XML -XML method will unmarshal body to xml. +**XML** unmarshals the response body into the provided variable `v` using XML decoding. ```go title="Signature" func (r *Response) XML(v any) error @@ -246,7 +249,7 @@ func (r *Response) XML(v any) error ## CBOR -CBOR method will unmarshal body to CBOR. +**CBOR** unmarshals the response body into `v` using CBOR decoding. ```go title="Signature" func (r *Response) CBOR(v any) error @@ -254,7 +257,7 @@ func (r *Response) CBOR(v any) error ## Save -Save method will save the body to a file or io.Writer. +**Save** writes the response body to a file or an `io.Writer`. If `v` is a string, it interprets it as a file path, creates the file (and directories if needed), and writes the response to it. If `v` is an `io.Writer`, it writes directly to it. ```go title="Signature" func (r *Response) Save(v any) error @@ -262,15 +265,19 @@ func (r *Response) Save(v any) error ## Reset -Reset clears the Response object. +**Reset** clears the `Response` object, making it ready for reuse by `ReleaseResponse`. ```go title="Signature" -func (r *Response) Reset() +func (r *Response) Reset() ``` ## Close -Close method will release the Request and Response objects; after calling Close, please do not use these objects. +**Close** releases both the associated `Request` and `Response` objects back to their pools. + +:::warning +After calling `Close`, any attempt to use the request or response may result in data races or undefined behavior. Ensure all processing is complete before closing. +::: ```go title="Signature" func (r *Response) Close() diff --git a/docs/client/rest.md b/docs/client/rest.md index 04c7a5c4..c66e1103 100644 --- a/docs/client/rest.md +++ b/docs/client/rest.md @@ -7,18 +7,18 @@ sidebar_position: 1 toc_max_heading_level: 5 --- -The Fiber Client for Fiber v3 is a powerful HTTP client optimized for high performance and ease of use in server-side applications. Built on top of the robust FastHTTP library, it inherits FastHTTP's high-speed HTTP protocol implementation. The client is designed to make HTTP requests both internally within services or externally to other web services. +The Fiber Client for Fiber v3 is a powerful HTTP client optimized for high performance and ease of use in server-side applications. Built atop the FastHTTP library, it inherits FastHTTP's high-speed HTTP protocol implementation. The client is designed for making both internal requests (within a microservices architecture) and external requests to other web services. ## Features -- **Lightweight & Fast**: Leveraging the minimalistic design of FastHTTP, the Fiber Client is lightweight and extremely fast. -- **Flexible Configuration**: Configure client-level settings such as timeouts, headers, and more, which apply to all requests. Specific requests can further override or merge these settings. -- **Connection Pooling**: Manages a pool of persistent connections that reduce the overhead of repeatedly establishing connections. -- **Timeouts & Retries**: Supports setting request timeouts and retry mechanisms to handle transient failures. +- **Lightweight & Fast**: Due to its FastHTTP foundation, the Fiber Client is both lightweight and extremely performant. +- **Flexible Configuration**: Set client-level configurations (e.g., timeouts, headers) that apply to all requests, while still allowing overrides at the individual request level. +- **Connection Pooling**: Maintains a pool of persistent connections, minimizing the overhead of establishing new connections for each request. +- **Timeouts & Retries**: Supports request-level timeouts and configurable retries to handle transient errors gracefully. ## Usage -To use the Fiber Client, instantiate it with the desired configuration. Here's a simple example: +Instantiate the Fiber Client with your desired configurations, then send requests: ```go package main @@ -34,7 +34,7 @@ func main() { cc := client.New() cc.SetTimeout(10 * time.Second) - // Get request + // Send a GET request resp, err := cc.Get("https://httpbin.org/get") if err != nil { panic(err) @@ -45,7 +45,7 @@ func main() { } ``` -You can check out [examples](examples.md) for more examples! +Check out [examples](examples.md) for more detailed usage examples. ```go type Client struct { @@ -65,16 +65,16 @@ type Client struct { timeout time.Duration - // user defined request hooks + // user-defined request hooks userRequestHooks []RequestHook - // client package defined request hooks + // client package-defined request hooks builtinRequestHooks []RequestHook - // user defined response hooks + // user-defined response hooks userResponseHooks []ResponseHook - // client package defined response hooks + // client package-defined response hooks builtinResponseHooks []ResponseHook jsonMarshal utils.JSONMarshal @@ -99,7 +99,7 @@ type Client struct { ### New -New creates and returns a new Client object. +**New** creates and returns a new Client object. ```go title="Signature" func New() *Client @@ -107,7 +107,7 @@ func New() *Client ### NewWithClient -NewWithClient creates and returns a new Client object from an existing client object. +**NewWithClient** creates and returns a new Client object from an existing `fasthttp.Client`. ```go title="Signature" func NewWithClient(c *fasthttp.Client) *Client @@ -115,9 +115,11 @@ func NewWithClient(c *fasthttp.Client) *Client ## REST Methods +The following methods send HTTP requests using the configured client: + ### Get -Get provides an API like axios which sends a get request. +Sends a GET request, similar to axios. ```go title="Signature" func (c *Client) Get(url string, cfg ...Config) (*Response, error) @@ -125,7 +127,7 @@ func (c *Client) Get(url string, cfg ...Config) (*Response, error) ### Post -Post provides an API like axios which send post request. +Sends a POST request, similar to axios. ```go title="Signature" func (c *Client) Post(url string, cfg ...Config) (*Response, error) @@ -133,7 +135,7 @@ func (c *Client) Post(url string, cfg ...Config) (*Response, error) ### Put -Put provides an API like axios which send put request. +Sends a PUT request, similar to axios. ```go title="Signature" func (c *Client) Put(url string, cfg ...Config) (*Response, error) @@ -141,7 +143,7 @@ func (c *Client) Put(url string, cfg ...Config) (*Response, error) ### Patch -Patch provides an API like axios which send patch request. +Sends a PATCH request, similar to axios. ```go title="Signature" func (c *Client) Patch(url string, cfg ...Config) (*Response, error) @@ -149,7 +151,7 @@ func (c *Client) Patch(url string, cfg ...Config) (*Response, error) ### Delete -Delete provides an API like axios which send delete request. +Sends a DELETE request, similar to axios. ```go title="Signature" func (c *Client) Delete(url string, cfg ...Config) (*Response, error) @@ -157,7 +159,7 @@ func (c *Client) Delete(url string, cfg ...Config) (*Response, error) ### Head -Head provides an API like axios which send head request. +Sends a HEAD request, similar to axios. ```go title="Signature" func (c *Client) Head(url string, cfg ...Config) (*Response, error) @@ -165,7 +167,7 @@ func (c *Client) Head(url string, cfg ...Config) (*Response, error) ### Options -Options provides an API like axios which send options request. +Sends an OPTIONS request, similar to axios. ```go title="Signature" func (c *Client) Options(url string, cfg ...Config) (*Response, error) @@ -173,7 +175,7 @@ func (c *Client) Options(url string, cfg ...Config) (*Response, error) ### Custom -Custom provides an API like axios which send custom request. +Sends a custom HTTP request, similar to axios, allowing you to specify any method. ```go title="Signature" func (c *Client) Custom(url, method string, cfg ...Config) (*Response, error) @@ -181,9 +183,11 @@ func (c *Client) Custom(url, method string, cfg ...Config) (*Response, error) ## Request Configuration -Config for easy to set the request parameters, it should be noted that when setting the request body will use JSON as the default serialization mechanism, while the priority of Body is higher than FormData, and the priority of FormData is higher than File. +The `Config` type helps configure request parameters. When setting the request body, JSON is used as the default serialization. The priority of the body sources is: -It can be used to configure request data while sending requests using Get, Post, etc. +1. Body +2. FormData +3. File ```go type Config struct { @@ -205,221 +209,223 @@ type Config struct { } ``` -### R +## R -R raise a request from the client. -It acquires a request from the pool. You have to release it using `ReleaseRequest()` when it's no longer needed. +**R** creates a new `Request` object from the client's request pool. Use `ReleaseRequest()` to return it to the pool when done. ```go title="Signature" func (c *Client) R() *Request ``` -### Hooks +## Hooks -#### RequestHook +Hooks allow you to add custom logic before a request is sent or after a response is received. -RequestHook Request returns user-defined request hooks. +### RequestHook + +**RequestHook** returns user-defined request hooks. ```go title="Signature" func (c *Client) RequestHook() []RequestHook ``` -#### ResponseHook +### ResponseHook -ResponseHook return user-define response hooks. +**ResponseHook** returns user-defined response hooks. ```go title="Signature" func (c *Client) ResponseHook() []ResponseHook ``` -#### AddRequestHook +### AddRequestHook -AddRequestHook Add user-defined request hooks. +Adds one or more user-defined request hooks. ```go title="Signature" func (c *Client) AddRequestHook(h ...RequestHook) *Client ``` -#### AddResponseHook +### AddResponseHook -AddResponseHook Add user-defined response hooks. +Adds one or more user-defined response hooks. ```go title="Signature" func (c *Client) AddResponseHook(h ...ResponseHook) *Client ``` -### JSON +## JSON -#### JSONMarshal +### JSONMarshal -JSONMarshal returns json marshal function in Core. +Returns the JSON marshaler function used by the client. ```go title="Signature" func (c *Client) JSONMarshal() utils.JSONMarshal ``` -#### JSONUnmarshal +### JSONUnmarshal -JSONUnmarshal returns json unmarshal function in Core. +Returns the JSON unmarshaller function used by the client. ```go title="Signature" func (c *Client) JSONUnmarshal() utils.JSONUnmarshal ``` -#### SetJSONMarshal +### SetJSONMarshal -SetJSONMarshal sets the JSON encoder. +Sets a custom JSON marshaler. ```go title="Signature" func (c *Client) SetJSONMarshal(f utils.JSONMarshal) *Client ``` -#### SetJSONUnmarshal +### SetJSONUnmarshal -Set the JSON decoder. +Sets a custom JSON unmarshaller. ```go title="Signature" func (c *Client) SetJSONUnmarshal(f utils.JSONUnmarshal) *Client ``` -### XML +## XML -#### XMLMarshal +### XMLMarshal -XMLMarshal returns xml marshal function in Core. +Returns the XML marshaler function used by the client. ```go title="Signature" func (c *Client) XMLMarshal() utils.XMLMarshal ``` -#### XMLUnmarshal +### XMLUnmarshal -XMLUnmarshal returns xml unmarshal function in Core. +Returns the XML unmarshaller function used by the client. ```go title="Signature" func (c *Client) XMLUnmarshal() utils.XMLUnmarshal ``` -#### SetXMLMarshal +### SetXMLMarshal -SetXMLMarshal sets the XML encoder. +Sets a custom XML marshaler. ```go title="Signature" func (c *Client) SetXMLMarshal(f utils.XMLMarshal) *Client ``` -#### SetXMLUnmarshal +### SetXMLUnmarshal -SetXMLUnmarshal sets the XML decoder. +Sets a custom XML unmarshaller. ```go title="Signature" func (c *Client) SetXMLUnmarshal(f utils.XMLUnmarshal) *Client ``` -### CBOR +## CBOR -#### CBORMarshal +### CBORMarshal -CBORMarshal returns CBOR marshal function in Core. +Returns the CBOR marshaler function used by the client. ```go title="Signature" func (c *Client) CBORMarshal() utils.CBORMarshal ``` -#### CBORUnmarshal +### CBORUnmarshal -CBORUnmarshal returns CBOR unmarshal function in Core. +Returns the CBOR unmarshaller function used by the client. ```go title="Signature" func (c *Client) CBORUnmarshal() utils.CBORUnmarshal ``` -#### SetCBORMarshal +### SetCBORMarshal -SetCBORMarshal sets CBOR encoder. +Sets a custom CBOR marshaler. ```go title="Signature" func (c *Client) SetCBORMarshal(f utils.CBORMarshal) *Client ``` -#### SetCBORUnmarshal +### SetCBORUnmarshal -SetCBORUnmarshal sets CBOR decoder. +Sets a custom CBOR unmarshaller. ```go title="Signature" func (c *Client) SetCBORUnmarshal(f utils.CBORUnmarshal) *Client ``` -### TLS +## TLS -#### TLSConfig +### TLSConfig -TLSConfig returns tlsConfig in client. -If the client doesn't have a tlsConfig, this function will initialize it. +Returns the client's TLS configuration. If none is set, it initializes a new one. ```go title="Signature" func (c *Client) TLSConfig() *tls.Config ``` -#### SetTLSConfig +### SetTLSConfig -SetTLSConfig sets tlsConfig in client. +Sets the TLS configuration for the client. ```go title="Signature" func (c *Client) SetTLSConfig(config *tls.Config) *Client ``` -#### SetCertificates +### SetCertificates -SetCertificates method sets client certificates into client. +Adds client certificates to the TLS configuration. ```go title="Signature" func (c *Client) SetCertificates(certs ...tls.Certificate) *Client ``` -#### SetRootCertificate +### SetRootCertificate -SetRootCertificate adds one or more root certificates into client. +Adds one or more root certificates to the client's trust store. ```go title="Signature" func (c *Client) SetRootCertificate(path string) *Client ``` -#### SetRootCertificateFromString +### SetRootCertificateFromString -SetRootCertificateFromString method adds one or more root certificates into the client. +Adds one or more root certificates from a string. ```go title="Signature" func (c *Client) SetRootCertificateFromString(pem string) *Client ``` -### SetProxyURL +## SetProxyURL -SetProxyURL sets proxy url in client. It will apply via core to hostclient. +Sets a proxy URL for the client. All subsequent requests will use this proxy. ```go title="Signature" func (c *Client) SetProxyURL(proxyURL string) error ``` -### RetryConfig +## RetryConfig -RetryConfig returns retry config in client. +Returns the retry configuration of the client. ```go title="Signature" func (c *Client) RetryConfig() *RetryConfig ``` -### SetRetryConfig +## SetRetryConfig -SetRetryConfig sets retry config in client, which is impl by addon/retry package. +Sets the retry configuration for the client. ```go title="Signature" func (c *Client) SetRetryConfig(config *RetryConfig) *Client ``` +## BaseURL + ### BaseURL -BaseURL returns baseurl in Client instance. +**BaseURL** returns the base URL currently set in the client. ```go title="Signature" func (c *Client) BaseURL() string @@ -427,12 +433,14 @@ func (c *Client) BaseURL() string ### SetBaseURL -SetBaseURL Set baseUrl which is prefix of real url. +Sets a base URL prefix for all requests made by the client. ```go title="Signature" func (c *Client) SetBaseURL(url string) *Client ``` +**Example:** + ```go title="Example" cc := client.New() cc.SetBaseURL("https://httpbin.org/") @@ -450,132 +458,118 @@ fmt.Println(string(resp.Body())) ```json { - "args": {}, + "args": {}, ... } ```
+## Headers + ### Header -Header method returns header value via key, this method will visit all field in the header +Retrieves all values of a header key at the client level. The returned values apply to all requests. ```go title="Signature" func (c *Client) Header(key string) []string ``` -#### AddHeader +### AddHeader -AddHeader method adds a single header field and its value in the client instance. -These headers will be applied to all requests raised from this client instance. -Also, it can be overridden at request level header options. +Adds a single header to all requests initiated by this client. ```go title="Signature" func (c *Client) AddHeader(key, val string) *Client ``` -#### SetHeader +### SetHeader -SetHeader method sets a single header field and its value in the client instance. -These headers will be applied to all requests raised from this client instance. -Also, it can be overridden at request level header options. +Sets a single header, overriding any existing headers with the same key. ```go title="Signature" func (c *Client) SetHeader(key, val string) *Client ``` -#### AddHeaders +### AddHeaders -AddHeaders method adds multiple headers field and its values at one go in the client instance. -These headers will be applied to all requests raised from this client instance. -Also it can be overridden at request level headers options. +Adds multiple headers at once, all applying to all future requests from this client. ```go title="Signature" func (c *Client) AddHeaders(h map[string][]string) *Client ``` -#### SetHeaders +### SetHeaders -SetHeaders method sets multiple headers field and its values at one go in the client instance. -These headers will be applied to all requests raised from this client instance. -Also it can be overridden at request level headers options. +Sets multiple headers at once, overriding previously set headers. ```go title="Signature" func (c *Client) SetHeaders(h map[string]string) *Client ``` +## Query Parameters + ### Param -Param method returns params value via key, this method will visit all field in the query param. +Returns the values for a given query parameter key. ```go title="Signature" func (c *Client) Param(key string) []string ``` -#### AddParam +### AddParam -AddParam method adds a single query param field and its value in the client instance. -These params will be applied to all requests raised from this client instance. -Also, it can be overridden at request level param options. +Adds a single query parameter for all requests. ```go title="Signature" func (c *Client) AddParam(key, val string) *Client ``` -#### SetParam +### SetParam -SetParam method sets a single query param field and its value in the client instance. -These params will be applied to all requests raised from this client instance. -Also, it can be overridden at request level param options. +Sets a single query parameter, overriding previously set values. ```go title="Signature" func (c *Client) SetParam(key, val string) *Client ``` -#### AddParams +### AddParams -AddParams method adds multiple query params field and its values at one go in the client instance. -These params will be applied to all requests raised from this client instance. -Also it can be overridden at request level params options. +Adds multiple query parameters from a map of string slices. ```go title="Signature" func (c *Client) AddParams(m map[string][]string) *Client ``` -#### SetParams +### SetParams -SetParams method sets multiple params field and its values at one go in the client instance. -These params will be applied to all requests raised from this client instance. -Also it can be overridden at request level params options. +Sets multiple query parameters from a map, overriding previously set values. ```go title="Signature" func (c *Client) SetParams(m map[string]string) *Client ``` -#### SetParamsWithStruct +### SetParamsWithStruct -SetParamsWithStruct method sets multiple params field and its values at one go in the client instance. -These params will be applied to all requests raised from this client instance. -Also it can be overridden at request level params options. +Sets multiple query parameters from a struct. Nested structs are not currently supported. ```go title="Signature" func (c *Client) SetParamsWithStruct(v any) *Client ``` -#### DelParams +### DelParams -DelParams method deletes single or multiple params field and its values in client. +Deletes one or more query parameters. ```go title="Signature" func (c *Client) DelParams(key ...string) *Client ``` +## UserAgent & Referer + ### SetUserAgent -SetUserAgent method sets the userAgent field and its value in the client instance. -This ua will be applied to all requests raised from this client instance. -Also it can be overridden at request level ua options. +Sets the user agent header for all requests. ```go title="Signature" func (c *Client) SetUserAgent(ua string) *Client @@ -583,80 +577,74 @@ func (c *Client) SetUserAgent(ua string) *Client ### SetReferer -SetReferer method sets referer field and its value in the client instance. -This referer will be applied to all requests raised from this client instance. -Also it can be overridden at request level referer options. +Sets the referer header for all requests. ```go title="Signature" func (c *Client) SetReferer(r string) *Client ``` +## Path Parameters + ### PathParam -PathParam returns the path param be set in request instance. -If the path param doesn't exist, return empty string. +Returns the value of a named path parameter, if set. ```go title="Signature" func (c *Client) PathParam(key string) string ``` -#### SetPathParam +### SetPathParam -SetPathParam method sets a single path param field and its value in the client instance. -These path params will be applied to all requests raised from this client instance. -Also it can be overridden at request level path params options. +Sets a single path parameter. ```go title="Signature" func (c *Client) SetPathParam(key, val string) *Client ``` -#### SetPathParams +### SetPathParams -SetPathParams method sets multiple path params field and its values at one go in the client instance. -These path params will be applied to all requests raised from this client instance. -Also it can be overridden at request level path params options. +Sets multiple path parameters at once. ```go title="Signature" func (c *Client) SetPathParams(m map[string]string) *Client ``` -#### SetPathParamsWithStruct +### SetPathParamsWithStruct -SetPathParamsWithStruct method sets multiple path params field and its values at one go in the client instance. -These path params will be applied to all requests raised from this client instance. -Also it can be overridden at request level path params options. +Sets multiple path parameters from a struct. ```go title="Signature" func (c *Client) SetPathParamsWithStruct(v any) *Client ``` -#### DelPathParams +### DelPathParams -DelPathParams method deletes single or multiple path params field and its values in client. +Deletes one or more path parameters. ```go title="Signature" func (c *Client) DelPathParams(key ...string) *Client ``` +## Cookies + ### Cookie -Cookie returns the cookie be set in request instance. -If cookie doesn't exist, return empty string. +Returns the value of a named cookie if set at the client level. ```go title="Signature" func (c *Client) Cookie(key string) string ``` -#### SetCookie +### SetCookie -SetCookie method sets a single cookie field and its value in the client instance. -These cookies will be applied to all requests raised from this client instance. -Also it can be overridden at request level cookie options. +Sets a single cookie for all requests. ```go title="Signature" func (c *Client) SetCookie(key, val string) *Client ``` +**Example:** + ```go title="Example" cc := client.New() cc.SetCookie("john", "doe") @@ -682,71 +670,73 @@ fmt.Println(string(resp.Body()))
-#### SetCookies +### SetCookies -SetCookies method sets multiple cookies field and its values at one go in the client instance. -These cookies will be applied to all requests raised from this client instance. -Also it can be overridden at request level cookie options. +Sets multiple cookies at once. ```go title="Signature" func (c *Client) SetCookies(m map[string]string) *Client ``` -#### SetCookiesWithStruct +### SetCookiesWithStruct -SetCookiesWithStruct method sets multiple cookies field and its values at one go in the client instance. -These cookies will be applied to all requests raised from this client instance. -Also it can be overridden at request level cookies options. +Sets multiple cookies from a struct. ```go title="Signature" func (c *Client) SetCookiesWithStruct(v any) *Client ``` -#### DelCookies +### DelCookies -DelCookies method deletes single or multiple cookies field and its values in client. +Deletes one or more cookies. ```go title="Signature" func (c *Client) DelCookies(key ...string) *Client ``` +## Timeout + ### SetTimeout -SetTimeout method sets timeout val in client instance. -This value will be applied to all requests raised from this client instance. -Also, it can be overridden at request level timeout options. +Sets a default timeout for all requests, which can be overridden per request. ```go title="Signature" func (c *Client) SetTimeout(t time.Duration) *Client ``` +## Debugging + ### Debug -Debug enable log debug level output. +Enables debug-level logging output. ```go title="Signature" func (c *Client) Debug() *Client ``` -#### DisableDebug +### DisableDebug -DisableDebug disables log debug level output. +Disables debug-level logging output. ```go title="Signature" func (c *Client) DisableDebug() *Client ``` +## Cookie Jar + ### SetCookieJar -SetCookieJar sets cookie jar in client instance. +Assigns a cookie jar to the client to store and manage cookies across requests. ```go title="Signature" func (c *Client) SetCookieJar(cookieJar *CookieJar) *Client ``` +## Dial & Logger + ### SetDial -SetDial sets dial function in client. +Sets a custom dial function. ```go title="Signature" func (c *Client) SetDial(dial fasthttp.DialFunc) *Client @@ -754,7 +744,7 @@ func (c *Client) SetDial(dial fasthttp.DialFunc) *Client ### SetLogger -SetLogger sets logger instance in client. +Sets the logger instance used by the client. ```go title="Signature" func (c *Client) SetLogger(logger log.CommonLogger) *Client @@ -762,15 +752,17 @@ func (c *Client) SetLogger(logger log.CommonLogger) *Client ### Logger -Logger returns logger instance of client. +Returns the current logger instance. ```go title="Signature" func (c *Client) Logger() log.CommonLogger ``` +## Reset + ### Reset -Reset clears the Client object +Clears and resets the client to its default state. ```go title="Signature" func (c *Client) Reset() @@ -778,12 +770,11 @@ func (c *Client) Reset() ## Default Client -Default client is default client object of Gofiber and created using `New()`. -You can configurate it as you wish or replace it with another clients. +Fiber provides a default client (created with `New()`). You can configure it or replace it as needed. ### C -C gets default client. +**C** returns the default client. ```go title="Signature" func C() *Client @@ -847,10 +838,10 @@ func Options(url string, cfg ...Config) (*Response, error) ### Replace -Replace the defaultClient, the returned function can undo. +**Replace** replaces the default client with a new one. It returns a function that can restore the old client. :::caution -The default client should not be changed concurrently. +Do not modify the default client concurrently. ::: ```go title="Signature"