Skip to content

Commit 2ab69d8

Browse files
authored
Merge pull request #1672 from pafuent/compress_middleware_pool_optimization
Adding sync.Pool to Compress Middleware
2 parents ceffc10 + ac54e13 commit 2ab69d8

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

middleware/compress.go

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"net"
99
"net/http"
1010
"strings"
11+
"sync"
1112

1213
"github.com/labstack/echo/v4"
1314
)
@@ -58,6 +59,8 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
5859
config.Level = DefaultGzipConfig.Level
5960
}
6061

62+
pool := gzipPool(config)
63+
6164
return func(next echo.HandlerFunc) echo.HandlerFunc {
6265
return func(c echo.Context) error {
6366
if config.Skipper(c) {
@@ -68,11 +71,13 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
6871
res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding)
6972
if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) {
7073
res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
71-
rw := res.Writer
72-
w, err := gzip.NewWriterLevel(rw, config.Level)
73-
if err != nil {
74-
return err
74+
i := pool.Get()
75+
w, ok := i.(*gzip.Writer)
76+
if !ok {
77+
return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error())
7578
}
79+
rw := res.Writer
80+
w.Reset(rw)
7681
defer func() {
7782
if res.Size == 0 {
7883
if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme {
@@ -85,6 +90,7 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
8590
w.Reset(ioutil.Discard)
8691
}
8792
w.Close()
93+
pool.Put(w)
8894
}()
8995
grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw}
9096
res.Writer = grw
@@ -126,3 +132,15 @@ func (w *gzipResponseWriter) Push(target string, opts *http.PushOptions) error {
126132
}
127133
return http.ErrNotSupported
128134
}
135+
136+
func gzipPool(config GzipConfig) sync.Pool {
137+
return sync.Pool{
138+
New: func() interface{} {
139+
w, err := gzip.NewWriterLevel(ioutil.Discard, config.Level)
140+
if err != nil {
141+
return err
142+
}
143+
return w
144+
},
145+
}
146+
}

middleware/compress_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,22 @@ func TestGzipErrorReturned(t *testing.T) {
120120
assert.Empty(t, rec.Header().Get(echo.HeaderContentEncoding))
121121
}
122122

123+
func TestGzipErrorReturnedInvalidConfig(t *testing.T) {
124+
e := echo.New()
125+
// Invalid level
126+
e.Use(GzipWithConfig(GzipConfig{Level: 12}))
127+
e.GET("/", func(c echo.Context) error {
128+
c.Response().Write([]byte("test"))
129+
return nil
130+
})
131+
req := httptest.NewRequest(http.MethodGet, "/", nil)
132+
req.Header.Set(echo.HeaderAcceptEncoding, gzipScheme)
133+
rec := httptest.NewRecorder()
134+
e.ServeHTTP(rec, req)
135+
assert.Equal(t, http.StatusInternalServerError, rec.Code)
136+
assert.Contains(t, rec.Body.String(), "gzip")
137+
}
138+
123139
// Issue #806
124140
func TestGzipWithStatic(t *testing.T) {
125141
e := echo.New()
@@ -146,3 +162,25 @@ func TestGzipWithStatic(t *testing.T) {
146162
}
147163
}
148164
}
165+
166+
func BenchmarkGzip(b *testing.B) {
167+
e := echo.New()
168+
169+
req := httptest.NewRequest(http.MethodGet, "/", nil)
170+
req.Header.Set(echo.HeaderAcceptEncoding, gzipScheme)
171+
172+
h := Gzip()(func(c echo.Context) error {
173+
c.Response().Write([]byte("test")) // For Content-Type sniffing
174+
return nil
175+
})
176+
177+
b.ReportAllocs()
178+
b.ResetTimer()
179+
180+
for i := 0; i < b.N; i++ {
181+
// Gzip
182+
rec := httptest.NewRecorder()
183+
c := e.NewContext(req, rec)
184+
h(c)
185+
}
186+
}

0 commit comments

Comments
 (0)