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

Add /status/:code handler

parent a4426b4d
Branches
Tags
No related merge requests found
......@@ -5,8 +5,18 @@ import (
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
)
var acceptedMediaTypes = []string{
"image/webp",
"image/svg+xml",
"image/jpeg",
"image/png",
"image/",
}
// Index renders an HTML index page
func (h *HTTPBin) Index(w http.ResponseWriter, r *http.Request) {
w.Write(MustAsset("index.html"))
......@@ -87,3 +97,82 @@ func (h *HTTPBin) Headers(w http.ResponseWriter, r *http.Request) {
})
writeJSON(w, body, http.StatusOK)
}
// Status responds with the specified status code. TODO: support random choice
// from multiple, optionally weighted status codes.
func (h *HTTPBin) Status(w http.ResponseWriter, r *http.Request) {
parts := strings.Split(r.URL.Path, "/")
if len(parts) != 3 {
http.Error(w, "Not found", http.StatusNotFound)
return
}
code, err := strconv.Atoi(parts[2])
if err != nil {
http.Error(w, "Invalid status", http.StatusBadRequest)
}
type statusCase struct {
headers map[string]string
body []byte
}
redirectHeaders := &statusCase{
headers: map[string]string{
"Location": "/redirect/1",
},
}
notAcceptableBody, _ := json.Marshal(map[string]interface{}{
"message": "Client did not request a supported media type",
"accept": acceptedMediaTypes,
})
specialCases := map[int]*statusCase{
301: redirectHeaders,
302: redirectHeaders,
303: redirectHeaders,
305: redirectHeaders,
307: redirectHeaders,
401: &statusCase{
headers: map[string]string{
"WWW-Authenticate": `Basic realm="Fake Realm"`,
},
},
402: &statusCase{
body: []byte("Fuck you, pay me!"),
headers: map[string]string{
"X-More-Info": "http://vimeo.com/22053820",
},
},
406: &statusCase{
body: notAcceptableBody,
headers: map[string]string{
"Content-Type": "application/json; encoding=utf-8",
},
},
407: &statusCase{
headers: map[string]string{
"Proxy-Authenticate": `Basic realm="Fake Realm"`,
},
},
418: &statusCase{
body: []byte("I'm a teapot!"),
headers: map[string]string{
"X-More-Info": "http://tools.ietf.org/html/rfc2324",
},
},
}
if specialCase, ok := specialCases[code]; ok {
if specialCase.headers != nil {
for key, val := range specialCase.headers {
w.Header().Set(key, val)
}
}
w.WriteHeader(code)
if specialCase.body != nil {
w.Write(specialCase.body)
}
} else {
w.WriteHeader(code)
}
}
......@@ -3,6 +3,7 @@ package httpbin
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
......@@ -25,10 +26,14 @@ func assertStatusCode(t *testing.T, w *httptest.ResponseRecorder, code int) {
}
}
func assertContentType(t *testing.T, w *httptest.ResponseRecorder, contentType string) {
if w.Header().Get("Content-Type") != contentType {
t.Fatalf("expected content type %s, got %s", contentType, w.Header().Get("Content-Type"))
func assertHeader(t *testing.T, w *httptest.ResponseRecorder, key, val string) {
if w.Header().Get(key) != val {
t.Fatalf("expected header %s=%#v, got %#v", key, val, w.Header().Get(key))
}
}
func assertContentType(t *testing.T, w *httptest.ResponseRecorder, contentType string) {
assertHeader(t, w, "Content-Type", contentType)
}
func assertBodyContains(t *testing.T, w *httptest.ResponseRecorder, needle string) {
......@@ -118,9 +123,7 @@ func TestGet__CORSHeadersWithoutRequestOrigin(t *testing.T) {
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
if w.Header().Get("Access-Control-Allow-Origin") != "*" {
t.Fatalf("expected Access-Control-Allow-Origin=*, got %#v", w.Header().Get("Access-Control-Allow-Origin"))
}
assertHeader(t, w, "Access-Control-Allow-Origin", "*")
}
func TestGet__CORSHeadersWithRequestOrigin(t *testing.T) {
......@@ -129,9 +132,7 @@ func TestGet__CORSHeadersWithRequestOrigin(t *testing.T) {
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
if w.Header().Get("Access-Control-Allow-Origin") != "origin" {
t.Fatalf("expected Access-Control-Allow-Origin=origin, got %#v", w.Header().Get("Access-Control-Allow-Origin"))
}
assertHeader(t, w, "Access-Control-Allow-Origin", "origin")
}
func TestGet__CORSHeadersWithOptionsVerb(t *testing.T) {
......@@ -150,9 +151,7 @@ func TestGet__CORSHeadersWithOptionsVerb(t *testing.T) {
{"Access-Control-Allow-Headers", ""},
}
for _, test := range headerTests {
if w.Header().Get(test.key) != test.expected {
t.Fatalf("expected %s = %#v, got %#v", test.key, test.expected, w.Header().Get(test.key))
}
assertHeader(t, w, test.key, test.expected)
}
}
......@@ -169,9 +168,7 @@ func TestGet__CORSAllowHeaders(t *testing.T) {
{"Access-Control-Allow-Headers", "X-Test-Header"},
}
for _, test := range headerTests {
if w.Header().Get(test.key) != test.expected {
t.Fatalf("expected %s = %#v, got %#v", test.key, test.expected, w.Header().Get(test.key))
}
assertHeader(t, w, test.key, test.expected)
}
}
......@@ -424,3 +421,43 @@ func TestPost__BodyTooBig(t *testing.T) {
assertStatusCode(t, w, http.StatusBadRequest)
assertContentType(t, w, "application/json; encoding=utf-8")
}
func TestStatus__Simple(t *testing.T) {
redirectHeaders := map[string]string{
"Location": "/redirect/1",
}
unauthorizedHeaders := map[string]string{
"WWW-Authenticate": `Basic realm="Fake Realm"`,
}
var tests = []struct {
code int
headers map[string]string
body string
}{
{200, nil, ""},
{301, redirectHeaders, ""},
{302, redirectHeaders, ""},
{401, unauthorizedHeaders, ""},
{418, nil, "I'm a teapot!"},
}
for _, test := range tests {
r, _ := http.NewRequest("GET", fmt.Sprintf("/status/%d", test.code), nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
assertStatusCode(t, w, test.code)
if test.headers != nil {
for key, val := range test.headers {
assertHeader(t, w, key, val)
}
}
if test.body != "" {
if w.Body.String() != test.body {
t.Fatalf("expected body %#v, got %#v", test.body, w.Body.String())
}
}
}
}
......@@ -64,6 +64,7 @@ func (h *HTTPBin) Handler() http.Handler {
mux.HandleFunc("/ip", h.IP)
mux.HandleFunc("/user-agent", h.UserAgent)
mux.HandleFunc("/headers", h.Headers)
mux.HandleFunc("/status/", h.Status)
return logger(cors(mux))
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment