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  }