feat(net): response-body bounds — stall timeout + buffered size cap (ADR-0034 Am#13)#125
Conversation
Adds HttpConfig.body_stall_timeout (validated non-zero-when-Some via BuildError::ZeroDuration) and composes StallTimeoutLayer innermost in stack()'s LayerBuilder chain, just outside SetHeaders/Auth. This puts Guarded outside TimeoutBody so a mid-transfer body stall releases the concurrency permit instead of pinning it (ADR-0034 Amendment #13). Breaking: HttpConfig gains a new required field; updates the hyper build() doctest/test helper and the client_with_directives example.
Adds RangeHint (bounded-above-cap and unbounded-upper size_hint branches) and TrailersOnly (non-DATA frame accounting) test doubles.
Add ConnConfig.max_response_bytes: Option<usize>, threaded through HyperLeaf, and apply it only to the BufferMode::Buffer arm of HyperLeaf::call. Two-layer cap: an upfront fast-fail when the response's honest Content-Length (size_hint().upper()) already exceeds the cap, then wrap the incoming body in LimitedBody before collect() so an unsized/chunked body is caught mid-collect. None stays unbounded (current behavior); overflow surfaces as HttpError::BodyTooLarge. Streaming responses are untouched (the stall timeout already covers those). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records the streaming stall-timeout (StallTimeoutLayer/TimeoutBody) and buffered size-cap (LimitedBody/ConnConfig::max_response_bytes) work (Tasks 1-6 on this branch) as ADR-0034 Amendment #13, un-deferring Amendment #6's TimeoutBody and wiring Section 2's size guard. Adds the matching Unreleased CHANGELOG entries (Added + a breaking-change note under Changed).
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (20)
📝 WalkthroughWalkthroughAdds per-frame streaming stall timeouts and configurable buffered response-size limits. It introduces associated timer sleep types, new body-size errors and classifications, stack configuration, Hyper enforcement, tests, examples, and ADR/changelog documentation. ChangesTimer and error contracts
Streaming stall timeout
Stack configuration and wiring
Buffered response-size enforcement
ADR and release documentation
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Closes #124.
Bounds a misbehaving venue HTTP response body on two independent axes, landing ADR-0034 Amendment #13 (un-defers Am#6's
TimeoutBody; wires §2's size guard). Implements the Tier-2 "Streaming stall TimeoutBody" item (#102) plus the untracked N1 buffered-cap straggler.What
1. Streaming stall timeout — a per-frame inactivity deadline for streaming bodies via the runtime-neutral
Timerseam.TimeoutBody<B,T>+StallTimeoutLayer<T>wrap the response body and yieldHttpError::Timeoutif no frame arrives withinHttpConfig.body_stall_timeout. The layer is composed innermost instack()soGuarded(the permit-carrying wrapper) ends up outsideTimeoutBody— a stall therefore releases the concurrency permit instead of wedging it. Inert on buffered bodies and whenNone.2. Buffered size cap —
LimitedBody<B>, a typed max-bytes wrapper (yieldsHttpError::BodyTooLargeonce cumulative DATA bytes exceed the cap; clampssize_hint), applied in the hyper leaf'sBufferMode::Bufferarm viaConnConfig.max_response_bytes. Two layers: an upfrontsize_hint().upper()fast-fail for an honest Content-Length, plus theLimitedBodymid-collect wrapper for unsized/chunked bodies. Overflow is the new non-retryable, breaker-neutralHttpError::BodyTooLarge/ErrorKind::BodyTooLarge(error_kind="body_too_large").Breaking (pre-release)
Timergains an associatedtype Sleep: Future<Output=()> + Send(was-> impl Future), so body wrappers store the sleep future inline without boxing. All impls updated (TokioTimer,MockTimer,FixedTimer).HttpConfiggainsbody_stall_timeout: Option<Duration>;ConnConfiggainsmax_response_bytes: Option<usize>(both new required fields).body_stall_timeoutisvalidate_config-rejected whenSome(ZERO).HttpError/ErrorKindgain aBodyTooLargevariant.Tests & verification
None-inert / transparency / permit-release (the crux);LimitedBodyunder/at/over cap, bothsize_hint-clamp branches, trailers-don't-count; leaf cap on honest Content-Length (fast-fail) and raw-TCP chunked (mid-collect); a pinning test thatBodyTooLargeclassifies breaker-Ignored.just cigreen (fmt, clippy-D warnings, 244 tests + doctests, doc, deny, typos, machete, gitleaks, actionlint, shellcheck).Design:
docs/superpowers/specs/2026-07-10-net-http-response-body-bounds-design.md· Plan:docs/superpowers/plans/2026-07-10-net-http-response-body-bounds.md· CHANGELOG[Unreleased]updated.🤖 Generated with Claude Code
Summary by CodeRabbit