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