From aabe5a1f7db073b37ab74c60088ffea8ba5d2dad Mon Sep 17 00:00:00 2001 From: Will McCutchen <will@mccutch.org> Date: Fri, 2 Dec 2016 08:43:46 -0800 Subject: [PATCH] Add /deflate handler --- httpbin/handlers.go | 22 +++++++++++++++++++++ httpbin/handlers_test.go | 41 ++++++++++++++++++++++++++++++++++++++++ httpbin/httpbin.go | 7 +++++++ 3 files changed, 70 insertions(+) diff --git a/httpbin/handlers.go b/httpbin/handlers.go index bf2e889..f2b6629 100644 --- a/httpbin/handlers.go +++ b/httpbin/handlers.go @@ -2,6 +2,7 @@ package httpbin import ( "bytes" + "compress/flate" "compress/gzip" "encoding/json" "fmt" @@ -90,6 +91,27 @@ func (h *HTTPBin) Gzip(w http.ResponseWriter, r *http.Request) { writeJSON(w, gzBody, http.StatusOK) } +// Deflate returns a gzipped response +func (h *HTTPBin) Deflate(w http.ResponseWriter, r *http.Request) { + resp := &deflateResponse{ + Headers: r.Header, + Origin: getOrigin(r), + Deflated: true, + } + body, _ := json.Marshal(resp) + + buf := &bytes.Buffer{} + w2, _ := flate.NewWriter(buf, flate.DefaultCompression) + w2.Write(body) + w2.Close() + + compressedBody := buf.Bytes() + + w.Header().Set("Content-Encoding", "deflate") + w.Header().Set("Content-Length", fmt.Sprintf("%d", len(compressedBody))) + writeJSON(w, compressedBody, 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 b48f492..f3cbef8 100644 --- a/httpbin/handlers_test.go +++ b/httpbin/handlers_test.go @@ -2,6 +2,7 @@ package httpbin import ( "bytes" + "compress/flate" "compress/gzip" "encoding/json" "fmt" @@ -1122,3 +1123,43 @@ func TestGzip(t *testing.T) { t.Fatalf("expected compressed body") } } + +func TestDeflate(t *testing.T) { + r, _ := http.NewRequest("GET", "/deflate", nil) + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + + assertContentType(t, w, "application/json; encoding=utf-8") + assertHeader(t, w, "Content-Encoding", "deflate") + assertStatusCode(t, w, http.StatusOK) + + contentLengthHeader := w.HeaderMap.Get("Content-Length") + if contentLengthHeader == "" { + t.Fatalf("missing Content-Length header in response") + } + + contentLength, err := strconv.Atoi(contentLengthHeader) + if err != nil { + t.Fatal(err) + } + + reader := flate.NewReader(w.Body) + body, err := ioutil.ReadAll(reader) + if err != nil { + t.Fatal(err) + } + + var resp *deflateResponse + err = json.Unmarshal(body, &resp) + if err != nil { + t.Fatalf("error unmarshalling response: %s", err) + } + + if resp.Deflated != true { + t.Fatalf("expected resp.Deflated == true") + } + + if len(body) >= contentLength { + t.Fatalf("expected compressed body") + } +} diff --git a/httpbin/httpbin.go b/httpbin/httpbin.go index d08b57e..41ff3da 100644 --- a/httpbin/httpbin.go +++ b/httpbin/httpbin.go @@ -52,6 +52,12 @@ type gzipResponse struct { Gzipped bool `json:"gzipped"` } +type deflateResponse struct { + Headers http.Header `json:"headers"` + Origin string `json:"origin"` + Deflated bool `json:"deflated"` +} + // Options are used to configure HTTPBin type Options struct { MaxMemory int64 @@ -94,6 +100,7 @@ func (h *HTTPBin) Handler() http.Handler { mux.HandleFunc("/hidden-basic-auth/", h.HiddenBasicAuth) mux.HandleFunc("/digest-auth/", h.DigestAuth) + mux.HandleFunc("/deflate", h.Deflate) mux.HandleFunc("/gzip", h.Gzip) // Make sure our ServeMux doesn't "helpfully" redirect these invalid -- GitLab