1
2
3
4
5 package http3
6
7 import (
8 "context"
9 "crypto/tls"
10 "errors"
11 "fmt"
12 "math"
13 "net"
14 "net/http"
15 "net/url"
16 "sync"
17
18 "golang.org/x/net/quic"
19 )
20
21
22
23
24
25
26
27
28 type transport struct {
29 tr1 *http.Transport
30 opts TransportOpts
31
32 mu sync.Mutex
33
34
35
36
37 endpoint *quic.Endpoint
38 activeConns map[*clientConn]struct{}
39 inFlightDials int
40 }
41
42
43
44 type netHTTPTransport struct {
45 *transport
46 }
47
48
49 func (t netHTTPTransport) Registered(tr1 *http.Transport) {
50 t.transport.tr1 = tr1
51 }
52
53
54
55
56
57 func (t netHTTPTransport) RoundTrip(*http.Request) (*http.Response, error) {
58 panic("netHTTPTransport.RoundTrip should never be called")
59 }
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80 func (t netHTTPTransport) DialClientConn(ctx context.Context, addr string, _ *url.URL, tlsConfig *tls.Config, stateHook func()) (http.RoundTripper, error) {
81 return t.transport.dial(ctx, addr, tlsConfig, stateHook)
82 }
83
84 type TransportOpts struct {
85
86
87
88 ListenQUIC func(addr string, config *quic.Config) (*quic.Endpoint, error)
89
90
91
92
93
94 ListenPacket func(network, addr string) (net.PacketConn, error)
95
96
97
98
99
100
101
102 QUICConfig *quic.Config
103 }
104
105
106 func RegisterTransport(tr *http.Transport, opts TransportOpts) error {
107 tr3 := &transport{
108 opts: opts,
109 activeConns: make(map[*clientConn]struct{}),
110 }
111
112 tr.RegisterProtocol("http/3", netHTTPTransport{tr3})
113 if tr3.tr1 != tr {
114 return errors.New("http3: net/http does not support HTTP/3")
115 }
116 return nil
117 }
118
119 func (tr *transport) incInFlightDials() {
120 tr.mu.Lock()
121 defer tr.mu.Unlock()
122 tr.inFlightDials++
123 }
124
125 func (tr *transport) decInFlightDials() {
126 tr.mu.Lock()
127 defer tr.mu.Unlock()
128 tr.inFlightDials--
129 }
130
131 func (tr *transport) initEndpoint() (err error) {
132 tr.mu.Lock()
133 defer tr.mu.Unlock()
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 if tr.endpoint == nil {
160 quicConfig := newQUICConfig(tr.opts.QUICConfig, tr.tr1.TLSClientConfig)
161 if tr.opts.ListenQUIC != nil {
162 tr.endpoint, err = tr.opts.ListenQUIC(":0", quicConfig)
163 } else if tr.opts.ListenPacket != nil {
164 var conn net.PacketConn
165 conn, err = tr.opts.ListenPacket("udp", ":0")
166 if err != nil {
167 return err
168 }
169 tr.endpoint, err = quic.NewEndpoint(conn, quicConfig)
170 if err != nil {
171 conn.Close()
172 }
173 } else {
174 tr.endpoint, err = quic.Listen("udp", ":0", quicConfig)
175 }
176 }
177 return err
178 }
179
180
181 func (tr *transport) dial(ctx context.Context, target string, tlsConfig *tls.Config, stateHook func()) (*clientConn, error) {
182 tr.incInFlightDials()
183 defer tr.decInFlightDials()
184
185 if err := tr.initEndpoint(); err != nil {
186 return nil, err
187 }
188 qconn, err := tr.endpoint.Dial(ctx, "udp", target, newQUICConfig(tr.opts.QUICConfig, tlsConfig))
189 if err != nil {
190 return nil, err
191 }
192 return tr.newClientConn(ctx, qconn, stateHook)
193 }
194
195
196
197
198
199
200
201 func (tr *transport) CloseIdleConnections() {
202 tr.mu.Lock()
203 defer tr.mu.Unlock()
204 if tr.endpoint == nil || len(tr.activeConns) > 0 || tr.inFlightDials > 0 {
205 return
206 }
207 tr.endpoint.Close(canceledCtx)
208 tr.endpoint = nil
209 }
210
211
212
213
214 type clientConn struct {
215 tr *transport
216 unregistered chan struct{}
217
218 qconn *quic.Conn
219 genericConn
220
221 enc qpackEncoder
222 dec qpackDecoder
223
224
225 reserved int
226 active int
227 closed bool
228
229 stateHook func()
230 }
231
232 func (tr *transport) registerConn(cc *clientConn) {
233 tr.mu.Lock()
234 defer tr.mu.Unlock()
235 tr.activeConns[cc] = struct{}{}
236 }
237
238 func (tr *transport) unregisterConn(cc *clientConn) {
239 tr.mu.Lock()
240 defer tr.mu.Unlock()
241 delete(tr.activeConns, cc)
242 close(cc.unregistered)
243 }
244
245 func (tr *transport) maxResponseHeaderBytes() int64 {
246 if tr.tr1 != nil && tr.tr1.MaxResponseHeaderBytes > 0 {
247 return tr.tr1.MaxResponseHeaderBytes
248 }
249 return 10 << 20
250 }
251
252 func (tr *transport) newClientConn(ctx context.Context, qconn *quic.Conn, stateHook func()) (*clientConn, error) {
253 cc := &clientConn{
254 tr: tr,
255 unregistered: make(chan struct{}),
256 qconn: qconn,
257 stateHook: stateHook,
258 }
259 tr.registerConn(cc)
260 cc.enc.init()
261
262
263 controlStream, err := newConnStream(ctx, cc.qconn, streamTypeControl)
264 if err != nil {
265 tr.unregisterConn(cc)
266 return nil, fmt.Errorf("http3: cannot create control stream: %v", err)
267 }
268 controlStream.writeSettings(settingsMaxFieldSectionSize, tr.maxResponseHeaderBytes())
269 controlStream.Flush()
270
271 go func() {
272 cc.acceptStreams(qconn, cc)
273 cc.mu.Lock()
274 cc.closed = true
275 cc.mu.Unlock()
276 cc.maybeCallStateHook()
277 tr.unregisterConn(cc)
278 }()
279 return cc, nil
280 }
281
282 func (cc *clientConn) Close() error {
283 err := cc.qconn.Close()
284
285
286
287
288
289 <-cc.unregistered
290 return err
291 }
292
293 func (cc *clientConn) Proto() int {
294 return 3
295 }
296
297 func (cc *clientConn) IsAlt() bool {
298 return true
299 }
300
301 func (cc *clientConn) IsReused() bool {
302 return false
303 }
304
305 func (cc *clientConn) NetConn() net.Conn {
306 return nil
307 }
308
309 func (cc *clientConn) CloseWithError(err error) {
310 cc.Close()
311 }
312
313 func (cc *clientConn) Err() error {
314 cc.mu.Lock()
315 defer cc.mu.Unlock()
316 if cc.closed {
317 return errors.New("connection closed")
318 }
319 return nil
320 }
321
322 func (cc *clientConn) Reserve() error {
323 cc.mu.Lock()
324 defer cc.mu.Unlock()
325 if cc.closed {
326 return errors.New("connection closed")
327 }
328 cc.reserved++
329 return nil
330 }
331
332 func (cc *clientConn) Release() {
333 cc.mu.Lock()
334 defer cc.mu.Unlock()
335
336
337 if cc.reserved > 0 {
338 cc.reserved--
339 }
340 }
341
342 func (cc *clientConn) Available() int {
343 cc.mu.Lock()
344 defer cc.mu.Unlock()
345 if cc.closed {
346 return 0
347 }
348
349
350
351
352
353
354
355 return math.MaxInt
356 }
357
358 func (cc *clientConn) InFlight() int {
359 cc.mu.Lock()
360 defer cc.mu.Unlock()
361 if cc.closed {
362 return 0
363 }
364 return cc.reserved + cc.active
365 }
366
367 func (cc *clientConn) maybeCallStateHook() {
368 if cc.stateHook != nil {
369 cc.stateHook()
370 }
371 }
372
373 func (cc *clientConn) handleControlStream(st *stream) error {
374
375
376 if err := st.readSettings(func(settingsType, settingsValue int64) error {
377 switch settingsType {
378 case settingsMaxFieldSectionSize:
379 _ = settingsValue
380 case settingsQPACKMaxTableCapacity:
381 _ = settingsValue
382 case settingsQPACKBlockedStreams:
383 _ = settingsValue
384 default:
385
386 }
387 return nil
388 }); err != nil {
389 return err
390 }
391
392 for {
393 ftype, err := st.readFrameHeader()
394 if err != nil {
395 return err
396 }
397 switch ftype {
398 case frameTypeCancelPush:
399
400
401
402
403 return &connectionError{
404 code: errH3IDError,
405 message: "CANCEL_PUSH received when no MAX_PUSH_ID has been sent",
406 }
407 case frameTypeGoaway:
408
409 return errH3NoError
410 default:
411
412 if err := st.discardUnknownFrame(ftype); err != nil {
413 return err
414 }
415 }
416 }
417 }
418
419 func (cc *clientConn) handleEncoderStream(*stream) error {
420
421 return nil
422 }
423
424 func (cc *clientConn) handleDecoderStream(*stream) error {
425
426 return nil
427 }
428
429 func (cc *clientConn) handlePushStream(*stream) error {
430
431
432
433 return &connectionError{
434 code: errH3IDError,
435 message: "push stream created when no MAX_PUSH_ID has been sent",
436 }
437 }
438
439 func (cc *clientConn) handleRequestStream(st *stream) error {
440
441
442
443 return &connectionError{
444 code: errH3StreamCreationError,
445 message: "server created bidirectional stream",
446 }
447 }
448
449
450 func (cc *clientConn) abort(err error) {
451 if e, ok := err.(*connectionError); ok {
452 cc.qconn.Abort(&quic.ConnectionCloseError{
453 Code: uint64(e.code),
454 Reason: e.message,
455 })
456 } else {
457 cc.qconn.Abort(err)
458 }
459 }
460
View as plain text