Skip to content

Commit eb10ac4

Browse files
committed
Retry-After take precendence
1 parent 1f2d2d3 commit eb10ac4

1 file changed

Lines changed: 41 additions & 10 deletions

File tree

client/customRetryPolicies.go

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ var (
1515
"x-instant-test-rate-limit-reset",
1616
}
1717
resetHeaderPattern = regexp.MustCompile(`^\s*[0-9]+\s*$`)
18+
timeNow = time.Now
1819
)
1920

2021
// Custom backoff function
@@ -23,41 +24,71 @@ func customBackoff(min, max time.Duration, attemptNum int, resp *http.Response)
2324
return 0
2425
}
2526

27+
if resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode == http.StatusServiceUnavailable {
28+
if sleep, ok := parseRetryAfterHeader(resp.Header["Retry-After"]); ok {
29+
return sleep
30+
}
31+
}
32+
2633
for _, header := range rateLimitResetHeaders {
2734
if resetTimeStr := resp.Header.Get(header); resetTimeStr != "" {
28-
if waitDuration := parseResetHeader(&resetTimeStr); waitDuration != nil {
29-
return *waitDuration
35+
if sleep, ok := parseResetHeader(&resetTimeStr); ok {
36+
return sleep
3037
}
3138

3239
// Default backoff if no valid reset time is found
3340
return retryablehttp.DefaultBackoff(min, max, attemptNum, resp)
3441
}
3542
}
3643

37-
// Default case for no rateLimitResetHeaders
44+
// Default case for no Retry-After and rateLimitResetHeaders
3845
return retryablehttp.DefaultBackoff(min, max, attemptNum, resp)
3946
}
4047

48+
func parseRetryAfterHeader(headers []string) (time.Duration, bool) {
49+
if len(headers) == 0 || headers[0] == "" {
50+
return 0, false
51+
}
52+
header := headers[0]
53+
// Retry-After: 120
54+
if sleep, err := strconv.ParseInt(header, 10, 64); err == nil {
55+
if sleep < 0 { // a negative sleep doesn't make sense
56+
return 0, false
57+
}
58+
return time.Second * time.Duration(sleep), true
59+
}
60+
61+
// Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
62+
retryTime, err := time.Parse(time.RFC1123, header)
63+
if err != nil {
64+
return 0, false
65+
}
66+
if until := retryTime.Sub(timeNow()); until > 0 {
67+
return until, true
68+
}
69+
// date is in the past
70+
return 0, true
71+
}
72+
4173
// Function to parse the reset header
42-
func parseResetHeader(value *string) *time.Duration {
74+
func parseResetHeader(value *string) (time.Duration, bool) {
4375
if value == nil || !resetHeaderPattern.MatchString(*value) {
44-
return nil
76+
return 0, false
4577
}
4678

4779
// Parse the header value to a Unix timestamp
4880
resetTimeUnix, err := strconv.ParseInt(*value, 10, 64)
4981
if err != nil {
50-
return nil
82+
return 0, false
5183
}
5284

5385
// Calculate the duration until the reset time
5486
resetTime := time.Unix(resetTimeUnix, 0)
5587
waitDuration := time.Until(resetTime)
5688

57-
// Return nil if the reset time has already passed
89+
// Return 0 if the reset time has already passed
5890
if waitDuration < 0 {
59-
zeroDuration := time.Duration(0)
60-
return &zeroDuration
91+
return 0, true
6192
}
62-
return &waitDuration
93+
return waitDuration, true
6394
}

0 commit comments

Comments
 (0)