aboutsummaryrefslogtreecommitdiff
path: root/src/net
diff options
context:
space:
mode:
authorDamien Neil <dneil@google.com>2024-09-25 11:10:07 -0700
committerGopher Robot <gobot@golang.org>2025-09-26 16:14:14 -0700
commit3a5df9d2b20c0f059c463b5d51e89be17a2c685b (patch)
tree8163d083beaf4b1f5683fb3ea56ed97bff67f39a /src/net
parent16be34df02ba073456e5f31203549165585f3e6c (diff)
downloadgo-3a5df9d2b20c0f059c463b5d51e89be17a2c685b.tar.xz
net/http: add HTTP2Config.StrictMaxConcurrentRequests
Add a field to HTTP2Config controlling how we behave when an HTTP/2 connection reaches its concurrency limit. This field will have no effect until golang.org/x/net/http2 is updated to make use of it, and h2_bundle.go is updated with the new http2 package. For #67813 Change-Id: Ic72a0986528abb21649f28e9fe7cf6e1236b388d Reviewed-on: https://go-review.googlesource.com/c/go/+/615875 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Damien Neil <dneil@google.com> Reviewed-by: Nicholas Husin <nsh@golang.org> Reviewed-by: Nicholas Husin <husin@google.com>
Diffstat (limited to 'src/net')
-rw-r--r--src/net/http/http.go15
-rw-r--r--src/net/http/transport_dial_test.go39
2 files changed, 52 insertions, 2 deletions
diff --git a/src/net/http/http.go b/src/net/http/http.go
index 0f9165bf03..e7959fa3b6 100644
--- a/src/net/http/http.go
+++ b/src/net/http/http.go
@@ -235,10 +235,23 @@ type Pusher interface {
// both [Transport] and [Server].
type HTTP2Config struct {
// MaxConcurrentStreams optionally specifies the number of
- // concurrent streams that a peer may have open at a time.
+ // concurrent streams that a client may have open at a time.
// If zero, MaxConcurrentStreams defaults to at least 100.
+ //
+ // This parameter only applies to Servers.
MaxConcurrentStreams int
+ // StrictMaxConcurrentRequests controls whether an HTTP/2 server's
+ // concurrency limit should be respected across all connections
+ // to that server.
+ // If true, new requests sent when a connection's concurrency limit
+ // has been exceeded will block until an existing request completes.
+ // If false, an additional connection will be opened if all
+ // existing connections are at their limit.
+ //
+ // This parameter only applies to Transports.
+ StrictMaxConcurrentRequests bool
+
// MaxDecoderHeaderTableSize optionally specifies an upper limit for the
// size of the header compression table used for decoding headers sent
// by the peer.
diff --git a/src/net/http/transport_dial_test.go b/src/net/http/transport_dial_test.go
index d7f7a3d539..086039ece9 100644
--- a/src/net/http/transport_dial_test.go
+++ b/src/net/http/transport_dial_test.go
@@ -59,7 +59,7 @@ func TestTransportPoolConnCannotReuseConnectionInUse(t *testing.T) {
// When an HTTP/2 connection is at its stream limit
// a new request is made on a new connection.
-func TestTransportPoolConnHTTP2OverStreamLimit(t *testing.T) {
+func testTransportPoolConnHTTP2NoStrictMaxConcurrentRequests(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
dt := newTransportDialTester(t, http2Mode, func(srv *http.Server) {
srv.HTTP2 = &http.HTTP2Config{
@@ -100,6 +100,43 @@ func TestTransportPoolConnHTTP2OverStreamLimit(t *testing.T) {
})
}
+// When an HTTP/2 connection is at its stream limit
+// and StrictMaxConcurrentRequests = true,
+// a new request waits for a slot on the existing connection.
+func TestTransportPoolConnHTTP2StrictMaxConcurrentRequests(t *testing.T) {
+ t.Skip("skipped until h2_bundle.go includes support for StrictMaxConcurrentRequests")
+
+ synctest.Test(t, func(t *testing.T) {
+ dt := newTransportDialTester(t, http2Mode, func(srv *http.Server) {
+ srv.HTTP2.MaxConcurrentStreams = 2
+ }, func(tr *http.Transport) {
+ tr.HTTP2 = &http.HTTP2Config{
+ StrictMaxConcurrentRequests: true,
+ }
+ })
+
+ // First request dials an HTTP/2 connection.
+ rt1 := dt.roundTrip()
+ c1 := dt.wantDial()
+ c1.finish(nil)
+ rt1.wantDone(c1, "HTTP/2.0")
+
+ // Second request uses the existing connection.
+ rt2 := dt.roundTrip()
+ rt2.wantDone(c1, "HTTP/2.0")
+
+ // Third request blocks waiting for a slot on the existing connection.
+ rt3 := dt.roundTrip()
+
+ // First request finishing unblocks the thirrd.
+ rt1.finish()
+ rt3.wantDone(c1, "HTTP/2.0")
+
+ rt2.finish()
+ rt3.finish()
+ })
+}
+
// A new request made while an HTTP/2 dial is in progress will start a second dial.
func TestTransportPoolConnHTTP2Startup(t *testing.T) {
synctest.Test(t, func(t *testing.T) {