diff --git a/httpbin/handlers.go b/httpbin/handlers.go index c0115f2c826b9ab1fba339b9cfa0b1a51495cbd8..322fc5b30eaffedec2825d540c29ab1117877a7c 100644 --- a/httpbin/handlers.go +++ b/httpbin/handlers.go @@ -44,7 +44,7 @@ func (h *HTTPBin) Get(w http.ResponseWriter, r *http.Request) { Args: args, Headers: r.Header, Origin: getOrigin(r), - URL: getURL(r), + URL: getURL(r).String(), } body, _ := json.Marshal(resp) writeJSON(w, body, http.StatusOK) @@ -62,7 +62,7 @@ func (h *HTTPBin) RequestWithBody(w http.ResponseWriter, r *http.Request) { Args: args, Headers: r.Header, Origin: getOrigin(r), - URL: getURL(r), + URL: getURL(r).String(), } err = parseBody(w, r, resp, h.options.MaxMemory) @@ -197,8 +197,30 @@ func (h *HTTPBin) ResponseHeaders(w http.ResponseWriter, r *http.Request) { w.Write(body) } -// RelativeRedirect responds with an HTTP 302 redirect a given number of times -func (h *HTTPBin) RelativeRedirect(w http.ResponseWriter, r *http.Request) { +func redirectLocation(r *http.Request, relative bool, n int) string { + var location string + var path string + + if n < 1 { + path = "/get" + } else if relative { + path = fmt.Sprintf("/relative-redirect/%d", n) + } else { + path = fmt.Sprintf("/absolute-redirect/%d", n) + } + + if relative { + location = path + } else { + u := getURL(r) + u.Path = path + location = u.String() + } + + return location +} + +func doRedirect(w http.ResponseWriter, r *http.Request, relative bool) { parts := strings.Split(r.URL.Path, "/") if len(parts) != 3 { http.Error(w, "Not found", http.StatusNotFound) @@ -209,11 +231,16 @@ func (h *HTTPBin) RelativeRedirect(w http.ResponseWriter, r *http.Request) { http.Error(w, "Invalid redirect", http.StatusBadRequest) } - location := fmt.Sprintf("/relative-redirect/%d", n-1) - if n == 1 { - location = "/get" - } - - w.Header().Set("Location", location) + w.Header().Set("Location", redirectLocation(r, relative, n-1)) w.WriteHeader(http.StatusFound) } + +// 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) +} + +// AbsoluteRedirect responds with an HTTP 302 redirect a given number of times +func (h *HTTPBin) AbsoluteRedirect(w http.ResponseWriter, r *http.Request) { + doRedirect(w, r, false) +} diff --git a/httpbin/handlers_test.go b/httpbin/handlers_test.go index 149d877c743dff1ef8dcb15f2183b687a512c685..bed1d976fb29b9282fbbd5d5ca10d20c555c8fe0 100644 --- a/httpbin/handlers_test.go +++ b/httpbin/handlers_test.go @@ -750,18 +750,30 @@ func TestResponseHeaders__InvalidQuery(t *testing.T) { assertStatusCode(t, w, http.StatusBadRequest) } -func TestRelativeRedirect__OK(t *testing.T) { +func TestRedirects__OK(t *testing.T) { var tests = []struct { + relative bool n int location string }{ - {1, "/get"}, - {2, "/relative-redirect/1"}, - {100, "/relative-redirect/99"}, + {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 { - r, _ := http.NewRequest("GET", fmt.Sprintf("/relative-redirect/%d", test.n), nil) + var urlTemplate string + if test.relative { + urlTemplate = "/relative-redirect/%d" + } else { + urlTemplate = "/absolute-redirect/%d" + } + r, _ := http.NewRequest("GET", fmt.Sprintf(urlTemplate, test.n), nil) + r.Host = "host" w := httptest.NewRecorder() handler.ServeHTTP(w, r) @@ -770,20 +782,33 @@ func TestRelativeRedirect__OK(t *testing.T) { } } -func TestRelativeRedirect__Errors(t *testing.T) { +func TestRedirects__Errors(t *testing.T) { var tests = []struct { - given interface{} - status int + relative bool + given interface{} + status int }{ - {3.14, http.StatusBadRequest}, - {-1, http.StatusBadRequest}, - {"", http.StatusBadRequest}, - {"foo", http.StatusBadRequest}, - {"10/bar", http.StatusNotFound}, + {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 { - r, _ := http.NewRequest("GET", fmt.Sprintf("/relative-redirect/%v", test.given), nil) + var urlTemplate string + if test.relative { + urlTemplate = "/relative-redirect/%v" + } else { + urlTemplate = "/absolute-redirect/%v" + } + r, _ := http.NewRequest("GET", fmt.Sprintf(urlTemplate, test.given), nil) w := httptest.NewRecorder() handler.ServeHTTP(w, r) diff --git a/httpbin/helpers.go b/httpbin/helpers.go index 9ded2e4edd252c8602ef606cef1909438320f017..9ac8f4230d022d4536d5d25bed165c249369cc94 100644 --- a/httpbin/helpers.go +++ b/httpbin/helpers.go @@ -16,7 +16,7 @@ func getOrigin(r *http.Request) string { return origin } -func getURL(r *http.Request) string { +func getURL(r *http.Request) *url.URL { scheme := r.Header.Get("X-Forwarded-Proto") if scheme == "" { scheme = r.Header.Get("X-Forwarded-Protocol") @@ -33,7 +33,7 @@ func getURL(r *http.Request) string { host = r.Host } - u := &url.URL{ + return &url.URL{ Scheme: scheme, Opaque: r.URL.Opaque, User: r.URL.User, @@ -44,7 +44,6 @@ func getURL(r *http.Request) string { RawQuery: r.URL.RawQuery, Fragment: r.URL.Fragment, } - return u.String() } func writeJSON(w http.ResponseWriter, body []byte, status int) { diff --git a/httpbin/httpbin.go b/httpbin/httpbin.go index 531e616d5b2d4780f72f4e1399f1ba0fc33a61a8..2dffdb4bcb346c76a629a70b84db2a8d8032edbf 100644 --- a/httpbin/httpbin.go +++ b/httpbin/httpbin.go @@ -68,6 +68,7 @@ func (h *HTTPBin) Handler() http.Handler { mux.HandleFunc("/response-headers", h.ResponseHeaders) mux.HandleFunc("/relative-redirect/", h.RelativeRedirect) + mux.HandleFunc("/absolute-redirect/", h.AbsoluteRedirect) return logger(cors(mux)) }