1 // Copyright 2018 Google LLC
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
17 // Package propagation implements X-Cloud-Trace-Context header propagation used
18 // by Google Cloud products.
29 "go.opencensus.io/trace"
30 "go.opencensus.io/trace/propagation"
34 httpHeaderMaxSize = 200
35 httpHeader = `X-Cloud-Trace-Context`
38 var _ propagation.HTTPFormat = (*HTTPFormat)(nil)
40 // HTTPFormat implements propagation.HTTPFormat to propagate
41 // traces in HTTP headers for Google Cloud Platform and Stackdriver Trace.
42 type HTTPFormat struct{}
44 // SpanContextFromRequest extracts a Stackdriver Trace span context from incoming requests.
45 func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
46 h := req.Header.Get(httpHeader)
47 // See https://cloud.google.com/trace/docs/faq for the header HTTPFormat.
48 // Return if the header is empty or missing, or if the header is unreasonably
49 // large, to avoid making unnecessary copies of a large string.
50 if h == "" || len(h) > httpHeaderMaxSize {
51 return trace.SpanContext{}, false
54 // Parse the trace id field.
55 slash := strings.Index(h, `/`)
57 return trace.SpanContext{}, false
59 tid, h := h[:slash], h[slash+1:]
61 buf, err := hex.DecodeString(tid)
63 return trace.SpanContext{}, false
65 copy(sc.TraceID[:], buf)
67 // Parse the span id field.
69 semicolon := strings.Index(h, `;`)
71 spanstr, h = h[:semicolon], h[semicolon+1:]
73 sid, err := strconv.ParseUint(spanstr, 10, 64)
75 return trace.SpanContext{}, false
77 binary.BigEndian.PutUint64(sc.SpanID[:], sid)
79 // Parse the options field, options field is optional.
80 if !strings.HasPrefix(h, "o=") {
83 o, err := strconv.ParseUint(h[2:], 10, 64)
85 return trace.SpanContext{}, false
87 sc.TraceOptions = trace.TraceOptions(o)
91 // SpanContextToRequest modifies the given request to include a Stackdriver Trace header.
92 func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
93 sid := binary.BigEndian.Uint64(sc.SpanID[:])
94 header := fmt.Sprintf("%s/%d;o=%d", hex.EncodeToString(sc.TraceID[:]), sid, int64(sc.TraceOptions))
95 req.Header.Set(httpHeader, header)