You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

97 lines
2.8 KiB

  1. // Copyright 2018 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // +build go1.8
  15. // Package propagation implements X-Cloud-Trace-Context header propagation used
  16. // by Google Cloud products.
  17. package propagation
  18. import (
  19. "encoding/binary"
  20. "encoding/hex"
  21. "fmt"
  22. "net/http"
  23. "strconv"
  24. "strings"
  25. "go.opencensus.io/trace"
  26. "go.opencensus.io/trace/propagation"
  27. )
  28. const (
  29. httpHeaderMaxSize = 200
  30. httpHeader = `X-Cloud-Trace-Context`
  31. )
  32. var _ propagation.HTTPFormat = (*HTTPFormat)(nil)
  33. // HTTPFormat implements propagation.HTTPFormat to propagate
  34. // traces in HTTP headers for Google Cloud Platform and Stackdriver Trace.
  35. type HTTPFormat struct{}
  36. // SpanContextFromRequest extracts a Stackdriver Trace span context from incoming requests.
  37. func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
  38. h := req.Header.Get(httpHeader)
  39. // See https://cloud.google.com/trace/docs/faq for the header HTTPFormat.
  40. // Return if the header is empty or missing, or if the header is unreasonably
  41. // large, to avoid making unnecessary copies of a large string.
  42. if h == "" || len(h) > httpHeaderMaxSize {
  43. return trace.SpanContext{}, false
  44. }
  45. // Parse the trace id field.
  46. slash := strings.Index(h, `/`)
  47. if slash == -1 {
  48. return trace.SpanContext{}, false
  49. }
  50. tid, h := h[:slash], h[slash+1:]
  51. buf, err := hex.DecodeString(tid)
  52. if err != nil {
  53. return trace.SpanContext{}, false
  54. }
  55. copy(sc.TraceID[:], buf)
  56. // Parse the span id field.
  57. spanstr := h
  58. semicolon := strings.Index(h, `;`)
  59. if semicolon != -1 {
  60. spanstr, h = h[:semicolon], h[semicolon+1:]
  61. }
  62. sid, err := strconv.ParseUint(spanstr, 10, 64)
  63. if err != nil {
  64. return trace.SpanContext{}, false
  65. }
  66. binary.BigEndian.PutUint64(sc.SpanID[:], sid)
  67. // Parse the options field, options field is optional.
  68. if !strings.HasPrefix(h, "o=") {
  69. return sc, true
  70. }
  71. o, err := strconv.ParseUint(h[2:], 10, 64)
  72. if err != nil {
  73. return trace.SpanContext{}, false
  74. }
  75. sc.TraceOptions = trace.TraceOptions(o)
  76. return sc, true
  77. }
  78. // SpanContextToRequest modifies the given request to include a Stackdriver Trace header.
  79. func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
  80. sid := binary.BigEndian.Uint64(sc.SpanID[:])
  81. header := fmt.Sprintf("%s/%d;o=%d", hex.EncodeToString(sc.TraceID[:]), sid, int64(sc.TraceOptions))
  82. req.Header.Set(httpHeader, header)
  83. }