Files
Francis LavoieandClaude Opus 5.5 30d3bd2a89 caddyhttp: only bound HTTP/2 body reads in flight with the idle timeout (#8125)
* caddyhttp: only bound HTTP/2 body reads in flight with the idle timeout

On HTTP/2, the read deadline is a timer that fails the request body
when it fires, even with no read in flight. IdleTimeoutReader left the
idle deadline armed between reads, so a handler that stopped reading
for longer than read_body_idle (1 minute by default) lost the body,
although the client was only held back by flow control. For example,
reverse_proxy uploading to an upstream that's slow to accept the body
failed with a 502.

This is the read side of #8118. Like IdleTimeoutWriter does since
read on HTTP/2, so the idle deadline only bounds reads in flight. A
client that stops sending is still cut off, since the deadline is set
before each read. HTTP/1 and HTTP/3 only check the deadline during a
read, so they're unchanged.

Written with Claude Code (Claude Opus 5.5); the human submitter is
responsible for reviewing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* caddyhttp: track a released hard read deadline as armed

From review: with HardDeadline set, releasing the idle deadline between
reads left an HTTP/2 timer armed (at HardDeadline) while deadlineSet
recorded that none was, so clearDeadlineLocked would skip clearing it.

Track which deadline is armed (none, idle, or only the hard one) instead
of a bool, and have each check ask the question it means: re-arming and
releasing only apply to an armed idle deadline, while clearing applies
to any.

The writer's deadlineSet already only means "idle deadline armed",
since nothing clears the hard deadline it leaves; document that.

Written with Claude Code (Claude Opus 5.5); the human submitter is
responsible for reviewing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* caddyhttp: clear released hard deadline at handler end, skip no-op sets

From review:

- After a partial read releases the idle deadline to HardDeadline, a
  handler that returns without reading again left that timer armed past
  HandlerDone, which only cleared after a terminal error. HandlerDone
  now also clears a released hard deadline.
- When the idle deadline is capped to HardDeadline, the reader and
  writer set the same deadline again when releasing it, and the writer
  once more in HandlerDone. On HTTP/2 each is a message to the serve
  loop. Both now remember the deadline they armed and skip setting it
  again. Within a request, only these wrappers set the client's
  deadlines.

Replaces the earlier test that faked a terminal error with the partial
read followed directly by HandlerDone.

Written with Claude Code (Claude Opus 5.5); the human submitter is
responsible for reviewing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* caddyhttp: only skip re-setting an armed hard deadline

Skipping any deadline equal to the one already armed also skipped idle
deadlines on Windows, where two calls to time.Now in the same clock tick
return the same time, so successive idle deadlines can be equal. That
broke tests counting deadline calls there. Only skip HardDeadline, the
case from review where the idle deadline is capped to it; other
deadlines are always set.

Written with Claude Code (Claude Opus 5.5); the human submitter is
responsible for reviewing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* caddyhttp: restore deadlines without trusting cached transport state

Always set the requested read and write deadlines because another handler can change them through its own response controller. Remove the armedAt shortcut and cover intervening deadline changes for reads, writes, ReadFrom, flushes, and HandlerDone.

AI-assisted with OpenAI Codex (GPT-6); tested under human direction.

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 07:47:14 +10:00
..