@@ -368,25 +368,26 @@ type ClientConn struct {
368368 idleTimeout time.Duration // or 0 for never
369369 idleTimer timer
370370
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
390391 // Settings from peer: (also guarded by wmu)
391392 maxFrameSize uint32
392393 maxConcurrentStreams uint32
@@ -396,6 +397,7 @@ type ClientConn struct {
396397 initialStreamRecvWindowSize int32
397398 readIdleTimeout time.Duration
398399 pingTimeout time.Duration
400+ extendedConnectAllowed bool
399401
400402 // pendingResets is the number of RST_STREAM frames we have sent to the peer,
401403 // 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
819821 peerMaxHeaderListSize : 0xffffffffffffffff , // "infinite", per spec. Use 2^64-1 instead.
820822 streams : make (map [uint32 ]* clientStream ),
821823 singleUse : singleUse ,
824+ seenSettingsChan : make (chan struct {}),
822825 wantSettingsAck : true ,
823826 readIdleTimeout : conf .SendPingTimeout ,
824827 pingTimeout : conf .PingTimeout ,
@@ -1466,6 +1469,8 @@ func (cs *clientStream) doRequest(req *http.Request, streamf func(*clientStream)
14661469 cs .cleanupWriteRequest (err )
14671470}
14681471
1472+ var errExtendedConnectNotSupported = errors .New ("net/http: extended connect not supported by peer" )
1473+
14691474// writeRequest sends a request.
14701475//
14711476// 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
14811486 return err
14821487 }
14831488
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+
14841496 // Acquire the new-request lock by writing to reqHeaderMu.
14851497 // This lock guards the critical section covering allocating a new stream ID
14861498 // (requires mu) and creating the stream (requires wmu).
14871499 if cc .reqHeaderMu == nil {
14881500 panic ("RoundTrip on uninitialized ClientConn" ) // for tests
14891501 }
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+ }
14901514 select {
14911515 case cc .reqHeaderMu <- struct {}{}:
14921516 case <- cs .reqCancel :
@@ -2030,7 +2054,7 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error)
20302054
20312055func validateHeaders (hdrs http.Header ) string {
20322056 for k , vv := range hdrs {
2033- if ! httpguts .ValidHeaderFieldName (k ) {
2057+ if ! httpguts .ValidHeaderFieldName (k ) && k != ":protocol" {
20342058 return fmt .Sprintf ("name %q" , k )
20352059 }
20362060 for _ , v := range vv {
@@ -2046,6 +2070,10 @@ func validateHeaders(hdrs http.Header) string {
20462070
20472071var errNilRequestURL = errors .New ("http2: Request.URI is nil" )
20482072
2073+ func isNormalConnect (req * http.Request ) bool {
2074+ return req .Method == "CONNECT" && req .Header .Get (":protocol" ) == ""
2075+ }
2076+
20492077// requires cc.wmu be held.
20502078func (cc * ClientConn ) encodeHeaders (req * http.Request , addGzipHeader bool , trailers string , contentLength int64 ) ([]byte , error ) {
20512079 cc .hbuf .Reset ()
@@ -2066,7 +2094,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
20662094 }
20672095
20682096 var path string
2069- if req . Method != "CONNECT" {
2097+ if ! isNormalConnect ( req ) {
20702098 path = req .URL .RequestURI ()
20712099 if ! validPseudoPath (path ) {
20722100 orig := path
@@ -2103,7 +2131,7 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
21032131 m = http .MethodGet
21042132 }
21052133 f (":method" , m )
2106- if req . Method != "CONNECT" {
2134+ if ! isNormalConnect ( req ) {
21072135 f (":path" , path )
21082136 f (":scheme" , req .URL .Scheme )
21092137 }
@@ -2507,6 +2535,9 @@ func (rl *clientConnReadLoop) run() error {
25072535 if VerboseLogs {
25082536 cc .vlogf ("http2: Transport conn %p received error from processing frame %v: %v" , cc , summarizeFrame (f ), err )
25092537 }
2538+ if ! cc .seenSettings {
2539+ close (cc .seenSettingsChan )
2540+ }
25102541 return err
25112542 }
25122543 }
@@ -3073,6 +3104,21 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
30733104 case SettingHeaderTableSize :
30743105 cc .henc .SetMaxDynamicTableSize (s .Val )
30753106 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+ }
30763122 default :
30773123 cc .vlogf ("Unhandled Setting: %v" , s )
30783124 }
@@ -3090,6 +3136,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
30903136 // connection can establish to our default.
30913137 cc .maxConcurrentStreams = defaultMaxConcurrentStreams
30923138 }
3139+ close (cc .seenSettingsChan )
30933140 cc .seenSettings = true
30943141 }
30953142
0 commit comments