From 1e86249835f402f3224b41014534ad507351b8ef Mon Sep 17 00:00:00 2001 From: Frederik Creemers Date: Sat, 29 Jul 2017 19:42:09 +0200 Subject: [PATCH 1/3] Add Context.Cookies This gives easy access to request cookies, and makes it easy to set cookies on the response. Closes #391 --- context.go | 1 + cookies.go | 46 ++++++++++++++++++++++++++++++++++++++++ cookies_test.go | 53 ++++++++++++++++++++++++++++++++++++++++++++++ default_context.go | 5 +++++ 4 files changed, 105 insertions(+) create mode 100644 cookies.go create mode 100644 cookies_test.go diff --git a/context.go b/context.go index 2268c6b9d..8f0d0d3ba 100644 --- a/context.go +++ b/context.go @@ -18,6 +18,7 @@ type Context interface { Response() http.ResponseWriter Request() *http.Request Session() *Session + Cookies() *Cookies Params() ParamValues Param(string) string Set(string, interface{}) diff --git a/cookies.go b/cookies.go new file mode 100644 index 000000000..d1f46ea59 --- /dev/null +++ b/cookies.go @@ -0,0 +1,46 @@ +package buffalo + +import ( + "net/http" + "time" +) + +// Cookies allows you to easily get cookies from the request, and set cookies on the response. +type Cookies struct { + req *http.Request + res http.ResponseWriter +} + +// Get returns the value of the cookie with the given name. Returns http.ErrNoCookie if there's no cookie with that name in the request. +func (c *Cookies) Get(name string) (string, error) { + ck, err := c.req.Cookie(name) + if err != nil { + return "", err + } + + return ck.Value, nil +} + +// Sets a cookie on the response, which will expire after the given duration. +func (c *Cookies) Set(name, value string, maxAge time.Duration) { + ck := http.Cookie{ + Name: name, + Value: value, + MaxAge: int(maxAge.Seconds()), + } + + http.SetCookie(c.res, &ck) +} + +// SetWithExpirationTime sets a cookie that will expire at a specific time. +// Note that the time is determined by the client's browser, so it might not expire at the expected time, +// for example if the client has changed the time on their computer. +func (c *Cookies) SetWithExpirationTime(name, value string, expires time.Time) { + ck := http.Cookie{ + Name: name, + Value: value, + Expires: expires, + } + + http.SetCookie(c.res, &ck) +} diff --git a/cookies_test.go b/cookies_test.go new file mode 100644 index 000000000..fa45bdefe --- /dev/null +++ b/cookies_test.go @@ -0,0 +1,53 @@ +package buffalo + +import ( + "github.com/stretchr/testify/require" + "net/http" + "net/http/httptest" + "testing" + "time" +) + +func TestCookies_Get(t *testing.T) { + r := require.New(t) + req := httptest.NewRequest("POST", "/", nil) + req.Header.Set("Cookie", "name=Arthur Dent; answer=42") + + c := Cookies{req, nil} + + v, err := c.Get("name") + r.NoError(err) + r.Equal("Arthur Dent", v) + + v, err = c.Get("answer") + r.NoError(err) + r.Equal("42", v) + + _, err = c.Get("unknown") + r.EqualError(err, http.ErrNoCookie.Error()) +} + +func TestCookies_Set(t *testing.T) { + r := require.New(t) + res := httptest.NewRecorder() + + c := Cookies{&http.Request{}, res} + + c.Set("name", "Rob Pike", time.Hour*24) + + h := res.Header().Get("Set-Cookie") + r.Equal("name=Rob Pike; Max-Age=86400", h) +} + +func TestCookies_SetWithExpirationTime(t *testing.T) { + r := require.New(t) + res := httptest.NewRecorder() + + c := Cookies{&http.Request{}, res} + + e := time.Date(2017, 7, 29, 19, 28, 45, 0, time.UTC) + c.SetWithExpirationTime("name", "Rob Pike", e) + + h := res.Header().Get("Set-Cookie") + r.Equal("name=Rob Pike; Expires=Sat, 29 Jul 2017 19:28:45 GMT", h) +} diff --git a/default_context.go b/default_context.go index cd50c8187..0142a5d39 100644 --- a/default_context.go +++ b/default_context.go @@ -83,6 +83,11 @@ func (d *DefaultContext) Session() *Session { return d.session } +// cookies for the associated request and response. +func (d *DefaultContext) Cookies() *Cookies { + return &Cookies{d.request, d.response} +} + // Flash messages for the associated Request. func (d *DefaultContext) Flash() *Flash { return d.flash From f7372d51a40fd638c71b3931df07a768846ba36d Mon Sep 17 00:00:00 2001 From: Frederik Creemers Date: Sat, 29 Jul 2017 19:51:27 +0200 Subject: [PATCH 2/3] Fix comment format --- cookies.go | 2 +- default_context.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cookies.go b/cookies.go index d1f46ea59..eeac1a856 100644 --- a/cookies.go +++ b/cookies.go @@ -21,7 +21,7 @@ func (c *Cookies) Get(name string) (string, error) { return ck.Value, nil } -// Sets a cookie on the response, which will expire after the given duration. +// Set a cookie on the response, which will expire after the given duration. func (c *Cookies) Set(name, value string, maxAge time.Duration) { ck := http.Cookie{ Name: name, diff --git a/default_context.go b/default_context.go index 0142a5d39..df451b3bb 100644 --- a/default_context.go +++ b/default_context.go @@ -83,7 +83,7 @@ func (d *DefaultContext) Session() *Session { return d.session } -// cookies for the associated request and response. +// Cookies for the associated request and response. func (d *DefaultContext) Cookies() *Cookies { return &Cookies{d.request, d.response} } From 1684e471ae871e63734596995bd2c98ed250c905 Mon Sep 17 00:00:00 2001 From: Frederik Creemers Date: Sat, 29 Jul 2017 20:01:56 +0200 Subject: [PATCH 3/3] Add Cookies.Delete method --- cookies.go | 13 +++++++++++++ cookies_test.go | 12 ++++++++++++ 2 files changed, 25 insertions(+) diff --git a/cookies.go b/cookies.go index eeac1a856..131317a64 100644 --- a/cookies.go +++ b/cookies.go @@ -44,3 +44,16 @@ func (c *Cookies) SetWithExpirationTime(name, value string, expires time.Time) { http.SetCookie(c.res, &ck) } + +// Delete sets a header that tells the browser to remove the cookie with the given name. +func (c *Cookies) Delete(name string) { + ck := http.Cookie{ + Name: name, + Value: "v", + // Setting a time in the distant past, like the unix epoch, removes the cookie, + // since it has long expired. + Expires: time.Unix(0, 0), + } + + http.SetCookie(c.res, &ck) +} diff --git a/cookies_test.go b/cookies_test.go index fa45bdefe..67a7306fe 100644 --- a/cookies_test.go +++ b/cookies_test.go @@ -51,3 +51,15 @@ func TestCookies_SetWithExpirationTime(t *testing.T) { h := res.Header().Get("Set-Cookie") r.Equal("name=Rob Pike; Expires=Sat, 29 Jul 2017 19:28:45 GMT", h) } + +func TestCookies_Delete(t *testing.T) { + r := require.New(t) + res := httptest.NewRecorder() + + c := Cookies{&http.Request{}, res} + + c.Delete("remove-me") + + h := res.Header().Get("Set-Cookie") + r.Equal("remove-me=v; Expires=Thu, 01 Jan 1970 00:00:00 GMT", h) +}