1
0
mirror of https://github.com/labstack/echo.git synced 2024-12-24 20:14:31 +02:00

Adding sync.Pool to Decompress middleware

Fixing a http.Request.Body leak on the decompress middleware that were
not properly Close
Removing the defer on the call to gzip.Reader, because that reader is
already exausted after the call to io.Copy
This commit is contained in:
Pablo Andres Fuente 2020-11-27 03:01:04 +00:00
parent 502cce28d5
commit 14e020bc07
2 changed files with 46 additions and 7 deletions

View File

@ -59,7 +59,7 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
config.Level = DefaultGzipConfig.Level config.Level = DefaultGzipConfig.Level
} }
pool := gzipPool(config) pool := gzipCompressPool(config)
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error { return func(c echo.Context) error {
@ -133,7 +133,7 @@ func (w *gzipResponseWriter) Push(target string, opts *http.PushOptions) error {
return http.ErrNotSupported return http.ErrNotSupported
} }
func gzipPool(config GzipConfig) sync.Pool { func gzipCompressPool(config GzipConfig) sync.Pool {
return sync.Pool{ return sync.Pool{
New: func() interface{} { New: func() interface{} {
w, err := gzip.NewWriterLevel(ioutil.Discard, config.Level) w, err := gzip.NewWriterLevel(ioutil.Discard, config.Level)

View File

@ -3,9 +3,12 @@ package middleware
import ( import (
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"github.com/labstack/echo/v4"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http"
"sync"
"github.com/labstack/echo/v4"
) )
type ( type (
@ -32,27 +35,63 @@ func Decompress() echo.MiddlewareFunc {
//DecompressWithConfig decompresses request body based if content encoding type is set to "gzip" with config //DecompressWithConfig decompresses request body based if content encoding type is set to "gzip" with config
func DecompressWithConfig(config DecompressConfig) echo.MiddlewareFunc { func DecompressWithConfig(config DecompressConfig) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(next echo.HandlerFunc) echo.HandlerFunc {
pool := gzipDecompressPool()
return func(c echo.Context) error { return func(c echo.Context) error {
if config.Skipper(c) { if config.Skipper(c) {
return next(c) return next(c)
} }
switch c.Request().Header.Get(echo.HeaderContentEncoding) { switch c.Request().Header.Get(echo.HeaderContentEncoding) {
case GZIPEncoding: case GZIPEncoding:
gr, err := gzip.NewReader(c.Request().Body) b := c.Request().Body
if err != nil {
i := pool.Get()
gr, ok := i.(*gzip.Reader)
if !ok {
return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error())
}
if err := gr.Reset(b); err != nil {
pool.Put(gr)
if err == io.EOF { //ignore if body is empty if err == io.EOF { //ignore if body is empty
return next(c) return next(c)
} }
return err return err
} }
defer gr.Close()
var buf bytes.Buffer var buf bytes.Buffer
io.Copy(&buf, gr) io.Copy(&buf, gr)
gr.Close()
pool.Put(gr)
b.Close() // http.Request.Body is closed by the Server, but because we are replacing it, it must be closed here
r := ioutil.NopCloser(&buf) r := ioutil.NopCloser(&buf)
defer r.Close()
c.Request().Body = r c.Request().Body = r
} }
return next(c) return next(c)
} }
} }
} }
func gzipDecompressPool() sync.Pool {
return sync.Pool{
New: func() interface{} {
// create with an empty reader (but with GZIP header)
w, err := gzip.NewWriterLevel(ioutil.Discard, gzip.BestSpeed)
if err != nil {
return err
}
b := new(bytes.Buffer)
w.Reset(b)
w.Flush()
w.Close()
r, err := gzip.NewReader(bytes.NewReader(b.Bytes()))
if err != nil {
return err
}
return r
},
}
}