Files
gocryptotrader/exchanges/request/retry.go
David Ackroyd 56e535001c Implement Request Retry and Backoff (#491)
Refactoring the timeout retries into a more general 'retry policy' with
 support for retrying on HTTP 429 (Too Many Requests) and other responses
 with a `Retry-After` header

The delay between requests is controlled by a combination of a 'backoff'
 (currently only a simple linear backoff), and honouring the
 `Retry-After` value (longest delay wins)

This makes the 'rate limiter' an optional argument as well, removing the
 use of `nil` when one isn't supplied

Signed-off-by: David Ackroyd <daveo.ackroyd@gmail.com>
2020-05-05 13:12:29 +10:00

56 lines
1.1 KiB
Go

package request
import (
"net"
"net/http"
"strconv"
"time"
)
const (
headerRetryAfter = "Retry-After"
)
// DefaultRetryPolicy determines whether the request should be retried, implemented with a default strategy.
func DefaultRetryPolicy(resp *http.Response, err error) (bool, error) {
if err != nil {
if timeoutErr, ok := err.(net.Error); ok && timeoutErr.Timeout() {
return true, nil
}
return false, err
}
if resp.StatusCode == http.StatusTooManyRequests {
return true, nil
}
if resp.Header.Get(headerRetryAfter) != "" {
return true, nil
}
return false, nil
}
// RetryAfter parses the Retry-After header in the response to determine the minimum
// duration needed to wait before retrying.
func RetryAfter(resp *http.Response, now time.Time) time.Duration {
if resp == nil {
return 0
}
after := resp.Header.Get(headerRetryAfter)
if after == "" {
return 0
}
if sec, err := strconv.ParseInt(after, 10, 32); err == nil {
return time.Duration(sec) * time.Second
}
if when, err := time.Parse(time.RFC1123, after); err == nil {
return when.Sub(now)
}
return 0
}