Skip to content

Commit 0d1114d

Browse files
committed
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. Fixes golang/go#39389
1 parent 627f964 commit 0d1114d

File tree

1 file changed

+27
-4
lines changed

1 file changed

+27
-4
lines changed

http2/transport.go

Lines changed: 27 additions & 4 deletions
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.
@@ -237,6 +246,7 @@ type ClientConn struct {
237246
inflow flow // peer's conn-level flow control
238247
closing bool
239248
closed bool
249+
seenSettings bool // true if we've seen a settings frame, false otherwise
240250
wantSettingsAck bool // we sent a SETTINGS frame and haven't heard back
241251
goAway *GoAwayFrame // if non-nil, the GoAwayFrame we received
242252
goAwayDebug string // goAway frame's debug data, retained as a string
@@ -634,10 +644,10 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
634644
tconn: c,
635645
readerDone: make(chan struct{}),
636646
nextStreamID: 1,
637-
maxFrameSize: 16 << 10, // spec default
638-
initialWindowSize: 65535, // spec default
639-
maxConcurrentStreams: 1000, // "infinite", per spec. 1000 seems good enough.
640-
peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead.
647+
maxFrameSize: 16 << 10, // spec default
648+
initialWindowSize: 65535, // spec default
649+
maxConcurrentStreams: initialMaxConcurrentStreams, // "infinite", per spec. Use a smaller value until we have received server settings.
650+
peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead.
641651
streams: make(map[uint32]*clientStream),
642652
singleUse: singleUse,
643653
wantSettingsAck: true,
@@ -2319,12 +2329,14 @@ func (rl *clientConnReadLoop) processSettings(f *SettingsFrame) error {
23192329
return ConnectionError(ErrCodeProtocol)
23202330
}
23212331

2332+
var seenMaxConcurrentStreams bool
23222333
err := f.ForeachSetting(func(s Setting) error {
23232334
switch s.ID {
23242335
case SettingMaxFrameSize:
23252336
cc.maxFrameSize = s.Val
23262337
case SettingMaxConcurrentStreams:
23272338
cc.maxConcurrentStreams = s.Val
2339+
seenMaxConcurrentStreams = true
23282340
case SettingMaxHeaderListSize:
23292341
cc.peerMaxHeaderListSize = uint64(s.Val)
23302342
case SettingInitialWindowSize:
@@ -2356,6 +2368,17 @@ func (rl *clientConnReadLoop) processSettings(f *SettingsFrame) error {
23562368
return err
23572369
}
23582370

2371+
if !cc.seenSettings {
2372+
if !seenMaxConcurrentStreams {
2373+
// This was the servers initial SETTINGS frame and it
2374+
// didn't contain a MAX_CONCURRENT_STREAMS field so
2375+
// increase the number of concurrent streams this
2376+
// connection can establish to our default.
2377+
cc.maxConcurrentStreams = defaultMaxConcurrentStreams
2378+
}
2379+
cc.seenSettings = true
2380+
}
2381+
23592382
cc.wmu.Lock()
23602383
defer cc.wmu.Unlock()
23612384

0 commit comments

Comments
 (0)