Skip to content

Commit c3e1503

Browse files
authored
Merge branch 'main' into audit-and-improve-documentation-in-docs-directory
2 parents 64cbf93 + 82a8485 commit c3e1503

File tree

11 files changed

+226
-38
lines changed

11 files changed

+226
-38
lines changed

binder/cookie.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,15 @@ func (*CookieBinding) Name() string {
1818
// Bind parses the request cookie and returns the result.
1919
func (b *CookieBinding) Bind(req *fasthttp.Request, out any) error {
2020
data := make(map[string][]string)
21-
var err error
2221

2322
for key, val := range req.Header.Cookies() {
2423
k := utils.UnsafeString(key)
2524
v := utils.UnsafeString(val)
26-
err = formatBindData(b.Name(), out, data, k, v, b.EnableSplitting, false)
27-
if err != nil {
28-
break
25+
if err := formatBindData(b.Name(), out, data, k, v, b.EnableSplitting, false); err != nil {
26+
return err
2927
}
3028
}
3129

32-
if err != nil {
33-
return err
34-
}
35-
3630
return parse(b.Name(), out, data)
3731
}
3832

binder/cookie_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,16 @@ func Benchmark_CookieBinder_Bind(b *testing.B) {
8585
require.Contains(b, user.Posts, "post2")
8686
require.Contains(b, user.Posts, "post3")
8787
}
88+
89+
func Test_CookieBinder_Bind_ParseError(t *testing.T) {
90+
b := &CookieBinding{}
91+
type User struct {
92+
Age int `cookie:"age"`
93+
}
94+
var user User
95+
req := fasthttp.AcquireRequest()
96+
req.Header.SetCookie("age", "invalid")
97+
t.Cleanup(func() { fasthttp.ReleaseRequest(req) })
98+
err := b.Bind(req, &user)
99+
require.Error(t, err)
100+
}

binder/form.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ func (*FormBinding) Name() string {
2222
// Bind parses the request body and returns the result.
2323
func (b *FormBinding) Bind(req *fasthttp.Request, out any) error {
2424
data := make(map[string][]string)
25-
var err error
2625

2726
// Handle multipart form
2827
if FilterFlags(utils.UnsafeString(req.Header.ContentType())) == MIMEMultipartForm {
@@ -32,16 +31,11 @@ func (b *FormBinding) Bind(req *fasthttp.Request, out any) error {
3231
for key, val := range req.PostArgs().All() {
3332
k := utils.UnsafeString(key)
3433
v := utils.UnsafeString(val)
35-
err = formatBindData(b.Name(), out, data, k, v, b.EnableSplitting, true)
36-
if err != nil {
37-
break
34+
if err := formatBindData(b.Name(), out, data, k, v, b.EnableSplitting, true); err != nil {
35+
return err
3836
}
3937
}
4038

41-
if err != nil {
42-
return err
43-
}
44-
4539
return parse(b.Name(), out, data)
4640
}
4741

binder/form_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ func Test_FormBinder_Bind(t *testing.T) {
5454
require.False(t, b.EnableSplitting)
5555
}
5656

57+
func Test_FormBinder_Bind_ParseError(t *testing.T) {
58+
b := &FormBinding{}
59+
type User struct {
60+
Age int `form:"age"`
61+
}
62+
var user User
63+
req := fasthttp.AcquireRequest()
64+
req.SetBodyString("age=invalid")
65+
req.Header.SetContentType("application/x-www-form-urlencoded")
66+
t.Cleanup(func() { fasthttp.ReleaseRequest(req) })
67+
err := b.Bind(req, &user)
68+
require.Error(t, err)
69+
}
70+
5771
func Benchmark_FormBinder_Bind(b *testing.B) {
5872
b.ReportAllocs()
5973

@@ -191,6 +205,39 @@ func Test_FormBinder_BindMultipart(t *testing.T) {
191205
require.Equal(t, "avatar2", string(content))
192206
}
193207

208+
func Test_FormBinder_BindMultipart_ValueError(t *testing.T) {
209+
b := &FormBinding{}
210+
req := fasthttp.AcquireRequest()
211+
buf := &bytes.Buffer{}
212+
mw := multipart.NewWriter(buf)
213+
require.NoError(t, mw.WriteField("invalid[", "val"))
214+
require.NoError(t, mw.Close())
215+
req.Header.SetContentType(mw.FormDataContentType())
216+
req.SetBody(buf.Bytes())
217+
t.Cleanup(func() { fasthttp.ReleaseRequest(req) })
218+
err := b.Bind(req, &struct{}{})
219+
require.Error(t, err)
220+
require.Contains(t, err.Error(), "unmatched brackets")
221+
}
222+
223+
func Test_FormBinder_BindMultipart_FileError(t *testing.T) {
224+
b := &FormBinding{}
225+
req := fasthttp.AcquireRequest()
226+
buf := &bytes.Buffer{}
227+
mw := multipart.NewWriter(buf)
228+
writer, err := mw.CreateFormFile("invalid[", "file.txt")
229+
require.NoError(t, err)
230+
_, err = writer.Write([]byte("content"))
231+
require.NoError(t, err)
232+
require.NoError(t, mw.Close())
233+
req.Header.SetContentType(mw.FormDataContentType())
234+
req.SetBody(buf.Bytes())
235+
t.Cleanup(func() { fasthttp.ReleaseRequest(req) })
236+
err = b.Bind(req, &struct{}{})
237+
require.Error(t, err)
238+
require.Contains(t, err.Error(), "unmatched brackets")
239+
}
240+
194241
func Benchmark_FormBinder_BindMultipart(b *testing.B) {
195242
b.ReportAllocs()
196243

binder/header.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,14 @@ func (*HeaderBinding) Name() string {
1818
// Bind parses the request header and returns the result.
1919
func (b *HeaderBinding) Bind(req *fasthttp.Request, out any) error {
2020
data := make(map[string][]string)
21-
var err error
2221
for key, val := range req.Header.All() {
2322
k := utils.UnsafeString(key)
2423
v := utils.UnsafeString(val)
25-
err = formatBindData(b.Name(), out, data, k, v, b.EnableSplitting, false)
26-
if err != nil {
27-
break
24+
if err := formatBindData(b.Name(), out, data, k, v, b.EnableSplitting, false); err != nil {
25+
return err
2826
}
2927
}
3028

31-
if err != nil {
32-
return err
33-
}
34-
3529
return parse(b.Name(), out, data)
3630
}
3731

binder/header_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,16 @@ func Benchmark_HeaderBinder_Bind(b *testing.B) {
8585
require.Contains(b, user.Posts, "post2")
8686
require.Contains(b, user.Posts, "post3")
8787
}
88+
89+
func Test_HeaderBinder_Bind_ParseError(t *testing.T) {
90+
b := &HeaderBinding{}
91+
type User struct {
92+
Age int `header:"Age"`
93+
}
94+
var user User
95+
req := fasthttp.AcquireRequest()
96+
req.Header.Set("age", "invalid")
97+
t.Cleanup(func() { fasthttp.ReleaseRequest(req) })
98+
err := b.Bind(req, &user)
99+
require.Error(t, err)
100+
}

binder/mapping_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ import (
44
"errors"
55
"mime/multipart"
66
"reflect"
7+
"strconv"
78
"testing"
89

10+
"github.com/gofiber/schema"
911
"github.com/stretchr/testify/require"
1012
)
1113

@@ -344,6 +346,130 @@ func Test_formatBindData_ErrorCases(t *testing.T) {
344346
})
345347
}
346348

349+
func Test_decoderBuilder(t *testing.T) {
350+
t.Parallel()
351+
type customInt int
352+
conv := func(s string) reflect.Value {
353+
i, err := strconv.Atoi(s)
354+
if err != nil {
355+
panic(err)
356+
}
357+
return reflect.ValueOf(customInt(i))
358+
}
359+
parserConfig := ParserConfig{
360+
SetAliasTag: "custom",
361+
ParserType: []ParserType{{
362+
CustomType: customInt(0),
363+
Converter: conv,
364+
}},
365+
IgnoreUnknownKeys: false,
366+
ZeroEmpty: false,
367+
}
368+
decAny := decoderBuilder(parserConfig)
369+
dec, ok := decAny.(*schema.Decoder)
370+
require.True(t, ok)
371+
var out struct {
372+
X customInt `custom:"x"`
373+
}
374+
err := dec.Decode(&out, map[string][]string{"x": {"7"}})
375+
require.NoError(t, err)
376+
require.Equal(t, customInt(7), out.X)
377+
}
378+
379+
func Test_parseToMap_Extended(t *testing.T) {
380+
t.Parallel()
381+
data := map[string][]string{
382+
"empty": {},
383+
"key1": {"value1"},
384+
}
385+
386+
m := make(map[string]string)
387+
err := parseToMap(m, data)
388+
require.NoError(t, err)
389+
require.Equal(t, "", m["empty"])
390+
391+
m2 := make(map[string][]int)
392+
err = parseToMap(m2, data)
393+
require.ErrorIs(t, err, ErrMapNotConvertible)
394+
395+
m3 := make(map[string]int)
396+
err = parseToMap(m3, data)
397+
require.NoError(t, err)
398+
}
399+
400+
func Test_decoderPoolMapInit(t *testing.T) {
401+
for _, tag := range tags {
402+
decAny := decoderPoolMap[tag].Get()
403+
dec, ok := decAny.(*schema.Decoder)
404+
require.True(t, ok)
405+
require.NotNil(t, dec)
406+
}
407+
}
408+
409+
func Test_getFieldCache(t *testing.T) {
410+
t.Parallel()
411+
require.NotNil(t, getFieldCache("header"))
412+
require.NotNil(t, getFieldCache("respHeader"))
413+
require.NotNil(t, getFieldCache("cookie"))
414+
require.NotNil(t, getFieldCache("form"))
415+
require.NotNil(t, getFieldCache("uri"))
416+
require.NotNil(t, getFieldCache("query"))
417+
require.Panics(t, func() { getFieldCache("unknown") })
418+
}
419+
420+
func Test_EqualFieldType_Map(t *testing.T) {
421+
t.Parallel()
422+
m := map[string]int{}
423+
require.True(t, equalFieldType(&m, reflect.Int, "any", "query"))
424+
}
425+
426+
func Test_equalFieldType_CacheTypeMismatch(t *testing.T) {
427+
type Sample struct {
428+
Field string `query:"field"`
429+
}
430+
cache := getFieldCache("query")
431+
typ := reflect.TypeOf(Sample{})
432+
cache.Store(typ, 1)
433+
defer cache.Delete(typ)
434+
var s Sample
435+
require.False(t, equalFieldType(&s, reflect.String, "field", "query"))
436+
}
437+
438+
func Test_buildFieldInfo_Unexported(t *testing.T) {
439+
t.Parallel()
440+
type nested struct {
441+
export int
442+
Exported int
443+
}
444+
_ = nested{export: 0}
445+
type outer struct {
446+
Name string
447+
Nested nested
448+
}
449+
info := buildFieldInfo(reflect.TypeOf(outer{}), "query")
450+
require.Contains(t, info.names, "name")
451+
_, ok := info.nestedKinds[reflect.Int]
452+
require.True(t, ok)
453+
}
454+
455+
func Test_formatBindData_BracketNotationSuccess(t *testing.T) {
456+
t.Parallel()
457+
out := struct{}{}
458+
data := make(map[string][]string)
459+
err := formatBindData("query", out, data, "user[name]", "john", false, true)
460+
require.NoError(t, err)
461+
require.Equal(t, "john", data["user.name"][0])
462+
}
463+
464+
func Test_formatBindData_FileHeaderTypeMismatch(t *testing.T) {
465+
t.Parallel()
466+
out := struct{}{}
467+
data := map[string][]int{}
468+
files := []*multipart.FileHeader{{Filename: "file1.txt"}}
469+
err := formatBindData("query", out, data, "file", files, false, false)
470+
require.EqualError(t, err, "unsupported value type: []*multipart.FileHeader")
471+
}
472+
347473
func Benchmark_equalFieldType(b *testing.B) {
348474
type Nested struct {
349475
Name string `query:"name"`

binder/resp_header.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,15 @@ func (*RespHeaderBinding) Name() string {
1818
// Bind parses the response header and returns the result.
1919
func (b *RespHeaderBinding) Bind(resp *fasthttp.Response, out any) error {
2020
data := make(map[string][]string)
21-
var err error
2221

2322
for key, val := range resp.Header.All() {
2423
k := utils.UnsafeString(key)
2524
v := utils.UnsafeString(val)
26-
err = formatBindData(b.Name(), out, data, k, v, b.EnableSplitting, false)
27-
if err != nil {
28-
break
25+
if err := formatBindData(b.Name(), out, data, k, v, b.EnableSplitting, false); err != nil {
26+
return err
2927
}
3028
}
3129

32-
if err != nil {
33-
return err
34-
}
35-
3630
return parse(b.Name(), out, data)
3731
}
3832

binder/resp_header_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,16 @@ func Benchmark_RespHeaderBinder_Bind(b *testing.B) {
7575
require.Equal(b, 42, user.Age)
7676
require.Equal(b, []string{"post1", "post2", "post3"}, user.Posts)
7777
}
78+
79+
func Test_RespHeaderBinder_Bind_ParseError(t *testing.T) {
80+
b := &RespHeaderBinding{}
81+
type User struct {
82+
Age int `respHeader:"age"`
83+
}
84+
var user User
85+
resp := fasthttp.AcquireResponse()
86+
resp.Header.Set("age", "invalid")
87+
t.Cleanup(func() { fasthttp.ReleaseResponse(resp) })
88+
err := b.Bind(resp, &user)
89+
require.Error(t, err)
90+
}

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ require (
99
github.com/mattn/go-colorable v0.1.14
1010
github.com/mattn/go-isatty v0.0.20
1111
github.com/shamaton/msgpack/v2 v2.3.0
12-
github.com/stretchr/testify v1.11.0
13-
github.com/tinylib/msgp v1.3.0
12+
github.com/stretchr/testify v1.11.1
13+
github.com/tinylib/msgp v1.4.0
1414
github.com/valyala/bytebufferpool v1.0.0
1515
github.com/valyala/fasthttp v1.65.0
1616
golang.org/x/crypto v0.41.0

0 commit comments

Comments
 (0)