Skip to content

Commit 57744eb

Browse files
authored
🐛 bug: fix EnableSplittingOnParsers is not functional (#3231)
* 🐛 bug: fix EnableSplittingOnParsers is not functional * remove wrong testcase * add support for external xml decoders * improve test coverage * fix linter * update * add reset methods * improve test coverage * merge Form and MultipartForm methods * fix linter * split reset and putting steps * fix linter
1 parent 58677d5 commit 57744eb

30 files changed

+1339
-152
lines changed

app.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,13 @@ type Config struct { //nolint:govet // Aligning the struct fields is not necessa
341341
// Default: xml.Marshal
342342
XMLEncoder utils.XMLMarshal `json:"-"`
343343

344+
// XMLDecoder set by an external client of Fiber it will use the provided implementation of a
345+
// XMLUnmarshal
346+
//
347+
// Allowing for flexibility in using another XML library for decoding
348+
// Default: xml.Unmarshal
349+
XMLDecoder utils.XMLUnmarshal `json:"-"`
350+
344351
// If you find yourself behind some sort of proxy, like a load balancer,
345352
// then certain header information may be sent to you using special X-Forwarded-* headers or the Forwarded header.
346353
// For example, the Host HTTP header is usually used to return the requested host.
@@ -560,6 +567,9 @@ func New(config ...Config) *App {
560567
if app.config.XMLEncoder == nil {
561568
app.config.XMLEncoder = xml.Marshal
562569
}
570+
if app.config.XMLDecoder == nil {
571+
app.config.XMLDecoder = xml.Unmarshal
572+
}
563573
if len(app.config.RequestMethods) == 0 {
564574
app.config.RequestMethods = DefaultMethods
565575
}

bind.go

Lines changed: 90 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,16 @@ func (b *Bind) Custom(name string, dest any) error {
7777

7878
// Header binds the request header strings into the struct, map[string]string and map[string][]string.
7979
func (b *Bind) Header(out any) error {
80-
if err := b.returnErr(binder.HeaderBinder.Bind(b.ctx.Request(), out)); err != nil {
80+
bind := binder.GetFromThePool[*binder.HeaderBinding](&binder.HeaderBinderPool)
81+
bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
82+
83+
// Reset & put binder
84+
defer func() {
85+
bind.Reset()
86+
binder.PutToThePool(&binder.HeaderBinderPool, bind)
87+
}()
88+
89+
if err := b.returnErr(bind.Bind(b.ctx.Request(), out)); err != nil {
8190
return err
8291
}
8392

@@ -86,7 +95,16 @@ func (b *Bind) Header(out any) error {
8695

8796
// RespHeader binds the response header strings into the struct, map[string]string and map[string][]string.
8897
func (b *Bind) RespHeader(out any) error {
89-
if err := b.returnErr(binder.RespHeaderBinder.Bind(b.ctx.Response(), out)); err != nil {
98+
bind := binder.GetFromThePool[*binder.RespHeaderBinding](&binder.RespHeaderBinderPool)
99+
bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
100+
101+
// Reset & put binder
102+
defer func() {
103+
bind.Reset()
104+
binder.PutToThePool(&binder.RespHeaderBinderPool, bind)
105+
}()
106+
107+
if err := b.returnErr(bind.Bind(b.ctx.Response(), out)); err != nil {
90108
return err
91109
}
92110

@@ -96,7 +114,16 @@ func (b *Bind) RespHeader(out any) error {
96114
// Cookie binds the request cookie strings into the struct, map[string]string and map[string][]string.
97115
// NOTE: If your cookie is like key=val1,val2; they'll be binded as an slice if your map is map[string][]string. Else, it'll use last element of cookie.
98116
func (b *Bind) Cookie(out any) error {
99-
if err := b.returnErr(binder.CookieBinder.Bind(b.ctx.RequestCtx(), out)); err != nil {
117+
bind := binder.GetFromThePool[*binder.CookieBinding](&binder.CookieBinderPool)
118+
bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
119+
120+
// Reset & put binder
121+
defer func() {
122+
bind.Reset()
123+
binder.PutToThePool(&binder.CookieBinderPool, bind)
124+
}()
125+
126+
if err := b.returnErr(bind.Bind(&b.ctx.RequestCtx().Request, out)); err != nil {
100127
return err
101128
}
102129

@@ -105,7 +132,16 @@ func (b *Bind) Cookie(out any) error {
105132

106133
// Query binds the query string into the struct, map[string]string and map[string][]string.
107134
func (b *Bind) Query(out any) error {
108-
if err := b.returnErr(binder.QueryBinder.Bind(b.ctx.RequestCtx(), out)); err != nil {
135+
bind := binder.GetFromThePool[*binder.QueryBinding](&binder.QueryBinderPool)
136+
bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
137+
138+
// Reset & put binder
139+
defer func() {
140+
bind.Reset()
141+
binder.PutToThePool(&binder.QueryBinderPool, bind)
142+
}()
143+
144+
if err := b.returnErr(bind.Bind(&b.ctx.RequestCtx().Request, out)); err != nil {
109145
return err
110146
}
111147

@@ -114,7 +150,16 @@ func (b *Bind) Query(out any) error {
114150

115151
// JSON binds the body string into the struct.
116152
func (b *Bind) JSON(out any) error {
117-
if err := b.returnErr(binder.JSONBinder.Bind(b.ctx.Body(), b.ctx.App().Config().JSONDecoder, out)); err != nil {
153+
bind := binder.GetFromThePool[*binder.JSONBinding](&binder.JSONBinderPool)
154+
bind.JSONDecoder = b.ctx.App().Config().JSONDecoder
155+
156+
// Reset & put binder
157+
defer func() {
158+
bind.Reset()
159+
binder.PutToThePool(&binder.JSONBinderPool, bind)
160+
}()
161+
162+
if err := b.returnErr(bind.Bind(b.ctx.Body(), out)); err != nil {
118163
return err
119164
}
120165

@@ -123,24 +168,54 @@ func (b *Bind) JSON(out any) error {
123168

124169
// CBOR binds the body string into the struct.
125170
func (b *Bind) CBOR(out any) error {
126-
if err := b.returnErr(binder.CBORBinder.Bind(b.ctx.Body(), b.ctx.App().Config().CBORDecoder, out)); err != nil {
171+
bind := binder.GetFromThePool[*binder.CBORBinding](&binder.CBORBinderPool)
172+
bind.CBORDecoder = b.ctx.App().Config().CBORDecoder
173+
174+
// Reset & put binder
175+
defer func() {
176+
bind.Reset()
177+
binder.PutToThePool(&binder.CBORBinderPool, bind)
178+
}()
179+
180+
if err := b.returnErr(bind.Bind(b.ctx.Body(), out)); err != nil {
127181
return err
128182
}
129183
return b.validateStruct(out)
130184
}
131185

132186
// XML binds the body string into the struct.
133187
func (b *Bind) XML(out any) error {
134-
if err := b.returnErr(binder.XMLBinder.Bind(b.ctx.Body(), out)); err != nil {
188+
bind := binder.GetFromThePool[*binder.XMLBinding](&binder.XMLBinderPool)
189+
bind.XMLDecoder = b.ctx.App().config.XMLDecoder
190+
191+
// Reset & put binder
192+
defer func() {
193+
bind.Reset()
194+
binder.PutToThePool(&binder.XMLBinderPool, bind)
195+
}()
196+
197+
if err := b.returnErr(bind.Bind(b.ctx.Body(), out)); err != nil {
135198
return err
136199
}
137200

138201
return b.validateStruct(out)
139202
}
140203

141204
// Form binds the form into the struct, map[string]string and map[string][]string.
205+
// If Content-Type is "application/x-www-form-urlencoded" or "multipart/form-data", it will bind the form values.
206+
//
207+
// Binding multipart files is not supported yet.
142208
func (b *Bind) Form(out any) error {
143-
if err := b.returnErr(binder.FormBinder.Bind(b.ctx.RequestCtx(), out)); err != nil {
209+
bind := binder.GetFromThePool[*binder.FormBinding](&binder.FormBinderPool)
210+
bind.EnableSplitting = b.ctx.App().config.EnableSplittingOnParsers
211+
212+
// Reset & put binder
213+
defer func() {
214+
bind.Reset()
215+
binder.PutToThePool(&binder.FormBinderPool, bind)
216+
}()
217+
218+
if err := b.returnErr(bind.Bind(&b.ctx.RequestCtx().Request, out)); err != nil {
144219
return err
145220
}
146221

@@ -149,16 +224,14 @@ func (b *Bind) Form(out any) error {
149224

150225
// URI binds the route parameters into the struct, map[string]string and map[string][]string.
151226
func (b *Bind) URI(out any) error {
152-
if err := b.returnErr(binder.URIBinder.Bind(b.ctx.Route().Params, b.ctx.Params, out)); err != nil {
153-
return err
154-
}
227+
bind := binder.GetFromThePool[*binder.URIBinding](&binder.URIBinderPool)
155228

156-
return b.validateStruct(out)
157-
}
229+
// Reset & put binder
230+
defer func() {
231+
binder.PutToThePool(&binder.URIBinderPool, bind)
232+
}()
158233

159-
// MultipartForm binds the multipart form into the struct, map[string]string and map[string][]string.
160-
func (b *Bind) MultipartForm(out any) error {
161-
if err := b.returnErr(binder.FormBinder.BindMultipart(b.ctx.RequestCtx(), out)); err != nil {
234+
if err := b.returnErr(bind.Bind(b.ctx.Route().Params, b.ctx.Params, out)); err != nil {
162235
return err
163236
}
164237

@@ -193,10 +266,8 @@ func (b *Bind) Body(out any) error {
193266
return b.XML(out)
194267
case MIMEApplicationCBOR:
195268
return b.CBOR(out)
196-
case MIMEApplicationForm:
269+
case MIMEApplicationForm, MIMEMultipartForm:
197270
return b.Form(out)
198-
case MIMEMultipartForm:
199-
return b.MultipartForm(out)
200271
}
201272

202273
// No suitable content type found

bind_test.go

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ func Test_returnErr(t *testing.T) {
3232
// go test -run Test_Bind_Query -v
3333
func Test_Bind_Query(t *testing.T) {
3434
t.Parallel()
35-
app := New()
35+
app := New(Config{
36+
EnableSplittingOnParsers: true,
37+
})
3638
c := app.AcquireCtx(&fasthttp.RequestCtx{})
3739

3840
type Query struct {
@@ -111,7 +113,9 @@ func Test_Bind_Query(t *testing.T) {
111113
func Test_Bind_Query_Map(t *testing.T) {
112114
t.Parallel()
113115

114-
app := New()
116+
app := New(Config{
117+
EnableSplittingOnParsers: true,
118+
})
115119
c := app.AcquireCtx(&fasthttp.RequestCtx{})
116120

117121
c.Request().SetBody([]byte(``))
@@ -318,13 +322,13 @@ func Test_Bind_Header(t *testing.T) {
318322
c.Request().Header.Add("Hobby", "golang,fiber")
319323
q := new(Header)
320324
require.NoError(t, c.Bind().Header(q))
321-
require.Len(t, q.Hobby, 2)
325+
require.Len(t, q.Hobby, 1)
322326

323327
c.Request().Header.Del("hobby")
324328
c.Request().Header.Add("Hobby", "golang,fiber,go")
325329
q = new(Header)
326330
require.NoError(t, c.Bind().Header(q))
327-
require.Len(t, q.Hobby, 3)
331+
require.Len(t, q.Hobby, 1)
328332

329333
empty := new(Header)
330334
c.Request().Header.Del("hobby")
@@ -357,7 +361,7 @@ func Test_Bind_Header(t *testing.T) {
357361
require.Equal(t, "go,fiber", h2.Hobby)
358362
require.True(t, h2.Bool)
359363
require.Equal(t, "Jane Doe", h2.Name) // check value get overwritten
360-
require.Equal(t, []string{"milo", "coke", "pepsi"}, h2.FavouriteDrinks)
364+
require.Equal(t, []string{"milo,coke,pepsi"}, h2.FavouriteDrinks)
361365
var nilSlice []string
362366
require.Equal(t, nilSlice, h2.Empty)
363367
require.Equal(t, []string{""}, h2.Alloc)
@@ -386,13 +390,13 @@ func Test_Bind_Header_Map(t *testing.T) {
386390
c.Request().Header.Add("Hobby", "golang,fiber")
387391
q := make(map[string][]string, 0)
388392
require.NoError(t, c.Bind().Header(&q))
389-
require.Len(t, q["Hobby"], 2)
393+
require.Len(t, q["Hobby"], 1)
390394

391395
c.Request().Header.Del("hobby")
392396
c.Request().Header.Add("Hobby", "golang,fiber,go")
393397
q = make(map[string][]string, 0)
394398
require.NoError(t, c.Bind().Header(&q))
395-
require.Len(t, q["Hobby"], 3)
399+
require.Len(t, q["Hobby"], 1)
396400

397401
empty := make(map[string][]string, 0)
398402
c.Request().Header.Del("hobby")
@@ -543,7 +547,9 @@ func Test_Bind_Header_Schema(t *testing.T) {
543547
// go test -run Test_Bind_Resp_Header -v
544548
func Test_Bind_RespHeader(t *testing.T) {
545549
t.Parallel()
546-
app := New()
550+
app := New(Config{
551+
EnableSplittingOnParsers: true,
552+
})
547553
c := app.AcquireCtx(&fasthttp.RequestCtx{})
548554

549555
type Header struct {
@@ -627,13 +633,13 @@ func Test_Bind_RespHeader_Map(t *testing.T) {
627633
c.Response().Header.Add("Hobby", "golang,fiber")
628634
q := make(map[string][]string, 0)
629635
require.NoError(t, c.Bind().RespHeader(&q))
630-
require.Len(t, q["Hobby"], 2)
636+
require.Len(t, q["Hobby"], 1)
631637

632638
c.Response().Header.Del("hobby")
633639
c.Response().Header.Add("Hobby", "golang,fiber,go")
634640
q = make(map[string][]string, 0)
635641
require.NoError(t, c.Bind().RespHeader(&q))
636-
require.Len(t, q["Hobby"], 3)
642+
require.Len(t, q["Hobby"], 1)
637643

638644
empty := make(map[string][]string, 0)
639645
c.Response().Header.Del("hobby")
@@ -751,7 +757,9 @@ func Benchmark_Bind_Query_WithParseParam(b *testing.B) {
751757
func Benchmark_Bind_Query_Comma(b *testing.B) {
752758
var err error
753759

754-
app := New()
760+
app := New(Config{
761+
EnableSplittingOnParsers: true,
762+
})
755763
c := app.AcquireCtx(&fasthttp.RequestCtx{})
756764

757765
type Query struct {
@@ -1341,7 +1349,9 @@ func Benchmark_Bind_URI_Map(b *testing.B) {
13411349
func Test_Bind_Cookie(t *testing.T) {
13421350
t.Parallel()
13431351

1344-
app := New()
1352+
app := New(Config{
1353+
EnableSplittingOnParsers: true,
1354+
})
13451355
c := app.AcquireCtx(&fasthttp.RequestCtx{})
13461356

13471357
type Cookie struct {
@@ -1414,7 +1424,9 @@ func Test_Bind_Cookie(t *testing.T) {
14141424
func Test_Bind_Cookie_Map(t *testing.T) {
14151425
t.Parallel()
14161426

1417-
app := New()
1427+
app := New(Config{
1428+
EnableSplittingOnParsers: true,
1429+
})
14181430
c := app.AcquireCtx(&fasthttp.RequestCtx{})
14191431

14201432
c.Request().SetBody([]byte(``))

0 commit comments

Comments
 (0)