HTTP Retry Client (Rust)
Topics: RoundTripper/Transport, retries, backoff, cancellation
Problem
Implement RetryTransport, a Transport that retries a base transport. A Transport is one round
trip (Request -> Result<Response, TransportError>) — the analog of Go's http.RoundTripper. It
composes: RetryTransport wraps another transport (which in tests is a mock).
pub trait Transport { fn round_trip(&self, req: Request) -> Result<Response, TransportError>; }
pub struct RetryTransport<B: Transport> { /* … */ }
impl<B: Transport> RetryTransport<B> {
pub fn new(base: B, max_attempts: usize,
should_retry: impl Fn(&Result<Response, TransportError>) -> bool + 'static) -> Self;
pub fn with_backoff(self, backoff: impl Fn(usize) -> Duration + 'static) -> Self;
pub fn with_cancel(self, cancel: Cancel) -> Self;
}
- Attempt the request up to
max_attempts times.
- After each attempt call
should_retry(&result); if it returns false, return immediately.
- Between retries wait
max(backoff(attempt), Retry-After); honor a server's Retry-After header
when it asks for longer.
- Abort the wait and return an error if the
Cancel token fires.
- Return the last result after attempts are exhausted.
Key concepts
- Transport = RoundTripper: one method,
Request -> Result<Response, _>, so transports compose;
RetryTransport<B> wraps any base B: Transport.
- Owned body: the
Request body is an owned String, so it is inherently re-sendable across
attempts — no GetBody factory needed (Go needs one because its body is a consumed stream).
- Backoff + Retry-After: instant retries make an outage worse; wait between attempts, and let a
server's
Retry-After override when it asks for longer (retry_after is provided).
- Cancellable wait:
sleep_or_cancel (provided) polls the token while waiting, so a cancelled
request doesn't sit blocked in a backoff sleep.
Grading
Your solution.rs is compiled together with a trusted tests.rs (which include!s it) using
rustc --test. Only solution.rs is yours to edit.
Sign in to submit your solution.