From 7e6536fb2494f93ddfaa74bbfb5add8f843cb380 Mon Sep 17 00:00:00 2001 From: Will McCutchen <will@mccutch.org> Date: Thu, 1 Dec 2016 15:21:03 -0800 Subject: [PATCH] Add /gzip handler --- httpbin/handlers.go | 23 ++++++++++++++++++++ httpbin/handlers_test.go | 47 ++++++++++++++++++++++++++++++++++++++++ httpbin/httpbin.go | 8 +++++++ 3 files changed, 78 insertions(+) diff --git a/httpbin/handlers.go b/httpbin/handlers.go index 6963faa..bf2e889 100644 --- a/httpbin/handlers.go +++ b/httpbin/handlers.go @@ -1,6 +1,8 @@ package httpbin import ( + "bytes" + "compress/gzip" "encoding/json" "fmt" "net/http" @@ -67,6 +69,27 @@ func (h *HTTPBin) RequestWithBody(w http.ResponseWriter, r *http.Request) { writeJSON(w, body, http.StatusOK) } +// Gzip returns a gzipped response +func (h *HTTPBin) Gzip(w http.ResponseWriter, r *http.Request) { + resp := &gzipResponse{ + Headers: r.Header, + Origin: getOrigin(r), + Gzipped: true, + } + body, _ := json.Marshal(resp) + + buf := &bytes.Buffer{} + gzw := gzip.NewWriter(buf) + gzw.Write(body) + gzw.Close() + + gzBody := buf.Bytes() + + w.Header().Set("Content-Encoding", "gzip") + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(gzBody))) + writeJSON(w, gzBody, http.StatusOK) +} + // IP echoes the IP address of the incoming request func (h *HTTPBin) IP(w http.ResponseWriter, r *http.Request) { body, _ := json.Marshal(&ipResponse{ diff --git a/httpbin/handlers_test.go b/httpbin/handlers_test.go index fca583b..b48f492 100644 --- a/httpbin/handlers_test.go +++ b/httpbin/handlers_test.go @@ -2,13 +2,16 @@ package httpbin import ( "bytes" + "compress/gzip" "encoding/json" "fmt" + "io/ioutil" "mime/multipart" "net/http" "net/http/httptest" "net/url" "reflect" + "strconv" "strings" "testing" "time" @@ -1075,3 +1078,47 @@ func TestDigestAuth(t *testing.T) { }) } } + +func TestGzip(t *testing.T) { + r, _ := http.NewRequest("GET", "/gzip", nil) + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + + assertContentType(t, w, "application/json; encoding=utf-8") + assertHeader(t, w, "Content-Encoding", "gzip") + assertStatusCode(t, w, http.StatusOK) + + zippedContentLengthStr := w.HeaderMap.Get("Content-Length") + if zippedContentLengthStr == "" { + t.Fatalf("missing Content-Length header in response") + } + + zippedContentLength, err := strconv.Atoi(zippedContentLengthStr) + if err != nil { + t.Fatalf("error converting Content-Lengh %v to integer: %s", zippedContentLengthStr, err) + } + + gzipReader, err := gzip.NewReader(w.Body) + if err != nil { + t.Fatalf("error creating gzip reader: %s", err) + } + + unzippedBody, err := ioutil.ReadAll(gzipReader) + if err != nil { + t.Fatalf("error reading gzipped body: %s", err) + } + + var resp *gzipResponse + err = json.Unmarshal(unzippedBody, &resp) + if err != nil { + t.Fatalf("error unmarshalling response: %s", err) + } + + if resp.Gzipped != true { + t.Fatalf("expected resp.Gzipped == true") + } + + if len(unzippedBody) >= zippedContentLength { + t.Fatalf("expected compressed body") + } +} diff --git a/httpbin/httpbin.go b/httpbin/httpbin.go index 52b819b..d08b57e 100644 --- a/httpbin/httpbin.go +++ b/httpbin/httpbin.go @@ -46,6 +46,12 @@ type authResponse struct { User string `json:"user"` } +type gzipResponse struct { + Headers http.Header `json:"headers"` + Origin string `json:"origin"` + Gzipped bool `json:"gzipped"` +} + // Options are used to configure HTTPBin type Options struct { MaxMemory int64 @@ -88,6 +94,8 @@ func (h *HTTPBin) Handler() http.Handler { mux.HandleFunc("/hidden-basic-auth/", h.HiddenBasicAuth) mux.HandleFunc("/digest-auth/", h.DigestAuth) + mux.HandleFunc("/gzip", h.Gzip) + // Make sure our ServeMux doesn't "helpfully" redirect these invalid // endpoints by adding a trailing slash. See the ServeMux docs for more // info: https://golang.org/pkg/net/http/#ServeMux -- GitLab