crowdsec/pkg/apiclient/auth_retry.go

82 lines
1.5 KiB
Go

package apiclient
import (
"math/rand"
"net/http"
"time"
log "github.com/sirupsen/logrus"
"github.com/crowdsecurity/crowdsec/pkg/fflag"
)
type retryRoundTripper struct {
next http.RoundTripper
maxAttempts int
retryStatusCodes []int
withBackOff bool
onBeforeRequest func(attempt int)
}
func (r retryRoundTripper) ShouldRetry(statusCode int) bool {
for _, code := range r.retryStatusCodes {
if code == statusCode {
return true
}
}
return false
}
func (r retryRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
var (
resp *http.Response
err error
)
backoff := 0
maxAttempts := r.maxAttempts
if fflag.DisableHttpRetryBackoff.IsEnabled() {
maxAttempts = 1
}
for i := 0; i < maxAttempts; i++ {
if i > 0 {
if r.withBackOff {
//nolint:gosec
backoff += 10 + rand.Intn(20)
}
log.Infof("retrying in %d seconds (attempt %d of %d)", backoff, i+1, r.maxAttempts)
select {
case <-req.Context().Done():
return nil, req.Context().Err()
case <-time.After(time.Duration(backoff) * time.Second):
}
}
if r.onBeforeRequest != nil {
r.onBeforeRequest(i)
}
clonedReq := cloneRequest(req)
resp, err = r.next.RoundTrip(clonedReq)
if err != nil {
if left := maxAttempts - i - 1; left > 0 {
log.Errorf("error while performing request: %s; %d retries left", err, left)
}
continue
}
if !r.ShouldRetry(resp.StatusCode) {
return resp, nil
}
}
return resp, err
}