summaryrefslogtreecommitdiff
path: root/vendor/github.com/uber/jaeger-client-go/propagation.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/uber/jaeger-client-go/propagation.go')
-rw-r--r--vendor/github.com/uber/jaeger-client-go/propagation.go300
1 files changed, 300 insertions, 0 deletions
diff --git a/vendor/github.com/uber/jaeger-client-go/propagation.go b/vendor/github.com/uber/jaeger-client-go/propagation.go
new file mode 100644
index 000000000..abca67a3c
--- /dev/null
+++ b/vendor/github.com/uber/jaeger-client-go/propagation.go
@@ -0,0 +1,300 @@
+// Copyright (c) 2017 Uber Technologies, Inc.
+//
+// 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 jaeger
+
+import (
+ "bytes"
+ "encoding/binary"
+ "fmt"
+ "io"
+ "log"
+ "net/url"
+ "strings"
+ "sync"
+
+ opentracing "github.com/opentracing/opentracing-go"
+)
+
+// Injector is responsible for injecting SpanContext instances in a manner suitable
+// for propagation via a format-specific "carrier" object. Typically the
+// injection will take place across an RPC boundary, but message queues and
+// other IPC mechanisms are also reasonable places to use an Injector.
+type Injector interface {
+ // Inject takes `SpanContext` and injects it into `carrier`. The actual type
+ // of `carrier` depends on the `format` passed to `Tracer.Inject()`.
+ //
+ // Implementations may return opentracing.ErrInvalidCarrier or any other
+ // implementation-specific error if injection fails.
+ Inject(ctx SpanContext, carrier interface{}) error
+}
+
+// Extractor is responsible for extracting SpanContext instances from a
+// format-specific "carrier" object. Typically the extraction will take place
+// on the server side of an RPC boundary, but message queues and other IPC
+// mechanisms are also reasonable places to use an Extractor.
+type Extractor interface {
+ // Extract decodes a SpanContext instance from the given `carrier`,
+ // or (nil, opentracing.ErrSpanContextNotFound) if no context could
+ // be found in the `carrier`.
+ Extract(carrier interface{}) (SpanContext, error)
+}
+
+type textMapPropagator struct {
+ headerKeys *HeadersConfig
+ metrics Metrics
+ encodeValue func(string) string
+ decodeValue func(string) string
+}
+
+func newTextMapPropagator(headerKeys *HeadersConfig, metrics Metrics) *textMapPropagator {
+ return &textMapPropagator{
+ headerKeys: headerKeys,
+ metrics: metrics,
+ encodeValue: func(val string) string {
+ return val
+ },
+ decodeValue: func(val string) string {
+ return val
+ },
+ }
+}
+
+func newHTTPHeaderPropagator(headerKeys *HeadersConfig, metrics Metrics) *textMapPropagator {
+ return &textMapPropagator{
+ headerKeys: headerKeys,
+ metrics: metrics,
+ encodeValue: func(val string) string {
+ return url.QueryEscape(val)
+ },
+ decodeValue: func(val string) string {
+ // ignore decoding errors, cannot do anything about them
+ if v, err := url.QueryUnescape(val); err == nil {
+ return v
+ }
+ return val
+ },
+ }
+}
+
+type binaryPropagator struct {
+ tracer *Tracer
+ buffers sync.Pool
+}
+
+func newBinaryPropagator(tracer *Tracer) *binaryPropagator {
+ return &binaryPropagator{
+ tracer: tracer,
+ buffers: sync.Pool{New: func() interface{} { return &bytes.Buffer{} }},
+ }
+}
+
+func (p *textMapPropagator) Inject(
+ sc SpanContext,
+ abstractCarrier interface{},
+) error {
+ textMapWriter, ok := abstractCarrier.(opentracing.TextMapWriter)
+ if !ok {
+ return opentracing.ErrInvalidCarrier
+ }
+
+ // Do not encode the string with trace context to avoid accidental double-encoding
+ // if people are using opentracing < 0.10.0. Our colon-separated representation
+ // of the trace context is already safe for HTTP headers.
+ textMapWriter.Set(p.headerKeys.TraceContextHeaderName, sc.String())
+ for k, v := range sc.baggage {
+ safeKey := p.addBaggageKeyPrefix(k)
+ safeVal := p.encodeValue(v)
+ textMapWriter.Set(safeKey, safeVal)
+ }
+ return nil
+}
+
+func (p *textMapPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) {
+ textMapReader, ok := abstractCarrier.(opentracing.TextMapReader)
+ if !ok {
+ return emptyContext, opentracing.ErrInvalidCarrier
+ }
+ var ctx SpanContext
+ var baggage map[string]string
+ err := textMapReader.ForeachKey(func(rawKey, value string) error {
+ key := strings.ToLower(rawKey) // TODO not necessary for plain TextMap
+ if key == p.headerKeys.TraceContextHeaderName {
+ var err error
+ safeVal := p.decodeValue(value)
+ if ctx, err = ContextFromString(safeVal); err != nil {
+ return err
+ }
+ } else if key == p.headerKeys.JaegerDebugHeader {
+ ctx.debugID = p.decodeValue(value)
+ } else if key == p.headerKeys.JaegerBaggageHeader {
+ if baggage == nil {
+ baggage = make(map[string]string)
+ }
+ for k, v := range p.parseCommaSeparatedMap(value) {
+ baggage[k] = v
+ }
+ } else if strings.HasPrefix(key, p.headerKeys.TraceBaggageHeaderPrefix) {
+ if baggage == nil {
+ baggage = make(map[string]string)
+ }
+ safeKey := p.removeBaggageKeyPrefix(key)
+ safeVal := p.decodeValue(value)
+ baggage[safeKey] = safeVal
+ }
+ return nil
+ })
+ if err != nil {
+ p.metrics.DecodingErrors.Inc(1)
+ return emptyContext, err
+ }
+ if !ctx.traceID.IsValid() && ctx.debugID == "" && len(baggage) == 0 {
+ return emptyContext, opentracing.ErrSpanContextNotFound
+ }
+ ctx.baggage = baggage
+ return ctx, nil
+}
+
+func (p *binaryPropagator) Inject(
+ sc SpanContext,
+ abstractCarrier interface{},
+) error {
+ carrier, ok := abstractCarrier.(io.Writer)
+ if !ok {
+ return opentracing.ErrInvalidCarrier
+ }
+
+ // Handle the tracer context
+ if err := binary.Write(carrier, binary.BigEndian, sc.traceID); err != nil {
+ return err
+ }
+ if err := binary.Write(carrier, binary.BigEndian, sc.spanID); err != nil {
+ return err
+ }
+ if err := binary.Write(carrier, binary.BigEndian, sc.parentID); err != nil {
+ return err
+ }
+ if err := binary.Write(carrier, binary.BigEndian, sc.flags); err != nil {
+ return err
+ }
+
+ // Handle the baggage items
+ if err := binary.Write(carrier, binary.BigEndian, int32(len(sc.baggage))); err != nil {
+ return err
+ }
+ for k, v := range sc.baggage {
+ if err := binary.Write(carrier, binary.BigEndian, int32(len(k))); err != nil {
+ return err
+ }
+ io.WriteString(carrier, k)
+ if err := binary.Write(carrier, binary.BigEndian, int32(len(v))); err != nil {
+ return err
+ }
+ io.WriteString(carrier, v)
+ }
+
+ return nil
+}
+
+func (p *binaryPropagator) Extract(abstractCarrier interface{}) (SpanContext, error) {
+ carrier, ok := abstractCarrier.(io.Reader)
+ if !ok {
+ return emptyContext, opentracing.ErrInvalidCarrier
+ }
+ var ctx SpanContext
+
+ if err := binary.Read(carrier, binary.BigEndian, &ctx.traceID); err != nil {
+ return emptyContext, opentracing.ErrSpanContextCorrupted
+ }
+ if err := binary.Read(carrier, binary.BigEndian, &ctx.spanID); err != nil {
+ return emptyContext, opentracing.ErrSpanContextCorrupted
+ }
+ if err := binary.Read(carrier, binary.BigEndian, &ctx.parentID); err != nil {
+ return emptyContext, opentracing.ErrSpanContextCorrupted
+ }
+ if err := binary.Read(carrier, binary.BigEndian, &ctx.flags); err != nil {
+ return emptyContext, opentracing.ErrSpanContextCorrupted
+ }
+
+ // Handle the baggage items
+ var numBaggage int32
+ if err := binary.Read(carrier, binary.BigEndian, &numBaggage); err != nil {
+ return emptyContext, opentracing.ErrSpanContextCorrupted
+ }
+ if iNumBaggage := int(numBaggage); iNumBaggage > 0 {
+ ctx.baggage = make(map[string]string, iNumBaggage)
+ buf := p.buffers.Get().(*bytes.Buffer)
+ defer p.buffers.Put(buf)
+
+ var keyLen, valLen int32
+ for i := 0; i < iNumBaggage; i++ {
+ if err := binary.Read(carrier, binary.BigEndian, &keyLen); err != nil {
+ return emptyContext, opentracing.ErrSpanContextCorrupted
+ }
+ buf.Reset()
+ buf.Grow(int(keyLen))
+ if n, err := io.CopyN(buf, carrier, int64(keyLen)); err != nil || int32(n) != keyLen {
+ return emptyContext, opentracing.ErrSpanContextCorrupted
+ }
+ key := buf.String()
+
+ if err := binary.Read(carrier, binary.BigEndian, &valLen); err != nil {
+ return emptyContext, opentracing.ErrSpanContextCorrupted
+ }
+ buf.Reset()
+ buf.Grow(int(valLen))
+ if n, err := io.CopyN(buf, carrier, int64(valLen)); err != nil || int32(n) != valLen {
+ return emptyContext, opentracing.ErrSpanContextCorrupted
+ }
+ ctx.baggage[key] = buf.String()
+ }
+ }
+
+ return ctx, nil
+}
+
+// Converts a comma separated key value pair list into a map
+// e.g. key1=value1, key2=value2, key3 = value3
+// is converted to map[string]string { "key1" : "value1",
+// "key2" : "value2",
+// "key3" : "value3" }
+func (p *textMapPropagator) parseCommaSeparatedMap(value string) map[string]string {
+ baggage := make(map[string]string)
+ value, err := url.QueryUnescape(value)
+ if err != nil {
+ log.Printf("Unable to unescape %s, %v", value, err)
+ return baggage
+ }
+ for _, kvpair := range strings.Split(value, ",") {
+ kv := strings.Split(strings.TrimSpace(kvpair), "=")
+ if len(kv) == 2 {
+ baggage[kv[0]] = kv[1]
+ } else {
+ log.Printf("Malformed value passed in for %s", p.headerKeys.JaegerBaggageHeader)
+ }
+ }
+ return baggage
+}
+
+// Converts a baggage item key into an http header format,
+// by prepending TraceBaggageHeaderPrefix and encoding the key string
+func (p *textMapPropagator) addBaggageKeyPrefix(key string) string {
+ // TODO encodeBaggageKeyAsHeader add caching and escaping
+ return fmt.Sprintf("%v%v", p.headerKeys.TraceBaggageHeaderPrefix, key)
+}
+
+func (p *textMapPropagator) removeBaggageKeyPrefix(key string) string {
+ // TODO decodeBaggageHeaderKey add caching and escaping
+ return key[len(p.headerKeys.TraceBaggageHeaderPrefix):]
+}