@@ -40,6 +40,30 @@ func (c *APIClient) parse(body []byte) (response, error) {
4040 return r , nil
4141}
4242
43+ // readResponseBodyWithRetry reads the response body and retries once on unexpected EOF
44+ func (c * APIClient ) readResponseBodyWithRetry (resp * http.Response , req * http.Request ) ([]byte , * http.Response , error ) {
45+ bodyBytes , err := io .ReadAll (resp .Body )
46+ if err != nil {
47+ if strings .Contains (err .Error (), "unexpected EOF" ) {
48+ time .Sleep (5 * time .Second )
49+ // Retry once on unexpected EOF
50+ retryResp , retryErr := c .HttpClient .Do (req )
51+ if retryErr != nil {
52+ return nil , nil , retryErr
53+ }
54+ defer retryResp .Body .Close ()
55+
56+ bodyBytes , retryErr = io .ReadAll (retryResp .Body )
57+ if retryErr != nil {
58+ return nil , nil , fmt .Errorf ("failed to read response body (retry): %w" , retryErr )
59+ }
60+ return bodyBytes , retryResp , nil
61+ }
62+ return nil , nil , fmt .Errorf ("failed to read response body: %w" , err )
63+ }
64+ return bodyBytes , resp , nil
65+ }
66+
4367func toQueryParams (params map [string ]string ) url.Values {
4468 query := url.Values {}
4569 for k , v := range params {
@@ -76,28 +100,12 @@ func (c *APIClient) Post(ctx context.Context, endpoint string, payload interface
76100 }
77101 defer r .Body .Close ()
78102
79- bodyBytes , err := io . ReadAll ( r . Body )
103+ bodyBytes , r , err := c . readResponseBodyWithRetry ( r , req )
80104 if err != nil {
81- if strings .Contains (err .Error (), "unexpected EOF" ) {
82- time .Sleep (5 * time .Second )
83- // Retry once on unexpected EOF
84- r2 , err2 := c .HttpClient .Do (req )
85- if err2 != nil {
86- if e , ok := err2 .(* url.Error ); ok && e .Timeout () {
87- return nil , gitlab .TransientError (fmt .Errorf ("failed to send POST request to %s with payload %+v (retry): %w" , endpointUrl , payload , e ))
88- }
89- return nil , fmt .Errorf ("failed to send POST request to %s with payload %+v (retry): %w" , endpoint , payload , err2 )
90- }
91- defer r2 .Body .Close ()
92-
93- bodyBytes , err2 = io .ReadAll (r2 .Body )
94- if err2 != nil {
95- return nil , fmt .Errorf ("failed to read response body (retry): %w" , err2 )
96- }
97- r = r2
98- } else {
99- return nil , fmt .Errorf ("failed to read response body: %w" , err )
105+ if e , ok := err .(* url.Error ); ok && e .Timeout () {
106+ return nil , gitlab .TransientError (fmt .Errorf ("failed to send POST request to %s with payload %+v (retry): %w" , endpointUrl , payload , e ))
100107 }
108+ return nil , err
101109 }
102110
103111 baseResponse , err := c .parse (bodyBytes )
@@ -141,28 +149,12 @@ func (c *APIClient) Delete(ctx context.Context, endpoint string, payload interfa
141149 }
142150 defer r .Body .Close ()
143151
144- bodyBytes , err := io . ReadAll ( r . Body )
152+ bodyBytes , r , err := c . readResponseBodyWithRetry ( r , req )
145153 if err != nil {
146- if strings .Contains (err .Error (), "unexpected EOF" ) {
147- time .Sleep (5 * time .Second )
148- // Retry once on unexpected EOF
149- r2 , err2 := c .HttpClient .Do (req )
150- if err2 != nil {
151- if e , ok := err2 .(* url.Error ); ok && e .Timeout () {
152- return nil , gitlab .TransientError (fmt .Errorf ("failed to send DELETE request to %s with payload %+v (retry): %w" , endpointUrl , payload , e ))
153- }
154- return nil , fmt .Errorf ("failed to send DELETE request to %s with payload %+v (retry): %w" , endpoint , payload , err2 )
155- }
156- defer r2 .Body .Close ()
157-
158- bodyBytes , err2 = io .ReadAll (r2 .Body )
159- if err2 != nil {
160- return nil , fmt .Errorf ("failed to read response body (retry): %w" , err2 )
161- }
162- r = r2
163- } else {
164- return nil , fmt .Errorf ("failed to read response body: %w" , err )
154+ if e , ok := err .(* url.Error ); ok && e .Timeout () {
155+ return nil , gitlab .TransientError (fmt .Errorf ("failed to send DELETE request to %s with payload %+v (retry): %w" , endpointUrl , payload , e ))
165156 }
157+ return nil , err
166158 }
167159
168160 baseResponse , err := c .parse (bodyBytes )
@@ -203,28 +195,12 @@ func (c *APIClient) Get(ctx context.Context, endpoint string, queryParams map[st
203195 }
204196 defer r .Body .Close ()
205197
206- bodyBytes , err := io . ReadAll ( r . Body )
198+ bodyBytes , r , err := c . readResponseBodyWithRetry ( r , req )
207199 if err != nil {
208- if strings .Contains (err .Error (), "unexpected EOF" ) {
209- time .Sleep (5 * time .Second )
210- // Retry once on unexpected EOF
211- r2 , err2 := c .HttpClient .Do (req )
212- if err2 != nil {
213- if e , ok := err2 .(* url.Error ); ok && e .Timeout () {
214- return nil , gitlab .TransientError (fmt .Errorf ("failed to send GET request to %s (retry): %w" , endpointUrl , e ))
215- }
216- return nil , fmt .Errorf ("failed to send GET request to %s (retry): %w" , endpointUrl , err2 )
217- }
218- defer r2 .Body .Close ()
219-
220- bodyBytes , err2 = io .ReadAll (r2 .Body )
221- if err2 != nil {
222- return nil , fmt .Errorf ("failed to read response body (retry): %w" , err2 )
223- }
224- r = r2
225- } else {
226- return nil , fmt .Errorf ("failed to read response body: %w" , err )
200+ if e , ok := err .(* url.Error ); ok && e .Timeout () {
201+ return nil , gitlab .TransientError (fmt .Errorf ("failed to send GET request to %s (retry): %w" , endpointUrl , e ))
227202 }
203+ return nil , err
228204 }
229205
230206 baseResponse , err := c .parse (bodyBytes )
0 commit comments