mirror of
https://github.com/golang/net.git
synced 2026-03-31 02:17:08 +09:00
http2: support HTTP2Config.StrictMaxConcurrentRequests
When HTTP2Config.StrictMaxConcurrentRequests is set (added in Go 1.26), use it to override the value of Transport.StrictMaxConcurrentStreams. Permits configuring this parameter from net/http without importing x/net/http2. For golang/go#67813 Change-Id: Ie7fa5a8ac033b1827cf7fef4e23b5110a05dc95f Reviewed-on: https://go-review.googlesource.com/c/net/+/707315 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Nicholas Husin <husin@google.com> Reviewed-by: Nicholas Husin <nsh@golang.org>
This commit is contained in:
@@ -27,6 +27,7 @@ import (
|
||||
// - If the resulting value is zero or out of range, use a default.
|
||||
type http2Config struct {
|
||||
MaxConcurrentStreams uint32
|
||||
StrictMaxConcurrentRequests bool
|
||||
MaxDecoderHeaderTableSize uint32
|
||||
MaxEncoderHeaderTableSize uint32
|
||||
MaxReadFrameSize uint32
|
||||
@@ -64,12 +65,13 @@ func configFromServer(h1 *http.Server, h2 *Server) http2Config {
|
||||
// (the net/http Transport).
|
||||
func configFromTransport(h2 *Transport) http2Config {
|
||||
conf := http2Config{
|
||||
MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize,
|
||||
MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize,
|
||||
MaxReadFrameSize: h2.MaxReadFrameSize,
|
||||
SendPingTimeout: h2.ReadIdleTimeout,
|
||||
PingTimeout: h2.PingTimeout,
|
||||
WriteByteTimeout: h2.WriteByteTimeout,
|
||||
StrictMaxConcurrentRequests: h2.StrictMaxConcurrentStreams,
|
||||
MaxEncoderHeaderTableSize: h2.MaxEncoderHeaderTableSize,
|
||||
MaxDecoderHeaderTableSize: h2.MaxDecoderHeaderTableSize,
|
||||
MaxReadFrameSize: h2.MaxReadFrameSize,
|
||||
SendPingTimeout: h2.ReadIdleTimeout,
|
||||
PingTimeout: h2.PingTimeout,
|
||||
WriteByteTimeout: h2.WriteByteTimeout,
|
||||
}
|
||||
|
||||
// Unlike most config fields, where out-of-range values revert to the default,
|
||||
@@ -128,6 +130,9 @@ func fillNetHTTPConfig(conf *http2Config, h2 *http.HTTP2Config) {
|
||||
if h2.MaxConcurrentStreams != 0 {
|
||||
conf.MaxConcurrentStreams = uint32(h2.MaxConcurrentStreams)
|
||||
}
|
||||
if http2ConfigStrictMaxConcurrentRequests(h2) {
|
||||
conf.StrictMaxConcurrentRequests = true
|
||||
}
|
||||
if h2.MaxEncoderHeaderTableSize != 0 {
|
||||
conf.MaxEncoderHeaderTableSize = uint32(h2.MaxEncoderHeaderTableSize)
|
||||
}
|
||||
|
||||
15
http2/config_go125.go
Normal file
15
http2/config_go125.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2025 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.26
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func http2ConfigStrictMaxConcurrentRequests(h2 *http.HTTP2Config) bool {
|
||||
return false
|
||||
}
|
||||
15
http2/config_go126.go
Normal file
15
http2/config_go126.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2025 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.26
|
||||
|
||||
package http2
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func http2ConfigStrictMaxConcurrentRequests(h2 *http.HTTP2Config) bool {
|
||||
return h2.StrictMaxConcurrentRequests
|
||||
}
|
||||
@@ -355,6 +355,7 @@ type ClientConn struct {
|
||||
readIdleTimeout time.Duration
|
||||
pingTimeout time.Duration
|
||||
extendedConnectAllowed bool
|
||||
strictMaxConcurrentStreams bool
|
||||
|
||||
// rstStreamPingsBlocked works around an unfortunate gRPC behavior.
|
||||
// gRPC strictly limits the number of PING frames that it will receive.
|
||||
@@ -784,7 +785,8 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
|
||||
initialWindowSize: 65535, // spec default
|
||||
initialStreamRecvWindowSize: conf.MaxUploadBufferPerStream,
|
||||
maxConcurrentStreams: initialMaxConcurrentStreams, // "infinite", per spec. Use a smaller value until we have received server settings.
|
||||
peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead.
|
||||
strictMaxConcurrentStreams: conf.StrictMaxConcurrentRequests,
|
||||
peerMaxHeaderListSize: 0xffffffffffffffff, // "infinite", per spec. Use 2^64-1 instead.
|
||||
streams: make(map[uint32]*clientStream),
|
||||
singleUse: singleUse,
|
||||
seenSettingsChan: make(chan struct{}),
|
||||
@@ -1018,7 +1020,7 @@ func (cc *ClientConn) idleStateLocked() (st clientConnIdleState) {
|
||||
return
|
||||
}
|
||||
var maxConcurrentOkay bool
|
||||
if cc.t.StrictMaxConcurrentStreams {
|
||||
if cc.strictMaxConcurrentStreams {
|
||||
// We'll tell the caller we can take a new request to
|
||||
// prevent the caller from dialing a new TCP
|
||||
// connection, but then we'll block later before
|
||||
|
||||
@@ -3475,14 +3475,28 @@ func TestTransportRequestsLowServerLimit(t *testing.T) {
|
||||
|
||||
// tests Transport.StrictMaxConcurrentStreams
|
||||
func TestTransportRequestsStallAtServerLimit(t *testing.T) {
|
||||
synctestTest(t, testTransportRequestsStallAtServerLimit)
|
||||
synctestSubtest(t, "Transport", func(t testing.TB) {
|
||||
testTransportRequestsStallAtServerLimit(t, func(tr *Transport) {
|
||||
tr.StrictMaxConcurrentStreams = true
|
||||
})
|
||||
})
|
||||
synctestSubtest(t, "HTTP2Config", func(t testing.TB) {
|
||||
// HTTP2Config.StrictMaxConcurrentRequests was added in Go 1.26.
|
||||
h2 := &http.HTTP2Config{}
|
||||
v := reflect.ValueOf(h2).Elem().FieldByName("StrictMaxConcurrentRequests")
|
||||
if !v.IsValid() {
|
||||
t.Skip("HTTP2Config does not contain StrictMaxConcurrentRequests")
|
||||
}
|
||||
v.SetBool(true)
|
||||
testTransportRequestsStallAtServerLimit(t, func(tr *http.Transport) {
|
||||
tr.HTTP2 = h2
|
||||
})
|
||||
})
|
||||
}
|
||||
func testTransportRequestsStallAtServerLimit(t testing.TB) {
|
||||
func testTransportRequestsStallAtServerLimit(t testing.TB, opt any) {
|
||||
const maxConcurrent = 2
|
||||
|
||||
tc := newTestClientConn(t, func(tr *Transport) {
|
||||
tr.StrictMaxConcurrentStreams = true
|
||||
})
|
||||
tc := newTestClientConn(t, opt)
|
||||
tc.greet(Setting{SettingMaxConcurrentStreams, maxConcurrent})
|
||||
|
||||
cancelClientRequest := make(chan struct{})
|
||||
|
||||
Reference in New Issue
Block a user