└── propagation └── http.go /propagation/http.go: -------------------------------------------------------------------------------- 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 | 15 | // Package propagation implement X-Cloud-Function-Trace-Context header propagation used 16 | // by Google Cloud products. 17 | package propagation // import "github.com/kelseyhightower/functions/propagation" 18 | 19 | import ( 20 | "encoding/binary" 21 | "encoding/hex" 22 | "fmt" 23 | "net/http" 24 | "strconv" 25 | "strings" 26 | 27 | "go.opencensus.io/trace" 28 | ) 29 | 30 | const ( 31 | httpHeaderMaxSize = 200 32 | httpHeader = `X-Cloud-Function-Trace-Context` 33 | ) 34 | 35 | 36 | // SpanContextFromRequest extracts a Stackdriver Trace span context from incoming requests. 37 | func 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 | 46 | // Parse the trace id field. 47 | slash := strings.Index(h, `/`) 48 | if slash == -1 { 49 | return trace.SpanContext{}, false 50 | } 51 | tid, h := h[:slash], h[slash+1:] 52 | 53 | buf, err := hex.DecodeString(tid) 54 | if err != nil { 55 | return trace.SpanContext{}, false 56 | } 57 | copy(sc.TraceID[:], buf) 58 | 59 | // Parse the span id field. 60 | spanstr := h 61 | semicolon := strings.Index(h, `;`) 62 | if semicolon != -1 { 63 | spanstr, h = h[:semicolon], h[semicolon+1:] 64 | } 65 | sid, err := strconv.ParseUint(spanstr, 10, 64) 66 | if err != nil { 67 | return trace.SpanContext{}, false 68 | } 69 | binary.BigEndian.PutUint64(sc.SpanID[:], sid) 70 | 71 | // Parse the options field, options field is optional. 72 | if !strings.HasPrefix(h, "o=") { 73 | return sc, true 74 | } 75 | o, err := strconv.ParseUint(h[2:], 10, 64) 76 | if err != nil { 77 | return trace.SpanContext{}, false 78 | } 79 | sc.TraceOptions = trace.TraceOptions(o) 80 | return sc, true 81 | } 82 | 83 | // SpanContextToRequest modifies the given request to include a Stackdriver Trace header. 84 | func SpanContextToRequest(sc trace.SpanContext, req *http.Request) { 85 | sid := binary.BigEndian.Uint64(sc.SpanID[:]) 86 | header := fmt.Sprintf("%s/%d;o=%d", hex.EncodeToString(sc.TraceID[:]), sid, int64(sc.TraceOptions)) 87 | req.Header.Set(httpHeader, header) 88 | } 89 | --------------------------------------------------------------------------------