| 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 |
| ) |
| |
| // getDaemonUserAgent 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) |
| } |