Skip to content

Commit

Permalink
feature: added a cookie parser and tests appropriate tests
Browse files Browse the repository at this point in the history
  • Loading branch information
joey1123455 committed Oct 4, 2023
1 parent adc2542 commit 5a5af19
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 4 deletions.
42 changes: 38 additions & 4 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,23 @@ const (
// maxParams defines the maximum number of parameters per route.
const maxParams = 30

// Some constants for BodyParser, QueryParser and ReqHeaderParser.
// Some constants for BodyParser, QueryParser, CookieParser and ReqHeaderParser.
const (
queryTag = "query"
reqHeaderTag = "reqHeader"
bodyTag = "form"
paramsTag = "params"
cookieTag = "cookie"
)

// userContextKey define the key name for storing context.Context in *fasthttp.RequestCtx
const userContextKey = "__local_user_context__"

var (
// decoderPoolMap helps to improve BodyParser's, QueryParser's and ReqHeaderParser's performance
// decoderPoolMap helps to improve BodyParser's, QueryParser's, CookieParser's and ReqHeaderParser's performance
decoderPoolMap = map[string]*sync.Pool{}
// tags is used to classify parser's pool
tags = []string{queryTag, bodyTag, reqHeaderTag, paramsTag}
tags = []string{queryTag, bodyTag, reqHeaderTag, paramsTag, cookieTag}
)

func init() {
Expand Down Expand Up @@ -481,7 +482,7 @@ func (c *Ctx) Cookie(cookie *Cookie) {

switch utils.ToLower(cookie.SameSite) {
case CookieSameSiteStrictMode:
fcookie.SetSameSite(fasthttp.zCookieSameSiteStrictMode)
fcookie.SetSameSite(fasthttp.CookieSameSiteStrictMode)
case CookieSameSiteNoneMode:
fcookie.SetSameSite(fasthttp.CookieSameSiteNoneMode)
case CookieSameSiteDisabled:
Expand All @@ -503,6 +504,39 @@ func (c *Ctx) Cookies(key string, defaultValue ...string) string {
return defaultString(c.app.getString(c.fasthttp.Request.Header.Cookie(key)), defaultValue)
}

// CookieParser is used to bind cookies to a struct
func (c *Ctx) CookieParser(out interface{}) error {
data := make(map[string][]string)
var err error

c.fasthttp.Request.Header.VisitAllCookie(func(key, val []byte) {
if err != nil {
return
}

k := c.app.getString(key)
v := c.app.getString(val)

if strings.Contains(k, "[") {
k, err = parseParamSquareBrackets(k)
}

if c.app.config.EnableSplittingOnParsers && strings.Contains(v, ",") && equalFieldType(out, reflect.Slice, k, cookieTag) {
values := strings.Split(v, ",")
for i := 0; i < len(values); i++ {
data[k] = append(data[k], values[i])
}
} else {
data[k] = append(data[k], v)
}
})
if err != nil {
return err
}

return c.parseToStruct(cookieTag, out, data)
}

// Download transfers the file from path as an attachment.
// Typically, browsers will prompt the user for download.
// By default, the Content-Disposition header filename= parameter is the filepath (this typically appears in the browser dialog).
Expand Down
80 changes: 80 additions & 0 deletions ctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,86 @@ func Benchmark_Ctx_Cookie(b *testing.B) {
utils.AssertEqual(b, "John=Doe; path=/; SameSite=Lax", app.getString(c.Response().Header.Peek("Set-Cookie")))
}

// go test -run Test_Ctx_CookieParser -v
func Test_Ctx_CookieParser(t *testing.T) {
t.Parallel()
app := New(Config{EnableSplittingOnParsers: true})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
type Cookie struct {
Name string
Class int
Courses []string
}
c.Request().Header.Set("Cookie", "name=doe")
c.Request().Header.Set("Cookie", "class=100")
c.Request().Header.Set("Cookie", "courses=maths,english")
cookie := new(Cookie)

// correct test cases
utils.AssertEqual(t, nil, c.CookieParser(cookie))
utils.AssertEqual(t, "doe", cookie.Name)
utils.AssertEqual(t, 100, cookie.Class)
utils.AssertEqual(t, 2, len(cookie.Courses))

// wrong test cases
empty := new(Cookie)
c.Request().Header.Set("Cookie", "name")
c.Request().Header.Set("Cookie", "class")
c.Request().Header.Set("Cookie", "courses")
utils.AssertEqual(t, nil, c.CookieParser(cookie))
utils.AssertEqual(t, "", empty.Name)
utils.AssertEqual(t, 0, empty.Class)
utils.AssertEqual(t, 0, len(empty.Courses))
}

// go test - run Test_Ctx_CookieParserUsingTag -v
func Test_Ctx_CookieParserUsingTag(t *testing.T) {
t.Parallel()
app := New(Config{EnableSplittingOnParsers: true})
c := app.AcquireCtx(&fasthttp.RequestCtx{})
defer app.ReleaseCtx(c)
type Cook struct {
ID int `cookie:"id"`
Name string `cookie:"name"`
Courses []string `cookie:"courses"`
Enrolled bool `cookie:"student"`
Fees float32 `cookie:"fee"`
Grades []uint8 `cookie:"score"`
}
cookie1 := new(Cook)
cookie1.Name = "Joseph"
utils.AssertEqual(t, "Joseph", cookie1.Name)

c.Request().Header.Set("Cookie", "id=1")
c.Request().Header.Set("Cookie", "name=Joey")
c.Request().Header.Set("Cookie", "courses=maths,english, chemistry, physics")
c.Request().Header.Set("Cookie", "student=true")
c.Request().Header.Set("Cookie", "fee=45.78")
c.Request().Header.Set("Cookie", "score=7,6,10")
utils.AssertEqual(t, nil, c.CookieParser(cookie1))
utils.AssertEqual(t, "Joey", cookie1.Name)
utils.AssertEqual(t, true, cookie1.Enrolled)
utils.AssertEqual(t, float32(45.78), cookie1.Fees)
utils.AssertEqual(t, []uint8{7, 6, 10}, cookie1.Grades)

type RequiredCookie struct {
House string `cookie:"house,required"`
}
rc := new(RequiredCookie)
utils.AssertEqual(t, "failed to decode: house is empty", c.CookieParser(rc).Error())

type ArrayCookie struct {
Dates []int
}

ac := new(ArrayCookie)
c.Request().Header.Set("Cookie", "dates[]=7,6,10")
utils.AssertEqual(t, nil, c.CookieParser(ac))
utils.AssertEqual(t, 3, len(ac.Dates))

}

// go test -run Test_Ctx_Cookies
func Test_Ctx_Cookies(t *testing.T) {
t.Parallel()
Expand Down

0 comments on commit 5a5af19

Please sign in to comment.