useragent.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. package dockerversion // import "github.com/docker/docker/dockerversion"
  2. import (
  3. "context"
  4. "fmt"
  5. "runtime"
  6. "sync"
  7. "github.com/docker/docker/pkg/parsers/kernel"
  8. "github.com/docker/docker/pkg/useragent"
  9. )
  10. // UAStringKey is used as key type for user-agent string in net/context struct
  11. type UAStringKey struct{}
  12. // DockerUserAgent is the User-Agent the Docker client uses to identify itself.
  13. // In accordance with RFC 7231 (5.5.3) is of the form:
  14. //
  15. // [docker client's UA] UpstreamClient([upstream client's UA])
  16. func DockerUserAgent(ctx context.Context) string {
  17. daemonUA := getDaemonUserAgent()
  18. if upstreamUA := getUserAgentFromContext(ctx); len(upstreamUA) > 0 {
  19. return insertUpstreamUserAgent(upstreamUA, daemonUA)
  20. }
  21. return daemonUA
  22. }
  23. var (
  24. daemonUAOnce sync.Once
  25. daemonUA string
  26. )
  27. // getDaemonUserAgent returns the user-agent to use for requests made by
  28. // the daemon.
  29. //
  30. // It includes;
  31. //
  32. // - the docker version
  33. // - go version
  34. // - git-commit
  35. // - kernel version
  36. // - os
  37. // - architecture
  38. func getDaemonUserAgent() string {
  39. daemonUAOnce.Do(func() {
  40. httpVersion := make([]useragent.VersionInfo, 0, 6)
  41. httpVersion = append(httpVersion, useragent.VersionInfo{Name: "docker", Version: Version})
  42. httpVersion = append(httpVersion, useragent.VersionInfo{Name: "go", Version: runtime.Version()})
  43. httpVersion = append(httpVersion, useragent.VersionInfo{Name: "git-commit", Version: GitCommit})
  44. if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
  45. httpVersion = append(httpVersion, useragent.VersionInfo{Name: "kernel", Version: kernelVersion.String()})
  46. }
  47. httpVersion = append(httpVersion, useragent.VersionInfo{Name: "os", Version: runtime.GOOS})
  48. httpVersion = append(httpVersion, useragent.VersionInfo{Name: "arch", Version: runtime.GOARCH})
  49. daemonUA = useragent.AppendVersions("", httpVersion...)
  50. })
  51. return daemonUA
  52. }
  53. // getUserAgentFromContext returns the previously saved user-agent context stored in ctx, if one exists
  54. func getUserAgentFromContext(ctx context.Context) string {
  55. var upstreamUA string
  56. if ctx != nil {
  57. var ki interface{} = ctx.Value(UAStringKey{})
  58. if ki != nil {
  59. upstreamUA = ctx.Value(UAStringKey{}).(string)
  60. }
  61. }
  62. return upstreamUA
  63. }
  64. const charsToEscape = `();\`
  65. // escapeStr returns s with every rune in charsToEscape escaped by a backslash
  66. func escapeStr(s string) string {
  67. var ret string
  68. for _, currRune := range s {
  69. appended := false
  70. for _, escapableRune := range charsToEscape {
  71. if currRune == escapableRune {
  72. ret += `\` + string(currRune)
  73. appended = true
  74. break
  75. }
  76. }
  77. if !appended {
  78. ret += string(currRune)
  79. }
  80. }
  81. return ret
  82. }
  83. // insertUpstreamUserAgent adds the upstream client useragent to create a user-agent
  84. // string of the form:
  85. //
  86. // $dockerUA UpstreamClient($upstreamUA)
  87. func insertUpstreamUserAgent(upstreamUA string, dockerUA string) string {
  88. return fmt.Sprintf("%s UpstreamClient(%s)", dockerUA, escapeStr(upstreamUA))
  89. }