1
2
3
4
5 package http3
6
7 import (
8 "context"
9 "fmt"
10 "net/http"
11 "net/url"
12 "sync"
13
14 "golang.org/x/net/quic"
15 )
16
17
18
19
20
21
22
23
24 type transport struct {
25
26 config *quic.Config
27
28 listenQUIC func(addr string, config *quic.Config) (*quic.Endpoint, error)
29
30 mu sync.Mutex
31
32
33
34
35 endpoint *quic.Endpoint
36 activeConns map[*clientConn]struct{}
37 inFlightDials int
38 }
39
40
41
42 type netHTTPTransport struct {
43 *transport
44 }
45
46
47
48
49
50 func (t netHTTPTransport) RoundTrip(*http.Request) (*http.Response, error) {
51 panic("netHTTPTransport.RoundTrip should never be called")
52 }
53
54 func (t netHTTPTransport) DialClientConn(ctx context.Context, addr string, _ *url.URL, _ func()) (http.RoundTripper, error) {
55 return t.transport.dial(ctx, addr)
56 }
57
58 type TransportOpts struct {
59
60
61
62 ListenQUIC func(addr string, config *quic.Config) (*quic.Endpoint, error)
63
64
65
66
67
68
69 QUICConfig *quic.Config
70 }
71
72
73 func RegisterTransport(tr *http.Transport, opts TransportOpts) {
74 if opts.QUICConfig == nil {
75 opts.QUICConfig = &quic.Config{}
76 }
77 if opts.QUICConfig.TLSConfig == nil {
78 opts.QUICConfig.TLSConfig = tr.TLSClientConfig
79 }
80 if opts.ListenQUIC == nil {
81 opts.ListenQUIC = func(addr string, config *quic.Config) (*quic.Endpoint, error) {
82 return quic.Listen("udp", addr, config)
83 }
84 }
85 tr3 := &transport{
86
87 config: initConfig(opts.QUICConfig),
88 listenQUIC: opts.ListenQUIC,
89 activeConns: make(map[*clientConn]struct{}),
90 }
91 tr.RegisterProtocol("http/3", netHTTPTransport{tr3})
92 }
93
94 func (tr *transport) incInFlightDials() {
95 tr.mu.Lock()
96 defer tr.mu.Unlock()
97 tr.inFlightDials++
98 }
99
100 func (tr *transport) decInFlightDials() {
101 tr.mu.Lock()
102 defer tr.mu.Unlock()
103 tr.inFlightDials--
104 }
105
106 func (tr *transport) initEndpoint() (err error) {
107 tr.mu.Lock()
108 defer tr.mu.Unlock()
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 if tr.endpoint == nil {
135 tr.endpoint, err = tr.listenQUIC(":0", tr.config)
136 }
137 return err
138 }
139
140
141 func (tr *transport) dial(ctx context.Context, target string) (*clientConn, error) {
142 tr.incInFlightDials()
143 defer tr.decInFlightDials()
144
145 if err := tr.initEndpoint(); err != nil {
146 return nil, err
147 }
148 qconn, err := tr.endpoint.Dial(ctx, "udp", target, tr.config)
149 if err != nil {
150 return nil, err
151 }
152 return tr.newClientConn(ctx, qconn)
153 }
154
155
156
157
158
159
160
161 func (tr *transport) CloseIdleConnections() {
162 tr.mu.Lock()
163 defer tr.mu.Unlock()
164 if tr.endpoint == nil || len(tr.activeConns) > 0 || tr.inFlightDials > 0 {
165 return
166 }
167 tr.endpoint.Close(canceledCtx)
168 tr.endpoint = nil
169 }
170
171
172
173
174 type clientConn struct {
175 qconn *quic.Conn
176 genericConn
177
178 enc qpackEncoder
179 dec qpackDecoder
180 }
181
182 func (tr *transport) registerConn(cc *clientConn) {
183 tr.mu.Lock()
184 defer tr.mu.Unlock()
185 tr.activeConns[cc] = struct{}{}
186 }
187
188 func (tr *transport) unregisterConn(cc *clientConn) {
189 tr.mu.Lock()
190 defer tr.mu.Unlock()
191 delete(tr.activeConns, cc)
192 }
193
194 func (tr *transport) newClientConn(ctx context.Context, qconn *quic.Conn) (*clientConn, error) {
195 cc := &clientConn{
196 qconn: qconn,
197 }
198 tr.registerConn(cc)
199 cc.enc.init()
200
201
202 controlStream, err := newConnStream(ctx, cc.qconn, streamTypeControl)
203 if err != nil {
204 tr.unregisterConn(cc)
205 return nil, fmt.Errorf("http3: cannot create control stream: %v", err)
206 }
207 controlStream.writeSettings()
208 controlStream.Flush()
209
210 go func() {
211 cc.acceptStreams(qconn, cc)
212 tr.unregisterConn(cc)
213 }()
214 return cc, nil
215 }
216
217
218 func (cc *clientConn) Close() error {
219
220
221
222
223
224
225 return cc.qconn.Close()
226 }
227
228 func (cc *clientConn) Err() error {
229 return nil
230 }
231
232 func (cc *clientConn) Reserve() error {
233 return nil
234 }
235
236 func (cc *clientConn) Release() {
237 }
238
239 func (cc *clientConn) Available() int {
240 return 0
241 }
242
243 func (cc *clientConn) InFlight() int {
244 return 0
245 }
246
247 func (cc *clientConn) handleControlStream(st *stream) error {
248
249
250 if err := st.readSettings(func(settingsType, settingsValue int64) error {
251 switch settingsType {
252 case settingsMaxFieldSectionSize:
253 _ = settingsValue
254 case settingsQPACKMaxTableCapacity:
255 _ = settingsValue
256 case settingsQPACKBlockedStreams:
257 _ = settingsValue
258 default:
259
260 }
261 return nil
262 }); err != nil {
263 return err
264 }
265
266 for {
267 ftype, err := st.readFrameHeader()
268 if err != nil {
269 return err
270 }
271 switch ftype {
272 case frameTypeCancelPush:
273
274
275
276
277 return &connectionError{
278 code: errH3IDError,
279 message: "CANCEL_PUSH received when no MAX_PUSH_ID has been sent",
280 }
281 case frameTypeGoaway:
282
283 return errH3NoError
284 default:
285
286 if err := st.discardUnknownFrame(ftype); err != nil {
287 return err
288 }
289 }
290 }
291 }
292
293 func (cc *clientConn) handleEncoderStream(*stream) error {
294
295 return nil
296 }
297
298 func (cc *clientConn) handleDecoderStream(*stream) error {
299
300 return nil
301 }
302
303 func (cc *clientConn) handlePushStream(*stream) error {
304
305
306
307 return &connectionError{
308 code: errH3IDError,
309 message: "push stream created when no MAX_PUSH_ID has been sent",
310 }
311 }
312
313 func (cc *clientConn) handleRequestStream(st *stream) error {
314
315
316
317 return &connectionError{
318 code: errH3StreamCreationError,
319 message: "server created bidirectional stream",
320 }
321 }
322
323
324 func (cc *clientConn) abort(err error) {
325 if e, ok := err.(*connectionError); ok {
326 cc.qconn.Abort(&quic.ApplicationError{
327 Code: uint64(e.code),
328 Reason: e.message,
329 })
330 } else {
331 cc.qconn.Abort(err)
332 }
333 }
334
View as plain text