Skip to content

Commit 14e020b

Browse files
committed
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
1 parent 502cce2 commit 14e020b

File tree

2 files changed

+46
-7
lines changed

2 files changed

+46
-7
lines changed

middleware/compress.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
5959
config.Level = DefaultGzipConfig.Level
6060
}
6161

62-
pool := gzipPool(config)
62+
pool := gzipCompressPool(config)
6363

6464
return func(next echo.HandlerFunc) echo.HandlerFunc {
6565
return func(c echo.Context) error {
@@ -133,7 +133,7 @@ func (w *gzipResponseWriter) Push(target string, opts *http.PushOptions) error {
133133
return http.ErrNotSupported
134134
}
135135

136-
func gzipPool(config GzipConfig) sync.Pool {
136+
func gzipCompressPool(config GzipConfig) sync.Pool {
137137
return sync.Pool{
138138
New: func() interface{} {
139139
w, err := gzip.NewWriterLevel(ioutil.Discard, config.Level)

middleware/decompress.go

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ package middleware
33
import (
44
"bytes"
55
"compress/gzip"
6-
"github.com/labstack/echo/v4"
76
"io"
87
"io/ioutil"
8+
"net/http"
9+
"sync"
10+
11+
"github.com/labstack/echo/v4"
912
)
1013

1114
type (
@@ -32,27 +35,63 @@ func Decompress() echo.MiddlewareFunc {
3235
//DecompressWithConfig decompresses request body based if content encoding type is set to "gzip" with config
3336
func DecompressWithConfig(config DecompressConfig) echo.MiddlewareFunc {
3437
return func(next echo.HandlerFunc) echo.HandlerFunc {
38+
pool := gzipDecompressPool()
3539
return func(c echo.Context) error {
3640
if config.Skipper(c) {
3741
return next(c)
3842
}
3943
switch c.Request().Header.Get(echo.HeaderContentEncoding) {
4044
case GZIPEncoding:
41-
gr, err := gzip.NewReader(c.Request().Body)
42-
if err != nil {
45+
b := c.Request().Body
46+
47+
i := pool.Get()
48+
gr, ok := i.(*gzip.Reader)
49+
if !ok {
50+
return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error())
51+
}
52+
53+
if err := gr.Reset(b); err != nil {
54+
pool.Put(gr)
4355
if err == io.EOF { //ignore if body is empty
4456
return next(c)
4557
}
4658
return err
4759
}
48-
defer gr.Close()
4960
var buf bytes.Buffer
5061
io.Copy(&buf, gr)
62+
63+
gr.Close()
64+
pool.Put(gr)
65+
66+
b.Close() // http.Request.Body is closed by the Server, but because we are replacing it, it must be closed here
67+
5168
r := ioutil.NopCloser(&buf)
52-
defer r.Close()
5369
c.Request().Body = r
5470
}
5571
return next(c)
5672
}
5773
}
5874
}
75+
76+
func gzipDecompressPool() sync.Pool {
77+
return sync.Pool{
78+
New: func() interface{} {
79+
// create with an empty reader (but with GZIP header)
80+
w, err := gzip.NewWriterLevel(ioutil.Discard, gzip.BestSpeed)
81+
if err != nil {
82+
return err
83+
}
84+
85+
b := new(bytes.Buffer)
86+
w.Reset(b)
87+
w.Flush()
88+
w.Close()
89+
90+
r, err := gzip.NewReader(bytes.NewReader(b.Bytes()))
91+
if err != nil {
92+
return err
93+
}
94+
return r
95+
},
96+
}
97+
}

0 commit comments

Comments
 (0)