Skip to content

Honour HeaderReceived's ReadTimeout per HTTP/2 stream - #3

Merged
localrivet merged 1 commit into
masterfrom
feat/per-request-read-timeout
Oct 7, 2026
Merged

localrivet merged 1 commit into
masterfrom
feat/per-request-read-timeout

Conversation

@localrivet

Copy link
Copy Markdown

EdgeLB gives one route, `POST /api/v1/files/upload`, a 10-minute read timeout through `fasthttp.Server.HeaderReceived`. Over HTTP/1.1 fasthttp applies that itself; over HTTP/2 this fork gave every stream the server-wide `ReadTimeout` (120 s at the edge), so a slow phone upload over HTTP/2 was still cut at 120 s.

Change

  • When a stream's headers end, the server calls `HeaderReceived`; a non-zero `ReadTimeout` replaces the server's for that one stream, measured from the stream's start.
  • The request timer arms for the earliest deadline among streams still receiving their request, and re-arms on every new stream (a long deadline must not hold the timer past a shorter one). The timeout sweep checks every stream instead of stopping at the first not-due one, since deadlines are no longer in creation order. With no hook set, behaviour is unchanged.
  • Only `ReadTimeout` is honoured; `MaxRequestBodySize` and `WriteTimeout` from the hook are not applied per stream (documented on the field).

Tests

  • New `TestHeaderReceivedReadTimeoutPerStream`: server ReadTimeout 300 ms; a ~1 s upload on the path the hook extends to 10 s completes with 200, the same upload on another path is cut.
  • `go test ./` passes.
  • `go test ./h2spec`: the same four cases fail on master without this change (4.2/2, 4.2/3, 5.1/8, 5.1/11), and 5.1/5, 5.1.1/2, 6.1/2 flake on both master and this branch over 12 runs each. Not caused by this change.

🤖 Generated with Claude Code

fasthttp.Server.HeaderReceived lets a server give one request its own read
timeout over HTTP/1.1. Over HTTP/2 every stream got the server's
ReadTimeout, so a long upload on HTTP/2 could not be given more time
without raising it for every request.

The server now calls HeaderReceived when a stream's headers end; a
non-zero ReadTimeout replaces the server's for that stream, measured from
the stream's start. The request timer arms for the earliest deadline of
the streams still receiving, and re-arms on every new stream, since one
long deadline may otherwise hold the timer past a shorter one. Only
ReadTimeout is honoured; MaxRequestBodySize and WriteTimeout are not
applied per stream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@localrivet
localrivet merged commit 0da25bb into master Oct 7, 2026
@localrivet
localrivet deleted the feat/per-request-read-timeout branch October 7, 2026 21:00
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