1
0
mirror of https://github.com/gofiber/fiber.git synced 2025-02-06 10:23:55 +00:00
fiber/bind_test.go
M. Efe Çetin ef04a8a99e
🐛 bug: Fix square bracket notation in Multipart FormData (#3235)
* 🐛 bug: add square bracket notation support to BindMultipart

* Fix golangci-lint issues

* Fixing undef variable

* Fix more lint issues

* test

* update1

* improve coverage

* fix linter

* reduce code duplication

* reduce code duplications in bindMultipart

---------

Co-authored-by: Juan Calderon-Perez <835733+gaby@users.noreply.github.com>
Co-authored-by: René <rene@gofiber.io>
2024-12-31 16:34:28 +01:00

1888 lines
50 KiB
Go

//nolint:wrapcheck,tagliatelle // We must not wrap errors in tests
package fiber
import (
"bytes"
"compress/gzip"
"encoding/json"
"errors"
"fmt"
"mime/multipart"
"net/http/httptest"
"reflect"
"testing"
"time"
"github.com/fxamacker/cbor/v2"
"github.com/gofiber/fiber/v3/binder"
"github.com/stretchr/testify/require"
"github.com/valyala/fasthttp"
)
const helloWorld = "hello world"
// go test -run Test_returnErr -v
func Test_returnErr(t *testing.T) {
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
err := c.Bind().WithAutoHandling().returnErr(nil)
require.NoError(t, err)
}
// go test -run Test_Bind_Query -v
func Test_Bind_Query(t *testing.T) {
t.Parallel()
app := New(Config{
EnableSplittingOnParsers: true,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Query struct {
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball&hobby=football")
q := new(Query)
require.NoError(t, c.Bind().Query(q))
require.Len(t, q.Hobby, 2)
c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football")
q = new(Query)
require.NoError(t, c.Bind().Query(q))
require.Len(t, q.Hobby, 2)
c.Request().URI().SetQueryString("id=1&name=tom&hobby=scoccer&hobby=basketball,football")
q = new(Query)
require.NoError(t, c.Bind().Query(q))
require.Len(t, q.Hobby, 3)
empty := new(Query)
c.Request().URI().SetQueryString("")
require.NoError(t, c.Bind().Query(empty))
require.Empty(t, empty.Hobby)
type Query2 struct {
Name string
Hobby string
Default string `query:"default,default:hello"`
FavouriteDrinks []string
Empty []string
Alloc []string
Defaults []string `query:"defaults,default:hello|world"`
No []int64
ID int
Bool bool
}
c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football&favouriteDrinks=milo,coke,pepsi&alloc=&no=1")
q2 := new(Query2)
q2.Bool = true
q2.Name = helloWorld
require.NoError(t, c.Bind().Query(q2))
require.Equal(t, "basketball,football", q2.Hobby)
require.True(t, q2.Bool)
require.Equal(t, "tom", q2.Name) // check value get overwritten
require.Equal(t, []string{"milo", "coke", "pepsi"}, q2.FavouriteDrinks)
var nilSlice []string
require.Equal(t, nilSlice, q2.Empty)
require.Equal(t, []string{""}, q2.Alloc)
require.Equal(t, []int64{1}, q2.No)
require.Equal(t, "hello", q2.Default)
require.Equal(t, []string{"hello", "world"}, q2.Defaults)
type RequiredQuery struct {
Name string `query:"name,required"`
}
rq := new(RequiredQuery)
c.Request().URI().SetQueryString("")
require.Equal(t, "bind: name is empty", c.Bind().Query(rq).Error())
type ArrayQuery struct {
Data []string
}
aq := new(ArrayQuery)
c.Request().URI().SetQueryString("data[]=john&data[]=doe")
require.NoError(t, c.Bind().Query(aq))
require.Len(t, aq.Data, 2)
}
// go test -run Test_Bind_Query_Map -v
func Test_Bind_Query_Map(t *testing.T) {
t.Parallel()
app := New(Config{
EnableSplittingOnParsers: true,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball&hobby=football")
q := make(map[string][]string)
require.NoError(t, c.Bind().Query(&q))
require.Len(t, q["hobby"], 2)
c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football")
q = make(map[string][]string)
require.NoError(t, c.Bind().Query(&q))
require.Len(t, q["hobby"], 2)
c.Request().URI().SetQueryString("id=1&name=tom&hobby=scoccer&hobby=basketball,football")
q = make(map[string][]string)
require.NoError(t, c.Bind().Query(&q))
require.Len(t, q["hobby"], 3)
c.Request().URI().SetQueryString("id=1&name=tom&hobby=scoccer")
qq := make(map[string]string)
require.NoError(t, c.Bind().Query(&qq))
require.Equal(t, "1", qq["id"])
empty := make(map[string][]string)
c.Request().URI().SetQueryString("")
require.NoError(t, c.Bind().Query(&empty))
require.Empty(t, empty["hobby"])
em := make(map[string][]int)
c.Request().URI().SetQueryString("")
require.ErrorIs(t, c.Bind().Query(&em), binder.ErrMapNotConvertable)
}
// go test -run Test_Bind_Query_WithSetParserDecoder -v
func Test_Bind_Query_WithSetParserDecoder(t *testing.T) {
type NonRFCTime time.Time
nonRFCConverter := func(value string) reflect.Value {
if v, err := time.Parse("2006-01-02", value); err == nil {
return reflect.ValueOf(v)
}
return reflect.Value{}
}
nonRFCTime := binder.ParserType{
CustomType: NonRFCTime{},
Converter: nonRFCConverter,
}
binder.SetParserDecoder(binder.ParserConfig{
IgnoreUnknownKeys: true,
ParserType: []binder.ParserType{nonRFCTime},
ZeroEmpty: true,
SetAliasTag: "query",
})
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type NonRFCTimeInput struct {
Date NonRFCTime `query:"date"`
Title string `query:"title"`
Body string `query:"body"`
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
q := new(NonRFCTimeInput)
c.Request().URI().SetQueryString("date=2021-04-10&title=CustomDateTest&Body=October")
require.NoError(t, c.Bind().Query(q))
require.Equal(t, "CustomDateTest", q.Title)
date := fmt.Sprintf("%v", q.Date)
require.Equal(t, "{0 63753609600 <nil>}", date)
require.Equal(t, "October", q.Body)
c.Request().URI().SetQueryString("date=2021-04-10&title&Body=October")
q = &NonRFCTimeInput{
Title: "Existing title",
Body: "Existing Body",
}
require.NoError(t, c.Bind().Query(q))
require.Equal(t, "", q.Title)
}
// go test -run Test_Bind_Query_Schema -v
func Test_Bind_Query_Schema(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Query1 struct {
Name string `query:"name,required"`
Nested struct {
Age int `query:"age"`
} `query:"nested,required"`
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().URI().SetQueryString("name=tom&nested.age=10")
q := new(Query1)
require.NoError(t, c.Bind().Query(q))
c.Request().URI().SetQueryString("namex=tom&nested.age=10")
q = new(Query1)
require.Equal(t, "bind: name is empty", c.Bind().Query(q).Error())
c.Request().URI().SetQueryString("name=tom&nested.agex=10")
q = new(Query1)
require.NoError(t, c.Bind().Query(q))
c.Request().URI().SetQueryString("name=tom&test.age=10")
q = new(Query1)
require.Equal(t, "bind: nested is empty", c.Bind().Query(q).Error())
type Query2 struct {
Name string `query:"name"`
Nested struct {
Age int `query:"age,required"`
} `query:"nested"`
}
c.Request().URI().SetQueryString("name=tom&nested.age=10")
q2 := new(Query2)
require.NoError(t, c.Bind().Query(q2))
c.Request().URI().SetQueryString("nested.age=10")
q2 = new(Query2)
require.NoError(t, c.Bind().Query(q2))
c.Request().URI().SetQueryString("nested.agex=10")
q2 = new(Query2)
require.Equal(t, "bind: nested.age is empty", c.Bind().Query(q2).Error())
c.Request().URI().SetQueryString("nested.agex=10")
q2 = new(Query2)
require.Equal(t, "bind: nested.age is empty", c.Bind().Query(q2).Error())
type Node struct {
Next *Node `query:"next,required"`
Value int `query:"val,required"`
}
c.Request().URI().SetQueryString("val=1&next.val=3")
n := new(Node)
require.NoError(t, c.Bind().Query(n))
require.Equal(t, 1, n.Value)
require.Equal(t, 3, n.Next.Value)
c.Request().URI().SetQueryString("next.val=2")
n = new(Node)
require.Equal(t, "bind: val is empty", c.Bind().Query(n).Error())
c.Request().URI().SetQueryString("val=3&next.value=2")
n = new(Node)
n.Next = new(Node)
require.NoError(t, c.Bind().Query(n))
require.Equal(t, 3, n.Value)
require.Equal(t, 0, n.Next.Value)
type Person struct {
Name string `query:"name"`
Age int `query:"age"`
}
type CollectionQuery struct {
Data []Person `query:"data"`
}
c.Request().URI().SetQueryString("data[0][name]=john&data[0][age]=10&data[1][name]=doe&data[1][age]=12")
cq := new(CollectionQuery)
require.NoError(t, c.Bind().Query(cq))
require.Len(t, cq.Data, 2)
require.Equal(t, "john", cq.Data[0].Name)
require.Equal(t, 10, cq.Data[0].Age)
require.Equal(t, "doe", cq.Data[1].Name)
require.Equal(t, 12, cq.Data[1].Age)
c.Request().URI().SetQueryString("data.0.name=john&data.0.age=10&data.1.name=doe&data.1.age=12")
cq = new(CollectionQuery)
require.NoError(t, c.Bind().Query(cq))
require.Len(t, cq.Data, 2)
require.Equal(t, "john", cq.Data[0].Name)
require.Equal(t, 10, cq.Data[0].Age)
require.Equal(t, "doe", cq.Data[1].Name)
require.Equal(t, 12, cq.Data[1].Age)
}
// go test -run Test_Bind_Header -v
func Test_Bind_Header(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Header struct {
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().Header.Add("id", "1")
c.Request().Header.Add("Name", "John Doe")
c.Request().Header.Add("Hobby", "golang,fiber")
q := new(Header)
require.NoError(t, c.Bind().Header(q))
require.Len(t, q.Hobby, 1)
c.Request().Header.Del("hobby")
c.Request().Header.Add("Hobby", "golang,fiber,go")
q = new(Header)
require.NoError(t, c.Bind().Header(q))
require.Len(t, q.Hobby, 1)
empty := new(Header)
c.Request().Header.Del("hobby")
require.NoError(t, c.Bind().Query(empty))
require.Empty(t, empty.Hobby)
type Header2 struct {
Name string
Hobby string
FavouriteDrinks []string
Empty []string
Alloc []string
No []int64
ID int
Bool bool
}
c.Request().Header.Add("id", "2")
c.Request().Header.Add("Name", "Jane Doe")
c.Request().Header.Del("hobby")
c.Request().Header.Add("Hobby", "go,fiber")
c.Request().Header.Add("favouriteDrinks", "milo,coke,pepsi")
c.Request().Header.Add("alloc", "")
c.Request().Header.Add("no", "1")
h2 := new(Header2)
h2.Bool = true
h2.Name = helloWorld
require.NoError(t, c.Bind().Header(h2))
require.Equal(t, "go,fiber", h2.Hobby)
require.True(t, h2.Bool)
require.Equal(t, "Jane Doe", h2.Name) // check value get overwritten
require.Equal(t, []string{"milo,coke,pepsi"}, h2.FavouriteDrinks)
var nilSlice []string
require.Equal(t, nilSlice, h2.Empty)
require.Equal(t, []string{""}, h2.Alloc)
require.Equal(t, []int64{1}, h2.No)
type RequiredHeader struct {
Name string `header:"name,required"`
}
rh := new(RequiredHeader)
c.Request().Header.Del("name")
require.Equal(t, "bind: name is empty", c.Bind().Header(rh).Error())
}
// go test -run Test_Bind_Header_Map -v
func Test_Bind_Header_Map(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().Header.Add("id", "1")
c.Request().Header.Add("Name", "John Doe")
c.Request().Header.Add("Hobby", "golang,fiber")
q := make(map[string][]string, 0)
require.NoError(t, c.Bind().Header(&q))
require.Len(t, q["Hobby"], 1)
c.Request().Header.Del("hobby")
c.Request().Header.Add("Hobby", "golang,fiber,go")
q = make(map[string][]string, 0)
require.NoError(t, c.Bind().Header(&q))
require.Len(t, q["Hobby"], 1)
empty := make(map[string][]string, 0)
c.Request().Header.Del("hobby")
require.NoError(t, c.Bind().Query(&empty))
require.Empty(t, empty["Hobby"])
}
// go test -run Test_Bind_Header_WithSetParserDecoder -v
func Test_Bind_Header_WithSetParserDecoder(t *testing.T) {
type NonRFCTime time.Time
nonRFCConverter := func(value string) reflect.Value {
if v, err := time.Parse("2006-01-02", value); err == nil {
return reflect.ValueOf(v)
}
return reflect.Value{}
}
nonRFCTime := binder.ParserType{
CustomType: NonRFCTime{},
Converter: nonRFCConverter,
}
binder.SetParserDecoder(binder.ParserConfig{
IgnoreUnknownKeys: true,
ParserType: []binder.ParserType{nonRFCTime},
ZeroEmpty: true,
SetAliasTag: "req",
})
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type NonRFCTimeInput struct {
Date NonRFCTime `req:"date"`
Title string `req:"title"`
Body string `req:"body"`
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
r := new(NonRFCTimeInput)
c.Request().Header.Add("Date", "2021-04-10")
c.Request().Header.Add("Title", "CustomDateTest")
c.Request().Header.Add("Body", "October")
require.NoError(t, c.Bind().Header(r))
require.Equal(t, "CustomDateTest", r.Title)
date := fmt.Sprintf("%v", r.Date)
require.Equal(t, "{0 63753609600 <nil>}", date)
require.Equal(t, "October", r.Body)
c.Request().Header.Add("Title", "")
r = &NonRFCTimeInput{
Title: "Existing title",
Body: "Existing Body",
}
require.NoError(t, c.Bind().Header(r))
require.Equal(t, "", r.Title)
}
// go test -run Test_Bind_Header_Schema -v
func Test_Bind_Header_Schema(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Header1 struct {
Name string `header:"Name,required"`
Nested struct {
Age int `header:"Age"`
} `header:"Nested,required"`
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().Header.Add("Name", "tom")
c.Request().Header.Add("Nested.Age", "10")
q := new(Header1)
require.NoError(t, c.Bind().Header(q))
c.Request().Header.Del("Name")
q = new(Header1)
require.Equal(t, "bind: Name is empty", c.Bind().Header(q).Error())
c.Request().Header.Add("Name", "tom")
c.Request().Header.Del("Nested.Age")
c.Request().Header.Add("Nested.Agex", "10")
q = new(Header1)
require.NoError(t, c.Bind().Header(q))
c.Request().Header.Del("Nested.Agex")
q = new(Header1)
require.Equal(t, "bind: Nested is empty", c.Bind().Header(q).Error())
c.Request().Header.Del("Nested.Agex")
c.Request().Header.Del("Name")
type Header2 struct {
Name string `header:"Name"`
Nested struct {
Age int `header:"age,required"`
} `header:"Nested"`
}
c.Request().Header.Add("Name", "tom")
c.Request().Header.Add("Nested.Age", "10")
h2 := new(Header2)
require.NoError(t, c.Bind().Header(h2))
c.Request().Header.Del("Name")
h2 = new(Header2)
require.NoError(t, c.Bind().Header(h2))
c.Request().Header.Del("Name")
c.Request().Header.Del("Nested.Age")
c.Request().Header.Add("Nested.Agex", "10")
h2 = new(Header2)
require.Equal(t, "bind: Nested.age is empty", c.Bind().Header(h2).Error())
type Node struct {
Next *Node `header:"Next,required"`
Value int `header:"Val,required"`
}
c.Request().Header.Add("Val", "1")
c.Request().Header.Add("Next.Val", "3")
n := new(Node)
require.NoError(t, c.Bind().Header(n))
require.Equal(t, 1, n.Value)
require.Equal(t, 3, n.Next.Value)
c.Request().Header.Del("Val")
n = new(Node)
require.Equal(t, "bind: Val is empty", c.Bind().Header(n).Error())
c.Request().Header.Add("Val", "3")
c.Request().Header.Del("Next.Val")
c.Request().Header.Add("Next.Value", "2")
n = new(Node)
n.Next = new(Node)
require.NoError(t, c.Bind().Header(n))
require.Equal(t, 3, n.Value)
require.Equal(t, 0, n.Next.Value)
}
// go test -run Test_Bind_Resp_Header -v
func Test_Bind_RespHeader(t *testing.T) {
t.Parallel()
app := New(Config{
EnableSplittingOnParsers: true,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Header struct {
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Response().Header.Add("id", "1")
c.Response().Header.Add("Name", "John Doe")
c.Response().Header.Add("Hobby", "golang,fiber")
q := new(Header)
require.NoError(t, c.Bind().RespHeader(q))
require.Len(t, q.Hobby, 2)
c.Response().Header.Del("hobby")
c.Response().Header.Add("Hobby", "golang,fiber,go")
q = new(Header)
require.NoError(t, c.Bind().RespHeader(q))
require.Len(t, q.Hobby, 3)
empty := new(Header)
c.Response().Header.Del("hobby")
require.NoError(t, c.Bind().Query(empty))
require.Empty(t, empty.Hobby)
type Header2 struct {
Name string
Hobby string
FavouriteDrinks []string
Empty []string
Alloc []string
No []int64
ID int
Bool bool
}
c.Response().Header.Add("id", "2")
c.Response().Header.Add("Name", "Jane Doe")
c.Response().Header.Del("hobby")
c.Response().Header.Add("Hobby", "go,fiber")
c.Response().Header.Add("favouriteDrinks", "milo,coke,pepsi")
c.Response().Header.Add("alloc", "")
c.Response().Header.Add("no", "1")
h2 := new(Header2)
h2.Bool = true
h2.Name = helloWorld
require.NoError(t, c.Bind().RespHeader(h2))
require.Equal(t, "go,fiber", h2.Hobby)
require.True(t, h2.Bool)
require.Equal(t, "Jane Doe", h2.Name) // check value get overwritten
require.Equal(t, []string{"milo", "coke", "pepsi"}, h2.FavouriteDrinks)
var nilSlice []string
require.Equal(t, nilSlice, h2.Empty)
require.Equal(t, []string{""}, h2.Alloc)
require.Equal(t, []int64{1}, h2.No)
type RequiredHeader struct {
Name string `respHeader:"name,required"`
}
rh := new(RequiredHeader)
c.Response().Header.Del("name")
require.Equal(t, "bind: name is empty", c.Bind().RespHeader(rh).Error())
}
// go test -run Test_Bind_RespHeader_Map -v
func Test_Bind_RespHeader_Map(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Response().Header.Add("id", "1")
c.Response().Header.Add("Name", "John Doe")
c.Response().Header.Add("Hobby", "golang,fiber")
q := make(map[string][]string, 0)
require.NoError(t, c.Bind().RespHeader(&q))
require.Len(t, q["Hobby"], 1)
c.Response().Header.Del("hobby")
c.Response().Header.Add("Hobby", "golang,fiber,go")
q = make(map[string][]string, 0)
require.NoError(t, c.Bind().RespHeader(&q))
require.Len(t, q["Hobby"], 1)
empty := make(map[string][]string, 0)
c.Response().Header.Del("hobby")
require.NoError(t, c.Bind().Query(&empty))
require.Empty(t, empty["Hobby"])
}
// go test -v -run=^$ -bench=Benchmark_Bind_Query -benchmem -count=4
func Benchmark_Bind_Query(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Query struct {
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball&hobby=football")
q := new(Query)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Query(q)
}
require.NoError(b, err)
require.Equal(b, "tom", q.Name)
require.Equal(b, 1, q.ID)
require.Len(b, q.Hobby, 2)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Query_Default -benchmem -count=4
func Benchmark_Bind_Query_Default(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Query struct {
Name string `query:"name,default:tom"`
Hobby []string `query:"hobby,default:football|basketball"`
ID int `query:"id,default:1"`
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().URI().SetQueryString("")
q := new(Query)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
*q = Query{}
err = c.Bind().Query(q)
}
require.NoError(b, err)
require.Equal(b, "tom", q.Name)
require.Equal(b, 1, q.ID)
require.Len(b, q.Hobby, 2)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Query_Map -benchmem -count=4
func Benchmark_Bind_Query_Map(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball&hobby=football")
q := make(map[string][]string)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Query(&q)
}
require.NoError(b, err)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Query_WithParseParam -benchmem -count=4
func Benchmark_Bind_Query_WithParseParam(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Person struct {
Name string `query:"name"`
Age int `query:"age"`
}
type CollectionQuery struct {
Data []Person `query:"data"`
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().URI().SetQueryString("data[0][name]=john&data[0][age]=10")
cq := new(CollectionQuery)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Query(cq)
}
require.NoError(b, err)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Query_Comma -benchmem -count=4
func Benchmark_Bind_Query_Comma(b *testing.B) {
var err error
app := New(Config{
EnableSplittingOnParsers: true,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Query struct {
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football")
q := new(Query)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Query(q)
}
require.NoError(b, err)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Header -benchmem -count=4
func Benchmark_Bind_Header(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type ReqHeader struct {
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().Header.Add("id", "1")
c.Request().Header.Add("Name", "John Doe")
c.Request().Header.Add("Hobby", "golang,fiber")
q := new(ReqHeader)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Header(q)
}
require.NoError(b, err)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Header_Map -benchmem -count=4
func Benchmark_Bind_Header_Map(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().Header.Add("id", "1")
c.Request().Header.Add("Name", "John Doe")
c.Request().Header.Add("Hobby", "golang,fiber")
q := make(map[string][]string)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Header(&q)
}
require.NoError(b, err)
}
// go test -v -run=^$ -bench=Benchmark_Bind_RespHeader -benchmem -count=4
func Benchmark_Bind_RespHeader(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type ReqHeader struct {
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Response().Header.Add("id", "1")
c.Response().Header.Add("Name", "John Doe")
c.Response().Header.Add("Hobby", "golang,fiber")
q := new(ReqHeader)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().RespHeader(q)
}
require.NoError(b, err)
}
// go test -v -run=^$ -bench=Benchmark_Bind_RespHeader_Map -benchmem -count=4
func Benchmark_Bind_RespHeader_Map(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Response().Header.Add("id", "1")
c.Response().Header.Add("Name", "John Doe")
c.Response().Header.Add("Hobby", "golang,fiber")
q := make(map[string][]string)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().RespHeader(&q)
}
require.NoError(b, err)
}
// go test -run Test_Bind_Body_Compression
func Test_Bind_Body(t *testing.T) {
t.Parallel()
app := New()
reqBody := []byte(`{"name":"john"}`)
type Demo struct {
Name string `json:"name" xml:"name" form:"name" query:"name"`
Names []string `json:"names" xml:"names" form:"names" query:"names"`
}
// Helper function to test compressed bodies
testCompressedBody := func(t *testing.T, compressedBody []byte, encoding string) {
t.Helper()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.SetContentType(MIMEApplicationJSON)
c.Request().Header.Set(fasthttp.HeaderContentEncoding, encoding)
c.Request().SetBody(compressedBody)
c.Request().Header.SetContentLength(len(compressedBody))
d := new(Demo)
require.NoError(t, c.Bind().Body(d))
require.Equal(t, "john", d.Name)
c.Request().Header.Del(fasthttp.HeaderContentEncoding)
}
t.Run("Gzip", func(t *testing.T) {
t.Parallel()
compressedBody := fasthttp.AppendGzipBytes(nil, reqBody)
require.NotEqual(t, reqBody, compressedBody)
testCompressedBody(t, compressedBody, "gzip")
})
t.Run("Deflate", func(t *testing.T) {
t.Parallel()
compressedBody := fasthttp.AppendDeflateBytes(nil, reqBody)
require.NotEqual(t, reqBody, compressedBody)
testCompressedBody(t, compressedBody, "deflate")
})
t.Run("Brotli", func(t *testing.T) {
t.Parallel()
compressedBody := fasthttp.AppendBrotliBytes(nil, reqBody)
require.NotEqual(t, reqBody, compressedBody)
testCompressedBody(t, compressedBody, "br")
})
t.Run("Zstd", func(t *testing.T) {
t.Parallel()
compressedBody := fasthttp.AppendZstdBytes(nil, reqBody)
require.NotEqual(t, reqBody, compressedBody)
testCompressedBody(t, compressedBody, "zstd")
})
testDecodeParser := func(t *testing.T, contentType string, body []byte) {
t.Helper()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.SetContentType(contentType)
c.Request().SetBody(body)
c.Request().Header.SetContentLength(len(body))
d := new(Demo)
require.NoError(t, c.Bind().Body(d))
require.Equal(t, "john", d.Name)
}
t.Run("JSON", func(t *testing.T) {
testDecodeParser(t, MIMEApplicationJSON, []byte(`{"name":"john"}`))
})
t.Run("CBOR", func(t *testing.T) {
enc, err := cbor.Marshal(&Demo{Name: "john"})
if err != nil {
t.Error(err)
}
testDecodeParser(t, MIMEApplicationCBOR, enc)
// Test invalid CBOR data
t.Run("Invalid", func(t *testing.T) {
invalidData := []byte{0xFF, 0xFF} // Invalid CBOR data
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.SetContentType(MIMEApplicationCBOR)
c.Request().SetBody(invalidData)
d := new(Demo)
require.Error(t, c.Bind().Body(d))
})
})
t.Run("XML", func(t *testing.T) {
testDecodeParser(t, MIMEApplicationXML, []byte(`<Demo><name>john</name></Demo>`))
})
t.Run("Form", func(t *testing.T) {
testDecodeParser(t, MIMEApplicationForm, []byte("name=john"))
})
t.Run("MultipartForm", func(t *testing.T) {
testDecodeParser(t, MIMEMultipartForm+`;boundary="b"`, []byte("--b\r\nContent-Disposition: form-data; name=\"name\"\r\n\r\njohn\r\n--b--"))
})
testDecodeParserError := func(t *testing.T, contentType, body string) {
t.Helper()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Header.SetContentType(contentType)
c.Request().SetBody([]byte(body))
c.Request().Header.SetContentLength(len(body))
require.Error(t, c.Bind().Body(nil))
}
t.Run("ErrorInvalidContentType", func(t *testing.T) {
testDecodeParserError(t, "invalid-content-type", "")
})
t.Run("ErrorMalformedMultipart", func(t *testing.T) {
testDecodeParserError(t, MIMEMultipartForm+`;boundary="b"`, "--b")
})
type CollectionQuery struct {
Data []Demo `query:"data"`
}
t.Run("MultipartCollectionQueryDotNotation", func(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Reset()
buf := &bytes.Buffer{}
writer := multipart.NewWriter(buf)
require.NoError(t, writer.WriteField("data.0.name", "john"))
require.NoError(t, writer.WriteField("data.1.name", "doe"))
require.NoError(t, writer.Close())
c.Request().Header.SetContentType(writer.FormDataContentType())
c.Request().SetBody(buf.Bytes())
c.Request().Header.SetContentLength(len(c.Body()))
cq := new(CollectionQuery)
require.NoError(t, c.Bind().Body(cq))
require.Len(t, cq.Data, 2)
require.Equal(t, "john", cq.Data[0].Name)
require.Equal(t, "doe", cq.Data[1].Name)
})
t.Run("MultipartCollectionQuerySquareBrackets", func(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Reset()
buf := &bytes.Buffer{}
writer := multipart.NewWriter(buf)
require.NoError(t, writer.WriteField("data[0][name]", "john"))
require.NoError(t, writer.WriteField("data[1][name]", "doe"))
require.NoError(t, writer.Close())
c.Request().Header.SetContentType(writer.FormDataContentType())
c.Request().SetBody(buf.Bytes())
c.Request().Header.SetContentLength(len(c.Body()))
cq := new(CollectionQuery)
require.NoError(t, c.Bind().Body(cq))
require.Len(t, cq.Data, 2)
require.Equal(t, "john", cq.Data[0].Name)
require.Equal(t, "doe", cq.Data[1].Name)
})
t.Run("CollectionQuerySquareBrackets", func(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Reset()
c.Request().Header.SetContentType(MIMEApplicationForm)
c.Request().SetBody([]byte("data[0][name]=john&data[1][name]=doe"))
c.Request().Header.SetContentLength(len(c.Body()))
cq := new(CollectionQuery)
require.NoError(t, c.Bind().Body(cq))
require.Len(t, cq.Data, 2)
require.Equal(t, "john", cq.Data[0].Name)
require.Equal(t, "doe", cq.Data[1].Name)
})
t.Run("CollectionQueryDotNotation", func(t *testing.T) {
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().Reset()
c.Request().Header.SetContentType(MIMEApplicationForm)
c.Request().SetBody([]byte("data.0.name=john&data.1.name=doe"))
c.Request().Header.SetContentLength(len(c.Body()))
cq := new(CollectionQuery)
require.NoError(t, c.Bind().Body(cq))
require.Len(t, cq.Data, 2)
require.Equal(t, "john", cq.Data[0].Name)
require.Equal(t, "doe", cq.Data[1].Name)
})
}
// go test -run Test_Bind_Body_WithSetParserDecoder
func Test_Bind_Body_WithSetParserDecoder(t *testing.T) {
type CustomTime time.Time
timeConverter := func(value string) reflect.Value {
if v, err := time.Parse("2006-01-02", value); err == nil {
return reflect.ValueOf(v)
}
return reflect.Value{}
}
customTime := binder.ParserType{
CustomType: CustomTime{},
Converter: timeConverter,
}
binder.SetParserDecoder(binder.ParserConfig{
IgnoreUnknownKeys: true,
ParserType: []binder.ParserType{customTime},
ZeroEmpty: true,
SetAliasTag: "form",
})
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Demo struct {
Date CustomTime `form:"date"`
Title string `form:"title"`
Body string `form:"body"`
}
testDecodeParser := func(contentType, body string) {
c.Request().Header.SetContentType(contentType)
c.Request().SetBody([]byte(body))
c.Request().Header.SetContentLength(len(body))
d := Demo{
Title: "Existing title",
Body: "Existing Body",
}
require.NoError(t, c.Bind().Body(&d))
date := fmt.Sprintf("%v", d.Date)
require.Equal(t, "{0 63743587200 <nil>}", date)
require.Equal(t, "", d.Title)
require.Equal(t, "New Body", d.Body)
}
testDecodeParser(MIMEApplicationForm, "date=2020-12-15&title=&body=New Body")
testDecodeParser(MIMEMultipartForm+`; boundary="b"`, "--b\r\nContent-Disposition: form-data; name=\"date\"\r\n\r\n2020-12-15\r\n--b\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\n\r\n--b\r\nContent-Disposition: form-data; name=\"body\"\r\n\r\nNew Body\r\n--b--")
}
// go test -v -run=^$ -bench=Benchmark_Bind_Body_JSON -benchmem -count=4
func Benchmark_Bind_Body_JSON(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Demo struct {
Name string `json:"name"`
}
body := []byte(`{"name":"john"}`)
c.Request().SetBody(body)
c.Request().Header.SetContentType(MIMEApplicationJSON)
c.Request().Header.SetContentLength(len(body))
d := new(Demo)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Body(d)
}
require.NoError(b, err)
require.Equal(b, "john", d.Name)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Body_XML -benchmem -count=4
func Benchmark_Bind_Body_XML(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Demo struct {
Name string `xml:"name"`
}
body := []byte("<Demo><name>john</name></Demo>")
c.Request().SetBody(body)
c.Request().Header.SetContentType(MIMEApplicationXML)
c.Request().Header.SetContentLength(len(body))
d := new(Demo)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Body(d)
}
require.NoError(b, err)
require.Equal(b, "john", d.Name)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Body_CBOR -benchmem -count=4
func Benchmark_Bind_Body_CBOR(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Demo struct {
Name string `json:"name"`
}
body, err := cbor.Marshal(&Demo{Name: "john"})
if err != nil {
b.Error(err)
}
c.Request().SetBody(body)
c.Request().Header.SetContentType(MIMEApplicationCBOR)
c.Request().Header.SetContentLength(len(body))
d := new(Demo)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Body(d)
}
require.NoError(b, err)
require.Equal(b, "john", d.Name)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Body_Form -benchmem -count=4
func Benchmark_Bind_Body_Form(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Demo struct {
Name string `form:"name"`
}
body := []byte("name=john")
c.Request().SetBody(body)
c.Request().Header.SetContentType(MIMEApplicationForm)
c.Request().Header.SetContentLength(len(body))
d := new(Demo)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Body(d)
}
require.NoError(b, err)
require.Equal(b, "john", d.Name)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Body_MultipartForm -benchmem -count=4
func Benchmark_Bind_Body_MultipartForm(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Demo struct {
Name string `form:"name"`
}
buf := &bytes.Buffer{}
writer := multipart.NewWriter(buf)
require.NoError(b, writer.WriteField("name", "john"))
require.NoError(b, writer.Close())
body := buf.Bytes()
c.Request().SetBody(body)
c.Request().Header.SetContentType(MIMEMultipartForm + `;boundary=` + writer.Boundary())
c.Request().Header.SetContentLength(len(body))
d := new(Demo)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Body(d)
}
require.NoError(b, err)
require.Equal(b, "john", d.Name)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Body_MultipartForm_Nested -benchmem -count=4
func Benchmark_Bind_Body_MultipartForm_Nested(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Person struct {
Name string `form:"name"`
Age int `form:"age"`
}
type Demo struct {
Name string `form:"name"`
Persons []Person `form:"persons"`
}
buf := &bytes.Buffer{}
writer := multipart.NewWriter(buf)
require.NoError(b, writer.WriteField("name", "john"))
require.NoError(b, writer.WriteField("persons.0.name", "john"))
require.NoError(b, writer.WriteField("persons[0][age]", "10"))
require.NoError(b, writer.WriteField("persons[1][name]", "doe"))
require.NoError(b, writer.WriteField("persons.1.age", "20"))
require.NoError(b, writer.Close())
body := buf.Bytes()
c.Request().SetBody(body)
c.Request().Header.SetContentType(MIMEMultipartForm + `;boundary=` + writer.Boundary())
c.Request().Header.SetContentLength(len(body))
d := new(Demo)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Body(d)
}
require.NoError(b, err)
require.Equal(b, "john", d.Name)
require.Equal(b, "john", d.Persons[0].Name)
require.Equal(b, 10, d.Persons[0].Age)
require.Equal(b, "doe", d.Persons[1].Name)
require.Equal(b, 20, d.Persons[1].Age)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Body_Form_Map -benchmem -count=4
func Benchmark_Bind_Body_Form_Map(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
body := []byte("name=john")
c.Request().SetBody(body)
c.Request().Header.SetContentType(MIMEApplicationForm)
c.Request().Header.SetContentLength(len(body))
d := make(map[string]string)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Body(&d)
}
require.NoError(b, err)
require.Equal(b, "john", d["name"])
}
// go test -run Test_Bind_URI
func Test_Bind_URI(t *testing.T) {
t.Parallel()
app := New()
app.Get("/test1/userId/role/:roleId", func(c Ctx) error {
type Demo struct {
UserID uint `uri:"userId"`
RoleID uint `uri:"roleId"`
}
d := new(Demo)
if err := c.Bind().URI(d); err != nil {
t.Fatal(err)
}
require.Equal(t, uint(111), d.UserID)
require.Equal(t, uint(222), d.RoleID)
return nil
})
_, err := app.Test(httptest.NewRequest(MethodGet, "/test1/111/role/222", nil))
require.NoError(t, err)
_, err = app.Test(httptest.NewRequest(MethodGet, "/test2/111/role/222", nil))
require.NoError(t, err)
}
// go test -run Test_Bind_URI_Map
func Test_Bind_URI_Map(t *testing.T) {
t.Parallel()
app := New()
app.Get("/test1/userId/role/:roleId", func(c Ctx) error {
d := make(map[string]string)
if err := c.Bind().URI(&d); err != nil {
t.Fatal(err)
}
require.Equal(t, uint(111), d["userId"])
require.Equal(t, uint(222), d["roleId"])
return nil
})
_, err := app.Test(httptest.NewRequest(MethodGet, "/test1/111/role/222", nil))
require.NoError(t, err)
_, err = app.Test(httptest.NewRequest(MethodGet, "/test2/111/role/222", nil))
require.NoError(t, err)
}
// go test -v -run=^$ -bench=Benchmark_Bind_URI -benchmem -count=4
func Benchmark_Bind_URI(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed
c.route = &Route{
Params: []string{
"param1", "param2", "param3", "param4",
},
}
c.values = [maxParams]string{
"john", "doe", "is", "awesome",
}
var res struct {
Param1 string `uri:"param1"`
Param2 string `uri:"param2"`
Param3 string `uri:"param3"`
Param4 string `uri:"param4"`
}
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().URI(&res)
}
require.NoError(b, err)
require.Equal(b, "john", res.Param1)
require.Equal(b, "doe", res.Param2)
require.Equal(b, "is", res.Param3)
require.Equal(b, "awesome", res.Param4)
}
// go test -v -run=^$ -bench=Benchmark_Bind_URI_Map -benchmem -count=4
func Benchmark_Bind_URI_Map(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{}).(*DefaultCtx) //nolint:errcheck, forcetypeassert // not needed
c.route = &Route{
Params: []string{
"param1", "param2", "param3", "param4",
},
}
c.values = [maxParams]string{
"john", "doe", "is", "awesome",
}
res := make(map[string]string)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().URI(&res)
}
require.NoError(b, err)
require.Equal(b, "john", res["param1"])
require.Equal(b, "doe", res["param2"])
require.Equal(b, "is", res["param3"])
require.Equal(b, "awesome", res["param4"])
}
// go test -run Test_Bind_Cookie -v
func Test_Bind_Cookie(t *testing.T) {
t.Parallel()
app := New(Config{
EnableSplittingOnParsers: true,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Cookie struct {
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().Header.SetCookie("id", "1")
c.Request().Header.SetCookie("Name", "John Doe")
c.Request().Header.SetCookie("Hobby", "golang,fiber")
q := new(Cookie)
require.NoError(t, c.Bind().Cookie(q))
require.Len(t, q.Hobby, 2)
c.Request().Header.DelCookie("hobby")
c.Request().Header.SetCookie("Hobby", "golang,fiber,go")
q = new(Cookie)
require.NoError(t, c.Bind().Cookie(q))
require.Len(t, q.Hobby, 3)
empty := new(Cookie)
c.Request().Header.DelCookie("hobby")
require.NoError(t, c.Bind().Query(empty))
require.Empty(t, empty.Hobby)
type Cookie2 struct {
Name string
Hobby string
FavouriteDrinks []string
Empty []string
Alloc []string
No []int64
ID int
Bool bool
}
c.Request().Header.SetCookie("id", "2")
c.Request().Header.SetCookie("Name", "Jane Doe")
c.Request().Header.DelCookie("hobby")
c.Request().Header.SetCookie("Hobby", "go,fiber")
c.Request().Header.SetCookie("favouriteDrinks", "milo,coke,pepsi")
c.Request().Header.SetCookie("alloc", "")
c.Request().Header.SetCookie("no", "1")
h2 := new(Cookie2)
h2.Bool = true
h2.Name = helloWorld
require.NoError(t, c.Bind().Cookie(h2))
require.Equal(t, "go,fiber", h2.Hobby)
require.True(t, h2.Bool)
require.Equal(t, "Jane Doe", h2.Name) // check value get overwritten
require.Equal(t, []string{"milo", "coke", "pepsi"}, h2.FavouriteDrinks)
var nilSlice []string
require.Equal(t, nilSlice, h2.Empty)
require.Equal(t, []string{""}, h2.Alloc)
require.Equal(t, []int64{1}, h2.No)
type RequiredCookie struct {
Name string `cookie:"name,required"`
}
rh := new(RequiredCookie)
c.Request().Header.DelCookie("name")
require.Equal(t, "bind: name is empty", c.Bind().Cookie(rh).Error())
}
// go test -run Test_Bind_Cookie_Map -v
func Test_Bind_Cookie_Map(t *testing.T) {
t.Parallel()
app := New(Config{
EnableSplittingOnParsers: true,
})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().Header.SetCookie("id", "1")
c.Request().Header.SetCookie("Name", "John Doe")
c.Request().Header.SetCookie("Hobby", "golang,fiber")
q := make(map[string][]string)
require.NoError(t, c.Bind().Cookie(&q))
require.Len(t, q["Hobby"], 2)
c.Request().Header.DelCookie("hobby")
c.Request().Header.SetCookie("Hobby", "golang,fiber,go")
q = make(map[string][]string)
require.NoError(t, c.Bind().Cookie(&q))
require.Len(t, q["Hobby"], 3)
empty := make(map[string][]string)
c.Request().Header.DelCookie("hobby")
require.NoError(t, c.Bind().Query(&empty))
require.Empty(t, empty["Hobby"])
}
// go test -run Test_Bind_Cookie_WithSetParserDecoder -v
func Test_Bind_Cookie_WithSetParserDecoder(t *testing.T) {
type NonRFCTime time.Time
nonRFCConverter := func(value string) reflect.Value {
if v, err := time.Parse("2006-01-02", value); err == nil {
return reflect.ValueOf(v)
}
return reflect.Value{}
}
nonRFCTime := binder.ParserType{
CustomType: NonRFCTime{},
Converter: nonRFCConverter,
}
binder.SetParserDecoder(binder.ParserConfig{
IgnoreUnknownKeys: true,
ParserType: []binder.ParserType{nonRFCTime},
ZeroEmpty: true,
SetAliasTag: "cerez",
})
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type NonRFCTimeInput struct {
Date NonRFCTime `cerez:"date"`
Title string `cerez:"title"`
Body string `cerez:"body"`
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
r := new(NonRFCTimeInput)
c.Request().Header.SetCookie("Date", "2021-04-10")
c.Request().Header.SetCookie("Title", "CustomDateTest")
c.Request().Header.SetCookie("Body", "October")
require.NoError(t, c.Bind().Cookie(r))
require.Equal(t, "CustomDateTest", r.Title)
date := fmt.Sprintf("%v", r.Date)
require.Equal(t, "{0 63753609600 <nil>}", date)
require.Equal(t, "October", r.Body)
c.Request().Header.SetCookie("Title", "")
r = &NonRFCTimeInput{
Title: "Existing title",
Body: "Existing Body",
}
require.NoError(t, c.Bind().Cookie(r))
require.Equal(t, "", r.Title)
}
// go test -run Test_Bind_Cookie_Schema -v
func Test_Bind_Cookie_Schema(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Cookie1 struct {
Name string `cookie:"Name,required"`
Nested struct {
Age int `cookie:"Age"`
} `cookie:"Nested,required"`
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().Header.SetCookie("Name", "tom")
c.Request().Header.SetCookie("Nested.Age", "10")
q := new(Cookie1)
require.NoError(t, c.Bind().Cookie(q))
c.Request().Header.DelCookie("Name")
q = new(Cookie1)
require.Equal(t, "bind: Name is empty", c.Bind().Cookie(q).Error())
c.Request().Header.SetCookie("Name", "tom")
c.Request().Header.DelCookie("Nested.Age")
c.Request().Header.SetCookie("Nested.Agex", "10")
q = new(Cookie1)
require.NoError(t, c.Bind().Cookie(q))
c.Request().Header.DelCookie("Nested.Agex")
q = new(Cookie1)
require.Equal(t, "bind: Nested is empty", c.Bind().Cookie(q).Error())
c.Request().Header.DelCookie("Nested.Agex")
c.Request().Header.DelCookie("Name")
type Cookie2 struct {
Name string `cookie:"Name"`
Nested struct {
Age int `cookie:"Age,required"`
} `cookie:"Nested"`
}
c.Request().Header.SetCookie("Name", "tom")
c.Request().Header.SetCookie("Nested.Age", "10")
h2 := new(Cookie2)
require.NoError(t, c.Bind().Cookie(h2))
c.Request().Header.DelCookie("Name")
h2 = new(Cookie2)
require.NoError(t, c.Bind().Cookie(h2))
c.Request().Header.DelCookie("Name")
c.Request().Header.DelCookie("Nested.Age")
c.Request().Header.SetCookie("Nested.Agex", "10")
h2 = new(Cookie2)
require.Equal(t, "bind: Nested.Age is empty", c.Bind().Cookie(h2).Error())
type Node struct {
Next *Node `cookie:"Next,required"`
Value int `cookie:"Val,required"`
}
c.Request().Header.SetCookie("Val", "1")
c.Request().Header.SetCookie("Next.Val", "3")
n := new(Node)
require.NoError(t, c.Bind().Cookie(n))
require.Equal(t, 1, n.Value)
require.Equal(t, 3, n.Next.Value)
c.Request().Header.DelCookie("Val")
n = new(Node)
require.Equal(t, "bind: Val is empty", c.Bind().Cookie(n).Error())
c.Request().Header.SetCookie("Val", "3")
c.Request().Header.DelCookie("Next.Val")
c.Request().Header.SetCookie("Next.Value", "2")
n = new(Node)
n.Next = new(Node)
require.NoError(t, c.Bind().Cookie(n))
require.Equal(t, 3, n.Value)
require.Equal(t, 0, n.Next.Value)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Cookie -benchmem -count=4
func Benchmark_Bind_Cookie(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type Cookie struct {
Name string
Hobby []string
ID int
}
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().Header.SetCookie("id", "1")
c.Request().Header.SetCookie("Name", "John Doe")
c.Request().Header.SetCookie("Hobby", "golang,fiber")
q := new(Cookie)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Cookie(q)
}
require.NoError(b, err)
}
// go test -v -run=^$ -bench=Benchmark_Bind_Cookie_Map -benchmem -count=4
func Benchmark_Bind_Cookie_Map(b *testing.B) {
var err error
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
c.Request().SetBody([]byte(``))
c.Request().Header.SetContentType("")
c.Request().Header.SetCookie("id", "1")
c.Request().Header.SetCookie("Name", "John Doe")
c.Request().Header.SetCookie("Hobby", "golang,fiber")
q := make(map[string][]string)
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
err = c.Bind().Cookie(&q)
}
require.NoError(b, err)
}
// custom binder for testing
type customBinder struct{}
func (*customBinder) Name() string {
return "custom"
}
func (*customBinder) MIMETypes() []string {
return []string{"test", "test2"}
}
func (*customBinder) Parse(c Ctx, out any) error {
return json.Unmarshal(c.Body(), out)
}
// go test -run Test_Bind_CustomBinder
func Test_Bind_CustomBinder(t *testing.T) {
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
// Register binder
customBinder := &customBinder{}
app.RegisterCustomBinder(customBinder)
type Demo struct {
Name string `json:"name"`
}
body := []byte(`{"name":"john"}`)
c.Request().SetBody(body)
c.Request().Header.SetContentType("test")
c.Request().Header.SetContentLength(len(body))
d := new(Demo)
require.NoError(t, c.Bind().Body(d))
require.NoError(t, c.Bind().Custom("custom", d))
require.Equal(t, ErrCustomBinderNotFound, c.Bind().Custom("not_custom", d))
require.Equal(t, "john", d.Name)
}
// go test -run Test_Bind_WithAutoHandling
func Test_Bind_WithAutoHandling(t *testing.T) {
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
type RequiredQuery struct {
Name string `query:"name,required"`
}
rq := new(RequiredQuery)
c.Request().URI().SetQueryString("")
err := c.Bind().WithAutoHandling().Query(rq)
require.Equal(t, StatusBadRequest, c.Response().StatusCode())
require.Equal(t, "Bad request: bind: name is empty", err.Error())
}
// simple struct validator for testing
type structValidator struct{}
func (*structValidator) Validate(out any) error {
out = reflect.ValueOf(out).Elem().Interface()
sq, ok := out.(simpleQuery)
if !ok {
return errors.New("failed to type-assert to simpleQuery")
}
if sq.Name != "john" {
return errors.New("you should have entered right name")
}
return nil
}
type simpleQuery struct {
Name string `query:"name"`
}
// go test -run Test_Bind_StructValidator
func Test_Bind_StructValidator(t *testing.T) {
app := New(Config{StructValidator: &structValidator{}})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
rq := new(simpleQuery)
c.Request().URI().SetQueryString("name=efe")
require.Equal(t, "you should have entered right name", c.Bind().Query(rq).Error())
rq = new(simpleQuery)
c.Request().URI().SetQueryString("name=john")
require.NoError(t, c.Bind().Query(rq))
}
// go test -run Test_Bind_RepeatParserWithSameStruct -v
func Test_Bind_RepeatParserWithSameStruct(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
type Request struct {
QueryParam string `query:"query_param"`
HeaderParam string `header:"header_param"`
BodyParam string `json:"body_param" xml:"body_param" form:"body_param"`
}
r := new(Request)
c.Request().URI().SetQueryString("query_param=query_param")
require.NoError(t, c.Bind().Query(r))
require.Equal(t, "query_param", r.QueryParam)
c.Request().Header.Add("header_param", "header_param")
require.NoError(t, c.Bind().Header(r))
require.Equal(t, "header_param", r.HeaderParam)
var gzipJSON bytes.Buffer
w := gzip.NewWriter(&gzipJSON)
_, err := w.Write([]byte(`{"body_param":"body_param"}`))
require.NoError(t, err)
err = w.Close()
require.NoError(t, err)
c.Request().Header.SetContentType(MIMEApplicationJSON)
c.Request().Header.Set(HeaderContentEncoding, "gzip")
c.Request().SetBody(gzipJSON.Bytes())
c.Request().Header.SetContentLength(len(gzipJSON.Bytes()))
require.NoError(t, c.Bind().Body(r))
require.Equal(t, "body_param", r.BodyParam)
c.Request().Header.Del(HeaderContentEncoding)
testDecodeParser := func(contentType, body string) {
c.Request().Header.SetContentType(contentType)
c.Request().SetBody([]byte(body))
c.Request().Header.SetContentLength(len(body))
require.NoError(t, c.Bind().Body(r))
require.Equal(t, "body_param", r.BodyParam)
}
cb, err := cbor.Marshal(&Request{BodyParam: "body_param"})
require.NoError(t, err, "Failed to marshal CBOR data")
testDecodeParser(MIMEApplicationJSON, `{"body_param":"body_param"}`)
testDecodeParser(MIMEApplicationXML, `<Demo><body_param>body_param</body_param></Demo>`)
testDecodeParser(MIMEApplicationCBOR, string(cb))
testDecodeParser(MIMEApplicationForm, "body_param=body_param")
testDecodeParser(MIMEMultipartForm+`;boundary="b"`, "--b\r\nContent-Disposition: form-data; name=\"body_param\"\r\n\r\nbody_param\r\n--b--")
}