diff --git a/middlewares/compress.go b/middlewares/compress.go index d4a07e37c..5df1dd9c9 100644 --- a/middlewares/compress.go +++ b/middlewares/compress.go @@ -6,12 +6,26 @@ import ( "github.com/NYTimes/gziphandler" ) -// Compress is a middleware that allows redirections -type Compress struct { +const ( + contentEncodingHeader = "Content-Encoding" +) + +// Compress is a middleware that allows redirection +type Compress struct{} + +// ServerHTTP is a function used by Negroni +func (c *Compress) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + if isEncoded(r.Header) { + next.ServeHTTP(rw, r) + } else { + newGzipHandler := gziphandler.GzipHandler(next) + newGzipHandler.ServeHTTP(rw, r) + } } -// ServerHTTP is a function used by negroni -func (c *Compress) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - newGzipHandler := gziphandler.GzipHandler(next) - newGzipHandler.ServeHTTP(rw, r) +func isEncoded(headers http.Header) bool { + header := headers.Get(contentEncodingHeader) + // According to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding, + // content is not encoded if the header 'Content-Encoding' is empty or equals to 'identity'. + return header != "" && header != "identity" } diff --git a/middlewares/compress_test.go b/middlewares/compress_test.go new file mode 100644 index 000000000..c83bd3b1d --- /dev/null +++ b/middlewares/compress_test.go @@ -0,0 +1,69 @@ +package middlewares + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/NYTimes/gziphandler" + "github.com/containous/traefik/testhelpers" + "github.com/stretchr/testify/assert" +) + +const ( + acceptEncodingHeader = "Accept-Encoding" + varyHeader = "Vary" + gzip = "gzip" +) + +func TestShouldCompressWhenNoContentEncodingHeader(t *testing.T) { + handler := &Compress{} + + req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil) + req.Header.Add(acceptEncodingHeader, gzip) + + baseBody := generateBytes(gziphandler.DefaultMinSize) + next := func(rw http.ResponseWriter, r *http.Request) { + rw.Write(baseBody) + } + rw := httptest.NewRecorder() + + handler.ServeHTTP(rw, req, next) + + assert.Equal(t, gzip, rw.Header().Get(contentEncodingHeader)) + assert.Equal(t, acceptEncodingHeader, rw.Header().Get(varyHeader)) + + if assert.ObjectsAreEqualValues(rw.Body.Bytes(), baseBody) { + assert.Fail(t, "expected a compressed body", "got %v", rw.Body.Bytes()) + } +} + +func TestShouldNotCompressWhenContentEncodingHeader(t *testing.T) { + handler := &Compress{} + + req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil) + req.Header.Add(acceptEncodingHeader, gzip) + req.Header.Add(contentEncodingHeader, gzip) + + baseBody := generateBytes(gziphandler.DefaultMinSize) + + next := func(rw http.ResponseWriter, r *http.Request) { + rw.Write(baseBody) + } + + rw := httptest.NewRecorder() + handler.ServeHTTP(rw, req, next) + + assert.Equal(t, "", rw.Header().Get(contentEncodingHeader)) + assert.Equal(t, "", rw.Header().Get(varyHeader)) + + assert.EqualValues(t, rw.Body.Bytes(), baseBody) +} + +func generateBytes(len int) []byte { + var value []byte + for i := 0; i < len; i++ { + value = append(value, 0x61) + } + return value +}