@@ -368,25 +368,26 @@ type ClientConn struct {
368
368
idleTimeout time.Duration // or 0 for never
369
369
idleTimer timer
370
370
371
- mu sync.Mutex // guards following
372
- cond * sync.Cond // hold mu; broadcast on flow/closed changes
373
- flow outflow // our conn-level flow control quota (cs.outflow is per stream)
374
- inflow inflow // peer's conn-level flow control
375
- doNotReuse bool // whether conn is marked to not be reused for any future requests
376
- closing bool
377
- closed bool
378
- seenSettings bool // true if we've seen a settings frame, false otherwise
379
- wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
380
- goAway * GoAwayFrame // if non-nil, the GoAwayFrame we received
381
- goAwayDebug string // goAway frame's debug data, retained as a string
382
- streams map [uint32 ]* clientStream // client-initiated
383
- streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip
384
- nextStreamID uint32
385
- pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams
386
- pings map [[8 ]byte ]chan struct {} // in flight ping data to notification channel
387
- br * bufio.Reader
388
- lastActive time.Time
389
- lastIdle time.Time // time last idle
371
+ mu sync.Mutex // guards following
372
+ cond * sync.Cond // hold mu; broadcast on flow/closed changes
373
+ flow outflow // our conn-level flow control quota (cs.outflow is per stream)
374
+ inflow inflow // peer's conn-level flow control
375
+ doNotReuse bool // whether conn is marked to not be reused for any future requests
376
+ closing bool
377
+ closed bool
378
+ seenSettings bool // true if we've seen a settings frame, false otherwise
379
+ seenSettingsChan chan struct {} // closed when seenSettings is true or frame reading fails
380
+ wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
381
+ goAway * GoAwayFrame // if non-nil, the GoAwayFrame we received
382
+ goAwayDebug string // goAway frame's debug data, retained as a string
383
+ streams map [uint32 ]* clientStream // client-initiated
384
+ streamsReserved int // incr by ReserveNewRequest; decr on RoundTrip
385
+ nextStreamID uint32
386
+ pendingRequests int // requests blocked and waiting to be sent because len(streams) == maxConcurrentStreams
387
+ pings map [[8 ]byte ]chan struct {} // in flight ping data to notification channel
388
+ br * bufio.Reader
389
+ lastActive time.Time
390
+ lastIdle time.Time // time last idle
390
391
// Settings from peer: (also guarded by wmu)
391
392
maxFrameSize uint32
392
393
maxConcurrentStreams uint32
@@ -396,6 +397,7 @@ type ClientConn struct {
396
397
initialStreamRecvWindowSize int32
397
398
readIdleTimeout time.Duration
398
399
pingTimeout time.Duration
400
+ extendedConnectAllowed bool
399
401
400
402
// pendingResets is the number of RST_STREAM frames we have sent to the peer,
401
403
// without confirming that the peer has received them. When we send a RST_STREAM,
@@ -819,6 +821,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
819
821
peerMaxHeaderListSize : 0xffffffffffffffff , // "infinite", per spec. Use 2^64-1 instead.
820
822
streams : make (map [uint32 ]* clientStream ),
821
823
singleUse : singleUse ,
824
+ seenSettingsChan : make (chan struct {}),
822
825
wantSettingsAck : true ,
823
826
readIdleTimeout : conf .SendPingTimeout ,
824
827
pingTimeout : conf .PingTimeout ,
@@ -1466,6 +1469,8 @@ func (cs *clientStream) doRequest(req *http.Request, streamf func(*clientStream)
1466
1469
cs .cleanupWriteRequest (err )
1467
1470
}
1468
1471
1472
+ var errExtendedConnectNotSupported = errors .New ("net/http: extended connect not supported by peer" )
1473
+
1469
1474
// writeRequest sends a request.
1470
1475
//
1471
1476
// It returns nil after the request is written, the response read,
@@ -1481,12 +1486,31 @@ func (cs *clientStream) writeRequest(req *http.Request, streamf func(*clientStre
1481
1486
return err
1482
1487
}
1483
1488
1489
+ // wait for setting frames to be received, a server can change this value later,
1490
+ // but we just wait for the first settings frame
1491
+ var isExtendedConnect bool
1492
+ if req .Method == "CONNECT" && req .Header .Get (":protocol" ) != "" {
1493
+ isExtendedConnect = true
1494
+ }
1495
+
1484
1496
// Acquire the new-request lock by writing to reqHeaderMu.
1485
1497
// This lock guards the critical section covering allocating a new stream ID
1486
1498
// (requires mu) and creating the stream (requires wmu).
1487
1499
if cc .reqHeaderMu == nil {
1488
1500
panic ("RoundTrip on uninitialized ClientConn" ) // for tests
1489
1501
}
1502
+ if isExtendedConnect {
1503
+ select {
1504
+ case <- cs .reqCancel :
1505
+ return errRequestCanceled
1506
+ case <- ctx .Done ():
1507
+ return ctx .Err ()
1508
+ case <- cc .seenSettingsChan :
1509
+ if ! cc .extendedConnectAllowed {
1510
+ return errExtendedConnectNotSupported
1511
+ }
1512
+ }
1513
+ }
1490
1514
select {
1491
1515
case cc .reqHeaderMu <- struct {}{}:
1492
1516
case <- cs .reqCancel :
@@ -2030,7 +2054,7 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error)
2030
2054
2031
2055
func validateHeaders (hdrs http.Header ) string {
2032
2056
for k , vv := range hdrs {
2033
- if ! httpguts .ValidHeaderFieldName (k ) {
2057
+ if ! httpguts .ValidHeaderFieldName (k ) && k != ":protocol" {
2034
2058
return fmt .Sprintf ("name %q" , k )
2035
2059
}
2036
2060
for _ , v := range vv {
@@ -2046,6 +2070,10 @@ func validateHeaders(hdrs http.Header) string {
2046
2070
2047
2071
var errNilRequestURL = errors .New ("http2: Request.URI is nil" )
2048
2072
2073
+ func isNormalConnect (req * http.Request ) bool {
2074
+ return req .Method == "CONNECT" && req .Header .Get (":protocol" ) == ""
2075
+ }
2076
+
2049
2077
// requires cc.wmu be held.
2050
2078
func (cc * ClientConn ) encodeHeaders (req * http.Request , addGzipHeader bool , trailers string , contentLength int64 ) ([]byte , error ) {
2051
2079
cc .hbuf .Reset ()
@@ -2066,7 +2094,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
2066
2094
}
2067
2095
2068
2096
var path string
2069
- if req . Method != "CONNECT" {
2097
+ if ! isNormalConnect ( req ) {
2070
2098
path = req .URL .RequestURI ()
2071
2099
if ! validPseudoPath (path ) {
2072
2100
orig := path
@@ -2103,7 +2131,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
2103
2131
m = http .MethodGet
2104
2132
}
2105
2133
f (":method" , m )
2106
- if req . Method != "CONNECT" {
2134
+ if ! isNormalConnect ( req ) {
2107
2135
f (":path" , path )
2108
2136
f (":scheme" , req .URL .Scheme )
2109
2137
}
@@ -2507,6 +2535,9 @@ func (rl *clientConnReadLoop) run() error {
2507
2535
if VerboseLogs {
2508
2536
cc .vlogf ("http2: Transport conn %p received error from processing frame %v: %v" , cc , summarizeFrame (f ), err )
2509
2537
}
2538
+ if ! cc .seenSettings {
2539
+ close (cc .seenSettingsChan )
2540
+ }
2510
2541
return err
2511
2542
}
2512
2543
}
@@ -3073,6 +3104,21 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
3073
3104
case SettingHeaderTableSize :
3074
3105
cc .henc .SetMaxDynamicTableSize (s .Val )
3075
3106
cc .peerMaxHeaderTableSize = s .Val
3107
+ case SettingEnableConnectProtocol :
3108
+ if err := s .Valid (); err != nil {
3109
+ return err
3110
+ }
3111
+ // If the peer wants to send us SETTINGS_ENABLE_CONNECT_PROTOCOL,
3112
+ // we require that it do so in the first SETTINGS frame.
3113
+ //
3114
+ // When we attempt to use extended CONNECT, we wait for the first
3115
+ // SETTINGS frame to see if the server supports it. If we let the
3116
+ // server enable the feature with a later SETTINGS frame, then
3117
+ // users will see inconsistent results depending on whether we've
3118
+ // seen that frame or not.
3119
+ if ! cc .seenSettings {
3120
+ cc .extendedConnectAllowed = s .Val == 1
3121
+ }
3076
3122
default :
3077
3123
cc .vlogf ("Unhandled Setting: %v" , s )
3078
3124
}
@@ -3090,6 +3136,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
3090
3136
// connection can establish to our default.
3091
3137
cc .maxConcurrentStreams = defaultMaxConcurrentStreams
3092
3138
}
3139
+ close (cc .seenSettingsChan )
3093
3140
cc .seenSettings = true
3094
3141
}
3095
3142
0 commit comments