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:
58
retry/retry_after_test.go
Normal file
58
retry/retry_after_test.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package retry
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestParseRetryAfter(t *testing.T) {
|
||||
t.Run("seconds format", func(t *testing.T) {
|
||||
resp := &http.Response{
|
||||
Header: http.Header{"Retry-After": []string{"120"}},
|
||||
}
|
||||
d, ok := ParseRetryAfter(resp)
|
||||
if !ok {
|
||||
t.Fatal("expected ok=true")
|
||||
}
|
||||
if d != 120*time.Second {
|
||||
t.Fatalf("expected 120s, got %v", d)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty header", func(t *testing.T) {
|
||||
resp := &http.Response{
|
||||
Header: make(http.Header),
|
||||
}
|
||||
d, ok := ParseRetryAfter(resp)
|
||||
if ok {
|
||||
t.Fatal("expected ok=false for empty header")
|
||||
}
|
||||
if d != 0 {
|
||||
t.Fatalf("expected 0, got %v", d)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("nil response", func(t *testing.T) {
|
||||
d, ok := ParseRetryAfter(nil)
|
||||
if ok {
|
||||
t.Fatal("expected ok=false for nil response")
|
||||
}
|
||||
if d != 0 {
|
||||
t.Fatalf("expected 0, got %v", d)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("negative value", func(t *testing.T) {
|
||||
resp := &http.Response{
|
||||
Header: http.Header{"Retry-After": []string{"-5"}},
|
||||
}
|
||||
d, ok := ParseRetryAfter(resp)
|
||||
if ok {
|
||||
t.Fatal("expected ok=false for negative value")
|
||||
}
|
||||
if d != 0 {
|
||||
t.Fatalf("expected 0, got %v", d)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user