mirror of
https://github.com/labstack/echo.git
synced 2024-12-24 20:14:31 +02:00
Adding sync.Pool to Compress Middleware
Adding a sync.Pool for the *gzip.Writer reduces the allocations of the Compress middleware in 50% and gives an increase on execution speed of a 85% This fix #1643
This commit is contained in:
parent
ceffc10ecb
commit
ac54e132e4
@ -8,6 +8,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
)
|
)
|
||||||
@ -58,6 +59,8 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
|||||||
config.Level = DefaultGzipConfig.Level
|
config.Level = DefaultGzipConfig.Level
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pool := gzipPool(config)
|
||||||
|
|
||||||
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
if config.Skipper(c) {
|
if config.Skipper(c) {
|
||||||
@ -68,11 +71,13 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
|||||||
res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding)
|
res.Header().Add(echo.HeaderVary, echo.HeaderAcceptEncoding)
|
||||||
if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) {
|
if strings.Contains(c.Request().Header.Get(echo.HeaderAcceptEncoding), gzipScheme) {
|
||||||
res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
|
res.Header().Set(echo.HeaderContentEncoding, gzipScheme) // Issue #806
|
||||||
rw := res.Writer
|
i := pool.Get()
|
||||||
w, err := gzip.NewWriterLevel(rw, config.Level)
|
w, ok := i.(*gzip.Writer)
|
||||||
if err != nil {
|
if !ok {
|
||||||
return err
|
return echo.NewHTTPError(http.StatusInternalServerError, i.(error).Error())
|
||||||
}
|
}
|
||||||
|
rw := res.Writer
|
||||||
|
w.Reset(rw)
|
||||||
defer func() {
|
defer func() {
|
||||||
if res.Size == 0 {
|
if res.Size == 0 {
|
||||||
if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme {
|
if res.Header().Get(echo.HeaderContentEncoding) == gzipScheme {
|
||||||
@ -85,6 +90,7 @@ func GzipWithConfig(config GzipConfig) echo.MiddlewareFunc {
|
|||||||
w.Reset(ioutil.Discard)
|
w.Reset(ioutil.Discard)
|
||||||
}
|
}
|
||||||
w.Close()
|
w.Close()
|
||||||
|
pool.Put(w)
|
||||||
}()
|
}()
|
||||||
grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw}
|
grw := &gzipResponseWriter{Writer: w, ResponseWriter: rw}
|
||||||
res.Writer = grw
|
res.Writer = grw
|
||||||
@ -126,3 +132,15 @@ func (w *gzipResponseWriter) Push(target string, opts *http.PushOptions) error {
|
|||||||
}
|
}
|
||||||
return http.ErrNotSupported
|
return http.ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gzipPool(config GzipConfig) sync.Pool {
|
||||||
|
return sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
w, err := gzip.NewWriterLevel(ioutil.Discard, config.Level)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -120,6 +120,22 @@ func TestGzipErrorReturned(t *testing.T) {
|
|||||||
assert.Empty(t, rec.Header().Get(echo.HeaderContentEncoding))
|
assert.Empty(t, rec.Header().Get(echo.HeaderContentEncoding))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGzipErrorReturnedInvalidConfig(t *testing.T) {
|
||||||
|
e := echo.New()
|
||||||
|
// Invalid level
|
||||||
|
e.Use(GzipWithConfig(GzipConfig{Level: 12}))
|
||||||
|
e.GET("/", func(c echo.Context) error {
|
||||||
|
c.Response().Write([]byte("test"))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
|
req.Header.Set(echo.HeaderAcceptEncoding, gzipScheme)
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
e.ServeHTTP(rec, req)
|
||||||
|
assert.Equal(t, http.StatusInternalServerError, rec.Code)
|
||||||
|
assert.Contains(t, rec.Body.String(), "gzip")
|
||||||
|
}
|
||||||
|
|
||||||
// Issue #806
|
// Issue #806
|
||||||
func TestGzipWithStatic(t *testing.T) {
|
func TestGzipWithStatic(t *testing.T) {
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
@ -146,3 +162,25 @@ func TestGzipWithStatic(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkGzip(b *testing.B) {
|
||||||
|
e := echo.New()
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
|
req.Header.Set(echo.HeaderAcceptEncoding, gzipScheme)
|
||||||
|
|
||||||
|
h := Gzip()(func(c echo.Context) error {
|
||||||
|
c.Response().Write([]byte("test")) // For Content-Type sniffing
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
// Gzip
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
c := e.NewContext(req, rec)
|
||||||
|
h(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user