Implements retry middleware as a RoundTripper wrapper: - Exponential and constant backoff strategies with jitter - RFC 7231 Retry-After header parsing (seconds and HTTP-date) - Default policy retries idempotent methods on 429/5xx and network errors - Body restoration via GetBody, context cancellation, response body cleanup
44 lines
991 B
Go
44 lines
991 B
Go
package retry
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// ParseRetryAfter extracts the delay from a Retry-After header (RFC 7231).
|
|
// It supports both the delay-seconds format ("120") and the HTTP-date format
|
|
// ("Fri, 31 Dec 1999 23:59:59 GMT"). Returns the duration and true if the
|
|
// header was present and valid; otherwise returns 0 and false.
|
|
func ParseRetryAfter(resp *http.Response) (time.Duration, bool) {
|
|
if resp == nil {
|
|
return 0, false
|
|
}
|
|
|
|
val := resp.Header.Get("Retry-After")
|
|
if val == "" {
|
|
return 0, false
|
|
}
|
|
|
|
// Try delay-seconds first (most common).
|
|
if seconds, err := strconv.ParseInt(val, 10, 64); err == nil {
|
|
if seconds < 0 {
|
|
return 0, false
|
|
}
|
|
return time.Duration(seconds) * time.Second, true
|
|
}
|
|
|
|
// Try HTTP-date format (RFC 7231 section 7.1.1.1).
|
|
t, err := http.ParseTime(val)
|
|
if err != nil {
|
|
return 0, false
|
|
}
|
|
|
|
delay := time.Until(t)
|
|
if delay < 0 {
|
|
// The date is in the past; no need to wait.
|
|
return 0, true
|
|
}
|
|
return delay, true
|
|
}
|