Skip to content

Commit

Permalink
Provide control over whether response is gzipped
Browse files Browse the repository at this point in the history
This replaces the ForceGzip Option with a ShouldGzip Option that takes a
function that can control when the response is gzipped.
  • Loading branch information
tmthrgd committed Apr 14, 2018
1 parent 60bd5db commit 7d70cbd
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 21 deletions.
47 changes: 36 additions & 11 deletions gzip.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,14 @@ func (h *handler) pool() *sync.Pool {
}

func (h *handler) shouldGzip(r *http.Request) bool {
if h.forceGzip {
return true
if h.config.shouldGzip != nil {
switch h.config.shouldGzip(r) {
case NegotiateGzip:
case SkipGzip:
return false
case ForceGzip:
return true
}
}

match := httputils.Negotiate(r.Header, "Accept-Encoding", "gzip")
Expand Down Expand Up @@ -419,7 +425,7 @@ type config struct {
level int
minSize int
contentTypes []string
forceGzip bool
shouldGzip func(*http.Request) ShouldGzipType
}

// Option customizes the behaviour of the gzip handler.
Expand Down Expand Up @@ -482,21 +488,40 @@ func ContentTypes(types []string) Option {
}
}

// ForceGzip makes the handler always return a gzipped
// response and not consult the request's Accept-Encoding
// ShouldGzip provides control over when the handler should
// return a gzipped response. It allows handlers to implement
// logic that doesn't consult the request's Accept-Encoding
// header.
//
// By default, responses are only gzipped if the request's
// Accept-Encoding header indicates gzip support.
//
// Note: ForceGzip does not affect MinSize or ContentTypes,
// it simply ignores the Accept-Encoding header.
var ForceGzip Option = forceGzip

func forceGzip(c *config) {
c.forceGzip = true
// Note: ShouldGzip does not affect MinSize or ContentTypes,
// it simply provides control over negotiating gzip support.
func ShouldGzip(fn func(*http.Request) ShouldGzipType) Option {
return func(c *config) {
c.shouldGzip = fn
}
}

// ShouldGzipType controls how the handler determines gzip
// support.
type ShouldGzipType int

const (
// NegotiateGzip defers gzip negotiation to the
// request's Accept-Encoding header.
NegotiateGzip ShouldGzipType = iota

// SkipGzip skips gzipping the response.
SkipGzip

// ForceGzip ignores the request's Accept-Encoding
// header and always gzips the response.
// (See ShouldGzip note).
ForceGzip
)

type (
// Each of these structs is intentionally small (1 pointer wide) so
// as to fit inside an interface{} without causing an allocaction.
Expand Down
45 changes: 35 additions & 10 deletions gzip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -681,18 +681,43 @@ func TestWrapper(t *testing.T) {
assert.Equal(t, Gzip(handler, MinSize(42)), Wrapper(MinSize(42))(handler))
}

func TestForceGzip(t *testing.T) {
handler := newTestHandler(testBody, ForceGzip)
func TestShouldGzip(t *testing.T) {
for _, tc := range []struct {
shouldGzip ShouldGzipType
advertise bool
expect bool
}{
{NegotiateGzip, false, false},
{NegotiateGzip, true, true},
{SkipGzip, false, false},
{SkipGzip, true, false},
{ForceGzip, false, true},
{ForceGzip, true, true},
} {
handler := newTestHandler(testBody, ShouldGzip(func(*http.Request) ShouldGzipType {
return tc.shouldGzip
}))

req := httptest.NewRequest(http.MethodGet, "/whatever", nil)
resp := httptest.NewRecorder()
handler.ServeHTTP(resp, req)
req := httptest.NewRequest(http.MethodGet, "/whatever", nil)
if tc.advertise {
req.Header.Set("Accept-Encoding", "gzip")
}

res := resp.Result()
assert.Equal(t, http.StatusOK, res.StatusCode)
assert.Equal(t, "gzip", res.Header.Get("Content-Encoding"))
assert.Equal(t, "Accept-Encoding", res.Header.Get("Vary"))
assert.Equal(t, gzipStrLevel(testBody, DefaultCompression), resp.Body.Bytes())
resp := httptest.NewRecorder()
handler.ServeHTTP(resp, req)

res := resp.Result()
assert.Equal(t, http.StatusOK, res.StatusCode, "%+v", tc)
assert.Equal(t, "Accept-Encoding", res.Header.Get("Vary"), "%+v", tc)

if tc.expect {
assert.Equal(t, "gzip", res.Header.Get("Content-Encoding"), "%+v", tc)
assert.Equal(t, gzipStrLevel(testBody, DefaultCompression), resp.Body.Bytes(), "%+v", tc)
} else {
assert.Equal(t, "", res.Header.Get("Content-Encoding"), "%+v", tc)
assert.Equal(t, testBody, resp.Body.String(), "%+v", tc)
}
}
}

// --------------------------------------------------------------------
Expand Down

0 comments on commit 7d70cbd

Please sign in to comment.