github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.opentelemetry.io/otel/trace/tracestate.go (about) 1 // Copyright The OpenTelemetry 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 trace // import "go.opentelemetry.io/otel/trace" 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "regexp" 21 "strings" 22 ) 23 24 const ( 25 maxListMembers = 32 26 27 listDelimiter = "," 28 29 // based on the W3C Trace Context specification, see 30 // https://www.w3.org/TR/trace-context-1/#tracestate-header 31 noTenantKeyFormat = `[a-z][_0-9a-z\-\*\/]*` 32 withTenantKeyFormat = `[a-z0-9][_0-9a-z\-\*\/]*@[a-z][_0-9a-z\-\*\/]*` 33 valueFormat = `[\x20-\x2b\x2d-\x3c\x3e-\x7e]*[\x21-\x2b\x2d-\x3c\x3e-\x7e]` 34 35 errInvalidKey errorConst = "invalid tracestate key" 36 errInvalidValue errorConst = "invalid tracestate value" 37 errInvalidMember errorConst = "invalid tracestate list-member" 38 errMemberNumber errorConst = "too many list-members in tracestate" 39 errDuplicate errorConst = "duplicate list-member in tracestate" 40 ) 41 42 var ( 43 noTenantKeyRe = regexp.MustCompile(`^` + noTenantKeyFormat + `$`) 44 withTenantKeyRe = regexp.MustCompile(`^` + withTenantKeyFormat + `$`) 45 valueRe = regexp.MustCompile(`^` + valueFormat + `$`) 46 memberRe = regexp.MustCompile(`^\s*((?:` + noTenantKeyFormat + `)|(?:` + withTenantKeyFormat + `))=(` + valueFormat + `)\s*$`) 47 ) 48 49 type member struct { 50 Key string 51 Value string 52 } 53 54 func newMember(key, value string) (member, error) { 55 if len(key) > 256 { 56 return member{}, fmt.Errorf("%w: %s", errInvalidKey, key) 57 } 58 if !noTenantKeyRe.MatchString(key) { 59 if !withTenantKeyRe.MatchString(key) { 60 return member{}, fmt.Errorf("%w: %s", errInvalidKey, key) 61 } 62 atIndex := strings.LastIndex(key, "@") 63 if atIndex > 241 || len(key)-1-atIndex > 14 { 64 return member{}, fmt.Errorf("%w: %s", errInvalidKey, key) 65 } 66 } 67 if len(value) > 256 || !valueRe.MatchString(value) { 68 return member{}, fmt.Errorf("%w: %s", errInvalidValue, value) 69 } 70 return member{Key: key, Value: value}, nil 71 } 72 73 func parseMember(m string) (member, error) { 74 matches := memberRe.FindStringSubmatch(m) 75 if len(matches) != 3 { 76 return member{}, fmt.Errorf("%w: %s", errInvalidMember, m) 77 } 78 result, e := newMember(matches[1], matches[2]) 79 if e != nil { 80 return member{}, fmt.Errorf("%w: %s", errInvalidMember, m) 81 } 82 return result, nil 83 } 84 85 // String encodes member into a string compliant with the W3C Trace Context 86 // specification. 87 func (m member) String() string { 88 return fmt.Sprintf("%s=%s", m.Key, m.Value) 89 } 90 91 // TraceState provides additional vendor-specific trace identification 92 // information across different distributed tracing systems. It represents an 93 // immutable list consisting of key/value pairs, each pair is referred to as a 94 // list-member. 95 // 96 // TraceState conforms to the W3C Trace Context specification 97 // (https://www.w3.org/TR/trace-context-1). All operations that create or copy 98 // a TraceState do so by validating all input and will only produce TraceState 99 // that conform to the specification. Specifically, this means that all 100 // list-member's key/value pairs are valid, no duplicate list-members exist, 101 // and the maximum number of list-members (32) is not exceeded. 102 type TraceState struct { //nolint:revive // revive complains about stutter of `trace.TraceState` 103 // list is the members in order. 104 list []member 105 } 106 107 var _ json.Marshaler = TraceState{} 108 109 // ParseTraceState attempts to decode a TraceState from the passed 110 // string. It returns an error if the input is invalid according to the W3C 111 // Trace Context specification. 112 func ParseTraceState(tracestate string) (TraceState, error) { 113 if tracestate == "" { 114 return TraceState{}, nil 115 } 116 117 wrapErr := func(err error) error { 118 return fmt.Errorf("failed to parse tracestate: %w", err) 119 } 120 121 var members []member 122 found := make(map[string]struct{}) 123 for _, memberStr := range strings.Split(tracestate, listDelimiter) { 124 if len(memberStr) == 0 { 125 continue 126 } 127 128 m, err := parseMember(memberStr) 129 if err != nil { 130 return TraceState{}, wrapErr(err) 131 } 132 133 if _, ok := found[m.Key]; ok { 134 return TraceState{}, wrapErr(errDuplicate) 135 } 136 found[m.Key] = struct{}{} 137 138 members = append(members, m) 139 if n := len(members); n > maxListMembers { 140 return TraceState{}, wrapErr(errMemberNumber) 141 } 142 } 143 144 return TraceState{list: members}, nil 145 } 146 147 // MarshalJSON marshals the TraceState into JSON. 148 func (ts TraceState) MarshalJSON() ([]byte, error) { 149 return json.Marshal(ts.String()) 150 } 151 152 // String encodes the TraceState into a string compliant with the W3C 153 // Trace Context specification. The returned string will be invalid if the 154 // TraceState contains any invalid members. 155 func (ts TraceState) String() string { 156 members := make([]string, len(ts.list)) 157 for i, m := range ts.list { 158 members[i] = m.String() 159 } 160 return strings.Join(members, listDelimiter) 161 } 162 163 // Get returns the value paired with key from the corresponding TraceState 164 // list-member if it exists, otherwise an empty string is returned. 165 func (ts TraceState) Get(key string) string { 166 for _, member := range ts.list { 167 if member.Key == key { 168 return member.Value 169 } 170 } 171 172 return "" 173 } 174 175 // Insert adds a new list-member defined by the key/value pair to the 176 // TraceState. If a list-member already exists for the given key, that 177 // list-member's value is updated. The new or updated list-member is always 178 // moved to the beginning of the TraceState as specified by the W3C Trace 179 // Context specification. 180 // 181 // If key or value are invalid according to the W3C Trace Context 182 // specification an error is returned with the original TraceState. 183 // 184 // If adding a new list-member means the TraceState would have more members 185 // then is allowed, the new list-member will be inserted and the right-most 186 // list-member will be dropped in the returned TraceState. 187 func (ts TraceState) Insert(key, value string) (TraceState, error) { 188 m, err := newMember(key, value) 189 if err != nil { 190 return ts, err 191 } 192 193 cTS := ts.Delete(key) 194 if cTS.Len()+1 <= maxListMembers { 195 cTS.list = append(cTS.list, member{}) 196 } 197 // When the number of members exceeds capacity, drop the "right-most". 198 copy(cTS.list[1:], cTS.list) 199 cTS.list[0] = m 200 201 return cTS, nil 202 } 203 204 // Delete returns a copy of the TraceState with the list-member identified by 205 // key removed. 206 func (ts TraceState) Delete(key string) TraceState { 207 members := make([]member, ts.Len()) 208 copy(members, ts.list) 209 for i, member := range ts.list { 210 if member.Key == key { 211 members = append(members[:i], members[i+1:]...) 212 // TraceState should contain no duplicate members. 213 break 214 } 215 } 216 return TraceState{list: members} 217 } 218 219 // Len returns the number of list-members in the TraceState. 220 func (ts TraceState) Len() int { 221 return len(ts.list) 222 }