fiber/client/request_test.go

1864 lines
40 KiB
Go

//nolint:goconst // Much easier to just ignore memory leaks in tests
package client
import (
"bytes"
"context"
"errors"
"io"
"maps"
"mime/multipart"
"net"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"github.com/gofiber/fiber/v3"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/valyala/fasthttp"
"github.com/valyala/fasthttp/fasthttputil"
)
func Test_Request_Method(t *testing.T) {
t.Parallel()
req := AcquireRequest()
req.SetMethod("GET")
require.Equal(t, "GET", req.Method())
req.SetMethod("POST")
require.Equal(t, "POST", req.Method())
req.SetMethod("PUT")
require.Equal(t, "PUT", req.Method())
req.SetMethod("DELETE")
require.Equal(t, "DELETE", req.Method())
req.SetMethod("PATCH")
require.Equal(t, "PATCH", req.Method())
req.SetMethod("OPTIONS")
require.Equal(t, "OPTIONS", req.Method())
req.SetMethod("HEAD")
require.Equal(t, "HEAD", req.Method())
req.SetMethod("TRACE")
require.Equal(t, "TRACE", req.Method())
req.SetMethod("CUSTOM")
require.Equal(t, "CUSTOM", req.Method())
}
func Test_Request_URL(t *testing.T) {
t.Parallel()
req := AcquireRequest()
req.SetURL("http://example.com/normal")
require.Equal(t, "http://example.com/normal", req.URL())
req.SetURL("https://example.com/normal")
require.Equal(t, "https://example.com/normal", req.URL())
}
func Test_Request_Client(t *testing.T) {
t.Parallel()
client := New()
req := AcquireRequest()
req.SetClient(client)
require.Equal(t, client, req.Client())
}
func Test_Request_Context(t *testing.T) {
t.Parallel()
req := AcquireRequest()
ctx := req.Context()
type ctxKey struct{}
var key ctxKey = struct{}{}
require.Nil(t, ctx.Value(key))
ctx = context.WithValue(ctx, key, "string")
req.SetContext(ctx)
ctx = req.Context()
v, ok := ctx.Value(key).(string)
require.True(t, ok)
require.Equal(t, "string", v)
}
func Test_Request_Header(t *testing.T) {
t.Parallel()
t.Run("add header", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
req.AddHeader("foo", "bar").AddHeader("foo", "fiber")
res := req.Header("foo")
require.Len(t, res, 2)
require.Equal(t, "bar", res[0])
require.Equal(t, "fiber", res[1])
})
t.Run("set header", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
req.AddHeader("foo", "bar").SetHeader("foo", "fiber")
res := req.Header("foo")
require.Len(t, res, 1)
require.Equal(t, "fiber", res[0])
})
t.Run("add headers", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
req.SetHeader("foo", "bar").
AddHeaders(map[string][]string{
"foo": {"fiber", "buaa"},
"bar": {"foo"},
})
res := req.Header("foo")
require.Len(t, res, 3)
require.Equal(t, "bar", res[0])
require.Equal(t, "fiber", res[1])
require.Equal(t, "buaa", res[2])
res = req.Header("bar")
require.Len(t, res, 1)
require.Equal(t, "foo", res[0])
})
t.Run("set headers", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
req.SetHeader("foo", "bar").
SetHeaders(map[string]string{
"foo": "fiber",
"bar": "foo",
})
res := req.Header("foo")
require.Len(t, res, 1)
require.Equal(t, "fiber", res[0])
res = req.Header("bar")
require.Len(t, res, 1)
require.Equal(t, "foo", res[0])
})
}
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()
t.Run("add param", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
req.AddParam("foo", "bar").AddParam("foo", "fiber")
res := req.Param("foo")
require.Len(t, res, 2)
require.Equal(t, "bar", res[0])
require.Equal(t, "fiber", res[1])
})
t.Run("set param", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
req.AddParam("foo", "bar").SetParam("foo", "fiber")
res := req.Param("foo")
require.Len(t, res, 1)
require.Equal(t, "fiber", res[0])
})
t.Run("add params", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
req.SetParam("foo", "bar").
AddParams(map[string][]string{
"foo": {"fiber", "buaa"},
"bar": {"foo"},
})
res := req.Param("foo")
require.Len(t, res, 3)
require.Equal(t, "bar", res[0])
require.Equal(t, "fiber", res[1])
require.Equal(t, "buaa", res[2])
res = req.Param("bar")
require.Len(t, res, 1)
require.Equal(t, "foo", res[0])
})
t.Run("set headers", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
req.SetParam("foo", "bar").
SetParams(map[string]string{
"foo": "fiber",
"bar": "foo",
})
res := req.Param("foo")
require.Len(t, res, 1)
require.Equal(t, "fiber", res[0])
res = req.Param("bar")
require.Len(t, res, 1)
require.Equal(t, "foo", res[0])
})
t.Run("set params with struct", func(t *testing.T) {
t.Parallel()
type args struct {
TString string
TSlice []string
TIntSlice []int `param:"int_slice"`
TInt int
TFloat float64
TBool bool
}
p := AcquireRequest()
p.SetParamsWithStruct(&args{
TInt: 5,
TString: "string",
TFloat: 3.1,
TBool: true,
TSlice: []string{"foo", "bar"},
TIntSlice: []int{1, 2},
})
require.Empty(t, p.Param("unexport"))
require.Len(t, p.Param("TInt"), 1)
require.Equal(t, "5", p.Param("TInt")[0])
require.Len(t, p.Param("TString"), 1)
require.Equal(t, "string", p.Param("TString")[0])
require.Len(t, p.Param("TFloat"), 1)
require.Equal(t, "3.1", p.Param("TFloat")[0])
require.Len(t, p.Param("TBool"), 1)
tslice := p.Param("TSlice")
require.Len(t, tslice, 2)
require.Equal(t, "foo", tslice[0])
require.Equal(t, "bar", tslice[1])
tint := p.Param("TSlice")
require.Len(t, tint, 2)
require.Equal(t, "foo", tint[0])
require.Equal(t, "bar", tint[1])
})
t.Run("del params", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
req.SetParam("foo", "bar").
SetParams(map[string]string{
"foo": "fiber",
"bar": "foo",
}).DelParams("foo", "bar")
res := req.Param("foo")
require.Empty(t, res)
res = req.Param("bar")
require.Empty(t, res)
})
}
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()
req := AcquireRequest().SetUserAgent("fiber")
require.Equal(t, "fiber", req.UserAgent())
req.SetUserAgent("foo")
require.Equal(t, "foo", req.UserAgent())
}
func Test_Request_Referer(t *testing.T) {
t.Parallel()
req := AcquireRequest().SetReferer("http://example.com")
require.Equal(t, "http://example.com", req.Referer())
req.SetReferer("https://example.com")
require.Equal(t, "https://example.com", req.Referer())
}
func Test_Request_Cookie(t *testing.T) {
t.Parallel()
t.Run("set cookie", func(t *testing.T) {
t.Parallel()
req := AcquireRequest().
SetCookie("foo", "bar")
require.Equal(t, "bar", req.Cookie("foo"))
req.SetCookie("foo", "bar1")
require.Equal(t, "bar1", req.Cookie("foo"))
})
t.Run("set cookies", func(t *testing.T) {
t.Parallel()
req := AcquireRequest().
SetCookies(map[string]string{
"foo": "bar",
"bar": "foo",
})
require.Equal(t, "bar", req.Cookie("foo"))
require.Equal(t, "foo", req.Cookie("bar"))
req.SetCookies(map[string]string{
"foo": "bar1",
})
require.Equal(t, "bar1", req.Cookie("foo"))
require.Equal(t, "foo", req.Cookie("bar"))
})
t.Run("set cookies with struct", func(t *testing.T) {
t.Parallel()
type args struct {
CookieString string `cookie:"string"`
CookieInt int `cookie:"int"`
}
req := AcquireRequest().SetCookiesWithStruct(&args{
CookieInt: 5,
CookieString: "foo",
})
require.Equal(t, "5", req.Cookie("int"))
require.Equal(t, "foo", req.Cookie("string"))
})
t.Run("del cookies", func(t *testing.T) {
t.Parallel()
req := AcquireRequest().
SetCookies(map[string]string{
"foo": "bar",
"bar": "foo",
})
require.Equal(t, "bar", req.Cookie("foo"))
require.Equal(t, "foo", req.Cookie("bar"))
req.DelCookies("foo")
require.Equal(t, "", req.Cookie("foo"))
require.Equal(t, "foo", req.Cookie("bar"))
})
}
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.NotPanics(t, func() {
for _, v := range req.Cookies() {
if v == "bar" {
break
}
}
})
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()
t.Run("set path param", func(t *testing.T) {
t.Parallel()
req := AcquireRequest().
SetPathParam("foo", "bar")
require.Equal(t, "bar", req.PathParam("foo"))
req.SetPathParam("foo", "bar1")
require.Equal(t, "bar1", req.PathParam("foo"))
})
t.Run("set path params", func(t *testing.T) {
t.Parallel()
req := AcquireRequest().
SetPathParams(map[string]string{
"foo": "bar",
"bar": "foo",
})
require.Equal(t, "bar", req.PathParam("foo"))
require.Equal(t, "foo", req.PathParam("bar"))
req.SetPathParams(map[string]string{
"foo": "bar1",
})
require.Equal(t, "bar1", req.PathParam("foo"))
require.Equal(t, "foo", req.PathParam("bar"))
})
t.Run("set path params with struct", func(t *testing.T) {
t.Parallel()
type args struct {
CookieString string `path:"string"`
CookieInt int `path:"int"`
}
req := AcquireRequest().SetPathParamsWithStruct(&args{
CookieInt: 5,
CookieString: "foo",
})
require.Equal(t, "5", req.PathParam("int"))
require.Equal(t, "foo", req.PathParam("string"))
})
t.Run("del path params", func(t *testing.T) {
t.Parallel()
req := AcquireRequest().
SetPathParams(map[string]string{
"foo": "bar",
"bar": "foo",
})
require.Equal(t, "bar", req.PathParam("foo"))
require.Equal(t, "foo", req.PathParam("bar"))
req.DelPathParams("foo")
require.Equal(t, "", req.PathParam("foo"))
require.Equal(t, "foo", req.PathParam("bar"))
})
t.Run("clear path params", func(t *testing.T) {
t.Parallel()
req := AcquireRequest().
SetPathParams(map[string]string{
"foo": "bar",
"bar": "foo",
})
require.Equal(t, "bar", req.PathParam("foo"))
require.Equal(t, "foo", req.PathParam("bar"))
req.ResetPathParams()
require.Equal(t, "", req.PathParam("foo"))
require.Equal(t, "", req.PathParam("bar"))
})
}
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)
require.NotPanics(t, func() {
for _, v := range req.PathParams() {
if v == "bar" {
break
}
}
})
}
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()
t.Run("add form data", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
defer ReleaseRequest(req)
req.AddFormData("foo", "bar").AddFormData("foo", "fiber")
res := req.FormData("foo")
require.Len(t, res, 2)
require.Equal(t, "bar", res[0])
require.Equal(t, "fiber", res[1])
})
t.Run("set param", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
defer ReleaseRequest(req)
req.AddFormData("foo", "bar").SetFormData("foo", "fiber")
res := req.FormData("foo")
require.Len(t, res, 1)
require.Equal(t, "fiber", res[0])
})
t.Run("add params", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
defer ReleaseRequest(req)
req.SetFormData("foo", "bar").
AddFormDataWithMap(map[string][]string{
"foo": {"fiber", "buaa"},
"bar": {"foo"},
})
res := req.FormData("foo")
require.Len(t, res, 3)
require.Contains(t, res, "bar")
require.Contains(t, res, "buaa")
require.Contains(t, res, "fiber")
res = req.FormData("bar")
require.Len(t, res, 1)
require.Equal(t, "foo", res[0])
})
t.Run("set headers", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
defer ReleaseRequest(req)
req.SetFormData("foo", "bar").
SetFormDataWithMap(map[string]string{
"foo": "fiber",
"bar": "foo",
})
res := req.FormData("foo")
require.Len(t, res, 1)
require.Equal(t, "fiber", res[0])
res = req.FormData("bar")
require.Len(t, res, 1)
require.Equal(t, "foo", res[0])
})
t.Run("set params with struct", func(t *testing.T) {
t.Parallel()
type args struct {
TString string
TSlice []string
TIntSlice []int `form:"int_slice"`
TInt int
TFloat float64
TBool bool
}
p := AcquireRequest()
defer ReleaseRequest(p)
p.SetFormDataWithStruct(&args{
TInt: 5,
TString: "string",
TFloat: 3.1,
TBool: true,
TSlice: []string{"foo", "bar"},
TIntSlice: []int{1, 2},
})
require.Empty(t, p.FormData("unexport"))
require.Len(t, p.FormData("TInt"), 1)
require.Equal(t, "5", p.FormData("TInt")[0])
require.Len(t, p.FormData("TString"), 1)
require.Equal(t, "string", p.FormData("TString")[0])
require.Len(t, p.FormData("TFloat"), 1)
require.Equal(t, "3.1", p.FormData("TFloat")[0])
require.Len(t, p.FormData("TBool"), 1)
tslice := p.FormData("TSlice")
require.Len(t, tslice, 2)
require.Contains(t, tslice, "bar")
require.Contains(t, tslice, "foo")
tint := p.FormData("TSlice")
require.Len(t, tint, 2)
require.Contains(t, tint, "bar")
require.Contains(t, tint, "foo")
})
t.Run("del params", func(t *testing.T) {
t.Parallel()
req := AcquireRequest()
defer ReleaseRequest(req)
req.SetFormData("foo", "bar").
SetFormDataWithMap(map[string]string{
"foo": "fiber",
"bar": "foo",
}).DelFormData("foo", "bar")
res := req.FormData("foo")
require.Empty(t, res)
res = req.FormData("bar")
require.Empty(t, res)
})
}
func Test_Request_File(t *testing.T) {
t.Parallel()
t.Run("add file", func(t *testing.T) {
t.Parallel()
req := AcquireRequest().
AddFile("../.github/index.html").
AddFiles(AcquireFile(SetFileName("tmp.txt")))
require.Equal(t, "../.github/index.html", req.File("index.html").path)
require.Equal(t, "../.github/index.html", req.FileByPath("../.github/index.html").path)
require.Equal(t, "tmp.txt", req.File("tmp.txt").name)
require.Nil(t, req.File("tmp2.txt"))
require.Nil(t, req.FileByPath("tmp2.txt"))
})
t.Run("add file by reader", func(t *testing.T) {
t.Parallel()
req := AcquireRequest().
AddFileWithReader("tmp.txt", io.NopCloser(strings.NewReader("world")))
require.Equal(t, "tmp.txt", req.File("tmp.txt").name)
content, err := io.ReadAll(req.File("tmp.txt").reader)
require.NoError(t, err)
require.Equal(t, "world", string(content))
})
t.Run("add files", func(t *testing.T) {
t.Parallel()
req := AcquireRequest().
AddFiles(AcquireFile(SetFileName("tmp.txt")), AcquireFile(SetFileName("foo.txt")))
require.Equal(t, "tmp.txt", req.File("tmp.txt").name)
require.Equal(t, "foo.txt", req.File("foo.txt").name)
})
}
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()
req := AcquireRequest().SetTimeout(5 * time.Second)
require.Equal(t, 5*time.Second, req.Timeout())
}
func Test_Request_Invalid_URL(t *testing.T) {
t.Parallel()
resp, err := AcquireRequest().
Get("http://example.com\r\n\r\nGET /\r\n\r\n")
require.Equal(t, ErrURLFormat, err)
require.Equal(t, (*Response)(nil), resp)
}
func Test_Request_Unsupport_Protocol(t *testing.T) {
t.Parallel()
resp, err := AcquireRequest().
Get("ftp://example.com")
require.Equal(t, ErrURLFormat, err)
require.Equal(t, (*Response)(nil), resp)
}
func Test_Request_Get(t *testing.T) {
t.Parallel()
app, ln, start := createHelperServer(t)
app.Get("/", func(c fiber.Ctx) error {
return c.SendString(c.Hostname())
})
go start()
time.Sleep(100 * time.Millisecond)
client := New().SetDial(ln)
for i := 0; i < 5; i++ {
req := AcquireRequest().SetClient(client)
resp, err := req.Get("http://example.com")
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode())
require.Equal(t, "example.com", resp.String())
resp.Close()
}
}
func Test_Request_Post(t *testing.T) {
t.Parallel()
app, ln, start := createHelperServer(t)
app.Post("/", func(c fiber.Ctx) error {
return c.Status(fiber.StatusCreated).
SendString(c.FormValue("foo"))
})
go start()
time.Sleep(100 * time.Millisecond)
client := New().SetDial(ln)
for i := 0; i < 5; i++ {
resp, err := AcquireRequest().
SetClient(client).
SetFormData("foo", "bar").
Post("http://example.com")
require.NoError(t, err)
require.Equal(t, fiber.StatusCreated, resp.StatusCode())
require.Equal(t, "bar", resp.String())
resp.Close()
}
}
func Test_Request_Head(t *testing.T) {
t.Parallel()
app, ln, start := createHelperServer(t)
app.Head("/", func(c fiber.Ctx) error {
return c.SendString(c.Hostname())
})
go start()
time.Sleep(100 * time.Millisecond)
client := New().SetDial(ln)
for i := 0; i < 5; i++ {
resp, err := AcquireRequest().
SetClient(client).
Head("http://example.com")
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode())
require.Equal(t, "", resp.String())
resp.Close()
}
}
func Test_Request_Put(t *testing.T) {
t.Parallel()
app, ln, start := createHelperServer(t)
app.Put("/", func(c fiber.Ctx) error {
return c.SendString(c.FormValue("foo"))
})
go start()
time.Sleep(100 * time.Millisecond)
client := New().SetDial(ln)
for i := 0; i < 5; i++ {
resp, err := AcquireRequest().
SetClient(client).
SetFormData("foo", "bar").
Put("http://example.com")
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode())
require.Equal(t, "bar", resp.String())
resp.Close()
}
}
func Test_Request_Delete(t *testing.T) {
t.Parallel()
app, ln, start := createHelperServer(t)
app.Delete("/", func(c fiber.Ctx) error {
return c.Status(fiber.StatusNoContent).
SendString("deleted")
})
go start()
time.Sleep(100 * time.Millisecond)
client := New().SetDial(ln)
for i := 0; i < 5; i++ {
resp, err := AcquireRequest().
SetClient(client).
Delete("http://example.com")
require.NoError(t, err)
require.Equal(t, fiber.StatusNoContent, resp.StatusCode())
require.Equal(t, "", resp.String())
resp.Close()
}
}
func Test_Request_Options(t *testing.T) {
t.Parallel()
app, ln, start := createHelperServer(t)
app.Options("/", func(c fiber.Ctx) error {
return c.Status(fiber.StatusOK).
SendString("options")
})
go start()
time.Sleep(100 * time.Millisecond)
client := New().SetDial(ln)
for i := 0; i < 5; i++ {
resp, err := AcquireRequest().
SetClient(client).
Options("http://example.com")
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode())
require.Equal(t, "options", resp.String())
resp.Close()
}
}
func Test_Request_Send(t *testing.T) {
t.Parallel()
app, ln, start := createHelperServer(t)
app.Post("/", func(c fiber.Ctx) error {
return c.Status(fiber.StatusOK).
SendString("post")
})
go start()
time.Sleep(100 * time.Millisecond)
client := New().SetDial(ln)
for i := 0; i < 5; i++ {
resp, err := AcquireRequest().
SetClient(client).
SetURL("http://example.com").
SetMethod(fiber.MethodPost).
Send()
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode())
require.Equal(t, "post", resp.String())
resp.Close()
}
}
func Test_Request_Patch(t *testing.T) {
t.Parallel()
app, ln, start := createHelperServer(t)
app.Patch("/", func(c fiber.Ctx) error {
return c.SendString(c.FormValue("foo"))
})
go start()
time.Sleep(100 * time.Millisecond)
client := New().SetDial(ln)
for i := 0; i < 5; i++ {
resp, err := AcquireRequest().
SetClient(client).
SetFormData("foo", "bar").
Patch("http://example.com")
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode())
require.Equal(t, "bar", resp.String())
resp.Close()
}
}
func Test_Request_Header_With_Server(t *testing.T) {
t.Parallel()
handler := func(c fiber.Ctx) error {
c.Request().Header.VisitAll(func(key, value []byte) {
if k := string(key); k == "K1" || k == "K2" {
_, err := c.Write(key)
require.NoError(t, err)
_, err = c.Write(value)
require.NoError(t, err)
}
})
return nil
}
wrapAgent := func(r *Request) {
r.SetHeader("k1", "v1").
AddHeader("k1", "v11").
AddHeaders(map[string][]string{
"k1": {"v22", "v33"},
}).
SetHeaders(map[string]string{
"k2": "v2",
}).
AddHeader("k2", "v22")
}
testRequest(t, handler, wrapAgent, "K1v1K1v11K1v22K1v33K2v2K2v22")
}
func Test_Request_UserAgent_With_Server(t *testing.T) {
t.Parallel()
handler := func(c fiber.Ctx) error {
return c.Send(c.Request().Header.UserAgent())
}
t.Run("default", func(t *testing.T) {
t.Parallel()
testRequest(t, handler, func(_ *Request) {}, defaultUserAgent, 5)
})
t.Run("custom", func(t *testing.T) {
t.Parallel()
testRequest(t, handler, func(agent *Request) {
agent.SetUserAgent("ua")
}, "ua", 5)
})
}
func Test_Request_Cookie_With_Server(t *testing.T) {
t.Parallel()
handler := func(c fiber.Ctx) error {
return c.SendString(
c.Cookies("k1") + c.Cookies("k2") + c.Cookies("k3") + c.Cookies("k4"))
}
wrapAgent := func(req *Request) {
req.SetCookie("k1", "v1").
SetCookies(map[string]string{
"k2": "v2",
"k3": "v3",
"k4": "v4",
}).DelCookies("k4")
}
testRequest(t, handler, wrapAgent, "v1v2v3")
}
func Test_Request_Referer_With_Server(t *testing.T) {
t.Parallel()
handler := func(c fiber.Ctx) error {
return c.Send(c.Request().Header.Referer())
}
wrapAgent := func(req *Request) {
req.SetReferer("http://referer.com")
}
testRequest(t, handler, wrapAgent, "http://referer.com")
}
func Test_Request_QueryString_With_Server(t *testing.T) {
t.Parallel()
handler := func(c fiber.Ctx) error {
return c.Send(c.Request().URI().QueryString())
}
wrapAgent := func(req *Request) {
req.SetParam("foo", "bar").
SetParams(map[string]string{
"bar": "baz",
})
}
testRequest(t, handler, wrapAgent, "foo=bar&bar=baz")
}
func checkFormFile(t *testing.T, fh *multipart.FileHeader, filename string) {
t.Helper()
basename := filepath.Base(filename)
require.Equal(t, fh.Filename, basename)
b1, err := os.ReadFile(filepath.Clean(filename))
require.NoError(t, err)
b2 := make([]byte, fh.Size)
f, err := fh.Open()
require.NoError(t, err)
defer func() { require.NoError(t, f.Close()) }()
_, err = f.Read(b2)
require.NoError(t, err)
require.Equal(t, b1, b2)
}
func Test_Request_Body_With_Server(t *testing.T) {
t.Parallel()
t.Run("json body", func(t *testing.T) {
t.Parallel()
testRequest(t,
func(c fiber.Ctx) error {
require.Equal(t, "application/json", string(c.Request().Header.ContentType()))
return c.SendString(string(c.Request().Body()))
},
func(agent *Request) {
agent.SetJSON(map[string]string{
"success": "hello",
})
},
"{\"success\":\"hello\"}",
)
})
t.Run("xml body", func(t *testing.T) {
t.Parallel()
testRequest(t,
func(c fiber.Ctx) error {
require.Equal(t, "application/xml", string(c.Request().Header.ContentType()))
return c.SendString(string(c.Request().Body()))
},
func(agent *Request) {
type args struct {
Content string `xml:"content"`
}
agent.SetXML(args{
Content: "hello",
})
},
"<args><content>hello</content></args>",
)
})
t.Run("cbor body", func(t *testing.T) {
t.Parallel()
testRequest(t,
func(c fiber.Ctx) error {
require.Equal(t, "application/cbor", string(c.Request().Header.ContentType()))
return c.SendString(string(c.Request().Body()))
},
func(agent *Request) {
type args struct {
Content string `cbor:"content"`
}
agent.SetCBOR(args{
Content: "hello",
})
},
"\xa1gcontentehello",
)
})
t.Run("formdata", func(t *testing.T) {
t.Parallel()
testRequest(t,
func(c fiber.Ctx) error {
require.Equal(t, fiber.MIMEApplicationForm, string(c.Request().Header.ContentType()))
return c.Send([]byte("foo=" + c.FormValue("foo") + "&bar=" + c.FormValue("bar") + "&fiber=" + c.FormValue("fiber")))
},
func(agent *Request) {
agent.SetFormData("foo", "bar").
SetFormDataWithMap(map[string]string{
"bar": "baz",
"fiber": "fast",
})
},
"foo=bar&bar=baz&fiber=fast")
})
t.Run("multipart form", func(t *testing.T) {
t.Parallel()
app, ln, start := createHelperServer(t)
app.Post("/", func(c fiber.Ctx) error {
require.Equal(t, "multipart/form-data; boundary=myBoundary", c.Get(fiber.HeaderContentType))
mf, err := c.MultipartForm()
require.NoError(t, err)
require.Equal(t, "bar", mf.Value["foo"][0])
return c.Send(c.Request().Body())
})
go start()
client := New().SetDial(ln)
req := AcquireRequest().
SetClient(client).
SetBoundary("myBoundary").
SetFormData("foo", "bar").
AddFiles(AcquireFile(
SetFileName("hello.txt"),
SetFileFieldName("foo"),
SetFileReader(io.NopCloser(strings.NewReader("world"))),
))
require.Equal(t, "myBoundary", req.Boundary())
resp, err := req.Post("http://exmaple.com")
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode())
form, err := multipart.NewReader(bytes.NewReader(resp.Body()), "myBoundary").ReadForm(1024 * 1024)
require.NoError(t, err)
require.Equal(t, "bar", form.Value["foo"][0])
resp.Close()
})
t.Run("multipart form send file", func(t *testing.T) {
t.Parallel()
app, ln, start := createHelperServer(t)
app.Post("/", func(c fiber.Ctx) error {
require.Equal(t, "multipart/form-data; boundary=myBoundary", c.Get(fiber.HeaderContentType))
fh1, err := c.FormFile("field1")
require.NoError(t, err)
require.Equal(t, "name", fh1.Filename)
buf := make([]byte, fh1.Size)
f, err := fh1.Open()
require.NoError(t, err)
defer func() { require.NoError(t, f.Close()) }()
_, err = f.Read(buf)
require.NoError(t, err)
require.Equal(t, "form file", string(buf))
fh2, err := c.FormFile("file2")
require.NoError(t, err)
checkFormFile(t, fh2, "../.github/testdata/index.html")
fh3, err := c.FormFile("file3")
require.NoError(t, err)
checkFormFile(t, fh3, "../.github/testdata/index.tmpl")
return c.SendString("multipart form files")
})
go start()
client := New().SetDial(ln)
for i := 0; i < 5; i++ {
req := AcquireRequest().
SetClient(client).
AddFiles(
AcquireFile(
SetFileFieldName("field1"),
SetFileName("name"),
SetFileReader(io.NopCloser(bytes.NewReader([]byte("form file")))),
),
).
AddFile("../.github/testdata/index.html").
AddFile("../.github/testdata/index.tmpl").
SetBoundary("myBoundary")
resp, err := req.Post("http://example.com")
require.NoError(t, err)
require.Equal(t, "multipart form files", resp.String())
resp.Close()
}
})
t.Run("multipart random boundary", func(t *testing.T) {
t.Parallel()
app, ln, start := createHelperServer(t)
app.Post("/", func(c fiber.Ctx) error {
reg := regexp.MustCompile(`multipart/form-data; boundary=[\-\w]{35}`)
require.True(t, reg.MatchString(c.Get(fiber.HeaderContentType)))
return c.Send(c.Request().Body())
})
go start()
client := New().SetDial(ln)
req := AcquireRequest().
SetClient(client).
SetFormData("foo", "bar").
AddFiles(AcquireFile(
SetFileName("hello.txt"),
SetFileFieldName("foo"),
SetFileReader(io.NopCloser(strings.NewReader("world"))),
))
resp, err := req.Post("http://exmaple.com")
require.NoError(t, err)
require.Equal(t, fiber.StatusOK, resp.StatusCode())
})
t.Run("raw body", func(t *testing.T) {
t.Parallel()
testRequest(t,
func(c fiber.Ctx) error {
return c.SendString(string(c.Request().Body()))
},
func(agent *Request) {
agent.SetRawBody([]byte("hello"))
},
"hello",
)
})
}
func Test_Request_AllFormData(t *testing.T) {
t.Parallel()
req := AcquireRequest()
req.AddFormDataWithMap(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.AddFormDataWithMap(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) {
t.Parallel()
testRequestFail(t,
func(c fiber.Ctx) error {
return c.SendString("")
},
func(agent *Request) {
agent.SetJSON(complex(1, 1))
},
errors.New("json: unsupported type: complex128"),
)
})
t.Run("xml error", func(t *testing.T) {
t.Parallel()
testRequestFail(t,
func(c fiber.Ctx) error {
return c.SendString("")
},
func(agent *Request) {
agent.SetXML(complex(1, 1))
},
errors.New("xml: unsupported type: complex128"),
)
})
t.Run("form body with invalid boundary", func(t *testing.T) {
t.Parallel()
_, err := AcquireRequest().
SetBoundary("*").
AddFileWithReader("t.txt", io.NopCloser(strings.NewReader("world"))).
Get("http://example.com")
require.Equal(t, "set boundary error: mime: invalid boundary character", err.Error())
})
t.Run("open non exist file", func(t *testing.T) {
t.Parallel()
_, err := AcquireRequest().
AddFile("non-exist-file!").
Get("http://example.com")
require.Contains(t, err.Error(), "open non-exist-file!")
})
}
func Test_Request_Timeout_With_Server(t *testing.T) {
t.Parallel()
app, ln, start := createHelperServer(t)
app.Get("/", func(c fiber.Ctx) error {
time.Sleep(time.Millisecond * 200)
return c.SendString("timeout")
})
go start()
client := New().SetDial(ln)
_, err := AcquireRequest().
SetClient(client).
SetTimeout(50 * time.Millisecond).
Get("http://example.com")
require.Equal(t, ErrTimeoutOrCancel, err)
}
func Test_Request_MaxRedirects(t *testing.T) {
t.Parallel()
ln := fasthttputil.NewInmemoryListener()
app := fiber.New()
app.Get("/", func(c fiber.Ctx) error {
if c.Request().URI().QueryArgs().Has("foo") {
return c.Redirect().To("/foo")
}
return c.Redirect().To("/")
})
app.Get("/foo", func(c fiber.Ctx) error {
return c.SendString("redirect")
})
go func() { assert.NoError(t, app.Listener(ln, fiber.ListenConfig{DisableStartupMessage: true})) }()
t.Run("success", func(t *testing.T) {
t.Parallel()
client := New().SetDial(func(_ string) (net.Conn, error) { return ln.Dial() })
resp, err := AcquireRequest().
SetClient(client).
SetMaxRedirects(1).
Get("http://example.com?foo")
body := resp.String()
code := resp.StatusCode()
require.Equal(t, 200, code)
require.Equal(t, "redirect", body)
require.NoError(t, err)
resp.Close()
})
t.Run("error", func(t *testing.T) {
t.Parallel()
client := New().SetDial(func(_ string) (net.Conn, error) { return ln.Dial() })
resp, err := AcquireRequest().
SetClient(client).
SetMaxRedirects(1).
Get("http://example.com")
require.Nil(t, resp)
require.Equal(t, "too many redirects detected when doing the request", err.Error())
})
t.Run("MaxRedirects", func(t *testing.T) {
t.Parallel()
client := New().SetDial(func(_ string) (net.Conn, error) { return ln.Dial() })
req := AcquireRequest().
SetClient(client).
SetMaxRedirects(3)
require.Equal(t, 3, req.MaxRedirects())
})
}
func Test_SetValWithStruct(t *testing.T) {
t.Parallel()
// 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
}
t.Run("the struct should be applied", func(t *testing.T) {
t.Parallel()
p := &QueryParam{
Args: fasthttp.AcquireArgs(),
}
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{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, []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" {
return true
}
}
return false
}())
require.True(t, func() bool {
for _, v := range p.PeekMulti("TSlice") {
if string(v) == "bar" {
return true
}
}
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" {
return true
}
}
return false
}())
require.True(t, func() bool {
for _, v := range p.PeekMulti("int_slice") {
if string(v) == "2" {
return true
}
}
return false
}())
})
t.Run("the pointer of a struct should be applied", func(t *testing.T) {
t.Parallel()
p := &QueryParam{
Args: fasthttp.AcquireArgs(),
}
SetValWithStruct(p, "param", &args{
TInt: 5,
TString: "string",
TFloat: 3.1,
TBool: true,
TSlice: []string{"foo", "bar"},
TIntSlice: []int{1, 2},
})
require.Equal(t, []byte("5"), p.Peek("TInt"))
require.Equal(t, []byte("string"), p.Peek("TString"))
require.Equal(t, []byte("3.1"), p.Peek("TFloat"))
require.Equal(t, "true", string(p.Peek("TBool")))
require.True(t, func() bool {
for _, v := range p.PeekMulti("TSlice") {
if string(v) == "foo" {
return true
}
}
return false
}())
require.True(t, func() bool {
for _, v := range p.PeekMulti("TSlice") {
if string(v) == "bar" {
return true
}
}
return false
}())
require.True(t, func() bool {
for _, v := range p.PeekMulti("int_slice") {
if string(v) == "1" {
return true
}
}
return false
}())
require.True(t, func() bool {
for _, v := range p.PeekMulti("int_slice") {
if string(v) == "2" {
return true
}
}
return false
}())
})
t.Run("error type should ignore", func(t *testing.T) {
t.Parallel()
p := &QueryParam{
Args: fasthttp.AcquireArgs(),
}
SetValWithStruct(p, "param", 5)
require.Equal(t, 0, p.Len())
})
}
func Benchmark_SetValWithStruct(b *testing.B) {
// 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
}
b.Run("the struct should be applied", func(b *testing.B) {
p := &QueryParam{
Args: fasthttp.AcquireArgs(),
}
b.ReportAllocs()
b.StartTimer()
for i := 0; i < b.N; i++ {
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{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, []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" {
return true
}
}
return false
}())
require.True(b, func() bool {
for _, v := range p.PeekMulti("TSlice") {
if string(v) == "bar" {
return true
}
}
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" {
return true
}
}
return false
}())
require.True(b, func() bool {
for _, v := range p.PeekMulti("int_slice") {
if string(v) == "2" {
return true
}
}
return false
}())
})
b.Run("the pointer of a struct should be applied", 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: 5,
TString: "string",
TFloat: 3.1,
TBool: true,
TSlice: []string{"foo", "bar"},
TIntSlice: []int{1, 2},
})
}
require.Equal(b, []byte("5"), p.Peek("TInt"))
require.Equal(b, []byte("string"), p.Peek("TString"))
require.Equal(b, []byte("3.1"), p.Peek("TFloat"))
require.Equal(b, "true", string(p.Peek("TBool")))
require.True(b, func() bool {
for _, v := range p.PeekMulti("TSlice") {
if string(v) == "foo" {
return true
}
}
return false
}())
require.True(b, func() bool {
for _, v := range p.PeekMulti("TSlice") {
if string(v) == "bar" {
return true
}
}
return false
}())
require.True(b, func() bool {
for _, v := range p.PeekMulti("int_slice") {
if string(v) == "1" {
return true
}
}
return false
}())
require.True(b, func() bool {
for _, v := range p.PeekMulti("int_slice") {
if string(v) == "2" {
return true
}
}
return false
}())
})
b.Run("error type should ignore", func(b *testing.B) {
p := &QueryParam{
Args: fasthttp.AcquireArgs(),
}
b.ReportAllocs()
b.StartTimer()
for i := 0; i < b.N; i++ {
SetValWithStruct(p, "param", 5)
}
require.Equal(b, 0, p.Len())
})
}