keel/net/http/middleware/gzip.go
2024-09-23 08:45:44 +02:00

103 lines
2.2 KiB
Go

package middleware
import (
"io"
"net/http"
"sync"
stdhttp "github.com/foomo/gostandards/http"
httputils "github.com/foomo/keel/utils/net/http"
"github.com/klauspost/compress/gzhttp"
"github.com/klauspost/compress/gzip"
"github.com/pkg/errors"
"go.uber.org/zap"
)
type (
GZipOptions struct {
CompressionLevel int
MinSize int
}
GZipOption func(*GZipOptions)
)
var DefaultGZipOptions = GZipOptions{
CompressionLevel: gzip.DefaultCompression,
MinSize: 1024,
}
// GZipWithLevel allows setting a specific compression level for gzip (default: gzip.DefaultCompression).
func GZipWithLevel(v int) GZipOption {
return func(o *GZipOptions) {
o.CompressionLevel = v
}
}
// GZipWithMinSize allows setting a minimum response body length to apply gzip compression (default: 1400 bytes).
func GZipWithMinSize(v int) GZipOption {
return func(o *GZipOptions) {
o.MinSize = v
}
}
// GZip middleware
func GZip(opts ...GZipOption) Middleware {
options := DefaultGZipOptions
for _, opt := range opts {
if opt != nil {
opt(&options)
}
}
return GZipWithOptions(options)
}
// GZipWithOptions middleware
func GZipWithOptions(opts GZipOptions) Middleware {
return func(l *zap.Logger, name string, next http.Handler) http.Handler {
pool := sync.Pool{
New: func() interface{} {
return new(gzip.Reader)
},
}
wrapper, err := gzhttp.NewWrapper(
gzhttp.CompressionLevel(opts.CompressionLevel),
gzhttp.MinSize(opts.MinSize),
)
if err != nil {
panic(err)
}
return wrapper(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get(stdhttp.HeaderContentEncoding.String()) != stdhttp.EncodingGzip.String() {
next.ServeHTTP(w, r)
return
}
gr, ok := pool.Get().(*gzip.Reader)
if !ok {
httputils.InternalServerError(l, w, r, errors.New("failed to retrieve gzip pool"))
return
}
defer pool.Put(gr)
b := r.Body
defer b.Close()
if err := gr.Reset(b); errors.Is(err, io.EOF) {
next.ServeHTTP(w, r)
return
} else if err != nil {
httputils.BadRequestServerError(l, w, r, errors.New("failed to reset gzip"))
return
}
defer gr.Close()
r.Header.Del(stdhttp.HeaderContentEncoding.String())
r.Body = gr
next.ServeHTTP(w, r)
}))
}
}