github.com/mier85/go-sensor@v1.30.1-0.20220920111756-9bf41b3bc7e0/w3ctrace/version.go (about) 1 // (c) Copyright IBM Corp. 2021 2 // (c) Copyright Instana Inc. 2020 3 4 package w3ctrace 5 6 import ( 7 "encoding/hex" 8 "fmt" 9 "strconv" 10 ) 11 12 // Version represents the W3C trace context version. It defines the format of `traceparent` header 13 type Version uint8 14 15 const ( 16 // Version_Invalid represend an invalid W3C Trace Context version 17 Version_Invalid Version = iota 18 // Version_0 represent the W3C Trace Context version 00 19 Version_0 20 // Version_Max is the latest version of W3C Trace Context supported by this package 21 Version_Max = Version_0 22 ) 23 24 // ParseVersion parses the version part of a `traceparent` header value. It returns ErrContextCorrupted 25 // if the version is malformed 26 func ParseVersion(s string) (Version, error) { 27 if len(s) < 2 || (len(s) > 2 && s[2] != '-') { 28 return Version_Invalid, ErrContextCorrupted 29 } 30 s = s[:2] 31 32 if s == "ff" { 33 return Version_Invalid, nil 34 } 35 36 ver, err := strconv.ParseUint(s, 16, 8) 37 if err != nil { 38 return Version_Invalid, ErrContextCorrupted 39 } 40 41 return Version(ver + 1), nil 42 } 43 44 // String returns string representation of a trace parent version. The returned value is compatible with the 45 // `traceparent` header format. The caller should take care of handling the Version_Unknown, otherwise this 46 // method will return "ff" which is considered invalid 47 func (ver Version) String() string { 48 if ver == Version_Invalid { 49 return "ff" 50 } 51 52 return fmt.Sprintf("%02x", uint8(ver)-1) 53 } 54 55 // parseParent parses the version-format string as described in https://www.w3.org/TR/trace-context/#version-format 56 func (ver Version) parseParent(s string) (Parent, error) { 57 if ver == Version_Invalid { 58 return Parent{Version: Version_Invalid}, ErrContextCorrupted 59 } 60 61 // If a higher version is detected, we try to parse it as the highest version 62 // that is currently supported 63 if ver > Version_Max { 64 ver = Version_Max 65 } 66 67 switch ver { 68 case Version_0: 69 return parseV0Parent(s) 70 default: 71 return Parent{Version: ver}, ErrUnsupportedVersion 72 } 73 } 74 75 // formatParent returns the version-format string for this version as described in 76 // https://www.w3.org/TR/trace-context/#version-format. The returned value is 77 // empty if the version is not supported or invalid 78 func (ver Version) formatParent(p Parent) string { 79 // Construct the new traceparent field according to the highest version of 80 // the specification known to the implementation 81 if ver > Version_Max { 82 ver = Version_Max 83 } 84 85 switch ver { 86 case Version_0: 87 return formatV0Parent(p) 88 default: 89 return "" 90 } 91 } 92 93 // W3C Trace Context v0 version-format parsing/formatting 94 const ( 95 v0SampledFlag uint8 = 1 << iota 96 ) 97 98 func parseV0Parent(s string) (Parent, error) { 99 const ( 100 versionFormatLen = 55 101 versionLen = 2 102 traceIDLen = 32 103 parentIDLen = 16 104 flagsLen = 2 105 separator = '-' 106 invalidTraceID = "00000000000000000000000000000000" 107 invalidParentID = "0000000000000000" 108 ) 109 110 // trim version part 111 if len(s) < versionFormatLen || s[versionLen] != separator { 112 return Parent{}, ErrContextCorrupted 113 } 114 _, s = s[:versionLen], s[versionLen+1:] 115 116 // extract trace id 117 if s[traceIDLen] != separator { 118 return Parent{}, ErrContextCorrupted 119 } 120 traceID, s := s[:traceIDLen], s[traceIDLen+1:] 121 122 if traceID == invalidTraceID || !isHex(traceID) { 123 return Parent{}, ErrContextCorrupted 124 } 125 126 // extract parent id 127 if s[parentIDLen] != separator { 128 return Parent{}, ErrContextCorrupted 129 } 130 parentID, s := s[:parentIDLen], s[parentIDLen+1:] 131 132 if parentID == invalidParentID || !isHex(parentID) { 133 return Parent{}, ErrContextCorrupted 134 } 135 136 // extract and parse flags 137 if len(s) > flagsLen && s[flagsLen] != separator { 138 return Parent{}, ErrContextCorrupted 139 } 140 141 flags, err := strconv.ParseUint(s[:flagsLen], 16, 8) 142 if err != nil { 143 return Parent{}, ErrContextCorrupted 144 } 145 146 return Parent{ 147 Version: Version_0, 148 TraceID: traceID, 149 ParentID: parentID, 150 Flags: Flags{ 151 Sampled: uint8(flags)&v0SampledFlag != 0, 152 }, 153 }, nil 154 } 155 156 func formatV0Parent(p Parent) string { 157 var flags uint8 158 if p.Flags.Sampled { 159 flags |= v0SampledFlag 160 } 161 162 return fmt.Sprintf("00-%032s-%016s-%02x", p.TraceID, p.ParentID, flags) 163 } 164 165 func isHex(s string) bool { 166 _, err := hex.DecodeString(s) 167 return err == nil 168 }