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  }