@@ -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"
@@ -2988,6 +2989,7 @@ type bodyEOFSignal struct {
2988
2989
}
2989
2990
2990
2991
var errReadOnClosedResBody = errors .New ("http: read on closed response body" )
2992
+ var errConcurrentReadOnResBody = errors .New ("http: concurrent read on response body" )
2991
2993
2992
2994
func (es * bodyEOFSignal ) Read (p []byte ) (n int , err error ) {
2993
2995
es .mu .Lock ()
@@ -3037,37 +3039,98 @@ func (es *bodyEOFSignal) condfn(err error) error {
3037
3039
}
3038
3040
3039
3041
// gzipReader wraps a response body so it can lazily
3040
- // call gzip.NewReader on the first call to Read
3042
+ // get gzip.Reader from the pool on the first call to Read.
3043
+ // After Close is called it puts gzip.Reader to the pool immediately
3044
+ // if there is no Read in progress or later when Read completes.
3041
3045
type gzipReader struct {
3042
3046
_ incomparable
3043
3047
body * bodyEOFSignal // underlying HTTP/1 response body framing
3044
- zr * gzip.Reader // lazily-initialized gzip reader
3045
- zerr error // any error from gzip.NewReader; sticky
3048
+ mu sync.Mutex // guards zr and zerr
3049
+ zr * gzip.Reader
3050
+ zerr error
3046
3051
}
3047
3052
3048
- func (gz * gzipReader ) Read (p []byte ) (n int , err error ) {
3053
+ type eofReader struct {}
3054
+
3055
+ func (eofReader ) Read ([]byte ) (int , error ) { return 0 , io .EOF }
3056
+ func (eofReader ) ReadByte () (byte , error ) { return 0 , io .EOF }
3057
+
3058
+ var gzipPool = sync.Pool {New : func () any { return new (gzip.Reader ) }}
3059
+
3060
+ // gzipPoolGet gets a gzip.Reader from the pool and resets it to read from r.
3061
+ func gzipPoolGet (r io.Reader ) (* gzip.Reader , error ) {
3062
+ zr := gzipPool .Get ().(* gzip.Reader )
3063
+ if err := zr .Reset (r ); err != nil {
3064
+ gzipPoolPut (zr )
3065
+ return nil , err
3066
+ }
3067
+ return zr , nil
3068
+ }
3069
+
3070
+ // gzipPoolPut puts a gzip.Reader back into the pool.
3071
+ func gzipPoolPut (zr * gzip.Reader ) {
3072
+ // Reset will allocate bufio.Reader if we pass it anything
3073
+ // other than a flate.Reader, so ensure that it's getting one.
3074
+ var r flate.Reader = eofReader {}
3075
+ zr .Reset (r )
3076
+ gzipPool .Put (zr )
3077
+ }
3078
+
3079
+ // acquire returns a gzip.Reader for reading response body.
3080
+ // The reader must be released after use.
3081
+ func (gz * gzipReader ) acquire () (* gzip.Reader , error ) {
3082
+ gz .mu .Lock ()
3083
+ defer gz .mu .Unlock ()
3084
+ if gz .zerr != nil {
3085
+ return nil , gz .zerr
3086
+ }
3049
3087
if gz .zr == nil {
3050
- if gz .zerr == nil {
3051
- gz .zr , gz .zerr = gzip .NewReader (gz .body )
3052
- }
3088
+ gz .zr , gz .zerr = gzipPoolGet (gz .body )
3053
3089
if gz .zerr != nil {
3054
- return 0 , gz .zerr
3090
+ return nil , gz .zerr
3055
3091
}
3056
3092
}
3093
+ ret := gz .zr
3094
+ gz .zr , gz .zerr = nil , errConcurrentReadOnResBody
3095
+ return ret , nil
3096
+ }
3097
+
3098
+ // release returns the gzip.Reader to the pool if Close was called during Read.
3099
+ func (gz * gzipReader ) release (zr * gzip.Reader ) {
3100
+ gz .mu .Lock ()
3101
+ defer gz .mu .Unlock ()
3102
+ if gz .zerr == errConcurrentReadOnResBody {
3103
+ gz .zr , gz .zerr = zr , nil
3104
+ } else { // errReadOnClosedResBody
3105
+ gzipPoolPut (zr )
3106
+ }
3107
+ }
3057
3108
3058
- gz .body .mu .Lock ()
3059
- if gz .body .closed {
3060
- err = errReadOnClosedResBody
3109
+ // close returns the gzip.Reader to the pool immediately or
3110
+ // signals release to do so after Read completes.
3111
+ func (gz * gzipReader ) close () {
3112
+ gz .mu .Lock ()
3113
+ defer gz .mu .Unlock ()
3114
+ if gz .zerr == nil && gz .zr != nil {
3115
+ gzipPoolPut (gz .zr )
3116
+ gz .zr = nil
3061
3117
}
3062
- gz .body .mu .Unlock ()
3118
+ gz .zerr = errReadOnClosedResBody
3119
+ }
3063
3120
3121
+ func (gz * gzipReader ) Read (p []byte ) (n int , err error ) {
3122
+ zr , err := gz .acquire ()
3064
3123
if err != nil {
3065
3124
return 0 , err
3066
3125
}
3067
- return gz .zr .Read (p )
3126
+ defer gz .release (zr )
3127
+
3128
+ return zr .Read (p )
3068
3129
}
3069
3130
3070
3131
func (gz * gzipReader ) Close () error {
3132
+ gz .close ()
3133
+
3071
3134
return gz .body .Close ()
3072
3135
}
3073
3136
0 commit comments