header.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // Copyright 2018, Google Inc.
  2. // All rights reserved.
  3. //
  4. // Redistribution and use in source and binary forms, with or without
  5. // modification, are permitted provided that the following conditions are
  6. // met:
  7. //
  8. // * Redistributions of source code must retain the above copyright
  9. // notice, this list of conditions and the following disclaimer.
  10. // * Redistributions in binary form must reproduce the above
  11. // copyright notice, this list of conditions and the following disclaimer
  12. // in the documentation and/or other materials provided with the
  13. // distribution.
  14. // * Neither the name of Google Inc. nor the names of its
  15. // contributors may be used to endorse or promote products derived from
  16. // this software without specific prior written permission.
  17. //
  18. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  19. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  20. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  21. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  22. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  23. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  24. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  25. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  26. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  28. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. package gax
  30. import (
  31. "bytes"
  32. "context"
  33. "fmt"
  34. "net/http"
  35. "runtime"
  36. "strings"
  37. "unicode"
  38. "github.com/googleapis/gax-go/v2/callctx"
  39. "google.golang.org/grpc/metadata"
  40. )
  41. var (
  42. // GoVersion is a header-safe representation of the current runtime
  43. // environment's Go version. This is for GAX consumers that need to
  44. // report the Go runtime version in API calls.
  45. GoVersion string
  46. // version is a package internal global variable for testing purposes.
  47. version = runtime.Version
  48. )
  49. // versionUnknown is only used when the runtime version cannot be determined.
  50. const versionUnknown = "UNKNOWN"
  51. func init() {
  52. GoVersion = goVersion()
  53. }
  54. // goVersion returns a Go runtime version derived from the runtime environment
  55. // that is modified to be suitable for reporting in a header, meaning it has no
  56. // whitespace. If it is unable to determine the Go runtime version, it returns
  57. // versionUnknown.
  58. func goVersion() string {
  59. const develPrefix = "devel +"
  60. s := version()
  61. if strings.HasPrefix(s, develPrefix) {
  62. s = s[len(develPrefix):]
  63. if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
  64. s = s[:p]
  65. }
  66. return s
  67. } else if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 {
  68. s = s[:p]
  69. }
  70. notSemverRune := func(r rune) bool {
  71. return !strings.ContainsRune("0123456789.", r)
  72. }
  73. if strings.HasPrefix(s, "go1") {
  74. s = s[2:]
  75. var prerelease string
  76. if p := strings.IndexFunc(s, notSemverRune); p >= 0 {
  77. s, prerelease = s[:p], s[p:]
  78. }
  79. if strings.HasSuffix(s, ".") {
  80. s += "0"
  81. } else if strings.Count(s, ".") < 2 {
  82. s += ".0"
  83. }
  84. if prerelease != "" {
  85. // Some release candidates already have a dash in them.
  86. if !strings.HasPrefix(prerelease, "-") {
  87. prerelease = "-" + prerelease
  88. }
  89. s += prerelease
  90. }
  91. return s
  92. }
  93. return "UNKNOWN"
  94. }
  95. // XGoogHeader is for use by the Google Cloud Libraries only. See package
  96. // [github.com/googleapis/gax-go/v2/callctx] for help setting/retrieving
  97. // request/response headers.
  98. //
  99. // XGoogHeader formats key-value pairs.
  100. // The resulting string is suitable for x-goog-api-client header.
  101. func XGoogHeader(keyval ...string) string {
  102. if len(keyval) == 0 {
  103. return ""
  104. }
  105. if len(keyval)%2 != 0 {
  106. panic("gax.Header: odd argument count")
  107. }
  108. var buf bytes.Buffer
  109. for i := 0; i < len(keyval); i += 2 {
  110. buf.WriteByte(' ')
  111. buf.WriteString(keyval[i])
  112. buf.WriteByte('/')
  113. buf.WriteString(keyval[i+1])
  114. }
  115. return buf.String()[1:]
  116. }
  117. // InsertMetadataIntoOutgoingContext is for use by the Google Cloud Libraries
  118. // only. See package [github.com/googleapis/gax-go/v2/callctx] for help
  119. // setting/retrieving request/response headers.
  120. //
  121. // InsertMetadataIntoOutgoingContext returns a new context that merges the
  122. // provided keyvals metadata pairs with any existing metadata/headers in the
  123. // provided context. keyvals should have a corresponding value for every key
  124. // provided. If there is an odd number of keyvals this method will panic.
  125. // Existing values for keys will not be overwritten, instead provided values
  126. // will be appended to the list of existing values.
  127. func InsertMetadataIntoOutgoingContext(ctx context.Context, keyvals ...string) context.Context {
  128. return metadata.NewOutgoingContext(ctx, insertMetadata(ctx, keyvals...))
  129. }
  130. // BuildHeaders is for use by the Google Cloud Libraries only. See package
  131. // [github.com/googleapis/gax-go/v2/callctx] for help setting/retrieving
  132. // request/response headers.
  133. //
  134. // BuildHeaders returns a new http.Header that merges the provided
  135. // keyvals header pairs with any existing metadata/headers in the provided
  136. // context. keyvals should have a corresponding value for every key provided.
  137. // If there is an odd number of keyvals this method will panic.
  138. // Existing values for keys will not be overwritten, instead provided values
  139. // will be appended to the list of existing values.
  140. func BuildHeaders(ctx context.Context, keyvals ...string) http.Header {
  141. return http.Header(insertMetadata(ctx, keyvals...))
  142. }
  143. func insertMetadata(ctx context.Context, keyvals ...string) metadata.MD {
  144. if len(keyvals)%2 != 0 {
  145. panic(fmt.Sprintf("gax: an even number of key value pairs must be provided, got %d", len(keyvals)))
  146. }
  147. out, ok := metadata.FromOutgoingContext(ctx)
  148. if !ok {
  149. out = metadata.MD(make(map[string][]string))
  150. }
  151. headers := callctx.HeadersFromContext(ctx)
  152. for k, v := range headers {
  153. out[k] = append(out[k], v...)
  154. }
  155. for i := 0; i < len(keyvals); i = i + 2 {
  156. out[keyvals[i]] = append(out[keyvals[i]], keyvals[i+1])
  157. }
  158. return out
  159. }