Skip to content

Commit

Permalink
🔥 add config to enable splitting by comma in parsers
Browse files Browse the repository at this point in the history
🔥 add config to enable splitting by comma in parsers
  • Loading branch information
efectn committed Aug 6, 2023
1 parent e91b02b commit c237fd6
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 56 deletions.
9 changes: 9 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,15 @@ type Config struct {
//
// Optional. Default: DefaultMethods
RequestMethods []string

// EnableSplittingOnParsers splits the query/body/header parameters by comma when it's true.
// For example, you can use it to parse multiple values from a query parameter like this:
// /api?foo=bar,baz == foo[]=bar&foo[]=baz
// If there is an escape character before the comma, parsers won't split it like the example below:
// /api?foo=bar\,baz == foo=bar,baz
//
// Optional. Default: false
EnableSplittingOnParsers bool `json:"enable_splitting_on_parsers"`
}

// Static defines configuration options when defining static assets.
Expand Down
25 changes: 13 additions & 12 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,13 +335,13 @@ func (c *Ctx) BodyParser(out interface{}) error {
k, err = parseParamSquareBrackets(k)
}

if strings.Contains(v, ",") && equalFieldType(out, reflect.Slice, k) {
values := strings.Split(v, ",")
if findNextNonEscapedCharsetPosition(v, []byte(",")) != -1 && equalFieldType(out, reflect.Slice, k) && c.app.config.EnableSplittingOnParsers {
values := splitNonEscaped(v, ",")
for i := 0; i < len(values); i++ {
data[k] = append(data[k], values[i])
data[k] = append(data[k], RemoveEscapeChar(values[i]))
}
} else {
data[k] = append(data[k], v)
data[k] = append(data[k], RemoveEscapeChar(v))
}
})

Expand Down Expand Up @@ -1159,13 +1159,13 @@ func (c *Ctx) QueryParser(out interface{}) error {
k, err = parseParamSquareBrackets(k)
}

if strings.Contains(v, ",") && equalFieldType(out, reflect.Slice, k) {
values := strings.Split(v, ",")
if findNextNonEscapedCharsetPosition(v, []byte(",")) != -1 && equalFieldType(out, reflect.Slice, k) && c.app.config.EnableSplittingOnParsers {
values := splitNonEscaped(v, ",")
for i := 0; i < len(values); i++ {
data[k] = append(data[k], values[i])
data[k] = append(data[k], RemoveEscapeChar(values[i]))
}
} else {
data[k] = append(data[k], v)
data[k] = append(data[k], RemoveEscapeChar(v))
}
})

Expand Down Expand Up @@ -1208,13 +1208,14 @@ func (c *Ctx) ReqHeaderParser(out interface{}) error {
k := c.app.getString(key)
v := c.app.getString(val)

if strings.Contains(v, ",") && equalFieldType(out, reflect.Slice, k) {
values := strings.Split(v, ",")
if findNextNonEscapedCharsetPosition(v, []byte(",")) != -1 && equalFieldType(out, reflect.Slice, k) && c.app.config.EnableSplittingOnParsers {
values := splitNonEscaped(v, ",")
for i := 0; i < len(values); i++ {
data[k] = append(data[k], values[i])
data[k] = append(data[k], RemoveEscapeChar(values[i]))
}
} else {
data[k] = append(data[k], v)
//fmt.Println(v)
data[k] = append(data[k], RemoveEscapeChar(v))
}
})

