Skip to content

fix(connection): keep HTTP/2 MaxConcurrentStreams within int32 - #1778

Open
amlaban65 wants to merge 2 commits into
cloudflare:masterfrom
amlaban65:fix-http2-max-concurrent-streams
Open

amlaban65 wants to merge 2 commits into
cloudflare:masterfrom
amlaban65:fix-http2-max-concurrent-streams

Conversation

@amlaban65

Copy link
Copy Markdown

Fixes #1777

Problem

connection.MaxConcurrentStreams is math.MaxUint32, and the HTTP/2 tunnel server uses it as http2.Server.MaxConcurrentStreams. With Go 1.27.3 and golang.org/x/net v0.61.0, http2.Server is backed by net/http. The adapter converts the limit to an int, and values above math.MaxInt32 are replaced with the default of 250 (see golang/go#82497). So every HTTP/2 tunnel connection advertises SETTINGS_MAX_CONCURRENT_STREAMS = 250 instead of an effectively unlimited value. Long-lived requests can then use up the stream slots for everything else on that connection.

Change

  • connection/connection.go: set MaxConcurrentStreams to math.MaxInt32, with a comment explaining why it must fit in an int32. It is only used for the HTTP/2 server.
  • connection/http2_test.go: add TestHTTP2AdvertisedMaxConcurrentStreams. It starts the HTTP/2 connection over net.Pipe, sends the client preface, reads the first frame with http2.Framer, and checks that the SETTINGS frame advertises MaxConcurrentStreams. It checks what is sent on the wire, as the issue suggests, because the configured field alone would not show the fallback.

Testing

Built with Go 1.27.3 (matching go.mod) and golang.org/x/net v0.61.0:

  • Without the fix, the new test fails: expected 0xffffffff, got 0xfa (250).
  • With the fix, the new test passes, go test ./connection passes, go vet ./connection is clean, and the changed files are gofmt-clean.

Not run: make lint (golangci-lint) and the component tests. I also didn't run the new test with the http2legacy build tag.

Related

#1773 also edits connection/http2.go (ping health checks). This PR doesn't touch that file, so they shouldn't conflict.

cloudflared configures its HTTP/2 tunnel server with
MaxConcurrentStreams = math.MaxUint32. With Go 1.27 and
golang.org/x/net v0.61.0, http2.Server is backed by net/http, which
converts the limit to an int and replaces anything above
math.MaxInt32 with its default of 250. Each HTTP/2 tunnel connection
was therefore silently limited to 250 concurrent streams.

Use math.MaxInt32 instead.

Fixes cloudflare#1777

Signed-off-by: Ahmed Laban <amlaban@outlook.com>
Read the SETTINGS frame the HTTP/2 server actually sends and check that
SETTINGS_MAX_CONCURRENT_STREAMS equals MaxConcurrentStreams. Checking
the configured http2.Server field alone would not catch the fallback
to 250 streams.

Signed-off-by: Ahmed Laban <amlaban@outlook.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 HTTP/2 tunnel concurrency silently falls to 250 streams with Go 1.27

1 participant