...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package transport
16
17 import (
18 "net/http"
19 "time"
20
21 "github.com/google/go-containerregistry/internal/retry"
22 )
23
24
25 var defaultBackoff = retry.Backoff{
26 Duration: 100 * time.Millisecond,
27 Factor: 3.0,
28 Jitter: 0.1,
29 Steps: 3,
30 }
31
32 var _ http.RoundTripper = (*retryTransport)(nil)
33
34
35 type retryTransport struct {
36 inner http.RoundTripper
37 backoff retry.Backoff
38 predicate retry.Predicate
39 codes []int
40 }
41
42
43 type Option func(*options)
44
45 type options struct {
46 backoff retry.Backoff
47 predicate retry.Predicate
48 codes []int
49 }
50
51
52 type Backoff = retry.Backoff
53
54
55 func WithRetryBackoff(backoff Backoff) Option {
56 return func(o *options) {
57 o.backoff = backoff
58 }
59 }
60
61
62 func WithRetryPredicate(predicate func(error) bool) Option {
63 return func(o *options) {
64 o.predicate = predicate
65 }
66 }
67
68
69 func WithRetryStatusCodes(codes ...int) Option {
70 return func(o *options) {
71 o.codes = codes
72 }
73 }
74
75
76 func NewRetry(inner http.RoundTripper, opts ...Option) http.RoundTripper {
77 o := &options{
78 backoff: defaultBackoff,
79 predicate: retry.IsTemporary,
80 }
81
82 for _, opt := range opts {
83 opt(o)
84 }
85
86 return &retryTransport{
87 inner: inner,
88 backoff: o.backoff,
89 predicate: o.predicate,
90 codes: o.codes,
91 }
92 }
93
94 func (t *retryTransport) RoundTrip(in *http.Request) (out *http.Response, err error) {
95 roundtrip := func() error {
96 out, err = t.inner.RoundTrip(in)
97 if !retry.Ever(in.Context()) {
98 return nil
99 }
100 if out != nil {
101 for _, code := range t.codes {
102 if out.StatusCode == code {
103 return retryError(out)
104 }
105 }
106 }
107 return err
108 }
109 retry.Retry(roundtrip, t.predicate, t.backoff)
110 return
111 }
112
View as plain text