mirror of
https://github.com/moby/moby.git
synced 2026-07-25 00:37:00 +00:00
The User-Agent includes the kernel version, which involves making a syscall (and parsing the results) on Linux, and reading (plus parsing) the registry on Windows. These operations are relatively costly, and we should not perform those on every request that uses the User-Agent. This patch adds a sync.Once so that we only perform these actions once for the lifetime of the daemon's process. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
100 lines
3.0 KiB
Go
100 lines
3.0 KiB
Go
package dockerversion // import "github.com/docker/docker/dockerversion"
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"runtime"
|
|
"sync"
|
|
|
|
"github.com/docker/docker/pkg/parsers/kernel"
|
|
"github.com/docker/docker/pkg/useragent"
|
|
)
|
|
|
|
// UAStringKey is used as key type for user-agent string in net/context struct
|
|
type UAStringKey struct{}
|
|
|
|
// DockerUserAgent is the User-Agent the Docker client uses to identify itself.
|
|
// In accordance with RFC 7231 (5.5.3) is of the form:
|
|
//
|
|
// [docker client's UA] UpstreamClient([upstream client's UA])
|
|
func DockerUserAgent(ctx context.Context) string {
|
|
daemonUA := getDaemonUserAgent()
|
|
if upstreamUA := getUserAgentFromContext(ctx); len(upstreamUA) > 0 {
|
|
return insertUpstreamUserAgent(upstreamUA, daemonUA)
|
|
}
|
|
return daemonUA
|
|
}
|
|
|
|
var (
|
|
daemonUAOnce sync.Once
|
|
daemonUA string
|
|
)
|
|
|
|
// getUserAgentFromContext returns the user-agent to use for requests made by
|
|
// the daemon.
|
|
//
|
|
// It includes;
|
|
//
|
|
// - the docker version
|
|
// - go version
|
|
// - git-commit
|
|
// - kernel version
|
|
// - os
|
|
// - architecture
|
|
func getDaemonUserAgent() string {
|
|
daemonUAOnce.Do(func() {
|
|
httpVersion := make([]useragent.VersionInfo, 0, 6)
|
|
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "docker", Version: Version})
|
|
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "go", Version: runtime.Version()})
|
|
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "git-commit", Version: GitCommit})
|
|
if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
|
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "kernel", Version: kernelVersion.String()})
|
|
}
|
|
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "os", Version: runtime.GOOS})
|
|
httpVersion = append(httpVersion, useragent.VersionInfo{Name: "arch", Version: runtime.GOARCH})
|
|
daemonUA = useragent.AppendVersions("", httpVersion...)
|
|
})
|
|
return daemonUA
|
|
}
|
|
|
|
// getUserAgentFromContext returns the previously saved user-agent context stored in ctx, if one exists
|
|
func getUserAgentFromContext(ctx context.Context) string {
|
|
var upstreamUA string
|
|
if ctx != nil {
|
|
var ki interface{} = ctx.Value(UAStringKey{})
|
|
if ki != nil {
|
|
upstreamUA = ctx.Value(UAStringKey{}).(string)
|
|
}
|
|
}
|
|
return upstreamUA
|
|
}
|
|
|
|
// escapeStr returns s with every rune in charsToEscape escaped by a backslash
|
|
func escapeStr(s string, charsToEscape string) string {
|
|
var ret string
|
|
for _, currRune := range s {
|
|
appended := false
|
|
for _, escapableRune := range charsToEscape {
|
|
if currRune == escapableRune {
|
|
ret += `\` + string(currRune)
|
|
appended = true
|
|
break
|
|
}
|
|
}
|
|
if !appended {
|
|
ret += string(currRune)
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
// insertUpstreamUserAgent adds the upstream client useragent to create a user-agent
|
|
// string of the form:
|
|
//
|
|
// $dockerUA UpstreamClient($upstreamUA)
|
|
func insertUpstreamUserAgent(upstreamUA string, dockerUA string) string {
|
|
charsToEscape := `();\`
|
|
upstreamUAEscaped := escapeStr(upstreamUA, charsToEscape)
|
|
return fmt.Sprintf("%s UpstreamClient(%s)", dockerUA, upstreamUAEscaped)
|
|
}
|