Add retry transport with configurable backoff and Retry-After support
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
This commit is contained in:
43
retry/retry_after.go
Normal file
43
retry/retry_after.go
Normal file
@@ -0,0 +1,43 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user