Skip to content

Support ResponseController read and write deadlines on HTTP/2 streams - #3

Open
pagpeter wants to merge 4 commits into
http2-cancel-handler-ctx-on-resetfrom
http2-stream-deadlines
Open

pagpeter wants to merge 4 commits into
http2-cancel-handler-ctx-on-resetfrom
http2-stream-deadlines

Conversation

@pagpeter

@pagpeter pagpeter commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Builds on bogdanfinn#38, whose commit is included until it is merged; only the last commit is new.

NewResponseController(w).SetReadDeadline and SetWriteDeadline return ErrNotSupported for HTTP/2, so a handler cannot bound how long it waits on one stream (for example a CONNECT tunnel whose peer stops reading) without a connection deadline that affects every stream.

This ports the per-stream deadlines from golang.org/x/net/http2 to http2/server.go and h2_bundle.go: the read-deadline timer, onReadTimeout and its stop on close from a870f352 (CL 446256), and SetReadDeadline, SetWriteDeadline and their serve-loop case from 93ec86b4 (CL 446335), unchanged. Not included: the per-stream Server.ReadTimeout and FlushError. Without x/net efda1ce1, a write that hits its deadline fails with http2: stream closed, not an error matching os.ErrDeadlineExceeded.

A write deadline resets the stream, which releases a write blocked on flow control only because of bogdanfinn#35, and cancels the handler's context only with bogdanfinn#38.

Tests (h2_server_frames_test.go, both servers): a write blocked on a zero flow-control window must fail after its deadline and cancel the handler's context; read deadlines in the future and in the past must release a read of a body that never comes; and a read deadline on a request without a body must not panic (golang.org/issue/58237). Each fails without the corresponding server's change. go test ./... passes.

🤖 Generated with Claude Code

@pagpeter
pagpeter force-pushed the http2-stream-deadlines branch from 9787cd2 to a9490de Compare October 26, 2026 09:54
@pagpeter
pagpeter changed the base branch from master to http2-cancel-handler-ctx-on-reset October 26, 2026 09:54
@pagpeter
pagpeter force-pushed the http2-cancel-handler-ctx-on-reset branch from b46f7e7 to 8f43832 Compare October 26, 2026 10:30
@pagpeter
pagpeter force-pushed the http2-stream-deadlines branch from a9490de to 2b24794 Compare October 26, 2026 10:30
pagpeter and others added 4 commits October 26, 2026 15:35
…anfinn#36)

The HTTP/1 request writer replaces a caller's Content-Length,
Transfer-Encoding or Trailer under any case with the value from the request,
written under the canonical name (bogdanfinn#32). A request whose headers are all lower
case, as Node's fetch writes them, therefore goes out with "Content-Length"
among lower-case headers, which changes how the request looks.

Keep bogdanfinn#32's single header and computed values, but write them under the name
the caller used. If the caller used more than one spelling, or none, the
name is canonical, as before.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Browsers take a server advertising extended CONNECT as support for
WebSockets over HTTP/2, which fails when the server's WebSocket handling
does not support it. Go disabled it by default for this reason.

Port golang.org/x/net/http2 0a5dcdd8 (CL 641475, golang/go#71128):
extended CONNECT is off unless GODEBUG contains http2xconnect=1. Also
port the check from 9a518991 (CL 610977) that a535bcd left out: while it
is off, the server rejects requests that use :protocol.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
When the server reset a stream because of a stream error, the handler's
request context stayed live, so a handler waiting on it never returned.
Only a client RST_STREAM canceled it.

Port golang.org/x/net/http2 8aa6dbf4 (CL 585595, golang/go#67036) to
http2 and to the bundled server in h2_bundle.go: closeStream cancels the
stream's context.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
NewResponseController(w).SetReadDeadline and SetWriteDeadline returned
ErrNotSupported for HTTP/2 requests, so a handler could not bound how long
it waits on a single stream, for example on a CONNECT tunnel whose peer
stops reading.

Port the per-stream deadlines from golang.org/x/net/http2 a870f352
(CL 446256) and 93ec86b4 (CL 446335) to http2 and to the bundled server
in h2_bundle.go. Server.ReadTimeout per stream and FlushError from those
changes are not included. Without x/net efda1ce1, also not included, a
write that hits its deadline fails with "http2: stream closed" rather
than an error matching os.ErrDeadlineExceeded.

A write deadline resets the stream. Releasing a write that is blocked on
flow control needs the RST_STREAM scheduling fix, and canceling the
handler's context needs closeStream to cancel it, so this builds on both.

The tests run against both servers: a write blocked on flow control, read
deadlines in the future and in the past, and a read deadline on a request
without a body (golang.org/issue/58237).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@pagpeter
pagpeter force-pushed the http2-stream-deadlines branch from 2b24794 to a8ade0b Compare October 26, 2026 16:30
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.

1 participant