Skip to content

Commit 7a6fb5e

Browse files
committed
add package "http/transport"
1 parent 4ea9f55 commit 7a6fb5e

1 file changed

Lines changed: 78 additions & 0 deletions

File tree

http/transport/retrier.go

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package transport
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io"
7+
"net/http"
8+
"time"
9+
)
10+
11+
// TODO: On the first request attempt of the retrier,
12+
// read and save the main request's body at the same time
13+
// as it's sending it to upstream.
14+
// On subsequent attempts, use the saved body.
15+
16+
// TODO: Use the bufpool package to store the body once it's ready.
17+
18+
type Retrier struct {
19+
MaxTries int
20+
RetryDelay time.Duration
21+
Transport http.RoundTripper // The upstream transport to send the requests to.
22+
}
23+
24+
func (r Retrier) RoundTrip(req *http.Request) (*http.Response, error) {
25+
26+
if r.Transport == nil {
27+
r.Transport = http.DefaultTransport
28+
}
29+
30+
var bodyReader *bytes.Reader
31+
32+
if req.Body != nil && req.Body != http.NoBody {
33+
b, err := io.ReadAll(req.Body)
34+
if err != nil {
35+
return nil, fmt.Errorf("could not read request body: %w", err)
36+
}
37+
bodyReader = bytes.NewReader(b)
38+
}
39+
40+
var lastErr error
41+
42+
i := r.MaxTries
43+
if i == 0 {
44+
i = -1
45+
}
46+
47+
for {
48+
if i == 0 {
49+
break
50+
}
51+
if i > 0 {
52+
i--
53+
}
54+
55+
// Clone the request
56+
newReq := req.Clone(req.Context())
57+
58+
// Renew the request body
59+
if bodyReader != nil {
60+
bodyReader.Seek(0, io.SeekStart)
61+
newReq.Body = io.NopCloser(bodyReader)
62+
}
63+
64+
// Make the request and get a response
65+
resp, err := r.Transport.RoundTrip(newReq)
66+
if err == nil {
67+
return resp, nil
68+
}
69+
70+
lastErr = err
71+
time.Sleep(r.RetryDelay)
72+
}
73+
74+
return nil, fmt.Errorf(
75+
"failed to make the request after %d tries: %w",
76+
r.MaxTries, lastErr,
77+
)
78+
}

0 commit comments

Comments
 (0)