@@ -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"
@@ -2795,6 +2796,7 @@ type bodyEOFSignal struct {
2795
2796
}
2796
2797
2797
2798
var errReadOnClosedResBody = errors .New ("http: read on closed response body" )
2799
+ var errConcurrentReadOnResBody = errors .New ("http: concurrent read on response body" )
2798
2800
2799
2801
func (es * bodyEOFSignal ) Read (p []byte ) (n int , err error ) {
2800
2802
es .mu .Lock ()
@@ -2844,37 +2846,94 @@ func (es *bodyEOFSignal) condfn(err error) error {
2844
2846
}
2845
2847
2846
2848
// gzipReader wraps a response body so it can lazily
2847
- // call gzip.NewReader on the first call to Read
2849
+ // get gzip.Reader from the pool on the first call to Read.
2850
+ // After Close is called it puts gzip.Reader to the pool immediately
2851
+ // if there is no Read in progress or later when Read completes.
2848
2852
type gzipReader struct {
2849
2853
_ incomparable
2850
2854
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
2855
+ mu sync.Mutex
2856
+ zr * gzip.Reader
2857
+ zerr error
2853
2858
}
2854
2859
2855
- func (gz * gzipReader ) Read (p []byte ) (n int , err error ) {
2860
+ type eofReader struct {}
2861
+
2862
+ func (eofReader ) Read ([]byte ) (int , error ) { return 0 , io .EOF }
2863
+ func (eofReader ) ReadByte () (byte , error ) { return 0 , io .EOF }
2864
+
2865
+ var (
2866
+ gzipPool = sync.Pool {New : func () any { return new (gzip.Reader ) }}
2867
+
2868
+ // parkedReader used to reset gzip.Reader before returning into the gzipPool as
2869
+ // gzip.Reader.Reset(flate.Reader) avoids calling bufio.NewReader
2870
+ parkedReader flate.Reader = eofReader {}
2871
+ )
2872
+
2873
+ func gzipPoolGet (r io.Reader ) (* gzip.Reader , error ) {
2874
+ zr := gzipPool .Get ().(* gzip.Reader )
2875
+ if err := zr .Reset (r ); err != nil {
2876
+ gzipPoolPut (zr )
2877
+ return nil , err
2878
+ }
2879
+ return zr , nil
2880
+ }
2881
+
2882
+ func gzipPoolPut (zr * gzip.Reader ) {
2883
+ _ = zr .Reset (parkedReader )
2884
+ gzipPool .Put (zr )
2885
+ }
2886
+
2887
+ func (gz * gzipReader ) get () (* gzip.Reader , error ) {
2888
+ gz .mu .Lock ()
2889
+ defer gz .mu .Unlock ()
2890
+ if gz .zerr != nil {
2891
+ return nil , gz .zerr
2892
+ }
2856
2893
if gz .zr == nil {
2857
- if gz .zerr == nil {
2858
- gz .zr , gz .zerr = gzip .NewReader (gz .body )
2859
- }
2894
+ gz .zr , gz .zerr = gzipPoolGet (gz .body )
2860
2895
if gz .zerr != nil {
2861
- return 0 , gz .zerr
2896
+ return nil , gz .zerr
2862
2897
}
2863
2898
}
2899
+ ret := gz .zr
2900
+ gz .zr , gz .zerr = nil , errConcurrentReadOnResBody
2901
+ return ret , nil
2902
+ }
2864
2903
2865
- gz .body .mu .Lock ()
2866
- if gz .body .closed {
2867
- err = errReadOnClosedResBody
2904
+ func (gz * gzipReader ) put (zr * gzip.Reader ) {
2905
+ gz .mu .Lock ()
2906
+ defer gz .mu .Unlock ()
2907
+ if gz .zerr == errConcurrentReadOnResBody {
2908
+ gz .zr , gz .zerr = zr , nil
2909
+ } else { // errReadOnClosedResBody
2910
+ gzipPoolPut (zr )
2868
2911
}
2869
- gz . body . mu . Unlock ()
2912
+ }
2870
2913
2914
+ func (gz * gzipReader ) close () {
2915
+ gz .mu .Lock ()
2916
+ defer gz .mu .Unlock ()
2917
+ if gz .zerr == nil && gz .zr != nil {
2918
+ gzipPoolPut (gz .zr )
2919
+ gz .zr = nil
2920
+ }
2921
+ gz .zerr = errReadOnClosedResBody
2922
+ }
2923
+
2924
+ func (gz * gzipReader ) Read (p []byte ) (n int , err error ) {
2925
+ zr , err := gz .get ()
2871
2926
if err != nil {
2872
2927
return 0 , err
2873
2928
}
2874
- return gz .zr .Read (p )
2929
+ defer gz .put (zr )
2930
+
2931
+ return zr .Read (p )
2875
2932
}
2876
2933
2877
2934
func (gz * gzipReader ) Close () error {
2935
+ gz .close ()
2936
+
2878
2937
return gz .body .Close ()
2879
2938
}
2880
2939
0 commit comments