@@ -11,6 +11,7 @@ package http
11
11
12
12
import (
13
13
"bufio"
14
+ "compress/flate"
14
15
"compress/gzip"
15
16
"container/list"
16
17
"context"
@@ -2844,37 +2845,84 @@ func (es *bodyEOFSignal) condfn(err error) error {
2844
2845
}
2845
2846
2846
2847
// gzipReader wraps a response body so it can lazily
2847
- // call gzip.NewReader on the first call to Read
2848
+ // get gzip.Reader from the pool on the first call to Read.
2849
+ // After Close is called it puts gzip.Reader to the pool immediately
2850
+ // if there is no Read in progress or later when Read completes.
2848
2851
type gzipReader struct {
2849
2852
_ incomparable
2850
2853
body * bodyEOFSignal // underlying HTTP/1 response body framing
2851
- zr * gzip.Reader // lazily-initialized gzip reader
2852
- zerr error // any error from gzip.NewReader; sticky
2854
+
2855
+ mu sync.Mutex
2856
+ zr * gzip.Reader // lazily-initialized gzip reader
2857
+ zerr error // any error from gzip.Reader.Reset; sticky
2858
+ reading bool // true if Read() is in progress
2859
+ closed bool // true if Close() was called
2853
2860
}
2854
2861
2855
- func (gz * gzipReader ) Read (p []byte ) (n int , err error ) {
2856
- if gz .zr == nil {
2857
- if gz .zerr == nil {
2858
- gz .zr , gz .zerr = gzip .NewReader (gz .body )
2859
- }
2860
- if gz .zerr != nil {
2861
- return 0 , gz .zerr
2862
- }
2863
- }
2862
+ type eofReader struct {}
2863
+
2864
+ func (eofReader ) Read ([]byte ) (int , error ) { return 0 , io .EOF }
2865
+ func (eofReader ) ReadByte () (byte , error ) { return 0 , io .EOF }
2864
2866
2865
- gz .body .mu .Lock ()
2866
- if gz .body .closed {
2867
+ var (
2868
+ gzipPool = sync.Pool {New : func () any { return new (gzip.Reader ) }}
2869
+
2870
+ // parkedReader used to reset gzip.Reader before returning into the gzipPool as
2871
+ // gzip.Reader.Reset(flate.Reader) avoids calling bufio.NewReader
2872
+ parkedReader flate.Reader = eofReader {}
2873
+ )
2874
+
2875
+ func gzipPoolPut (zr * gzip.Reader ) {
2876
+ _ = zr .Reset (parkedReader )
2877
+ gzipPool .Put (zr )
2878
+ }
2879
+
2880
+ func (gz * gzipReader ) Read (p []byte ) (n int , err error ) {
2881
+ gz .mu .Lock ()
2882
+ if gz .closed {
2867
2883
err = errReadOnClosedResBody
2884
+ } else {
2885
+ if gz .zr == nil {
2886
+ if gz .zerr == nil {
2887
+ zr := gzipPool .Get ().(* gzip.Reader )
2888
+ if gz .zerr = zr .Reset (gz .body ); gz .zerr == nil {
2889
+ gz .zr = zr
2890
+ } else {
2891
+ gzipPoolPut (zr )
2892
+ }
2893
+ }
2894
+ err = gz .zerr
2895
+ }
2868
2896
}
2869
- gz .body .mu .Unlock ()
2897
+ gz .reading = err == nil
2898
+ gz .mu .Unlock ()
2870
2899
2871
2900
if err != nil {
2872
2901
return 0 , err
2873
2902
}
2874
- return gz .zr .Read (p )
2903
+
2904
+ n , err = gz .zr .Read (p )
2905
+
2906
+ gz .mu .Lock ()
2907
+ if gz .closed {
2908
+ gzipPoolPut (gz .zr )
2909
+ gz .zr = nil
2910
+ }
2911
+ gz .reading = false
2912
+ gz .mu .Unlock ()
2913
+
2914
+ return
2875
2915
}
2876
2916
2877
2917
func (gz * gzipReader ) Close () error {
2918
+ gz .mu .Lock ()
2919
+ if ! gz .closed && ! gz .reading && gz .zr != nil {
2920
+ gzipPoolPut (gz .zr )
2921
+ gz .zr = nil
2922
+ }
2923
+ gz .closed = true
2924
+ gz .mu .Unlock ()
2925
+
2878
2926
return gz .body .Close ()
2879
2927
}
2880
2928
0 commit comments