Skip to content

Commit 59c0c25

Browse files
stevenhdmitshur
authored andcommitted
[internal-branch.go1.16-vendor] http2: limit client initial MAX_CONCURRENT_STREAMS
Prevent the client trying to establish more streams than the server is willing to accept during the initial life time of a connection by limiting `maxConcurrentStreams` to `100`, the http2 specifications recommended minimum, until we've received the initial `SETTINGS` frame from the server. After a `SETTINGS` frame has been received use the servers `MAX_CONCURRENT_STREAMS`, if present, otherwise use `1000` as a reasonable value. For normal consumers this will have very little impact, allowing a decent level of concurrency from the start, and for highly concurrent consumers or large bursts it will prevent significant number of rejected streams being attempted hence actually increasing performance. Updates golang/go#49076 Change-Id: I35fecd501ca39cd059c7afd1d44090b023f16e1e GitHub-Last-Rev: 0d1114d GitHub-Pull-Request: #73 Reviewed-on: https://go-review.googlesource.com/c/net/+/236497 Reviewed-by: Brad Fitzpatrick <[email protected]> Trust: Brad Fitzpatrick <[email protected]> Trust: Joe Tsai <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> Reviewed-on: https://go-review.googlesource.com/c/net/+/356979 Trust: Damien Neil <[email protected]> Run-TryBot: Damien Neil <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent 524fcad commit 59c0c25

File tree

1 file changed

+27
-4
lines changed

1 file changed

+27
-4
lines changed

http2/transport.go

+27-4
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,15 @@ const (
5151
transportDefaultStreamMinRefresh = 4 << 10
5252

5353
defaultUserAgent = "Go-http-client/2.0"
54+
55+
// initialMaxConcurrentStreams is a connections maxConcurrentStreams until
56+
// it's received servers initial SETTINGS frame, which corresponds with the
57+
// spec's minimum recommended value.
58+
initialMaxConcurrentStreams = 100
59+
60+
// defaultMaxConcurrentStreams is a connections default maxConcurrentStreams
61+
// if the server doesn't include one in its initial SETTINGS frame.
62+
defaultMaxConcurrentStreams = 1000
5463
)
5564

5665
// Transport is an HTTP/2 Transport.
@@ -247,6 +256,7 @@ type ClientConn struct {
247256
doNotReuse bool // whether conn is marked to not be reused for any future requests
248257
closing bool
249258
closed bool
259+
seenSettings bool // true if we've seen a settings frame, false otherwise
250260
wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
251261
goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received
252262
goAwayDebug string // goAway frame's debug data, retained as a string
@@ -646,10 +656,10 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
646656
tconn: c,
647657
readerDone: make(chan struct{}),
648658
nextStreamID: 1,
649-
maxFrameSize: 16 << 10, // spec default
650-
initialWindowSize: 65535, // spec default
651-
maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough.
652-
peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead.
659+
maxFrameSize: 16 << 10, // spec default
660+
initialWindowSize: 65535, // spec default
661+
maxConcurrentStreams: initialMaxConcurrentStreams, // "infinite", per spec. Use a smaller value until we have received server settings.
662+
peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead.
653663
streams: make(map[uint32]*clientStream),
654664
singleUse: singleUse,
655665
wantSettingsAck: true,
@@ -2357,12 +2367,14 @@ func (rl *clientConnReadLoop) processSettings(f *SettingsFrame) error {
23572367
return ConnectionError(ErrCodeProtocol)
23582368
}
23592369

2370+
var seenMaxConcurrentStreams bool
23602371
err := f.ForeachSetting(func(s Setting) error {
23612372
switch s.ID {
23622373
case SettingMaxFrameSize:
23632374
cc.maxFrameSize = s.Val
23642375
case SettingMaxConcurrentStreams:
23652376
cc.maxConcurrentStreams = s.Val
2377+
seenMaxConcurrentStreams = true
23662378
case SettingMaxHeaderListSize:
23672379
cc.peerMaxHeaderListSize = uint64(s.Val)
23682380
case SettingInitialWindowSize:
@@ -2394,6 +2406,17 @@ func (rl *clientConnReadLoop) processSettings(f *SettingsFrame) error {
23942406
return err
23952407
}
23962408

2409+
if !cc.seenSettings {
2410+
if !seenMaxConcurrentStreams {
2411+
// This was the servers initial SETTINGS frame and it
2412+
// didn't contain a MAX_CONCURRENT_STREAMS field so
2413+
// increase the number of concurrent streams this
2414+
// connection can establish to our default.
2415+
cc.maxConcurrentStreams = defaultMaxConcurrentStreams
2416+
}
2417+
cc.seenSettings = true
2418+
}
2419+
23972420
cc.wmu.Lock()
23982421
defer cc.wmu.Unlock()
23992422

0 commit comments

Comments
 (0)