Skip to content
Snippets Groups Projects
Commit af89c09d authored by Will McCutchen's avatar Will McCutchen
Browse files

Add /cookies/delete handler; simplify cookie handling

Stop handling multiple cookies with the same name, and revisit in the future
if it turns out to be useful/necessary.
parent d6e94c95
No related branches found
No related tags found
No related merge requests found
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
"time"
) )
var acceptedMediaTypes = []string{ var acceptedMediaTypes = []string{
...@@ -246,10 +247,7 @@ func (h *HTTPBin) AbsoluteRedirect(w http.ResponseWriter, r *http.Request) { ...@@ -246,10 +247,7 @@ func (h *HTTPBin) AbsoluteRedirect(w http.ResponseWriter, r *http.Request) {
func (h *HTTPBin) Cookies(w http.ResponseWriter, r *http.Request) { func (h *HTTPBin) Cookies(w http.ResponseWriter, r *http.Request) {
resp := cookiesResponse{} resp := cookiesResponse{}
for _, c := range r.Cookies() { for _, c := range r.Cookies() {
if _, found := resp[c.Name]; !found { resp[c.Name] = c.Value
resp[c.Name] = []string{}
}
resp[c.Name] = append(resp[c.Name], c.Value)
} }
body, _ := json.Marshal(resp) body, _ := json.Marshal(resp)
writeJSON(w, body, http.StatusOK) writeJSON(w, body, http.StatusOK)
...@@ -259,15 +257,29 @@ func (h *HTTPBin) Cookies(w http.ResponseWriter, r *http.Request) { ...@@ -259,15 +257,29 @@ func (h *HTTPBin) Cookies(w http.ResponseWriter, r *http.Request) {
// Cookies endpoint // Cookies endpoint
func (h *HTTPBin) SetCookies(w http.ResponseWriter, r *http.Request) { func (h *HTTPBin) SetCookies(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query() params := r.URL.Query()
for k, vs := range params { for k := range params {
for _, v := range vs {
fmt.Printf("setting cookie %#v = %#v\n", k, v)
http.SetCookie(w, &http.Cookie{ http.SetCookie(w, &http.Cookie{
Name: k, Name: k,
Value: v, Value: params.Get(k),
HttpOnly: true, HttpOnly: true,
}) })
} }
w.Header().Set("Location", "/cookies")
w.WriteHeader(http.StatusFound)
}
// DeleteCookies deletes cookies specified in query params and redirects to
// Cookies endpoint
func (h *HTTPBin) DeleteCookies(w http.ResponseWriter, r *http.Request) {
params := r.URL.Query()
for k := range params {
http.SetCookie(w, &http.Cookie{
Name: k,
Value: params.Get(k),
HttpOnly: true,
MaxAge: -1,
Expires: time.Now().Add(-1 * 24 * 365 * time.Hour),
})
} }
w.Header().Set("Location", "/cookies") w.Header().Set("Location", "/cookies")
w.WriteHeader(http.StatusFound) w.WriteHeader(http.StatusFound)
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"time"
) )
const maxMemory = 1024 * 1024 const maxMemory = 1024 * 1024
...@@ -812,14 +813,12 @@ func TestRedirects(t *testing.T) { ...@@ -812,14 +813,12 @@ func TestRedirects(t *testing.T) {
func TestCookies(t *testing.T) { func TestCookies(t *testing.T) {
testCookies := func(t *testing.T, cookies cookiesResponse) { testCookies := func(t *testing.T, cookies cookiesResponse) {
r, _ := http.NewRequest("GET", "/cookies", nil) r, _ := http.NewRequest("GET", "/cookies", nil)
for k, vs := range cookies { for k, v := range cookies {
for _, v := range vs {
r.AddCookie(&http.Cookie{ r.AddCookie(&http.Cookie{
Name: k, Name: k,
Value: v, Value: v,
}) })
} }
}
w := httptest.NewRecorder() w := httptest.NewRecorder()
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
...@@ -841,28 +840,24 @@ func TestCookies(t *testing.T) { ...@@ -841,28 +840,24 @@ func TestCookies(t *testing.T) {
testCookies(t, cookiesResponse{}) testCookies(t, cookiesResponse{})
}) })
t.Run("ok/single cookies", func(t *testing.T) { t.Run("ok/cookies", func(t *testing.T) {
testCookies(t, cookiesResponse{ testCookies(t, cookiesResponse{
"k1": {"v1"}, "k1": "v1",
"k2": {"v2"}, "k2": "v2",
})
})
t.Run("ok/duplicate cookies", func(t *testing.T) {
testCookies(t, cookiesResponse{
"k1": {"v1"},
"k2": {"v2a", "v2b"},
}) })
}) })
} }
func TestSetCookies(t *testing.T) { func TestSetCookies(t *testing.T) {
cookies := cookiesResponse{ cookies := cookiesResponse{
"k1": {"v1"}, "k1": "v1",
"k2": {"v2a", "v2b"}, "k2": "v2",
} }
params := url.Values(cookies) params := &url.Values{}
for k, v := range cookies {
params.Set(k, v)
}
r, _ := http.NewRequest("GET", fmt.Sprintf("/cookies/set?%s", params.Encode()), nil) r, _ := http.NewRequest("GET", fmt.Sprintf("/cookies/set?%s", params.Encode()), nil)
w := httptest.NewRecorder() w := httptest.NewRecorder()
...@@ -872,18 +867,44 @@ func TestSetCookies(t *testing.T) { ...@@ -872,18 +867,44 @@ func TestSetCookies(t *testing.T) {
assertHeader(t, w, "Location", "/cookies") assertHeader(t, w, "Location", "/cookies")
for _, c := range w.Result().Cookies() { for _, c := range w.Result().Cookies() {
values, ok := cookies[c.Name] v, ok := cookies[c.Name]
if !ok { if !ok {
t.Fatalf("got unexpected cookie %s=%s", c.Name, c.Value) t.Fatalf("got unexpected cookie %s=%s", c.Name, c.Value)
} }
found := false if v != c.Value {
for _, v := range values { t.Fatalf("got cookie %s=%s, expected value in %#v", c.Name, c.Value, v)
if v == c.Value { }
found = true }
}
func TestDeleteCookies(t *testing.T) {
cookies := cookiesResponse{
"k1": "v1",
"k2": "v2",
}
toDelete := "k2"
params := &url.Values{}
params.Set(toDelete, "")
r, _ := http.NewRequest("GET", fmt.Sprintf("/cookies/delete?%s", params.Encode()), nil)
for k, v := range cookies {
r.AddCookie(&http.Cookie{
Name: k,
Value: v,
})
} }
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
assertStatusCode(t, w, http.StatusFound)
assertHeader(t, w, "Location", "/cookies")
for _, c := range w.Result().Cookies() {
if c.Name == toDelete {
if time.Now().Sub(c.Expires) < (24*365-1)*time.Hour {
t.Fatalf("expected cookie %s to be deleted; got %#v", toDelete, c)
} }
if !found {
t.Fatalf("got cookie %s=%s, expected value in %#v", c.Name, c.Value, values)
} }
} }
} }
...@@ -39,7 +39,7 @@ type bodyResponse struct { ...@@ -39,7 +39,7 @@ type bodyResponse struct {
JSON interface{} `json:"json"` JSON interface{} `json:"json"`
} }
type cookiesResponse map[string][]string type cookiesResponse map[string]string
// Options are used to configure HTTPBin // Options are used to configure HTTPBin
type Options struct { type Options struct {
...@@ -77,6 +77,7 @@ func (h *HTTPBin) Handler() http.Handler { ...@@ -77,6 +77,7 @@ func (h *HTTPBin) Handler() http.Handler {
mux.HandleFunc("/cookies", h.Cookies) mux.HandleFunc("/cookies", h.Cookies)
mux.HandleFunc("/cookies/set", h.SetCookies) mux.HandleFunc("/cookies/set", h.SetCookies)
mux.HandleFunc("/cookies/delete", h.DeleteCookies)
// Make sure our ServeMux doesn't "helpfully" redirect these invalid // Make sure our ServeMux doesn't "helpfully" redirect these invalid
// endpoints by adding a trailing slash. See the ServeMux docs for more // endpoints by adding a trailing slash. See the ServeMux docs for more
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment