diff --git a/handlers.go b/handlers.go new file mode 100644 index 0000000000000000000000000000000000000000..f0ab2aec48b7770a187b5063ab4ec73b90c1c17f --- /dev/null +++ b/handlers.go @@ -0,0 +1,89 @@ +package main + +import ( + "encoding/json" + "fmt" + "html/template" + "net/http" + "net/url" +) + +// Index must be wrapped by the withTemplates middleware before it can be used +func index(w http.ResponseWriter, r *http.Request, t *template.Template) { + t = t.Lookup("index.html") + if t == nil { + http.Error(w, fmt.Sprintf("error looking up index.html"), http.StatusInternalServerError) + return + } + t.Execute(w, nil) +} + +// FormsPost must be wrapped by withTemplates middleware before it can be used +func formsPost(w http.ResponseWriter, r *http.Request, t *template.Template) { + t = t.Lookup("forms-post.html") + if t == nil { + http.Error(w, fmt.Sprintf("error looking up index.html"), http.StatusInternalServerError) + return + } + t.Execute(w, nil) +} + +func get(w http.ResponseWriter, r *http.Request) { + args, err := url.ParseQuery(r.URL.RawQuery) + if err != nil { + http.Error(w, fmt.Sprintf("error parsing query params: %s", err), http.StatusBadRequest) + return + } + resp := &getResponse{ + Args: args, + Headers: r.Header, + Origin: getOrigin(r), + URL: getURL(r), + } + body, _ := json.Marshal(resp) + writeJSON(w, body, http.StatusOK) +} + +func post(w http.ResponseWriter, r *http.Request) { + args, err := url.ParseQuery(r.URL.RawQuery) + if err != nil { + http.Error(w, fmt.Sprintf("error parsing query params: %s", err), http.StatusBadRequest) + return + } + + resp := &bodyResponse{ + Args: args, + Headers: r.Header, + Origin: getOrigin(r), + URL: getURL(r), + } + + err = parseBody(w, r, resp) + if err != nil { + http.Error(w, fmt.Sprintf("error parsing request body: %s", err), http.StatusBadRequest) + } + + body, _ := json.Marshal(resp) + writeJSON(w, body, http.StatusOK) +} + +func ip(w http.ResponseWriter, r *http.Request) { + body, _ := json.Marshal(&ipResponse{ + Origin: getOrigin(r), + }) + writeJSON(w, body, http.StatusOK) +} + +func userAgent(w http.ResponseWriter, r *http.Request) { + body, _ := json.Marshal(&userAgentResponse{ + UserAgent: r.Header.Get("User-Agent"), + }) + writeJSON(w, body, http.StatusOK) +} + +func headers(w http.ResponseWriter, r *http.Request) { + body, _ := json.Marshal(&headersResponse{ + Headers: r.Header, + }) + writeJSON(w, body, http.StatusOK) +} diff --git a/main_test.go b/handlers_test.go similarity index 97% rename from main_test.go rename to handlers_test.go index 1c8563d5a9338a2903c2553ee261775d8d027880..e923c880d897c3b6541e35e4b92e190d0db97a3f 100644 --- a/main_test.go +++ b/handlers_test.go @@ -42,7 +42,7 @@ func TestGet__Basic(t *testing.T) { t.Fatalf("expected status code 200, got %d", w.Code) } - var resp *Resp + var resp *bodyResponse err := json.Unmarshal(w.Body.Bytes(), &resp) if err != nil { t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err) @@ -160,7 +160,7 @@ func TestGet__XForwardedProto(t *testing.T) { w := httptest.NewRecorder() app().ServeHTTP(w, r) - var resp *Resp + var resp *bodyResponse err := json.Unmarshal(w.Body.Bytes(), &resp) if err != nil { t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err) @@ -178,7 +178,7 @@ func TestIP(t *testing.T) { w := httptest.NewRecorder() app().ServeHTTP(w, r) - var resp *IPResp + var resp *ipResponse err := json.Unmarshal(w.Body.Bytes(), &resp) if err != nil { t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err) @@ -195,7 +195,7 @@ func TestUserAgent(t *testing.T) { w := httptest.NewRecorder() app().ServeHTTP(w, r) - var resp *UserAgentResp + var resp *userAgentResponse err := json.Unmarshal(w.Body.Bytes(), &resp) if err != nil { t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err) @@ -215,7 +215,7 @@ func TestHeaders(t *testing.T) { w := httptest.NewRecorder() app().ServeHTTP(w, r) - var resp *HeadersResp + var resp *headersResponse err := json.Unmarshal(w.Body.Bytes(), &resp) if err != nil { t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err) @@ -237,7 +237,7 @@ func TestPost__EmptyBody(t *testing.T) { w := httptest.NewRecorder() app().ServeHTTP(w, r) - var resp *Resp + var resp *bodyResponse err := json.Unmarshal(w.Body.Bytes(), &resp) if err != nil { t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err) @@ -262,7 +262,7 @@ func TestPost__FormEncodedBody(t *testing.T) { w := httptest.NewRecorder() app().ServeHTTP(w, r) - var resp *Resp + var resp *bodyResponse err := json.Unmarshal(w.Body.Bytes(), &resp) if err != nil { t.Fatalf("failed to unmarshal body %#v from JSON: %s", w.Body.String(), err) @@ -295,7 +295,7 @@ func TestPost__FormEncodedBodyNoContentType(t *testing.T) { w := httptest.NewRecorder() app().ServeHTTP(w, r) - var resp *Resp + var resp *bodyResponse err := json.Unmarshal(w.Body.Bytes(), &resp) if err != nil { t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err) @@ -332,7 +332,7 @@ func TestPost__JSON(t *testing.T) { w := httptest.NewRecorder() app().ServeHTTP(w, r) - var resp *Resp + var resp *bodyResponse err := json.Unmarshal(w.Body.Bytes(), &resp) if err != nil { t.Fatalf("failed to unmarshal body %s from JSON: %s", w.Body, err) diff --git a/helpers.go b/helpers.go index e7c4a155d5ae7a357497f0df1f586a403e1082d4..b8daae9c46be8422dbb3f21fb4d1da86841927b5 100644 --- a/helpers.go +++ b/helpers.go @@ -47,14 +47,8 @@ func getURL(r *http.Request) string { return u.String() } -func writeResponse(w http.ResponseWriter, r *http.Request, resp *Resp) { - resp.Origin = getOrigin(r) - resp.URL = getURL(r) - body, _ := json.Marshal(resp) - writeJSON(w, body) -} - -func writeJSON(w http.ResponseWriter, body []byte) { +func writeJSON(w http.ResponseWriter, body []byte, status int) { + w.WriteHeader(status) w.Header().Set("Content-Type", "application/json; encoding=utf-8") w.Write(body) } @@ -62,7 +56,7 @@ func writeJSON(w http.ResponseWriter, body []byte) { // parseBody handles parsing a request body into our standard API response, // taking care to only consume the request body once based on the Content-Type // of the request. The given Resp will be updated. -func parseBody(w http.ResponseWriter, r *http.Request, resp *Resp) error { +func parseBody(w http.ResponseWriter, r *http.Request, resp *bodyResponse) error { if r.Body == nil { return nil } diff --git a/main.go b/main.go index a99de7ccf9f4dc7f9146f0efafd2b31bbbdd862f..891dfc6bf5157189319b5ebb99feb804b91f98f0 100644 --- a/main.go +++ b/main.go @@ -1,118 +1,13 @@ package main import ( - "encoding/json" - "fmt" - "html/template" "log" "net/http" - "net/url" ) -// Resp is the standard JSON response from httpbin -type Resp struct { - Args url.Values `json:"args"` - Headers http.Header `json:"headers"` - Origin string `json:"origin"` - URL string `json:"url"` - - Data []byte `json:"data,omitempty"` - Files map[string][]string `json:"files,omitempty"` - Form map[string][]string `json:"form,omitempty"` - JSON interface{} `json:"json,omitempty"` -} - -// IPResp is the response for the /ip endpoint -type IPResp struct { - Origin string `json:"origin"` -} - -// HeadersResp is the response for the /headers endpoint -type HeadersResp struct { - Headers http.Header `json:"headers"` -} - -// UserAgentResp is the response for the /user-agent endpoint -type UserAgentResp struct { - UserAgent string `json:"user-agent"` -} - // Max size of a request body we'll handle const maxMemory = 1024*1024*5 + 1 -// Index must be wrapped by the withTemplates middleware before it can be used -func index(w http.ResponseWriter, r *http.Request, t *template.Template) { - t = t.Lookup("index.html") - if t == nil { - http.Error(w, fmt.Sprintf("error looking up index.html"), http.StatusInternalServerError) - return - } - t.Execute(w, nil) -} - -// FormsPost must be wrapped by withTemplates middleware before it can be used -func formsPost(w http.ResponseWriter, r *http.Request, t *template.Template) { - t = t.Lookup("forms-post.html") - if t == nil { - http.Error(w, fmt.Sprintf("error looking up index.html"), http.StatusInternalServerError) - return - } - t.Execute(w, nil) -} - -func get(w http.ResponseWriter, r *http.Request) { - args, err := url.ParseQuery(r.URL.RawQuery) - if err != nil { - http.Error(w, fmt.Sprintf("error parsing query params: %s", err), http.StatusBadRequest) - return - } - resp := &Resp{ - Args: args, - Headers: r.Header, - } - writeResponse(w, r, resp) -} - -func post(w http.ResponseWriter, r *http.Request) { - args, err := url.ParseQuery(r.URL.RawQuery) - if err != nil { - http.Error(w, fmt.Sprintf("error parsing query params: %s", err), http.StatusBadRequest) - return - } - - resp := &Resp{ - Args: args, - Headers: r.Header, - } - - err = parseBody(w, r, resp) - if err != nil { - http.Error(w, fmt.Sprintf("error parsing request body: %s", err), http.StatusBadRequest) - } - writeResponse(w, r, resp) -} - -func ip(w http.ResponseWriter, r *http.Request) { - body, _ := json.Marshal(&IPResp{ - Origin: getOrigin(r), - }) - writeJSON(w, body) -} - -func userAgent(w http.ResponseWriter, r *http.Request) { - body, _ := json.Marshal(&UserAgentResp{ - UserAgent: r.Header.Get("User-Agent"), - }) - writeJSON(w, body) -} - -func headers(w http.ResponseWriter, r *http.Request) { - body, _ := json.Marshal(&HeadersResp{ - Headers: r.Header, - }) - writeJSON(w, body) -} - func app() http.Handler { h := http.NewServeMux() templateWrapper := withTemplates("templates/*.html") diff --git a/types.go b/types.go new file mode 100644 index 0000000000000000000000000000000000000000..75a314f132a21032391b657f9de9354eb1b6b0d2 --- /dev/null +++ b/types.go @@ -0,0 +1,38 @@ +package main + +import ( + "net/http" + "net/url" +) + +type headersResponse struct { + Headers http.Header `json:"headers"` +} + +type ipResponse struct { + Origin string `json:"origin"` +} + +type userAgentResponse struct { + UserAgent string `json:"user-agent"` +} + +type getResponse struct { + Args url.Values `json:"args"` + Headers http.Header `json:"headers"` + Origin string `json:"origin"` + URL string `json:"url"` +} + +// A generic response for any incoming request that might contain a body +type bodyResponse struct { + Args url.Values `json:"args"` + Headers http.Header `json:"headers"` + Origin string `json:"origin"` + URL string `json:"url"` + + Data []byte `json:"data"` + Files map[string][]string `json:"files"` + Form map[string][]string `json:"form"` + JSON interface{} `json:"json"` +}