compress: preserve status code
This commit is contained in:
parent
ec3e2c08b8
commit
5313922bb7
6 changed files with 189 additions and 56 deletions
97
vendor/github.com/NYTimes/gziphandler/gzip.go
generated
vendored
97
vendor/github.com/NYTimes/gziphandler/gzip.go
generated
vendored
|
@ -81,6 +81,8 @@ type GzipResponseWriter struct {
|
|||
|
||||
minSize int // Specifed the minimum response size to gzip. If the response length is bigger than this value, it is compressed.
|
||||
buf []byte // Holds the first part of the write before reaching the minSize or the end of the write.
|
||||
|
||||
contentTypes []string // Only compress if the response is one of these content-types. All are accepted if empty.
|
||||
}
|
||||
|
||||
// Write appends data to the gzip writer.
|
||||
|
@ -101,8 +103,10 @@ func (w *GzipResponseWriter) Write(b []byte) (int, error) {
|
|||
// On the first write, w.buf changes from nil to a valid slice
|
||||
w.buf = append(w.buf, b...)
|
||||
|
||||
// If the global writes are bigger than the minSize, compression is enable.
|
||||
if len(w.buf) >= w.minSize {
|
||||
// If the global writes are bigger than the minSize and we're about to write
|
||||
// a response containing a content type we want to handle, enable
|
||||
// compression.
|
||||
if len(w.buf) >= w.minSize && handleContentType(w.contentTypes, w) {
|
||||
err := w.startGzip()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
|
@ -231,24 +235,29 @@ func NewGzipLevelHandler(level int) (func(http.Handler) http.Handler, error) {
|
|||
// NewGzipLevelAndMinSize behave as NewGzipLevelHandler except it let the caller
|
||||
// specify the minimum size before compression.
|
||||
func NewGzipLevelAndMinSize(level, minSize int) (func(http.Handler) http.Handler, error) {
|
||||
return NewGzipHandler(level, minSize, &GzipResponseWriter{})
|
||||
return GzipHandlerWithOpts(&GzipResponseWriter{}, CompressionLevel(level), MinSize(minSize))
|
||||
}
|
||||
|
||||
// NewGzipHandler behave as NewGzipLevelHandler except it let the caller
|
||||
// specify the minimum size before compression and a GzipWriter.
|
||||
func NewGzipHandler(level, minSize int, gw GzipWriter) (func(http.Handler) http.Handler, error) {
|
||||
if level != gzip.DefaultCompression && (level < gzip.BestSpeed || level > gzip.BestCompression) {
|
||||
return nil, fmt.Errorf("invalid compression level requested: %d", level)
|
||||
}
|
||||
if minSize < 0 {
|
||||
return nil, errors.New("minimum size must be more than zero")
|
||||
}
|
||||
func GzipHandlerWithOpts(gw GzipWriter, opts ...option) (func(http.Handler) http.Handler, error) {
|
||||
if gw == nil {
|
||||
return nil, errors.New("the GzipWriter must be defined")
|
||||
}
|
||||
|
||||
c := &config{
|
||||
level: gzip.DefaultCompression,
|
||||
minSize: DefaultMinSize,
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(c)
|
||||
}
|
||||
|
||||
if err := c.validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return func(h http.Handler) http.Handler {
|
||||
index := poolIndex(level)
|
||||
index := poolIndex(c.level)
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add(vary, acceptEncoding)
|
||||
|
@ -256,7 +265,8 @@ func NewGzipHandler(level, minSize int, gw GzipWriter) (func(http.Handler) http.
|
|||
if acceptsGzip(r) {
|
||||
gw.SetResponseWriter(w)
|
||||
gw.setIndex(index)
|
||||
gw.setMinSize(minSize)
|
||||
gw.setMinSize(c.minSize)
|
||||
gw.setContentTypes(c.contentTypes)
|
||||
defer gw.Close()
|
||||
|
||||
h.ServeHTTP(gw, r)
|
||||
|
@ -267,6 +277,48 @@ func NewGzipHandler(level, minSize int, gw GzipWriter) (func(http.Handler) http.
|
|||
}, nil
|
||||
}
|
||||
|
||||
// Used for functional configuration.
|
||||
type config struct {
|
||||
minSize int
|
||||
level int
|
||||
contentTypes []string
|
||||
}
|
||||
|
||||
func (c *config) validate() error {
|
||||
if c.level != gzip.DefaultCompression && (c.level < gzip.BestSpeed || c.level > gzip.BestCompression) {
|
||||
return fmt.Errorf("invalid compression level requested: %d", c.level)
|
||||
}
|
||||
|
||||
if c.minSize < 0 {
|
||||
return fmt.Errorf("minimum size must be more than zero")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type option func(c *config)
|
||||
|
||||
func MinSize(size int) option {
|
||||
return func(c *config) {
|
||||
c.minSize = size
|
||||
}
|
||||
}
|
||||
|
||||
func CompressionLevel(level int) option {
|
||||
return func(c *config) {
|
||||
c.level = level
|
||||
}
|
||||
}
|
||||
|
||||
func ContentTypes(types []string) option {
|
||||
return func(c *config) {
|
||||
c.contentTypes = []string{}
|
||||
for _, v := range types {
|
||||
c.contentTypes = append(c.contentTypes, strings.ToLower(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GzipHandler wraps an HTTP handler, to transparently gzip the response body if
|
||||
// the client supports it (via the Accept-Encoding header). This will compress at
|
||||
// the default compression level.
|
||||
|
@ -282,6 +334,23 @@ func acceptsGzip(r *http.Request) bool {
|
|||
return acceptedEncodings["gzip"] > 0.0
|
||||
}
|
||||
|
||||
// returns true if we've been configured to compress the specific content type.
|
||||
func handleContentType(contentTypes []string, w http.ResponseWriter) bool {
|
||||
// If contentTypes is empty we handle all content types.
|
||||
if len(contentTypes) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
ct := strings.ToLower(w.Header().Get(contentType))
|
||||
for _, c := range contentTypes {
|
||||
if c == ct {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// parseEncodings attempts to parse a list of codings, per RFC 2616, as might
|
||||
// appear in an Accept-Encoding header. It returns a map of content-codings to
|
||||
// quality values, and an error containing the errors encountered. It's probably
|
||||
|
|
8
vendor/github.com/NYTimes/gziphandler/wrapper.go
generated
vendored
8
vendor/github.com/NYTimes/gziphandler/wrapper.go
generated
vendored
|
@ -23,6 +23,7 @@ type GzipWriter interface {
|
|||
SetResponseWriter(http.ResponseWriter)
|
||||
setIndex(int)
|
||||
setMinSize(int)
|
||||
setContentTypes([]string)
|
||||
}
|
||||
|
||||
func (w *GzipResponseWriter) SetResponseWriter(rw http.ResponseWriter) {
|
||||
|
@ -37,6 +38,10 @@ func (w *GzipResponseWriter) setMinSize(minSize int) {
|
|||
w.minSize = minSize
|
||||
}
|
||||
|
||||
func (w *GzipResponseWriter) setContentTypes(contentTypes []string) {
|
||||
w.contentTypes = contentTypes
|
||||
}
|
||||
|
||||
// --------
|
||||
|
||||
type GzipResponseWriterWrapper struct {
|
||||
|
@ -45,6 +50,9 @@ type GzipResponseWriterWrapper struct {
|
|||
|
||||
func (g *GzipResponseWriterWrapper) Write(b []byte) (int, error) {
|
||||
if g.gw == nil && isEncoded(g.Header()) {
|
||||
if g.code != 0 {
|
||||
g.ResponseWriter.WriteHeader(g.code)
|
||||
}
|
||||
return g.ResponseWriter.Write(b)
|
||||
}
|
||||
return g.GzipResponseWriter.Write(b)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue