github.com/crowdsecurity/crowdsec@v1.6.1/pkg/apiclient/auth_retry.go (about) 1 package apiclient 2 3 import ( 4 "math/rand" 5 "net/http" 6 "time" 7 8 log "github.com/sirupsen/logrus" 9 10 "github.com/crowdsecurity/crowdsec/pkg/fflag" 11 ) 12 13 type retryRoundTripper struct { 14 next http.RoundTripper 15 maxAttempts int 16 retryStatusCodes []int 17 withBackOff bool 18 onBeforeRequest func(attempt int) 19 } 20 21 func (r retryRoundTripper) ShouldRetry(statusCode int) bool { 22 for _, code := range r.retryStatusCodes { 23 if code == statusCode { 24 return true 25 } 26 } 27 28 return false 29 } 30 31 func (r retryRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { 32 var ( 33 resp *http.Response 34 err error 35 ) 36 37 backoff := 0 38 maxAttempts := r.maxAttempts 39 40 if fflag.DisableHttpRetryBackoff.IsEnabled() { 41 maxAttempts = 1 42 } 43 44 for i := 0; i < maxAttempts; i++ { 45 if i > 0 { 46 if r.withBackOff { 47 //nolint:gosec 48 backoff += 10 + rand.Intn(20) 49 } 50 51 log.Infof("retrying in %d seconds (attempt %d of %d)", backoff, i+1, r.maxAttempts) 52 53 select { 54 case <-req.Context().Done(): 55 return nil, req.Context().Err() 56 case <-time.After(time.Duration(backoff) * time.Second): 57 } 58 } 59 60 if r.onBeforeRequest != nil { 61 r.onBeforeRequest(i) 62 } 63 64 clonedReq := cloneRequest(req) 65 66 resp, err = r.next.RoundTrip(clonedReq) 67 if err != nil { 68 if left := maxAttempts - i - 1; left > 0 { 69 log.Errorf("error while performing request: %s; %d retries left", err, left) 70 } 71 72 continue 73 } 74 75 if !r.ShouldRetry(resp.StatusCode) { 76 return resp, nil 77 } 78 } 79 80 return resp, err 81 }