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