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:
parent
902c553552
commit
b437ee3879
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user