1
0
mirror of https://github.com/labstack/echo.git synced 2025-06-08 23:56:20 +02:00

stream decompression instead of buffering (#2018)

* stream decompression instead of buffering
* simple body replace with gzip reader with deferred close
* defer resource closes
* simply gzip.Reader pool
This commit is contained in:
David Desmarais-Michaud 2021-12-03 05:03:42 -05:00 committed by GitHub
parent 902c553552
commit b437ee3879
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -1,10 +1,8 @@
package middleware package middleware
import ( import (
"bytes"
"compress/gzip" "compress/gzip"
"io" "io"
"io/ioutil"
"net/http" "net/http"
"sync" "sync"
@ -43,26 +41,7 @@ type DefaultGzipDecompressPool struct {
} }
func (d *DefaultGzipDecompressPool) gzipDecompressPool() sync.Pool { func (d *DefaultGzipDecompressPool) gzipDecompressPool() sync.Pool {
return sync.Pool{ return sync.Pool{New: func() interface{} { return new(gzip.Reader) }}
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
},
}
} }
//Decompress decompresses request body based if content encoding type is set to "gzip" with default config //Decompress decompresses request body based if content encoding type is set to "gzip" with default config
@ -82,38 +61,38 @@ func DecompressWithConfig(config DecompressConfig) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(next echo.HandlerFunc) echo.HandlerFunc {
pool := config.GzipDecompressPool.gzipDecompressPool() pool := config.GzipDecompressPool.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) {
case GZIPEncoding: if c.Request().Header.Get(echo.HeaderContentEncoding) != GZIPEncoding {
b := c.Request().Body return next(c)
}
i := pool.Get() i := pool.Get()
gr, ok := i.(*gzip.Reader) gr, ok := i.(*gzip.Reader)
if !ok { if !ok || gr == nil {
return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error()) return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error())
} }
defer pool.Put(gr)
b := c.Request().Body
defer b.Close()
if err := gr.Reset(b); err != nil { 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
} }
var buf bytes.Buffer
io.Copy(&buf, gr)
gr.Close() // only Close gzip reader if it was set to a proper gzip source otherwise it will panic on close.
pool.Put(gr) defer gr.Close()
b.Close() // http.Request.Body is closed by the Server, but because we are replacing it, it must be closed here c.Request().Body = gr
r := ioutil.NopCloser(&buf)
c.Request().Body = r
}
return next(c) return next(c)
} }
} }