1
2
3
4
5 package httpcommon
6
7 import (
8 "compress/flate"
9 "compress/gzip"
10 "errors"
11 "io"
12 "io/fs"
13 "sync"
14 )
15
16 var errConcurrentRead = errors.New("http: concurrent read on response body")
17
18
19
20
21 type incomparable [0]func()
22
23
24
25
26
27 type GzipReader struct {
28 _ incomparable
29 Body io.ReadCloser
30 mu sync.Mutex
31 zr *gzip.Reader
32 zerr error
33 }
34
35 type eofReader struct{}
36
37 func (eofReader) Read([]byte) (int, error) { return 0, io.EOF }
38 func (eofReader) ReadByte() (byte, error) { return 0, io.EOF }
39
40 var gzipPool = sync.Pool{New: func() any { return new(gzip.Reader) }}
41
42
43 func gzipPoolGet(r io.Reader) (*gzip.Reader, error) {
44 zr := gzipPool.Get().(*gzip.Reader)
45 if err := zr.Reset(r); err != nil {
46 gzipPoolPut(zr)
47 return nil, err
48 }
49 return zr, nil
50 }
51
52
53 func gzipPoolPut(zr *gzip.Reader) {
54
55
56 var r flate.Reader = eofReader{}
57 zr.Reset(r)
58 gzipPool.Put(zr)
59 }
60
61
62
63 func (gz *GzipReader) acquire() (*gzip.Reader, error) {
64 gz.mu.Lock()
65 defer gz.mu.Unlock()
66 if gz.zerr != nil {
67 return nil, gz.zerr
68 }
69 if gz.zr == nil {
70
71
72
73
74 gz.zerr = errConcurrentRead
75 gz.mu.Unlock()
76 zr, err := gzipPoolGet(gz.Body)
77 gz.mu.Lock()
78
79 if gz.zerr != errConcurrentRead {
80 if zr != nil {
81 gzipPoolPut(zr)
82 }
83 return nil, gz.zerr
84 }
85 gz.zr, gz.zerr = zr, err
86 if gz.zerr != nil {
87 return nil, gz.zerr
88 }
89 }
90 ret := gz.zr
91 gz.zr, gz.zerr = nil, errConcurrentRead
92 return ret, nil
93 }
94
95
96 func (gz *GzipReader) release(zr *gzip.Reader) {
97 gz.mu.Lock()
98 defer gz.mu.Unlock()
99 if gz.zerr == errConcurrentRead {
100 gz.zr, gz.zerr = zr, nil
101 } else {
102 gzipPoolPut(zr)
103 }
104 }
105
106
107
108 func (gz *GzipReader) close() {
109 gz.mu.Lock()
110 defer gz.mu.Unlock()
111 if gz.zerr == nil && gz.zr != nil {
112 gzipPoolPut(gz.zr)
113 gz.zr = nil
114 }
115 gz.zerr = fs.ErrClosed
116 }
117
118 func (gz *GzipReader) Read(p []byte) (n int, err error) {
119 zr, err := gz.acquire()
120 if err != nil {
121 return 0, err
122 }
123 defer gz.release(zr)
124
125 return zr.Read(p)
126 }
127
128 func (gz *GzipReader) Close() error {
129 gz.close()
130
131 return gz.Body.Close()
132 }
133
View as plain text