-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathretry.go
93 lines (76 loc) · 3.33 KB
/
retry.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package http
import (
"context"
"crypto/x509"
"errors"
"net/url"
"regexp"
)
// RetryPolicy defines a function type that determines if an HTTP request should be retried.
// It is invoked after each request attempt, passing the request's context and any encountered error.
// The function returns a boolean indicating whether the request should be retried,
// and a secondary error value that, if non-nil, overrides the original error and terminates further retry attempts.
//
// Parameters:
// - ctx (context.Context): The request's context, carrying cancellation signals and deadlines.
// - err (error): The error encountered during the HTTP request, or nil if the request succeeded.
//
// Returns:
// - retry (bool): True if the request should be retried; false otherwise.
// - errr (error): An error to override the original error, typically when a non-retryable condition is met.
type RetryPolicy func(ctx context.Context, err error) (retry bool, errr error)
var (
// redirectsErrorRegex matches error strings that indicate the maximum number of redirects was exceeded.
// It is used to avoid retrying requests that have failed due to too many redirects.
redirectsErrorRegex = regexp.MustCompile(`stopped after \d+ redirects\z`)
// schemeErrorRegex matches error strings indicating an unsupported protocol scheme.
// This helps in identifying errors that should not be retried.
schemeErrorRegex = regexp.MustCompile(`unsupported protocol scheme`)
)
// isErrorRecoverable determines whether an error encountered during an HTTP request is recoverable,
// meaning that the request may be retried. It examines both the request context and the error details,
// filtering out conditions such as context cancellation, excessive redirects, unsupported protocol schemes,
// or TLS certificate verification failures (e.g., unknown authority).
//
// The function first checks the context for cancellation or deadline expiration. If the context
// has an error, it immediately returns that error, as the request should not be retried.
// It then inspects the error, particularly if it is of type *url.Error, and checks for specific error
// conditions using regular expressions and type assertions. If any non-retryable condition is detected,
// the error is returned and further retries are prevented.
//
// Parameters:
// - ctx (context.Context): The request's context containing cancellation signals or deadlines.
// - err (error): The error encountered during the HTTP request.
//
// Returns:
// - recoverable (bool): True if the error is considered recoverable and the request may be retried;
// false if the error is non-retryable.
// - errr (error): An error to override the original error when a non-retryable condition is detected,
// such as a cancelled context or a specific transport error.
func isErrorRecoverable(ctx context.Context, err error) (recoverable bool, errr error) {
if ctx.Err() != nil {
errr = ctx.Err()
return
}
var URLError *url.Error
if err != nil && errors.As(err, &URLError) {
if redirectsErrorRegex.MatchString(err.Error()) {
errr = err
return
}
if schemeErrorRegex.MatchString(err.Error()) {
errr = err
return
}
var UnknownAuthorityError x509.UnknownAuthorityError
if errors.As(err, &UnknownAuthorityError) {
errr = err
return
}
}
if err != nil {
recoverable = true
return
}
return
}