mirror of
https://github.com/mayflower/docker-ls.git
synced 2025-11-26 12:03:12 +03:00
137 lines
2.8 KiB
Go
137 lines
2.8 KiB
Go
package connector
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"git.mayflower.de/vaillant-team/docker-ls/lib/auth"
|
|
)
|
|
|
|
type tokenAuthConnector struct {
|
|
cfg Config
|
|
httpClient *http.Client
|
|
authenticator auth.Authenticator
|
|
semaphore semaphore
|
|
tokenCache *tokenCache
|
|
stat *statistics
|
|
}
|
|
|
|
func (r *tokenAuthConnector) Delete(url *url.URL, hint string) (*http.Response, error) {
|
|
return r.Request("DELETE", url, hint)
|
|
}
|
|
|
|
func (r *tokenAuthConnector) Get(url *url.URL, hint string) (*http.Response, error) {
|
|
return r.Request("GET", url, hint)
|
|
}
|
|
|
|
func (r *tokenAuthConnector) Request(method string, url *url.URL, hint string) (response *http.Response, err error) {
|
|
r.semaphore.Lock()
|
|
defer r.semaphore.Unlock()
|
|
|
|
r.stat.Request()
|
|
|
|
var token auth.Token
|
|
request, err := http.NewRequest(method, url.String(), strings.NewReader(""))
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if hint != "" {
|
|
if token = r.tokenCache.Get(hint); token != nil {
|
|
r.stat.CacheHitAtApiLevel()
|
|
} else {
|
|
r.stat.CacheMissAtApiLevel()
|
|
}
|
|
}
|
|
|
|
resp, err := r.attemptRequestWithToken(request, token)
|
|
|
|
if err != nil || resp.StatusCode != http.StatusUnauthorized {
|
|
response = resp
|
|
return
|
|
}
|
|
|
|
if token != nil {
|
|
r.stat.CacheFailAtApiLevel()
|
|
}
|
|
|
|
if resp.Close {
|
|
resp.Body.Close()
|
|
}
|
|
|
|
challenge, err := auth.ParseChallenge(resp.Header.Get("www-authenticate"))
|
|
|
|
if err != nil {
|
|
err = errors.New(err.Error() +
|
|
" Are you shure that you are using the correct (token) auth scheme?")
|
|
|
|
return
|
|
}
|
|
|
|
token, err = r.authenticator.Authenticate(challenge, false)
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if token != nil {
|
|
if token.Fresh() {
|
|
r.stat.CacheMissAtAuthLevel()
|
|
} else {
|
|
r.stat.CacheHitAtAuthLevel()
|
|
}
|
|
}
|
|
|
|
response, err = r.attemptRequestWithToken(request, token)
|
|
|
|
if err == nil &&
|
|
response.StatusCode == http.StatusUnauthorized &&
|
|
!token.Fresh() {
|
|
|
|
r.stat.CacheFailAtAuthLevel()
|
|
|
|
token, err = r.authenticator.Authenticate(challenge, true)
|
|
|
|
if err == nil {
|
|
return
|
|
}
|
|
|
|
response, err = r.attemptRequestWithToken(request, token)
|
|
}
|
|
|
|
if hint != "" && err == nil && response.StatusCode != http.StatusUnauthorized {
|
|
r.tokenCache.Set(hint, token)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (r *tokenAuthConnector) attemptRequestWithToken(request *http.Request, token auth.Token) (*http.Response, error) {
|
|
if token != nil {
|
|
request.Header.Set("Authorization", "Bearer "+token.Value())
|
|
}
|
|
|
|
return r.httpClient.Do(request)
|
|
}
|
|
|
|
func (r *tokenAuthConnector) GetStatistics() Statistics {
|
|
return r.stat
|
|
}
|
|
|
|
func NewTokenAuthConnector(cfg Config) Connector {
|
|
connector := tokenAuthConnector{
|
|
cfg: cfg,
|
|
httpClient: createHttpClient(cfg),
|
|
semaphore: newSemaphore(cfg.MaxConcurrentRequests()),
|
|
tokenCache: newTokenCache(),
|
|
stat: new(statistics),
|
|
}
|
|
|
|
connector.authenticator = auth.NewAuthenticator(connector.httpClient, cfg.Credentials())
|
|
|
|
return &connector
|
|
}
|