mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-29 04:06:58 +00:00
76 lines
2.4 KiB
Go
76 lines
2.4 KiB
Go
package credhelper
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"runtime"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// userAgent returns the User-Agent string for outgoing HTTP requests.
|
|
//
|
|
// Format: <binary-name>/<version> (<os>/<arch>; commit <short>)
|
|
//
|
|
// Format follows the convention Docker's own clients use, so it parses
|
|
// cleanly with the same regexes server-side log analyzers already
|
|
// understand. The commit suffix lets users on the device-approval page
|
|
// distinguish two devices on the same version line if they ever need to.
|
|
func userAgent() string {
|
|
short := cfg.Commit
|
|
if len(short) > 7 {
|
|
short = short[:7]
|
|
}
|
|
return fmt.Sprintf("%s/%s (%s/%s; commit %s)",
|
|
cfg.BinaryName, cfg.Version, runtime.GOOS, runtime.GOARCH, short)
|
|
}
|
|
|
|
// uaTransport wraps another RoundTripper and sets the User-Agent header
|
|
// on every request that doesn't already carry one. Used as the default
|
|
// transport for the helper's shared http.Client so we can't forget to
|
|
// set the UA on a future call site.
|
|
type uaTransport struct {
|
|
base http.RoundTripper
|
|
}
|
|
|
|
func (t *uaTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|
if req.Header.Get("User-Agent") == "" {
|
|
// Clone before mutating: net/http may retry a request and the
|
|
// caller could be using the same *Request elsewhere.
|
|
clone := req.Clone(req.Context())
|
|
clone.Header.Set("User-Agent", userAgent())
|
|
req = clone
|
|
}
|
|
base := t.base
|
|
if base == nil {
|
|
base = http.DefaultTransport
|
|
}
|
|
return base.RoundTrip(req)
|
|
}
|
|
|
|
var sharedHTTPClient = sync.OnceValue(func() *http.Client {
|
|
return &http.Client{
|
|
Transport: &uaTransport{base: http.DefaultTransport},
|
|
}
|
|
})
|
|
|
|
// httpClient returns the shared UA-tagged http.Client used for all of
|
|
// the helper's outgoing HTTP requests. It carries no per-request
|
|
// timeout — call sites that want one should use httpClientWithTimeout.
|
|
func httpClient() *http.Client {
|
|
return sharedHTTPClient()
|
|
}
|
|
|
|
// httpClientWithTimeout returns a fresh client that shares the shared
|
|
// transport (so connection pooling and the UA header are preserved) but
|
|
// scopes a per-client timeout. CheckRedirect can be supplied for cases
|
|
// like fetchLatestVersion that need to inspect a redirect rather than
|
|
// follow it.
|
|
func httpClientWithTimeout(timeout time.Duration, checkRedirect func(*http.Request, []*http.Request) error) *http.Client {
|
|
return &http.Client{
|
|
Transport: sharedHTTPClient().Transport,
|
|
Timeout: timeout,
|
|
CheckRedirect: checkRedirect,
|
|
}
|
|
}
|