github.com/instana/go-sensor@v1.62.2-0.20240520081010-4919868049e1/w3ctrace/context.go (about) 1 // (c) Copyright IBM Corp. 2021 2 // (c) Copyright Instana Inc. 2020 3 4 package w3ctrace 5 6 import ( 7 "errors" 8 "net/http" 9 "strings" 10 ) 11 12 const ( 13 // MaxStateEntries is the maximum number of items in `tracestate` as defined by 14 // https://www.w3.org/TR/trace-context/#tracestate-header-field-values 15 MaxStateEntries = 32 16 17 // TraceParentHeader is the W3C trace parent header name as defined by https://www.w3.org/TR/trace-context/ 18 TraceParentHeader = "traceparent" 19 // TraceStateHeader is the W3C trace state header name as defined by https://www.w3.org/TR/trace-context/ 20 TraceStateHeader = "tracestate" 21 ) 22 23 var ( 24 // ErrContextNotFound is an error retuned by w3ctrace.Extract() if provided HTTP headers does not contain W3C trace context 25 ErrContextNotFound = errors.New("no w3c context") 26 // ErrContextCorrupted is an error retuned by w3ctrace.Extract() if provided HTTP headers contain W3C trace context in unexpected format 27 ErrContextCorrupted = errors.New("corrupted w3c context") 28 // ErrUnsupportedVersion is an error retuned by w3ctrace.Extract() if the version of provided W3C trace context is not supported 29 ErrUnsupportedVersion = errors.New("unsupported w3c context version") 30 ) 31 32 // Context represents the W3C trace context 33 type Context struct { 34 RawParent string 35 RawState string 36 } 37 38 // New initializes a new W3C trace context from given parent 39 func New(parent Parent) Context { 40 return Context{ 41 RawParent: parent.String(), 42 } 43 } 44 45 // IsZero returns whether a context is a zero value 46 func (c Context) IsZero() bool { 47 return c.RawParent == "" 48 } 49 50 // Extract extracts the W3C trace context from HTTP headers. Returns ErrContextNotFound if 51 // provided value doesn't contain traceparent header. 52 func Extract(headers http.Header) (Context, error) { 53 var tr Context 54 55 for k, v := range headers { 56 if len(v) == 0 { 57 continue 58 } 59 60 switch { 61 case strings.EqualFold(k, TraceParentHeader): 62 tr.RawParent = v[0] 63 case strings.EqualFold(k, TraceStateHeader): 64 tr.RawState = v[0] 65 } 66 } 67 68 if tr.RawParent == "" { 69 return tr, ErrContextNotFound 70 } 71 72 return tr, nil 73 } 74 75 // Inject adds the w3c trace context headers, overriding any previously set values 76 func Inject(trCtx Context, headers http.Header) { 77 // delete existing headers ignoring the header name case 78 for k := range headers { 79 if strings.EqualFold(k, TraceParentHeader) || strings.EqualFold(k, TraceStateHeader) { 80 delete(headers, k) 81 } 82 } 83 84 headers.Set(TraceParentHeader, trCtx.RawParent) 85 if trCtx.RawState != "" { 86 headers.Set(TraceStateHeader, trCtx.RawState) 87 } 88 } 89 90 // State parses RawState and returns the corresponding list. 91 func (c Context) State() State { 92 return ParseState(c.RawState) 93 } 94 95 // Parent parses RawParent and returns the corresponding list. 96 // It silently discards malformed value. To check errors use ParseParent(). 97 func (c Context) Parent() Parent { 98 st, err := ParseParent(c.RawParent) 99 if err != nil { 100 return Parent{} 101 } 102 103 return st 104 }