Skip to content

Commit c47cbe7

Browse files
committed
fix(go/transport) Fix retry strategy because of req.Body read multiple times.
1 parent 1039305 commit c47cbe7

File tree

1 file changed

+37
-1
lines changed
  • clients/algoliasearch-client-go/algolia/transport

1 file changed

+37
-1
lines changed

clients/algoliasearch-client-go/algolia/transport/transport.go

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,27 @@ func New(cfg Configuration) *Transport {
4444
return transport
4545
}
4646

47+
func prepareRetryableRequest(req *http.Request) (*http.Request, error) {
48+
// Read the original body
49+
if req.Body == nil {
50+
return req, nil // Nothing to do if there's no body
51+
}
52+
53+
bodyBytes, err := io.ReadAll(req.Body)
54+
if err != nil {
55+
return nil, err
56+
}
57+
_ = req.Body.Close() // close the original body
58+
59+
// Set up GetBody to recreate the body for retries
60+
req.Body = io.NopCloser(bytes.NewReader(bodyBytes))
61+
req.GetBody = func() (io.ReadCloser, error) {
62+
return io.NopCloser(bytes.NewReader(bodyBytes)), nil
63+
}
64+
65+
return req, nil
66+
}
67+
4768
func (t *Transport) Request(ctx context.Context, req *http.Request, k call.Kind, c RequestConfiguration) (*http.Response, []byte, error) {
4869
var intermediateNetworkErrors []error
4970

@@ -52,7 +73,10 @@ func (t *Transport) Request(ctx context.Context, req *http.Request, k call.Kind,
5273
req.Header.Add("Content-Encoding", "gzip")
5374
}
5475

55-
for _, h := range t.retryStrategy.GetTryableHosts(k) {
76+
// Prepare the request to be retryable.
77+
req, _ = prepareRetryableRequest(req)
78+
79+
for i, h := range t.retryStrategy.GetTryableHosts(k) {
5680
// Handle per-request timeout by using a context with timeout.
5781
// Note that because we are in a loop, the cancel() callback cannot be
5882
// deferred. Instead, we call it precisely after the end of each loop or
@@ -62,8 +86,17 @@ func (t *Transport) Request(ctx context.Context, req *http.Request, k call.Kind,
6286
var (
6387
ctxTimeout time.Duration
6488
connectTimeout time.Duration
89+
err error
6590
)
6691

92+
// Reassign a fresh body for the retry
93+
if i > 0 && req.GetBody != nil {
94+
req.Body, err = req.GetBody()
95+
if err != nil {
96+
break
97+
}
98+
}
99+
67100
switch {
68101
case k == call.Read && c.ReadTimeout != nil:
69102
ctxTimeout = *c.ReadTimeout
@@ -113,6 +146,9 @@ func (t *Transport) Request(ctx context.Context, req *http.Request, k call.Kind,
113146
default:
114147
if err != nil {
115148
intermediateNetworkErrors = append(intermediateNetworkErrors, err)
149+
} else if res != nil {
150+
msg := fmt.Sprintf("cannot perform request:\n\tStatusCode=%d\n\tmethod=%s\n\turl=%s\n\t", res.StatusCode, req.Method, req.URL)
151+
intermediateNetworkErrors = append(intermediateNetworkErrors, errors.New(msg))
116152
}
117153
if res != nil && res.Body != nil {
118154
if err = res.Body.Close(); err != nil {

0 commit comments

Comments
 (0)