Expand Down
70 changes: 66 additions & 4 deletions ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3917,7 +3917,7 @@ func Benchmark_Ctx_Queries(b *testing.B) {
// go test -run Test_Ctx_QueryParser -v
func Test_Ctx_QueryParser(t *testing.T) {
t.Parallel()
app := New()
app := New(Config{EnableSplittingOnParsers: true})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
type Query struct {
Expand All @@ -3937,6 +3937,11 @@ func Test_Ctx_QueryParser(t *testing.T) {
utils.AssertEqual(t, nil, c.QueryParser(q))
utils.AssertEqual(t, 2, len(q.Hobby))

c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball\\,football")
q = new(Query)
utils.AssertEqual(t, nil, c.QueryParser(q))
utils.AssertEqual(t, 1, len(q.Hobby))

c.Request().URI().SetQueryString("id=1&name=tom&hobby=scoccer&hobby=basketball,football")
q = new(Query)
utils.AssertEqual(t, nil, c.QueryParser(q))
Expand Down Expand Up @@ -3988,6 +3993,29 @@ func Test_Ctx_QueryParser(t *testing.T) {
utils.AssertEqual(t, 2, len(aq.Data))
}

// go test -run Test_Ctx_QueryParser -v
func Test_Ctx_QueryParser_WithoutSplitting(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
type Query struct {
ID int
Name string
Hobby []string
}

c.Request().URI().SetQueryString("id=1&name=tom&hobby=basketball,football")
q := new(Query)
utils.AssertEqual(t, nil, c.QueryParser(q))
utils.AssertEqual(t, 1, len(q.Hobby))

c.Request().URI().SetQueryString("id=1&name=tom&hobby=scoccer&hobby=basketball,football")
q = new(Query)
utils.AssertEqual(t, nil, c.QueryParser(q))
utils.AssertEqual(t, 2, len(q.Hobby))
}

// go test -run Test_Ctx_QueryParser_WithSetParserDecoder -v
func Test_Ctx_QueryParser_WithSetParserDecoder(t *testing.T) {
t.Parallel()
Expand Down Expand Up @@ -4146,7 +4174,7 @@ func Test_Ctx_QueryParser_Schema(t *testing.T) {
// go test -run Test_Ctx_ReqHeaderParser -v
func Test_Ctx_ReqHeaderParser(t *testing.T) {
t.Parallel()
app := New()
app := New(Config{EnableSplittingOnParsers: true})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
type Header struct {
Expand All @@ -4170,6 +4198,12 @@ func Test_Ctx_ReqHeaderParser(t *testing.T) {
utils.AssertEqual(t, nil, c.ReqHeaderParser(q))
utils.AssertEqual(t, 3, len(q.Hobby))

c.Request().Header.Del("hobby")
c.Request().Header.Add("Hobby", "golang\\,fiber\\,go,fiber")
q = new(Header)
utils.AssertEqual(t, nil, c.ReqHeaderParser(q))
utils.AssertEqual(t, 2, len(q.Hobby))

empty := new(Header)
c.Request().Header.Del("hobby")
utils.AssertEqual(t, nil, c.QueryParser(empty))
Expand Down Expand Up @@ -4215,6 +4249,34 @@ func Test_Ctx_ReqHeaderParser(t *testing.T) {
utils.AssertEqual(t, "failed to decode: name is empty", c.ReqHeaderParser(rh).Error())
}

// go test -run Test_Ctx_ReqHeaderParser -v
func Test_Ctx_ReqHeaderParser_WithoutSplitting(t *testing.T) {
t.Parallel()
app := New()
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
type Header struct {
ID int
Name string
Hobby []string
}
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)
utils.AssertEqual(t, nil, c.ReqHeaderParser(q))
utils.AssertEqual(t, 1, len(q.Hobby))

c.Request().Header.Del("hobby")
c.Request().Header.Add("Hobby", "golang,fiber,go")
q = new(Header)
utils.AssertEqual(t, nil, c.ReqHeaderParser(q))
utils.AssertEqual(t, 1, len(q.Hobby))
}

// go test -run Test_Ctx_ReqHeaderParser_WithSetParserDecoder -v
func Test_Ctx_ReqHeaderParser_WithSetParserDecoder(t *testing.T) {
t.Parallel()
Expand Down Expand Up @@ -4439,7 +4501,7 @@ func Benchmark_Ctx_parseQuery(b *testing.B) {

// go test -v -run=^$ -bench=Benchmark_Ctx_QueryParser_Comma -benchmem -count=4
func Benchmark_Ctx_QueryParser_Comma(b *testing.B) {
app := New()
app := New(Config{EnableSplittingOnParsers: true})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
type Query struct {
Expand All @@ -4465,7 +4527,7 @@ func Benchmark_Ctx_QueryParser_Comma(b *testing.B) {

// go test -v -run=^$ -bench=Benchmark_Ctx_ReqHeaderParser -benchmem -count=4
func Benchmark_Ctx_ReqHeaderParser(b *testing.B) {
app := New()
app := New(Config{EnableSplittingOnParsers: true})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
type ReqHeader struct {
Expand Down
Loading

0 comments on commit c237fd6

Please sign in to comment.