Skip to content

Commit

Permalink
stream decompression instead of buffering (#2018)
Browse files Browse the repository at this point in the history
* stream decompression instead of buffering
* simple body replace with gzip reader with deferred close
* defer resource closes
* simply gzip.Reader pool
  • Loading branch information
davidmdm committed Dec 3, 2021
1 parent 902c553 commit b437ee3
Showing 1 changed file with 24 additions and 45 deletions.
69 changes: 24 additions & 45 deletions middleware/decompress.go
@@ -1,10 +1,8 @@
package middleware

import (
"bytes"
"compress/gzip"
"io"
"io/ioutil"
"net/http"
"sync"

Expand Down Expand Up @@ -43,26 +41,7 @@ type DefaultGzipDecompressPool struct {
}

func (d *DefaultGzipDecompressPool) 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
},
}
return sync.Pool{New: func() interface{} { return new(gzip.Reader) }}
}

//Decompress decompresses request body based if content encoding type is set to "gzip" with default config
Expand All @@ -82,38 +61,38 @@ func DecompressWithConfig(config DecompressConfig) echo.MiddlewareFunc {

return func(next echo.HandlerFunc) echo.HandlerFunc {
pool := config.GzipDecompressPool.gzipDecompressPool()

return func(c echo.Context) error {
if config.Skipper(c) {
return next(c)
}
switch c.Request().Header.Get(echo.HeaderContentEncoding) {
case GZIPEncoding:
b := c.Request().Body

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
return next(c)
}
return err
}
var buf bytes.Buffer
io.Copy(&buf, gr)
if c.Request().Header.Get(echo.HeaderContentEncoding) != GZIPEncoding {
return next(c)
}

gr.Close()
pool.Put(gr)
i := pool.Get()
gr, ok := i.(*gzip.Reader)
if !ok || gr == nil {
return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error())
}
defer pool.Put(gr)

b.Close() // http.Request.Body is closed by the Server, but because we are replacing it, it must be closed here
b := c.Request().Body
defer b.Close()

r := ioutil.NopCloser(&buf)
c.Request().Body = r
if err := gr.Reset(b); err != nil {
if err == io.EOF { //ignore if body is empty
return next(c)
}
return err
}

// only Close gzip reader if it was set to a proper gzip source otherwise it will panic on close.
defer gr.Close()

c.Request().Body = gr

return next(c)
}
}
Expand Down

0 comments on commit b437ee3

Please sign in to comment.