Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
apiclient: split auth_key, auth_retry, auth_jwt
- Loading branch information
Showing
5 changed files
with
186 additions
and
161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package apiclient | ||
|
||
import ( | ||
"errors" | ||
"net/http" | ||
"net/http/httputil" | ||
"net/url" | ||
|
||
log "github.com/sirupsen/logrus" | ||
) | ||
|
||
type APIKeyTransport struct { | ||
APIKey string | ||
// Transport is the underlying HTTP transport to use when making requests. | ||
// It will default to http.DefaultTransport if nil. | ||
Transport http.RoundTripper | ||
URL *url.URL | ||
VersionPrefix string | ||
UserAgent string | ||
} | ||
|
||
// RoundTrip implements the RoundTripper interface. | ||
func (t *APIKeyTransport) RoundTrip(req *http.Request) (*http.Response, error) { | ||
if t.APIKey == "" { | ||
return nil, errors.New("APIKey is empty") | ||
} | ||
|
||
// We must make a copy of the Request so | ||
// that we don't modify the Request we were given. This is required by the | ||
// specification of http.RoundTripper. | ||
req = cloneRequest(req) | ||
req.Header.Add("X-Api-Key", t.APIKey) | ||
|
||
if t.UserAgent != "" { | ||
req.Header.Add("User-Agent", t.UserAgent) | ||
} | ||
|
||
log.Debugf("req-api: %s %s", req.Method, req.URL.String()) | ||
|
||
if log.GetLevel() >= log.TraceLevel { | ||
dump, _ := httputil.DumpRequest(req, true) | ||
log.Tracef("auth-api request: %s", string(dump)) | ||
} | ||
|
||
// Make the HTTP request. | ||
resp, err := t.transport().RoundTrip(req) | ||
if err != nil { | ||
log.Errorf("auth-api: auth with api key failed return nil response, error: %s", err) | ||
|
||
return resp, err | ||
} | ||
|
||
if log.GetLevel() >= log.TraceLevel { | ||
dump, _ := httputil.DumpResponse(resp, true) | ||
log.Tracef("auth-api response: %s", string(dump)) | ||
} | ||
|
||
log.Debugf("resp-api: http %d", resp.StatusCode) | ||
|
||
return resp, err | ||
} | ||
|
||
func (t *APIKeyTransport) Client() *http.Client { | ||
return &http.Client{Transport: t} | ||
} | ||
|
||
func (t *APIKeyTransport) transport() http.RoundTripper { | ||
if t.Transport != nil { | ||
return t.Transport | ||
} | ||
|
||
return http.DefaultTransport | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package apiclient | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"net/http" | ||
) | ||
|
||
// cloneRequest returns a clone of the provided *http.Request. The clone is a | ||
// shallow copy of the struct and its Header map. | ||
func cloneRequest(r *http.Request) *http.Request { | ||
// shallow copy of the struct | ||
r2 := new(http.Request) | ||
*r2 = *r | ||
// deep copy of the Header | ||
r2.Header = make(http.Header, len(r.Header)) | ||
|
||
for k, s := range r.Header { | ||
r2.Header[k] = append([]string(nil), s...) | ||
} | ||
|
||
if r.Body != nil { | ||
var b bytes.Buffer | ||
|
||
b.ReadFrom(r.Body) | ||
|
||
r.Body = io.NopCloser(&b) | ||
r2.Body = io.NopCloser(bytes.NewReader(b.Bytes())) | ||
} | ||
|
||
return r2 | ||
} |