@@ -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 , fmt .Errorf ("cannot read body: %v" , 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,13 @@ 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 , err := prepareRetryableRequest (req )
78+ if err != nil {
79+ return nil , nil , err
80+ }
81+
82+ for i , h := range t .retryStrategy .GetTryableHosts (k ) {
5683 // Handle per-request timeout by using a context with timeout.
5784 // Note that because we are in a loop, the cancel() callback cannot be
5885 // deferred. Instead, we call it precisely after the end of each loop or
@@ -62,8 +89,17 @@ func (t *Transport) Request(ctx context.Context, req *http.Request, k call.Kind,
6289 var (
6390 ctxTimeout time.Duration
6491 connectTimeout time.Duration
92+ err error
6593 )
6694
95+ // Reassign a fresh body for the retry
96+ if i > 0 && req .GetBody != nil {
97+ req .Body , err = req .GetBody ()
98+ if err != nil {
99+ break
100+ }
101+ }
102+
67103 switch {
68104 case k == call .Read && c .ReadTimeout != nil :
69105 ctxTimeout = * c .ReadTimeout
@@ -113,6 +149,9 @@ func (t *Transport) Request(ctx context.Context, req *http.Request, k call.Kind,
113149 default :
114150 if err != nil {
115151 intermediateNetworkErrors = append (intermediateNetworkErrors , err )
152+ } else if res != nil {
153+ 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 )
154+ intermediateNetworkErrors = append (intermediateNetworkErrors , errors .New (msg ))
116155 }
117156 if res != nil && res .Body != nil {
118157 if err = res .Body .Close (); err != nil {
0 commit comments