HTTP Retry Client
Source: Custom
Topics: http.RoundTripper, http.Client, context, backoff
Problem
Implement RetryTransport, an http.RoundTripper that retries failed requests.
Requirements:
- Attempt the request up to
MaxAttempts times.
- After each attempt, call
ShouldRetry(resp, err) — if it returns false, return immediately.
- On retry, close the previous response body to avoid leaking connections.
- Reset the request body for each attempt using
req.GetBody if available.
- Between retries, wait for
max(Backoff(attempt), Retry-After header) — and abort the wait
(returning ctx.Err()) if the request context is cancelled.
- Return the last response and error after all attempts are exhausted.
Type:
type RetryTransport struct {
Base http.RoundTripper // nil → http.DefaultTransport
MaxAttempts int
ShouldRetry func(*http.Response, error) bool
Backoff func(attempt int) time.Duration // optional; nil → no delay
}
func (t *RetryTransport) RoundTrip(req *http.Request) (*http.Response, error)
Key concepts
- RoundTripper interface: one method —
RoundTrip(*Request) (*Response, error). Plug into any http.Client.
- req.Clone: RoundTripper must not modify the original request — clone it for each attempt.
- req.GetBody: a factory that re-creates the request body for retries. May be nil (e.g. streaming bodies).
- resp.Body.Close(): must be called on non-final responses to return the connection to the pool.
- Backoff + Retry-After: hammering a struggling server with instant retries makes the outage
worse. Wait between attempts (exponential backoff is typical), but if the server sent a
Retry-After header, honor it when it asks for longer — it knows when it'll be ready.
- Context-aware sleep: use
time.NewTimer + select on req.Context().Done() so a cancelled
request doesn't sit blocked in a backoff sleep.
Run
go test -v -bench=. -benchmem ./challenges/networking/http-retry-client/
Sign in to submit your solution.