@@ -350,31 +350,33 @@ type ClientConn struct {
350
350
idleTimeout time.Duration // or 0 for never
351
351
idleTimer timer
352
352
353
- mu sync.Mutex // guards following
354
- cond * sync.Cond // hold mu; broadcast on flow/closed changes
355
- flow outflow // our conn-level flow control quota (cs.outflow is per stream)
356
- inflow inflow // peer's conn-level flow control
357
- doNotReuse bool // whether conn is marked to not be reused for any future requests
358
- closing bool
359
- closed bool
360
- seenSettings bool // true if we've seen a settings frame, false otherwise
361
- wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
362
- goAway * GoAwayFrame // if non-nil, the GoAwayFrame we received
363
- goAwayDebug string // goAway frame's debug data, retained as a string
364
- streams map [uint32 ]* clientStream // client-initiated
365
- streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip
366
- nextStreamID uint32
367
- pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams
368
- pings map [[8 ]byte ]chan struct {} // in flight ping data to notification channel
369
- br * bufio.Reader
370
- lastActive time.Time
371
- lastIdle time.Time // time last idle
353
+ mu sync.Mutex // guards following
354
+ cond * sync.Cond // hold mu; broadcast on flow/closed changes
355
+ flow outflow // our conn-level flow control quota (cs.outflow is per stream)
356
+ inflow inflow // peer's conn-level flow control
357
+ doNotReuse bool // whether conn is marked to not be reused for any future requests
358
+ closing bool
359
+ closed bool
360
+ seenSettings bool // true if we've seen a settings frame, false otherwise
361
+ seenSettingsChan chan struct {} // closed when seenSettings is true or frame reading fails
362
+ wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
363
+ goAway * GoAwayFrame // if non-nil, the GoAwayFrame we received
364
+ goAwayDebug string // goAway frame's debug data, retained as a string
365
+ streams map [uint32 ]* clientStream // client-initiated
366
+ streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip
367
+ nextStreamID uint32
368
+ pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams
369
+ pings map [[8 ]byte ]chan struct {} // in flight ping data to notification channel
370
+ br * bufio.Reader
371
+ lastActive time.Time
372
+ lastIdle time.Time // time last idle
372
373
// Settings from peer: (also guarded by wmu)
373
374
maxFrameSize uint32
374
375
maxConcurrentStreams uint32
375
376
peerMaxHeaderListSize uint64
376
377
peerMaxHeaderTableSize uint32
377
378
initialWindowSize uint32
379
+ extendedConnecAllowed bool
378
380
379
381
// reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests.
380
382
// Write to reqHeaderMu to lock it, read from it to unlock.
@@ -788,6 +790,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
788
790
peerMaxHeaderListSize : 0xffffffffffffffff , // "infinite", per spec. Use 2^64-1 instead.
789
791
streams : make (map [uint32 ]* clientStream ),
790
792
singleUse : singleUse ,
793
+ seenSettingsChan : make (chan struct {}),
791
794
wantSettingsAck : true ,
792
795
pings : make (map [[8 ]byte ]chan struct {}),
793
796
reqHeaderMu : make (chan struct {}, 1 ),
@@ -1411,6 +1414,8 @@ func (cs *clientStream) doRequest(req *http.Request, streamf func(*clientStream)
1411
1414
cs .cleanupWriteRequest (err )
1412
1415
}
1413
1416
1417
+ var errExtendedConnectNotSupported = errors .New ("net/http: extended connect not supported by peer" )
1418
+
1414
1419
// writeRequest sends a request.
1415
1420
//
1416
1421
// It returns nil after the request is written, the response read,
@@ -1440,7 +1445,20 @@ func (cs *clientStream) writeRequest(req *http.Request, streamf func(*clientStre
1440
1445
return ctx .Err ()
1441
1446
}
1442
1447
1448
+ // wait for setting frames to be received, a server can change this value later,
1449
+ // but we just wait for the first settings frame
1450
+ var isExtendedConnect bool
1451
+ if req .Method == "CONNECT" && req .Header .Get (":protocol" ) != "" {
1452
+ isExtendedConnect = true
1453
+ <- cc .seenSettingsChan
1454
+ }
1455
+
1443
1456
cc .mu .Lock ()
1457
+ if isExtendedConnect && ! cc .extendedConnecAllowed {
1458
+ cc .mu .Unlock ()
1459
+ <- cc .reqHeaderMu
1460
+ return errExtendedConnectNotSupported
1461
+ }
1444
1462
if cc .idleTimer != nil {
1445
1463
cc .idleTimer .Stop ()
1446
1464
}
@@ -1945,7 +1963,7 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error)
1945
1963
1946
1964
func validateHeaders (hdrs http.Header ) string {
1947
1965
for k , vv := range hdrs {
1948
- if ! httpguts .ValidHeaderFieldName (k ) {
1966
+ if ! httpguts .ValidHeaderFieldName (k ) && k != ":protocol" {
1949
1967
return fmt .Sprintf ("name %q" , k )
1950
1968
}
1951
1969
for _ , v := range vv {
@@ -1961,6 +1979,10 @@ func validateHeaders(hdrs http.Header) string {
1961
1979
1962
1980
var errNilRequestURL = errors .New ("http2: Request.URI is nil" )
1963
1981
1982
+ func isNormalConnect (req * http.Request ) bool {
1983
+ return req .Method == "CONNECT" && req .Header .Get (":protocol" ) == ""
1984
+ }
1985
+
1964
1986
// requires cc.wmu be held.
1965
1987
func (cc * ClientConn ) encodeHeaders (req * http.Request , addGzipHeader bool , trailers string , contentLength int64 ) ([]byte , error ) {
1966
1988
cc .hbuf .Reset ()
@@ -1981,7 +2003,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
1981
2003
}
1982
2004
1983
2005
var path string
1984
- if req . Method != "CONNECT" {
2006
+ if ! isNormalConnect ( req ) {
1985
2007
path = req .URL .RequestURI ()
1986
2008
if ! validPseudoPath (path ) {
1987
2009
orig := path
@@ -2018,7 +2040,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
2018
2040
m = http .MethodGet
2019
2041
}
2020
2042
f (":method" , m )
2021
- if req . Method != "CONNECT" {
2043
+ if ! isNormalConnect ( req ) {
2022
2044
f (":path" , path )
2023
2045
f (":scheme" , req .URL .Scheme )
2024
2046
}
@@ -2405,6 +2427,9 @@ func (rl *clientConnReadLoop) run() error {
2405
2427
if VerboseLogs {
2406
2428
cc .vlogf ("http2: Transport conn %p received error from processing frame %v: %v" , cc , summarizeFrame (f ), err )
2407
2429
}
2430
+ if ! cc .seenSettings {
2431
+ close (cc .seenSettingsChan )
2432
+ }
2408
2433
return err
2409
2434
}
2410
2435
}
@@ -2952,6 +2977,15 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
2952
2977
case SettingHeaderTableSize :
2953
2978
cc .henc .SetMaxDynamicTableSize (s .Val )
2954
2979
cc .peerMaxHeaderTableSize = s .Val
2980
+ case SettingEnableConnectProtocol :
2981
+ if err := s .Valid (); err != nil {
2982
+ return err
2983
+ }
2984
+ // RFC 8441 section, https://datatracker.ietf.org/doc/html/rfc8441#section-3
2985
+ if s .Val == 0 && cc .extendedConnecAllowed {
2986
+ return ConnectionError (ErrCodeProtocol )
2987
+ }
2988
+ cc .extendedConnecAllowed = s .Val == 1
2955
2989
default :
2956
2990
cc .vlogf ("Unhandled Setting: %v" , s )
2957
2991
}
@@ -2969,6 +3003,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
2969
3003
// connection can establish to our default.
2970
3004
cc .maxConcurrentStreams = defaultMaxConcurrentStreams
2971
3005
}
3006
+ close (cc .seenSettingsChan )
2972
3007
cc .seenSettings = true
2973
3008
}
2974
3009
0 commit comments