diff --git a/httpbin/handlers.go b/httpbin/handlers.go index a3251015bae8e951f887bcab518314679981676a..eefc63500aa0d96d7827080d6372e9d2cd75eccf 100644 --- a/httpbin/handlers.go +++ b/httpbin/handlers.go @@ -197,6 +197,7 @@ func redirectLocation(r *http.Request, relative bool, n int) string { } else { u := getURL(r) u.Path = path + u.RawQuery = "" location = u.String() } @@ -218,6 +219,15 @@ func doRedirect(w http.ResponseWriter, r *http.Request, relative bool) { w.WriteHeader(http.StatusFound) } +// Redirect responds with 302 redirect a given number of times. Defaults to a +// relative redirect, but an ?absolute=true query param will trigger an +// absolute redirect. +func (h *HTTPBin) Redirect(w http.ResponseWriter, r *http.Request) { + params := r.URL.Query() + relative := strings.ToLower(params.Get("absolute")) != "true" + doRedirect(w, r, relative) +} + // RelativeRedirect responds with an HTTP 302 redirect a given number of times func (h *HTTPBin) RelativeRedirect(w http.ResponseWriter, r *http.Request) { doRedirect(w, r, true) diff --git a/httpbin/handlers_test.go b/httpbin/handlers_test.go index 5dd6e9489a81739eda684ebe8f50d4971bcf164b..601b764807b3491e35aa4bd356b8df15bd63bfd2 100644 --- a/httpbin/handlers_test.go +++ b/httpbin/handlers_test.go @@ -728,6 +728,68 @@ func TestResponseHeaders__OverrideContentType(t *testing.T) { assertContentType(t, w, contentType) } +func TestRedirect__OK(t *testing.T) { + var tests = []struct { + relative bool + n int + location string + }{ + {true, 1, "/get"}, + {true, 2, "/relative-redirect/1"}, + {true, 100, "/relative-redirect/99"}, + + {false, 1, "http://host/get"}, + {false, 2, "http://host/absolute-redirect/1"}, + {false, 100, "http://host/absolute-redirect/99"}, + } + + for _, test := range tests { + u := fmt.Sprintf("/redirect/%d", test.n) + if !test.relative { + u = fmt.Sprintf("%s?absolute=true", u) + } + r, _ := http.NewRequest("GET", u, nil) + r.Host = "host" + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + + assertStatusCode(t, w, http.StatusFound) + assertHeader(t, w, "Location", test.location) + } +} + +func TestRedirect__Errors(t *testing.T) { + var tests = []struct { + relative bool + given interface{} + status int + }{ + {true, 3.14, http.StatusBadRequest}, + {true, -1, http.StatusBadRequest}, + {true, "", http.StatusBadRequest}, + {true, "foo", http.StatusBadRequest}, + {true, "10/bar", http.StatusNotFound}, + + {false, 3.14, http.StatusBadRequest}, + {false, -1, http.StatusBadRequest}, + {false, "", http.StatusBadRequest}, + {false, "foo", http.StatusBadRequest}, + {false, "10/bar", http.StatusNotFound}, + } + + for _, test := range tests { + u := fmt.Sprintf("/redirect/%v", test.given) + if !test.relative { + u = fmt.Sprintf("%s?absolute=true", u) + } + r, _ := http.NewRequest("GET", u, nil) + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + + assertStatusCode(t, w, test.status) + } +} + func TestAbsoluteAndRelativeRedirects__OK(t *testing.T) { var tests = []struct { relative bool diff --git a/httpbin/httpbin.go b/httpbin/httpbin.go index 2dffdb4bcb346c76a629a70b84db2a8d8032edbf..35fca2f46dc2e1ca129a816b9123b51dfc44a43e 100644 --- a/httpbin/httpbin.go +++ b/httpbin/httpbin.go @@ -67,6 +67,7 @@ func (h *HTTPBin) Handler() http.Handler { mux.HandleFunc("/status/", h.Status) mux.HandleFunc("/response-headers", h.ResponseHeaders) + mux.HandleFunc("/redirect/", h.Redirect) mux.HandleFunc("/relative-redirect/", h.RelativeRedirect) mux.HandleFunc("/absolute-redirect/", h.AbsoluteRedirect)