Skip to content

Commit 5adcfa5

Browse files
committed
Add User-Agent to all our outgoing requests
This is something we should've been doing all along because we've been contributing to the thundering herd of useless `Go-http-client/1.1` agent values hitting Docker Hub.
1 parent 4e6eabf commit 5adcfa5

File tree

2 files changed

+38
-0
lines changed

2 files changed

+38
-0
lines changed

registry/client.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ func Client(host string, opts *ociclient.Options) (ociregistry.Interface, error)
3030
clientOptions.Transport = http.DefaultTransport
3131
}
3232

33+
// make sure we set User-Agent explicitly; this is first so that everything else has an explicit layer at the bottom setting User-Agent so we don't miss any requests
34+
// IMPORTANT: this wrapper stays first! (https://github.com/cue-labs/oci/issues/37#issuecomment-2628321222)
35+
clientOptions.Transport = &userAgentRoundTripper{
36+
roundTripper: clientOptions.Transport,
37+
userAgent: "https://github.com/docker-library/meta-scripts", // TODO allow this to be modified via environment variable
38+
}
39+
3340
// if we have a rate limiter configured for this registry, shim it in
3441
if limiter, ok := registryRateLimiters[host]; ok {
3542
clientOptions.Transport = &rateLimitedRetryingRoundTripper{

registry/user-agent.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package registry
2+
3+
// https://github.com/docker-library/meta-scripts/issues/111
4+
// https://github.com/cue-labs/oci/issues/37
5+
6+
import (
7+
"fmt"
8+
"maps"
9+
"net/http"
10+
)
11+
12+
// an implementation of [net/http.RoundTripper] that transparently injects User-Agent (as a wrapper around another [net/http.RoundTripper])
13+
type userAgentRoundTripper struct {
14+
roundTripper http.RoundTripper
15+
userAgent string
16+
}
17+
18+
func (d *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
19+
// if d is nil or if d.roundTripper is nil, we'll just let the runtime panic because those are both 100% coding errors in the consuming code
20+
21+
if d.userAgent == "" {
22+
// arguably we could `panic` here too since this is *also* a coding error, but it'd be pretty reasonable to source this from an environment variable so `panic` is perhaps a bit user-hostile
23+
return nil, fmt.Errorf("missing userAgent in userAgentRoundTripper! (request %s)", req.URL)
24+
}
25+
26+
// https://github.com/cue-lang/cue/blob/0a43336cccf3b6fc632e976912d74fb2c9670557/internal/cueversion/transport.go#L27-L34
27+
reqClone := *req
28+
reqClone.Header = maps.Clone(reqClone.Header)
29+
reqClone.Header.Set("User-Agent", d.userAgent)
30+
return d.roundTripper.RoundTrip(&reqClone)
31+
}

0 commit comments

Comments
 (0)