| // Copyright 2018, OpenCensus Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| // Package propagation implement X-Cloud-Trace-Context header propagation used |
| // by Google Cloud products. |
| package propagation // import "go.opencensus.io/exporter/stackdriver/propagation" |
| |
| import ( |
| "encoding/binary" |
| "encoding/hex" |
| "fmt" |
| "net/http" |
| "strconv" |
| "strings" |
| |
| "go.opencensus.io/trace" |
| "go.opencensus.io/trace/propagation" |
| ) |
| |
| const ( |
| httpHeaderMaxSize = 200 |
| httpHeader = `X-Cloud-Trace-Context` |
| ) |
| |
| var _ propagation.HTTPFormat = (*HTTPFormat)(nil) |
| |
| // HTTPFormat implements propagation.HTTPFormat to propagate |
| // traces in HTTP headers for Google Cloud Platform and Stackdriver Trace. |
| type HTTPFormat struct{} |
| |
| // SpanContextFromRequest extracts a Stackdriver Trace span context from incoming requests. |
| func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) { |
| h := req.Header.Get(httpHeader) |
| // See https://cloud.google.com/trace/docs/faq for the header HTTPFormat. |
| // Return if the header is empty or missing, or if the header is unreasonably |
| // large, to avoid making unnecessary copies of a large string. |
| if h == "" || len(h) > httpHeaderMaxSize { |
| return trace.SpanContext{}, false |
| } |
| |
| // Parse the trace id field. |
| slash := strings.Index(h, `/`) |
| if slash == -1 { |
| return trace.SpanContext{}, false |
| } |
| tid, h := h[:slash], h[slash+1:] |
| |
| buf, err := hex.DecodeString(tid) |
| if err != nil { |
| return trace.SpanContext{}, false |
| } |
| copy(sc.TraceID[:], buf) |
| |
| // Parse the span id field. |
| spanstr := h |
| semicolon := strings.Index(h, `;`) |
| if semicolon != -1 { |
| spanstr, h = h[:semicolon], h[semicolon+1:] |
| } |
| sid, err := strconv.ParseUint(spanstr, 10, 64) |
| if err != nil { |
| return trace.SpanContext{}, false |
| } |
| binary.BigEndian.PutUint64(sc.SpanID[:], sid) |
| |
| // Parse the options field, options field is optional. |
| if !strings.HasPrefix(h, "o=") { |
| return sc, true |
| } |
| o, err := strconv.ParseUint(h[2:], 10, 64) |
| if err != nil { |
| return trace.SpanContext{}, false |
| } |
| sc.TraceOptions = trace.TraceOptions(o) |
| return sc, true |
| } |
| |
| // SpanContextToRequest modifies the given request to include a Stackdriver Trace header. |
| func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) { |
| sid := binary.BigEndian.Uint64(sc.SpanID[:]) |
| header := fmt.Sprintf("%s/%d;o=%d", hex.EncodeToString(sc.TraceID[:]), sid, int64(sc.TraceOptions)) |
| req.Header.Set(httpHeader, header) |
| } |