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.
 
 
 

95 lines
2.9 KiB

  1. // Copyright 2018, OpenCensus Authors
  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. // Package propagation implement X-Cloud-Trace-Context header propagation used
  15. // by Google Cloud products.
  16. package propagation // import "go.opencensus.io/exporter/stackdriver/propagation"
  17. import (
  18. "encoding/binary"
  19. "encoding/hex"
  20. "fmt"
  21. "net/http"
  22. "strconv"
  23. "strings"
  24. "go.opencensus.io/trace"
  25. "go.opencensus.io/trace/propagation"
  26. )
  27. const (
  28. httpHeaderMaxSize = 200
  29. httpHeader = `X-Cloud-Trace-Context`
  30. )
  31. var _ propagation.HTTPFormat = (*HTTPFormat)(nil)
  32. // HTTPFormat implements propagation.HTTPFormat to propagate
  33. // traces in HTTP headers for Google Cloud Platform and Stackdriver Trace.
  34. type HTTPFormat struct{}
  35. // SpanContextFromRequest extracts a Stackdriver Trace span context from incoming requests.
  36. func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
  37. h := req.Header.Get(httpHeader)
  38. // See https://cloud.google.com/trace/docs/faq for the header HTTPFormat.
  39. // Return if the header is empty or missing, or if the header is unreasonably
  40. // large, to avoid making unnecessary copies of a large string.
  41. if h == "" || len(h) > httpHeaderMaxSize {
  42. return trace.SpanContext{}, false
  43. }
  44. // Parse the trace id field.
  45. slash := strings.Index(h, `/`)
  46. if slash == -1 {
  47. return trace.SpanContext{}, false
  48. }
  49. tid, h := h[:slash], h[slash+1:]
  50. buf, err := hex.DecodeString(tid)
  51. if err != nil {
  52. return trace.SpanContext{}, false
  53. }
  54. copy(sc.TraceID[:], buf)
  55. // Parse the span id field.
  56. spanstr := h
  57. semicolon := strings.Index(h, `;`)
  58. if semicolon != -1 {
  59. spanstr, h = h[:semicolon], h[semicolon+1:]
  60. }
  61. sid, err := strconv.ParseUint(spanstr, 10, 64)
  62. if err != nil {
  63. return trace.SpanContext{}, false
  64. }
  65. binary.BigEndian.PutUint64(sc.SpanID[:], sid)
  66. // Parse the options field, options field is optional.
  67. if !strings.HasPrefix(h, "o=") {
  68. return sc, true
  69. }
  70. o, err := strconv.ParseUint(h[2:], 10, 64)
  71. if err != nil {
  72. return trace.SpanContext{}, false
  73. }
  74. sc.TraceOptions = trace.TraceOptions(o)
  75. return sc, true
  76. }
  77. // SpanContextToRequest modifies the given request to include a Stackdriver Trace header.
  78. func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
  79. sid := binary.BigEndian.Uint64(sc.SpanID[:])
  80. header := fmt.Sprintf("%s/%d;o=%d", hex.EncodeToString(sc.TraceID[:]), sid, int64(sc.TraceOptions))
  81. req.Header.Set(httpHeader, header)
  82. }