1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package baggage
16
17 import (
18 "errors"
19 "fmt"
20 "net/url"
21 "regexp"
22 "strings"
23
24 "go.opentelemetry.io/otel/internal/baggage"
25 )
26
27 const (
28 maxMembers = 180
29 maxBytesPerMembers = 4096
30 maxBytesPerBaggageString = 8192
31
32 listDelimiter = ","
33 keyValueDelimiter = "="
34 propertyDelimiter = ";"
35
36 keyDef = `([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5a\x5e-\x7a\x7c\x7e]+)`
37 valueDef = `([\x21\x23-\x2b\x2d-\x3a\x3c-\x5B\x5D-\x7e]*)`
38 keyValueDef = `\s*` + keyDef + `\s*` + keyValueDelimiter + `\s*` + valueDef + `\s*`
39 )
40
41 var (
42 keyRe = regexp.MustCompile(`^` + keyDef + `$`)
43 valueRe = regexp.MustCompile(`^` + valueDef + `$`)
44 propertyRe = regexp.MustCompile(`^(?:\s*` + keyDef + `\s*|` + keyValueDef + `)$`)
45 )
46
47 var (
48 errInvalidKey = errors.New("invalid key")
49 errInvalidValue = errors.New("invalid value")
50 errInvalidProperty = errors.New("invalid baggage list-member property")
51 errInvalidMember = errors.New("invalid baggage list-member")
52 errMemberNumber = errors.New("too many list-members in baggage-string")
53 errMemberBytes = errors.New("list-member too large")
54 errBaggageBytes = errors.New("baggage-string too large")
55 )
56
57
58 type Property struct {
59 key, value string
60
61
62
63 hasValue bool
64 }
65
66
67
68
69 func NewKeyProperty(key string) (Property, error) {
70 if !keyRe.MatchString(key) {
71 return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
72 }
73
74 p := Property{key: key}
75 return p, nil
76 }
77
78
79
80
81 func NewKeyValueProperty(key, value string) (Property, error) {
82 if !keyRe.MatchString(key) {
83 return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
84 }
85 if !valueRe.MatchString(value) {
86 return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value)
87 }
88
89 p := Property{
90 key: key,
91 value: value,
92 hasValue: true,
93 }
94 return p, nil
95 }
96
97 func newInvalidProperty() Property {
98 return Property{}
99 }
100
101
102
103
104 func parseProperty(property string) (Property, error) {
105 if property == "" {
106 return newInvalidProperty(), nil
107 }
108
109 match := propertyRe.FindStringSubmatch(property)
110 if len(match) != 4 {
111 return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidProperty, property)
112 }
113
114 var p Property
115 if match[1] != "" {
116 p.key = match[1]
117 } else {
118 p.key = match[2]
119 p.value = match[3]
120 p.hasValue = true
121 }
122
123 return p, nil
124 }
125
126
127
128 func (p Property) validate() error {
129 errFunc := func(err error) error {
130 return fmt.Errorf("invalid property: %w", err)
131 }
132
133 if !keyRe.MatchString(p.key) {
134 return errFunc(fmt.Errorf("%w: %q", errInvalidKey, p.key))
135 }
136 if p.hasValue && !valueRe.MatchString(p.value) {
137 return errFunc(fmt.Errorf("%w: %q", errInvalidValue, p.value))
138 }
139 if !p.hasValue && p.value != "" {
140 return errFunc(errors.New("inconsistent value"))
141 }
142 return nil
143 }
144
145
146 func (p Property) Key() string {
147 return p.key
148 }
149
150
151
152
153 func (p Property) Value() (string, bool) {
154 return p.value, p.hasValue
155 }
156
157
158
159 func (p Property) String() string {
160 if p.hasValue {
161 return fmt.Sprintf("%s%s%v", p.key, keyValueDelimiter, p.value)
162 }
163 return p.key
164 }
165
166 type properties []Property
167
168 func fromInternalProperties(iProps []baggage.Property) properties {
169 if len(iProps) == 0 {
170 return nil
171 }
172
173 props := make(properties, len(iProps))
174 for i, p := range iProps {
175 props[i] = Property{
176 key: p.Key,
177 value: p.Value,
178 hasValue: p.HasValue,
179 }
180 }
181 return props
182 }
183
184 func (p properties) asInternal() []baggage.Property {
185 if len(p) == 0 {
186 return nil
187 }
188
189 iProps := make([]baggage.Property, len(p))
190 for i, prop := range p {
191 iProps[i] = baggage.Property{
192 Key: prop.key,
193 Value: prop.value,
194 HasValue: prop.hasValue,
195 }
196 }
197 return iProps
198 }
199
200 func (p properties) Copy() properties {
201 if len(p) == 0 {
202 return nil
203 }
204
205 props := make(properties, len(p))
206 copy(props, p)
207 return props
208 }
209
210
211
212 func (p properties) validate() error {
213 for _, prop := range p {
214 if err := prop.validate(); err != nil {
215 return err
216 }
217 }
218 return nil
219 }
220
221
222
223 func (p properties) String() string {
224 props := make([]string, len(p))
225 for i, prop := range p {
226 props[i] = prop.String()
227 }
228 return strings.Join(props, propertyDelimiter)
229 }
230
231
232
233 type Member struct {
234 key, value string
235 properties properties
236
237
238
239
240 hasData bool
241 }
242
243
244
245
246
247 func NewMember(key, value string, props ...Property) (Member, error) {
248 m := Member{
249 key: key,
250 value: value,
251 properties: properties(props).Copy(),
252 hasData: true,
253 }
254 if err := m.validate(); err != nil {
255 return newInvalidMember(), err
256 }
257 decodedValue, err := url.PathUnescape(value)
258 if err != nil {
259 return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
260 }
261 m.value = decodedValue
262 return m, nil
263 }
264
265 func newInvalidMember() Member {
266 return Member{}
267 }
268
269
270
271
272 func parseMember(member string) (Member, error) {
273 if n := len(member); n > maxBytesPerMembers {
274 return newInvalidMember(), fmt.Errorf("%w: %d", errMemberBytes, n)
275 }
276
277 var (
278 key, value string
279 props properties
280 )
281
282 keyValue, properties, found := strings.Cut(member, propertyDelimiter)
283 if found {
284
285 for _, pStr := range strings.Split(properties, propertyDelimiter) {
286 p, err := parseProperty(pStr)
287 if err != nil {
288 return newInvalidMember(), err
289 }
290 props = append(props, p)
291 }
292 }
293
294
295
296 k, v, found := strings.Cut(keyValue, keyValueDelimiter)
297 if !found {
298 return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidMember, member)
299 }
300
301
302 key = strings.TrimSpace(k)
303 var err error
304 value, err = url.PathUnescape(strings.TrimSpace(v))
305 if err != nil {
306 return newInvalidMember(), fmt.Errorf("%w: %q", err, value)
307 }
308 if !keyRe.MatchString(key) {
309 return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
310 }
311 if !valueRe.MatchString(value) {
312 return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
313 }
314
315 return Member{key: key, value: value, properties: props, hasData: true}, nil
316 }
317
318
319
320
321 func (m Member) validate() error {
322 if !m.hasData {
323 return fmt.Errorf("%w: %q", errInvalidMember, m)
324 }
325
326 if !keyRe.MatchString(m.key) {
327 return fmt.Errorf("%w: %q", errInvalidKey, m.key)
328 }
329 if !valueRe.MatchString(m.value) {
330 return fmt.Errorf("%w: %q", errInvalidValue, m.value)
331 }
332 return m.properties.validate()
333 }
334
335
336 func (m Member) Key() string { return m.key }
337
338
339 func (m Member) Value() string { return m.value }
340
341
342 func (m Member) Properties() []Property { return m.properties.Copy() }
343
344
345
346 func (m Member) String() string {
347
348 s := fmt.Sprintf("%s%s%s", m.key, keyValueDelimiter, url.QueryEscape(m.value))
349 if len(m.properties) > 0 {
350 s = fmt.Sprintf("%s%s%s", s, propertyDelimiter, m.properties.String())
351 }
352 return s
353 }
354
355
356
357 type Baggage struct {
358 list baggage.List
359 }
360
361
362
363
364
365 func New(members ...Member) (Baggage, error) {
366 if len(members) == 0 {
367 return Baggage{}, nil
368 }
369
370 b := make(baggage.List)
371 for _, m := range members {
372 if !m.hasData {
373 return Baggage{}, errInvalidMember
374 }
375
376
377 b[m.key] = baggage.Item{
378 Value: m.value,
379 Properties: m.properties.asInternal(),
380 }
381 }
382
383
384 if len(b) > maxMembers {
385 return Baggage{}, errMemberNumber
386 }
387
388 bag := Baggage{b}
389 if n := len(bag.String()); n > maxBytesPerBaggageString {
390 return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
391 }
392
393 return bag, nil
394 }
395
396
397
398
399
400
401
402
403
404 func Parse(bStr string) (Baggage, error) {
405 if bStr == "" {
406 return Baggage{}, nil
407 }
408
409 if n := len(bStr); n > maxBytesPerBaggageString {
410 return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
411 }
412
413 b := make(baggage.List)
414 for _, memberStr := range strings.Split(bStr, listDelimiter) {
415 m, err := parseMember(memberStr)
416 if err != nil {
417 return Baggage{}, err
418 }
419
420 b[m.key] = baggage.Item{
421 Value: m.value,
422 Properties: m.properties.asInternal(),
423 }
424 }
425
426
427
428
429 if len(b) > maxMembers {
430 return Baggage{}, errMemberNumber
431 }
432
433 return Baggage{b}, nil
434 }
435
436
437
438
439
440
441
442 func (b Baggage) Member(key string) Member {
443 v, ok := b.list[key]
444 if !ok {
445
446
447
448
449 return newInvalidMember()
450 }
451
452 return Member{
453 key: key,
454 value: v.Value,
455 properties: fromInternalProperties(v.Properties),
456 hasData: true,
457 }
458 }
459
460
461
462
463
464
465 func (b Baggage) Members() []Member {
466 if len(b.list) == 0 {
467 return nil
468 }
469
470 members := make([]Member, 0, len(b.list))
471 for k, v := range b.list {
472 members = append(members, Member{
473 key: k,
474 value: v.Value,
475 properties: fromInternalProperties(v.Properties),
476 hasData: true,
477 })
478 }
479 return members
480 }
481
482
483
484
485
486
487
488 func (b Baggage) SetMember(member Member) (Baggage, error) {
489 if !member.hasData {
490 return b, errInvalidMember
491 }
492
493 n := len(b.list)
494 if _, ok := b.list[member.key]; !ok {
495 n++
496 }
497 list := make(baggage.List, n)
498
499 for k, v := range b.list {
500
501 if k == member.key {
502 continue
503 }
504 list[k] = v
505 }
506
507 list[member.key] = baggage.Item{
508 Value: member.value,
509 Properties: member.properties.asInternal(),
510 }
511
512 return Baggage{list: list}, nil
513 }
514
515
516
517 func (b Baggage) DeleteMember(key string) Baggage {
518 n := len(b.list)
519 if _, ok := b.list[key]; ok {
520 n--
521 }
522 list := make(baggage.List, n)
523
524 for k, v := range b.list {
525 if k == key {
526 continue
527 }
528 list[k] = v
529 }
530
531 return Baggage{list: list}
532 }
533
534
535 func (b Baggage) Len() int {
536 return len(b.list)
537 }
538
539
540
541
542 func (b Baggage) String() string {
543 members := make([]string, 0, len(b.list))
544 for k, v := range b.list {
545 members = append(members, Member{
546 key: k,
547 value: v.Value,
548 properties: fromInternalProperties(v.Properties),
549 }.String())
550 }
551 return strings.Join(members, listDelimiter)
552 }
553
View as plain text