HTTP Retry With Exponential Backoff

Retry idempotent HTTP requests on server errors and throttling responses, backing off between attempts and honouring context cancellation.

GoResilience

Code

snippet
package httpx

import (
	"context"
	"net/http"
	"time"
)

func Retry(ctx context.Context, client *http.Client, req *http.Request, attempts int) (*http.Response, error) {
	var lastErr error
	delay := 100 * time.Millisecond
	for i := 0; i < attempts; i++ {
		resp, err := client.Do(req.WithContext(ctx))
		if err == nil {
			if resp.StatusCode < 500 && resp.StatusCode != http.StatusTooManyRequests {
				return resp, nil
			}
			resp.Body.Close()
		} else {
			lastErr = err
		}
		select {
		case <-ctx.Done():
			return nil, ctx.Err()
		case <-time.After(delay):
		}
		delay *= 2
	}
	if lastErr != nil {
		return nil, lastErr
	}
	return nil, context.DeadlineExceeded
}