diff --git a/gzip_response.go b/gzip_response.go index a6f7d17..28489d3 100644 --- a/gzip_response.go +++ b/gzip_response.go @@ -10,6 +10,7 @@ import ( "io" "net" "net/http" + "sync" "aahframework.org/essentials.v0" ) @@ -21,8 +22,19 @@ type GzipResponse struct { gw *gzip.Writer } -// interface compliance var ( + // GzipLevel holds value from app config. + GzipLevel int + + grPool = sync.Pool{ + New: func() interface{} { + return &GzipResponse{} + }, + } + + gwPool = sync.Pool{} + + // interface compliance _ http.CloseNotifier = &GzipResponse{} _ http.Flusher = &GzipResponse{} _ http.Hijacker = &GzipResponse{} @@ -35,15 +47,22 @@ var ( // Global methods //___________________________________ -// WrapGzipResponseWriter wraps `http.ResponseWriter`, returns aah framework response +// GetGzipResponseWriter wraps `http.ResponseWriter`, returns aah framework response // writer that allows to advantage of response process. -func WrapGzipResponseWriter(w http.ResponseWriter, level int) ResponseWriter { - rw := WrapResponseWriter(w) +func GetGzipResponseWriter(w ResponseWriter) ResponseWriter { + gr := grPool.Get().(*GzipResponse) + gr.gw = getGzipWriter(w) + gr.r = w.(*Response) + return gr +} - // Since Gzip level is validated in the framework while loading, - // so expected to have valid level which between 1 and 9. - gzw, _ := gzip.NewWriterLevel(rw, level) - return &GzipResponse{gw: gzw, r: rw.(*Response)} +// PutGzipResponseWiriter method resets and puts the gzip writer into pool. +func PutGzipResponseWiriter(rw ResponseWriter) { + gw := rw.(*GzipResponse) + putGzipWriter(gw.gw) + PutResponseWriter(gw.r) + _ = gw.Close() + grPool.Put(gw) } //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ @@ -119,3 +138,25 @@ func (g *GzipResponse) Hijack() (net.Conn, *bufio.ReadWriter, error) { func (g *GzipResponse) Push(target string, opts *http.PushOptions) error { return g.r.Push(target, opts) } + +//‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ +// GzipResponse Unexported methods +//___________________________________ + +func getGzipWriter(w io.Writer) *gzip.Writer { + gw := gwPool.Get() + if gw == nil { + if ngw, err := gzip.NewWriterLevel(w, GzipLevel); err == nil { + return ngw + } + return nil + } + ngw := gw.(*gzip.Writer) + ngw.Reset(w) + return ngw +} + +func putGzipWriter(gw *gzip.Writer) { + _ = gw.Close() + gwPool.Put(gw) +} diff --git a/gzip_response_test.go b/gzip_response_test.go index 7fc0bc3..af485a9 100644 --- a/gzip_response_test.go +++ b/gzip_response_test.go @@ -20,8 +20,9 @@ import ( func TestHTTPGzipWriter(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { - gw := WrapGzipResponseWriter(w, gzip.BestSpeed) - defer ess.CloseQuietly(gw) + GzipLevel = gzip.BestSpeed + gw := GetGzipResponseWriter(GetResponseWriter(w)) + defer PutGzipResponseWiriter(gw) gw.Header().Set(HeaderVary, HeaderAcceptEncoding) gw.Header().Set(HeaderContentEncoding, "gzip") @@ -42,6 +43,8 @@ func TestHTTPGzipWriter(t *testing.T) { gw.(http.Flusher).Flush() + _ = gw.(http.Pusher).Push("/test/sample.txt", nil) + ch := gw.(http.CloseNotifier).CloseNotify() assert.NotNil(t, ch) } @@ -53,7 +56,8 @@ func TestHTTPGzipWriter(t *testing.T) { func TestHTTPGzipHijack(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { - gw := WrapGzipResponseWriter(w, gzip.BestSpeed) + GzipLevel = gzip.BestSpeed + gw := GetGzipResponseWriter(GetResponseWriter(w)) con, rw, err := gw.(http.Hijacker).Hijack() assert.FailOnError(t, err, "") diff --git a/response.go b/response.go index ff65777..1267af6 100644 --- a/response.go +++ b/response.go @@ -10,6 +10,7 @@ import ( "io" "net" "net/http" + "sync" "aahframework.org/essentials.v0" ) @@ -40,8 +41,14 @@ type ( } ) -// interface compliance var ( + rPool = sync.Pool{ + New: func() interface{} { + return &Response{} + }, + } + + // interface compliance _ http.CloseNotifier = &Response{} _ http.Flusher = &Response{} _ http.Hijacker = &Response{} @@ -54,10 +61,22 @@ var ( // Global methods //___________________________________ -// WrapResponseWriter wraps `http.ResponseWriter`, returns aah framework response -// writer that allows to advantage of response process. -func WrapResponseWriter(w http.ResponseWriter) ResponseWriter { - return &Response{w: w} +// GetResponseWriter method wraps given writer and returns the aah response writer. +func GetResponseWriter(w http.ResponseWriter) ResponseWriter { + rw := rPool.Get().(*Response) + rw.w = w + return rw +} + +// PutResponseWriter method puts response writer back to pool. +func PutResponseWriter(aw ResponseWriter) { + r := aw.(*Response) + _ = r.Close() + r.w = nil + r.status = 0 + r.bytesWritten = 0 + r.wroteStatus = false + rPool.Put(r) } //‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ diff --git a/response_test.go b/response_test.go index 34dd4d1..04035eb 100644 --- a/response_test.go +++ b/response_test.go @@ -18,8 +18,8 @@ import ( func TestHTTPResponseWriter(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { - writer := WrapResponseWriter(w) - defer ess.CloseQuietly(writer) + writer := GetResponseWriter(w) + defer PutResponseWriter(writer) writer.WriteHeader(http.StatusOK) assert.Equal(t, http.StatusOK, writer.Status()) @@ -35,8 +35,8 @@ func TestHTTPResponseWriter(t *testing.T) { func TestHTTPNoStatusWritten(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { - writer := WrapResponseWriter(w) - defer ess.CloseQuietly(writer) + writer := GetResponseWriter(w) + defer PutResponseWriter(writer) _, _ = writer.Write([]byte("aah framework no status written")) assert.Equal(t, 31, writer.BytesWritten()) @@ -47,8 +47,8 @@ func TestHTTPNoStatusWritten(t *testing.T) { func TestHTTPMultipleStatusWritten(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { - writer := WrapResponseWriter(w) - defer ess.CloseQuietly(writer) + writer := GetResponseWriter(w) + defer PutResponseWriter(writer) writer.WriteHeader(http.StatusOK) writer.WriteHeader(http.StatusAccepted) @@ -62,7 +62,7 @@ func TestHTTPMultipleStatusWritten(t *testing.T) { func TestHTTPHijackCall(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { - writer := WrapResponseWriter(w) + writer := GetResponseWriter(w) con, rw, err := writer.(http.Hijacker).Hijack() assert.FailOnError(t, err, "") @@ -85,7 +85,8 @@ func TestHTTPHijackCall(t *testing.T) { func TestHTTPCallCloseNotifyAndFlush(t *testing.T) { handler := func(w http.ResponseWriter, r *http.Request) { - writer := WrapResponseWriter(w) + writer := GetResponseWriter(w) + defer PutResponseWriter(writer) _, _ = writer.Write([]byte("aah framework calling close notify and flush")) assert.Equal(t, 44, writer.BytesWritten())