Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add User-Agent to all our outgoing requests #113

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions registry/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ func Client(host string, opts *ociclient.Options) (ociregistry.Interface, error)
clientOptions.Transport = http.DefaultTransport
}

// 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
// IMPORTANT: this wrapper stays first! (https://github.com/cue-labs/oci/issues/37#issuecomment-2628321222)
clientOptions.Transport = &userAgentRoundTripper{
roundTripper: clientOptions.Transport,
userAgent: "https://github.com/docker-library/meta-scripts", // TODO allow this to be modified via environment variable
}

// if we have a rate limiter configured for this registry, shim it in
if limiter, ok := registryRateLimiters[host]; ok {
clientOptions.Transport = &rateLimitedRetryingRoundTripper{
Expand Down
31 changes: 31 additions & 0 deletions registry/user-agent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package registry

// https://github.com/docker-library/meta-scripts/issues/111
// https://github.com/cue-labs/oci/issues/37

import (
"fmt"
"maps"
"net/http"
)

// an implementation of [net/http.RoundTripper] that transparently injects User-Agent (as a wrapper around another [net/http.RoundTripper])
type userAgentRoundTripper struct {
roundTripper http.RoundTripper
userAgent string
}

func (d *userAgentRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
// 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

if d.userAgent == "" {
// 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
return nil, fmt.Errorf("missing userAgent in userAgentRoundTripper! (request %s)", req.URL)
}

// https://github.com/cue-lang/cue/blob/0a43336cccf3b6fc632e976912d74fb2c9670557/internal/cueversion/transport.go#L27-L34
reqClone := *req
reqClone.Header = maps.Clone(reqClone.Header)
reqClone.Header.Set("User-Agent", d.userAgent)
return d.roundTripper.RoundTrip(&reqClone)
}