@@ -335,25 +335,26 @@ type ClientConn struct {
335
335
idleTimeout time.Duration // or 0 for never
336
336
idleTimer timer
337
337
338
- mu sync.Mutex // guards following
339
- cond * sync.Cond // hold mu; broadcast on flow/closed changes
340
- flow outflow // our conn-level flow control quota (cs.outflow is per stream)
341
- inflow inflow // peer's conn-level flow control
342
- doNotReuse bool // whether conn is marked to not be reused for any future requests
343
- closing bool
344
- closed bool
345
- seenSettings bool // true if we've seen a settings frame, false otherwise
346
- wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
347
- goAway * GoAwayFrame // if non-nil, the GoAwayFrame we received
348
- goAwayDebug string // goAway frame's debug data, retained as a string
349
- streams map [uint32 ]* clientStream // client-initiated
350
- streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip
351
- nextStreamID uint32
352
- pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams
353
- pings map [[8 ]byte ]chan struct {} // in flight ping data to notification channel
354
- br * bufio.Reader
355
- lastActive time.Time
356
- lastIdle time.Time // time last idle
338
+ mu sync.Mutex // guards following
339
+ cond * sync.Cond // hold mu; broadcast on flow/closed changes
340
+ flow outflow // our conn-level flow control quota (cs.outflow is per stream)
341
+ inflow inflow // peer's conn-level flow control
342
+ doNotReuse bool // whether conn is marked to not be reused for any future requests
343
+ closing bool
344
+ closed bool
345
+ seenSettings bool // true if we've seen a settings frame, false otherwise
346
+ seenSettingsChan chan struct {} // closed when seenSettings is true or frame reading fails
347
+ wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
348
+ goAway * GoAwayFrame // if non-nil, the GoAwayFrame we received
349
+ goAwayDebug string // goAway frame's debug data, retained as a string
350
+ streams map [uint32 ]* clientStream // client-initiated
351
+ streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip
352
+ nextStreamID uint32
353
+ pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams
354
+ pings map [[8 ]byte ]chan struct {} // in flight ping data to notification channel
355
+ br * bufio.Reader
356
+ lastActive time.Time
357
+ lastIdle time.Time // time last idle
357
358
// Settings from peer: (also guarded by wmu)
358
359
maxFrameSize uint32
359
360
maxConcurrentStreams uint32
@@ -363,6 +364,7 @@ type ClientConn struct {
363
364
initialStreamRecvWindowSize int32
364
365
readIdleTimeout time.Duration
365
366
pingTimeout time.Duration
367
+ extendedConnecAllowed bool
366
368
367
369
// reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests.
368
370
// Write to reqHeaderMu to lock it, read from it to unlock.
@@ -752,6 +754,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
752
754
peerMaxHeaderListSize : 0xffffffffffffffff , // "infinite", per spec. Use 2^64-1 instead.
753
755
streams : make (map [uint32 ]* clientStream ),
754
756
singleUse : singleUse ,
757
+ seenSettingsChan : make (chan struct {}),
755
758
wantSettingsAck : true ,
756
759
readIdleTimeout : conf .SendPingTimeout ,
757
760
pingTimeout : conf .PingTimeout ,
@@ -1376,6 +1379,8 @@ func (cs *clientStream) doRequest(req *http.Request, streamf func(*clientStream)
1376
1379
cs .cleanupWriteRequest (err )
1377
1380
}
1378
1381
1382
+ var errExtendedConnectNotSupported = errors .New ("net/http: extended connect not supported by peer" )
1383
+
1379
1384
// writeRequest sends a request.
1380
1385
//
1381
1386
// It returns nil after the request is written, the response read,
@@ -1405,7 +1410,20 @@ func (cs *clientStream) writeRequest(req *http.Request, streamf func(*clientStre
1405
1410
return ctx .Err ()
1406
1411
}
1407
1412
1413
+ // wait for setting frames to be received, a server can change this value later,
1414
+ // but we just wait for the first settings frame
1415
+ var isExtendedConnect bool
1416
+ if req .Method == "CONNECT" && req .Header .Get (":protocol" ) != "" {
1417
+ isExtendedConnect = true
1418
+ <- cc .seenSettingsChan
1419
+ }
1420
+
1408
1421
cc .mu .Lock ()
1422
+ if isExtendedConnect && ! cc .extendedConnecAllowed {
1423
+ cc .mu .Unlock ()
1424
+ <- cc .reqHeaderMu
1425
+ return errExtendedConnectNotSupported
1426
+ }
1409
1427
if cc .idleTimer != nil {
1410
1428
cc .idleTimer .Stop ()
1411
1429
}
@@ -1910,7 +1928,7 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error)
1910
1928
1911
1929
func validateHeaders (hdrs http.Header ) string {
1912
1930
for k , vv := range hdrs {
1913
- if ! httpguts .ValidHeaderFieldName (k ) {
1931
+ if ! httpguts .ValidHeaderFieldName (k ) && k != ":protocol" {
1914
1932
return fmt .Sprintf ("name %q" , k )
1915
1933
}
1916
1934
for _ , v := range vv {
@@ -1926,6 +1944,10 @@ func validateHeaders(hdrs http.Header) string {
1926
1944
1927
1945
var errNilRequestURL = errors .New ("http2: Request.URI is nil" )
1928
1946
1947
+ func isNormalConnect (req * http.Request ) bool {
1948
+ return req .Method == "CONNECT" && req .Header .Get (":protocol" ) == ""
1949
+ }
1950
+
1929
1951
// requires cc.wmu be held.
1930
1952
func (cc * ClientConn ) encodeHeaders (req * http.Request , addGzipHeader bool , trailers string , contentLength int64 ) ([]byte , error ) {
1931
1953
cc .hbuf .Reset ()
@@ -1946,7 +1968,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
1946
1968
}
1947
1969
1948
1970
var path string
1949
- if req . Method != "CONNECT" {
1971
+ if ! isNormalConnect ( req ) {
1950
1972
path = req .URL .RequestURI ()
1951
1973
if ! validPseudoPath (path ) {
1952
1974
orig := path
@@ -1983,7 +2005,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
1983
2005
m = http .MethodGet
1984
2006
}
1985
2007
f (":method" , m )
1986
- if req . Method != "CONNECT" {
2008
+ if ! isNormalConnect ( req ) {
1987
2009
f (":path" , path )
1988
2010
f (":scheme" , req .URL .Scheme )
1989
2011
}
@@ -2370,6 +2392,9 @@ func (rl *clientConnReadLoop) run() error {
2370
2392
if VerboseLogs {
2371
2393
cc .vlogf ("http2: Transport conn %p received error from processing frame %v: %v" , cc , summarizeFrame (f ), err )
2372
2394
}
2395
+ if ! cc .seenSettings {
2396
+ close (cc .seenSettingsChan )
2397
+ }
2373
2398
return err
2374
2399
}
2375
2400
}
@@ -2917,6 +2942,15 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
2917
2942
case SettingHeaderTableSize :
2918
2943
cc .henc .SetMaxDynamicTableSize (s .Val )
2919
2944
cc .peerMaxHeaderTableSize = s .Val
2945
+ case SettingEnableConnectProtocol :
2946
+ if err := s .Valid (); err != nil {
2947
+ return err
2948
+ }
2949
+ // RFC 8441 section, https://datatracker.ietf.org/doc/html/rfc8441#section-3
2950
+ if s .Val == 0 && cc .extendedConnecAllowed {
2951
+ return ConnectionError (ErrCodeProtocol )
2952
+ }
2953
+ cc .extendedConnecAllowed = s .Val == 1
2920
2954
default :
2921
2955
cc .vlogf ("Unhandled Setting: %v" , s )
2922
2956
}
@@ -2934,6 +2968,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
2934
2968
// connection can establish to our default.
2935
2969
cc .maxConcurrentStreams = defaultMaxConcurrentStreams
2936
2970
}
2971
+ close (cc .seenSettingsChan )
2937
2972
cc .seenSettings = true
2938
2973
}
2939
2974
0 commit comments