81 lines
1.5 KiB
Go
81 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
|
|
}
|