@@ -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+
4768func (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 \t StatusCode=%d\n \t method=%s\n \t url=%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