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) IsAlt() bool {
294 return true
295 }
296
297 func (cc *clientConn) IsReused() bool {
298 return false
299 }
300
301 func (cc *clientConn) NetConn() net.Conn {
302 return nil
303 }
304
305 func (cc *clientConn) CloseWithError(err error) {
306 cc.Close()
307 }
308
309 func (cc *clientConn) Err() error {
310 cc.mu.Lock()
311 defer cc.mu.Unlock()
312 if cc.closed {
313 return errors.New("connection closed")
314 }
315 return nil
316 }
317
318 func (cc *clientConn) Reserve() error {
319 cc.mu.Lock()
320 defer cc.mu.Unlock()
321 if cc.closed {
322 return errors.New("connection closed")
323 }
324 cc.reserved++
325 return nil
326 }
327
328 func (cc *clientConn) Release() {
329 cc.mu.Lock()
330 defer cc.mu.Unlock()
331
332
333 if cc.reserved > 0 {
334 cc.reserved--
335 }
336 }
337
338 func (cc *clientConn) Available() int {
339 cc.mu.Lock()
340 defer cc.mu.Unlock()
341 if cc.closed {
342 return 0
343 }
344
345
346
347
348
349
350
351 return math.MaxInt
352 }
353
354 func (cc *clientConn) InFlight() int {
355 cc.mu.Lock()
356 defer cc.mu.Unlock()
357 if cc.closed {
358 return 0
359 }
360 return cc.reserved + cc.active
361 }
362
363 func (cc *clientConn) maybeCallStateHook() {
364 if cc.stateHook != nil {
365 cc.stateHook()
366 }
367 }
368
369 func (cc *clientConn) handleControlStream(st *stream) error {
370
371
372 if err := st.readSettings(func(settingsType, settingsValue int64) error {
373 switch settingsType {
374 case settingsMaxFieldSectionSize:
375 _ = settingsValue
376 case settingsQPACKMaxTableCapacity:
377 _ = settingsValue
378 case settingsQPACKBlockedStreams:
379 _ = settingsValue
380 default:
381
382 }
383 return nil
384 }); err != nil {
385 return err
386 }
387
388 for {
389 ftype, err := st.readFrameHeader()
390 if err != nil {
391 return err
392 }
393 switch ftype {
394 case frameTypeCancelPush:
395
396
397
398
399 return &connectionError{
400 code: errH3IDError,
401 message: "CANCEL_PUSH received when no MAX_PUSH_ID has been sent",
402 }
403 case frameTypeGoaway:
404
405 return errH3NoError
406 default:
407
408 if err := st.discardUnknownFrame(ftype); err != nil {
409 return err
410 }
411 }
412 }
413 }
414
415 func (cc *clientConn) handleEncoderStream(*stream) error {
416
417 return nil
418 }
419
420 func (cc *clientConn) handleDecoderStream(*stream) error {
421
422 return nil
423 }
424
425 func (cc *clientConn) handlePushStream(*stream) error {
426
427
428
429 return &connectionError{
430 code: errH3IDError,
431 message: "push stream created when no MAX_PUSH_ID has been sent",
432 }
433 }
434
435 func (cc *clientConn) handleRequestStream(st *stream) error {
436
437
438
439 return &connectionError{
440 code: errH3StreamCreationError,
441 message: "server created bidirectional stream",
442 }
443 }
444
445
446 func (cc *clientConn) abort(err error) {
447 if e, ok := err.(*connectionError); ok {
448 cc.qconn.Abort(&quic.ConnectionCloseError{
449 Code: uint64(e.code),
450 Reason: e.message,
451 })
452 } else {
453 cc.qconn.Abort(err)
454 }
455 }
456
View as plain text