aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/next/67813.txt1
-rw-r--r--doc/next/6-stdlib/99-minor/net/http/67813.md4
-rw-r--r--src/net/http/http.go15
-rw-r--r--src/net/http/transport_dial_test.go39
4 files changed, 57 insertions, 2 deletions
diff --git a/api/next/67813.txt b/api/next/67813.txt
new file mode 100644
index 0000000000..b420a141e1
--- /dev/null
+++ b/api/next/67813.txt
@@ -0,0 +1 @@
+pkg net/http, type HTTP2Config struct, StrictMaxConcurrentRequests bool #67813
diff --git a/doc/next/6-stdlib/99-minor/net/http/67813.md b/doc/next/6-stdlib/99-minor/net/http/67813.md
new file mode 100644
index 0000000000..74b8c7644f
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/net/http/67813.md
@@ -0,0 +1,4 @@
+The new
+[HTTP2Config.StrictMaxConcurrentRequests](/pkg/net/http#HTTP2Config.StrictMaxConcurrentRequests)
+field controls whether a new connection should be opened
+if an existing HTTP/2 connection has exceeded its stream limit.
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) {