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