@@ -364,7 +364,7 @@ type ClientConn struct {
364
364
initialStreamRecvWindowSize int32
365
365
readIdleTimeout time.Duration
366
366
pingTimeout time.Duration
367
- extendedConnecAllowed bool
367
+ extendedConnectAllowed bool
368
368
369
369
// reqHeaderMu is a 1-element semaphore channel controlling access to sending new requests.
370
370
// Write to reqHeaderMu to lock it, read from it to unlock.
@@ -1396,34 +1396,42 @@ func (cs *clientStream) writeRequest(req *http.Request, streamf func(*clientStre
1396
1396
return err
1397
1397
}
1398
1398
1399
+ // wait for setting frames to be received, a server can change this value later,
1400
+ // but we just wait for the first settings frame
1401
+ var isExtendedConnect bool
1402
+ if req .Method == "CONNECT" && req .Header .Get (":protocol" ) != "" {
1403
+ isExtendedConnect = true
1404
+ }
1405
+
1399
1406
// Acquire the new-request lock by writing to reqHeaderMu.
1400
1407
// This lock guards the critical section covering allocating a new stream ID
1401
1408
// (requires mu) and creating the stream (requires wmu).
1402
1409
if cc .reqHeaderMu == nil {
1403
1410
panic ("RoundTrip on uninitialized ClientConn" ) // for tests
1404
1411
}
1405
- select {
1406
- case cc .reqHeaderMu <- struct {}{}:
1407
- case <- cs .reqCancel :
1408
- return errRequestCanceled
1409
- case <- ctx .Done ():
1410
- return ctx .Err ()
1411
- }
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
1412
+ if isExtendedConnect {
1413
+ select {
1414
+ case cc .reqHeaderMu <- struct {}{}:
1415
+ case <- cs .reqCancel :
1416
+ return errRequestCanceled
1417
+ case <- ctx .Done ():
1418
+ return ctx .Err ()
1419
+ case <- cc .seenSettingsChan :
1420
+ if ! cc .extendedConnectAllowed {
1421
+ return errExtendedConnectNotSupported
1422
+ }
1423
+ }
1424
+ } else {
1425
+ select {
1426
+ case cc .reqHeaderMu <- struct {}{}:
1427
+ case <- cs .reqCancel :
1428
+ return errRequestCanceled
1429
+ case <- ctx .Done ():
1430
+ return ctx .Err ()
1431
+ }
1419
1432
}
1420
1433
1421
1434
cc .mu .Lock ()
1422
- if isExtendedConnect && ! cc .extendedConnecAllowed {
1423
- cc .mu .Unlock ()
1424
- <- cc .reqHeaderMu
1425
- return errExtendedConnectNotSupported
1426
- }
1427
1435
if cc .idleTimer != nil {
1428
1436
cc .idleTimer .Stop ()
1429
1437
}
@@ -2946,11 +2954,17 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
2946
2954
if err := s .Valid (); err != nil {
2947
2955
return err
2948
2956
}
2949
- // RFC 8441 section, https://datatracker.ietf.org/doc/html/rfc8441#section-3
2950
- if s .Val == 0 && cc .extendedConnecAllowed {
2951
- return ConnectionError (ErrCodeProtocol )
2957
+ // If the peer wants to send us SETTINGS_ENABLE_CONNECT_PROTOCOL,
2958
+ // we require that it do so in the first SETTINGS frame.
2959
+ //
2960
+ // When we attempt to use extended CONNECT, we wait for the first
2961
+ // SETTINGS frame to see if the server supports it. If we let the
2962
+ // server enable the feature with a later SETTINGS frame, then
2963
+ // users will see inconsistent results depending on whether we've
2964
+ // seen that frame or not.
2965
+ if ! cc .seenSettings {
2966
+ cc .extendedConnectAllowed = s .Val == 1
2952
2967
}
2953
- cc .extendedConnecAllowed = s .Val == 1
2954
2968
default :
2955
2969
cc .vlogf ("Unhandled Setting: %v" , s )
2956
2970
}
0 commit comments