Skip to content

Commit b437ee3

Browse files
authored
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
1 parent 902c553 commit b437ee3

File tree

1 file changed

+24
-45
lines changed

1 file changed

+24
-45
lines changed

middleware/decompress.go

+24-45
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package middleware
22

33
import (
4-
"bytes"
54
"compress/gzip"
65
"io"
7-
"io/ioutil"
86
"net/http"
97
"sync"
108

@@ -43,26 +41,7 @@ type DefaultGzipDecompressPool struct {
4341
}
4442

4543
func (d *DefaultGzipDecompressPool) gzipDecompressPool() sync.Pool {
46-
return sync.Pool{
47-
New: func() interface{} {
48-
// create with an empty reader (but with GZIP header)
49-
w, err := gzip.NewWriterLevel(ioutil.Discard, gzip.BestSpeed)
50-
if err != nil {
51-
return err
52-
}
53-
54-
b := new(bytes.Buffer)
55-
w.Reset(b)
56-
w.Flush()
57-
w.Close()
58-
59-
r, err := gzip.NewReader(bytes.NewReader(b.Bytes()))
60-
if err != nil {
61-
return err
62-
}
63-
return r
64-
},
65-
}
44+
return sync.Pool{New: func() interface{} { return new(gzip.Reader) }}
6645
}
6746

6847
//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 {
8261

8362
return func(next echo.HandlerFunc) echo.HandlerFunc {
8463
pool := config.GzipDecompressPool.gzipDecompressPool()
64+
8565
return func(c echo.Context) error {
8666
if config.Skipper(c) {
8767
return next(c)
8868
}
89-
switch c.Request().Header.Get(echo.HeaderContentEncoding) {
90-
case GZIPEncoding:
91-
b := c.Request().Body
92-
93-
i := pool.Get()
94-
gr, ok := i.(*gzip.Reader)
95-
if !ok {
96-
return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error())
97-
}
9869

99-
if err := gr.Reset(b); err != nil {
100-
pool.Put(gr)
101-
if err == io.EOF { //ignore if body is empty
102-
return next(c)
103-
}
104-
return err
105-
}
106-
var buf bytes.Buffer
107-
io.Copy(&buf, gr)
70+
if c.Request().Header.Get(echo.HeaderContentEncoding) != GZIPEncoding {
71+
return next(c)
72+
}
10873

109-
gr.Close()
110-
pool.Put(gr)
74+
i := pool.Get()
75+
gr, ok := i.(*gzip.Reader)
76+
if !ok || gr == nil {
77+
return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error())
78+
}
79+
defer pool.Put(gr)
11180

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

114-
r := ioutil.NopCloser(&buf)
115-
c.Request().Body = r
84+
if err := gr.Reset(b); err != nil {
85+
if err == io.EOF { //ignore if body is empty
86+
return next(c)
87+
}
88+
return err
11689
}
90+
91+
// only Close gzip reader if it was set to a proper gzip source otherwise it will panic on close.
92+
defer gr.Close()
93+
94+
c.Request().Body = gr
95+
11796
return next(c)
11897
}
11998
}

0 commit comments

Comments
 (0)