File: useragent.go

package info (click to toggle)
docker.io 27.5.1%2Bdfsg4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 67,384 kB
  • sloc: sh: 5,847; makefile: 1,146; ansic: 664; python: 162; asm: 133
file content (98 lines) | stat: -rw-r--r-- 2,903 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
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, extraVersions ...useragent.VersionInfo) string {
	ua := useragent.AppendVersions(getDaemonUserAgent(), extraVersions...)
	if upstreamUA := getUpstreamUserAgent(ctx); upstreamUA != "" {
		ua += " " + upstreamUA
	}
	return ua
}

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
}

// getUpstreamUserAgent returns the previously saved user-agent context stored
// in ctx, if one exists, and formats it as:
//
//	UpstreamClient(<upstream user agent string>)
//
// It returns an empty string if no user-agent is present in the context.
func getUpstreamUserAgent(ctx context.Context) string {
	var upstreamUA string
	if ctx != nil {
		if ki := ctx.Value(UAStringKey{}); ki != nil {
			upstreamUA = ctx.Value(UAStringKey{}).(string)
		}
	}
	if upstreamUA == "" {
		return ""
	}
	return fmt.Sprintf("UpstreamClient(%s)", escapeStr(upstreamUA))
}

const charsToEscape = `();\`

// escapeStr returns s with every rune in charsToEscape escaped by a backslash
func escapeStr(s 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
}