cfbefe6d04ebce739b6c383396569ebc93729164
A logical request's Retry-After waits are now bounded by max_total_retry_duration, and a retry storm can no longer re-arm a full ceiling per attempt: - BudgetClock anchored per logical request by RetryGateMiddleware (into the request Extensions, shared across retry attempts) and read by the inner RetryAfterMiddleware every attempt; the monotonic anchor projects the hard stop through wall-clock steps. - maybe_sleep_for truncates the sleep to the remaining budget; a spent budget skips the sleep entirely. - record() keeps the EARLIEST deadline per URL (retry storms cannot extend the first-seen deadline); a refresh that cannot make it under the budget hard stop drops the entry so the next attempt starts immediately instead of parking. - Middleware without a budget anchor (budget = 0) keeps the prior semantics; the shared client now wires HttpClientConfig.max_total_retry_duration into the Retry-After middleware. - Wire tests (tests/retry_after_budget.rs): always-429 responder with a 300 s Retry-After is bounded by budget + one attempt; separate logical requests still honor the recorded throttle window. - FWD-12 atomic reload pairing and the FWD-15 stream-client split untouched (stack built in build_client_with_pems for both).
Languages
Rust
98.6%
Python
1.3%
Shell
0.1%