baggage.go

   1// Copyright The OpenTelemetry Authors
   2// SPDX-License-Identifier: Apache-2.0
   3
   4package baggage // import "go.opentelemetry.io/otel/baggage"
   5
   6import (
   7	"errors"
   8	"fmt"
   9	"net/url"
  10	"strings"
  11	"unicode/utf8"
  12
  13	"go.opentelemetry.io/otel/internal/baggage"
  14)
  15
  16const (
  17	maxMembers               = 180
  18	maxBytesPerMembers       = 4096
  19	maxBytesPerBaggageString = 8192
  20
  21	listDelimiter     = ","
  22	keyValueDelimiter = "="
  23	propertyDelimiter = ";"
  24)
  25
  26var (
  27	errInvalidKey      = errors.New("invalid key")
  28	errInvalidValue    = errors.New("invalid value")
  29	errInvalidProperty = errors.New("invalid baggage list-member property")
  30	errInvalidMember   = errors.New("invalid baggage list-member")
  31	errMemberNumber    = errors.New("too many list-members in baggage-string")
  32	errMemberBytes     = errors.New("list-member too large")
  33	errBaggageBytes    = errors.New("baggage-string too large")
  34)
  35
  36// Property is an additional metadata entry for a baggage list-member.
  37type Property struct {
  38	key, value string
  39
  40	// hasValue indicates if a zero-value value means the property does not
  41	// have a value or if it was the zero-value.
  42	hasValue bool
  43}
  44
  45// NewKeyProperty returns a new Property for key.
  46//
  47// The passed key must be valid, non-empty UTF-8 string.
  48// If key is invalid, an error will be returned.
  49// However, the specific Propagators that are used to transmit baggage entries across
  50// component boundaries may impose their own restrictions on Property key.
  51// For example, the W3C Baggage specification restricts the Property keys to strings that
  52// satisfy the token definition from RFC7230, Section 3.2.6.
  53// For maximum compatibility, alphanumeric value are strongly recommended to be used as Property key.
  54func NewKeyProperty(key string) (Property, error) {
  55	if !validateBaggageName(key) {
  56		return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
  57	}
  58
  59	p := Property{key: key}
  60	return p, nil
  61}
  62
  63// NewKeyValueProperty returns a new Property for key with value.
  64//
  65// The passed key must be compliant with W3C Baggage specification.
  66// The passed value must be percent-encoded as defined in W3C Baggage specification.
  67//
  68// Notice: Consider using [NewKeyValuePropertyRaw] instead
  69// that does not require percent-encoding of the value.
  70func NewKeyValueProperty(key, value string) (Property, error) {
  71	if !validateKey(key) {
  72		return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
  73	}
  74
  75	if !validateValue(value) {
  76		return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value)
  77	}
  78	decodedValue, err := url.PathUnescape(value)
  79	if err != nil {
  80		return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value)
  81	}
  82	return NewKeyValuePropertyRaw(key, decodedValue)
  83}
  84
  85// NewKeyValuePropertyRaw returns a new Property for key with value.
  86//
  87// The passed key must be valid, non-empty UTF-8 string.
  88// The passed value must be valid UTF-8 string.
  89// However, the specific Propagators that are used to transmit baggage entries across
  90// component boundaries may impose their own restrictions on Property key.
  91// For example, the W3C Baggage specification restricts the Property keys to strings that
  92// satisfy the token definition from RFC7230, Section 3.2.6.
  93// For maximum compatibility, alphanumeric value are strongly recommended to be used as Property key.
  94func NewKeyValuePropertyRaw(key, value string) (Property, error) {
  95	if !validateBaggageName(key) {
  96		return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidKey, key)
  97	}
  98	if !validateBaggageValue(value) {
  99		return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidValue, value)
 100	}
 101
 102	p := Property{
 103		key:      key,
 104		value:    value,
 105		hasValue: true,
 106	}
 107	return p, nil
 108}
 109
 110func newInvalidProperty() Property {
 111	return Property{}
 112}
 113
 114// parseProperty attempts to decode a Property from the passed string. It
 115// returns an error if the input is invalid according to the W3C Baggage
 116// specification.
 117func parseProperty(property string) (Property, error) {
 118	if property == "" {
 119		return newInvalidProperty(), nil
 120	}
 121
 122	p, ok := parsePropertyInternal(property)
 123	if !ok {
 124		return newInvalidProperty(), fmt.Errorf("%w: %q", errInvalidProperty, property)
 125	}
 126
 127	return p, nil
 128}
 129
 130// validate ensures p conforms to the W3C Baggage specification, returning an
 131// error otherwise.
 132func (p Property) validate() error {
 133	errFunc := func(err error) error {
 134		return fmt.Errorf("invalid property: %w", err)
 135	}
 136
 137	if !validateBaggageName(p.key) {
 138		return errFunc(fmt.Errorf("%w: %q", errInvalidKey, p.key))
 139	}
 140	if !p.hasValue && p.value != "" {
 141		return errFunc(errors.New("inconsistent value"))
 142	}
 143	if p.hasValue && !validateBaggageValue(p.value) {
 144		return errFunc(fmt.Errorf("%w: %q", errInvalidValue, p.value))
 145	}
 146	return nil
 147}
 148
 149// Key returns the Property key.
 150func (p Property) Key() string {
 151	return p.key
 152}
 153
 154// Value returns the Property value. Additionally, a boolean value is returned
 155// indicating if the returned value is the empty if the Property has a value
 156// that is empty or if the value is not set.
 157func (p Property) Value() (string, bool) {
 158	return p.value, p.hasValue
 159}
 160
 161// String encodes Property into a header string compliant with the W3C Baggage
 162// specification.
 163// It would return empty string if the key is invalid with the W3C Baggage
 164// specification. This could happen for a UTF-8 key, as it may contain
 165// invalid characters.
 166func (p Property) String() string {
 167	//  W3C Baggage specification does not allow percent-encoded keys.
 168	if !validateKey(p.key) {
 169		return ""
 170	}
 171
 172	if p.hasValue {
 173		return fmt.Sprintf("%s%s%v", p.key, keyValueDelimiter, valueEscape(p.value))
 174	}
 175	return p.key
 176}
 177
 178type properties []Property
 179
 180func fromInternalProperties(iProps []baggage.Property) properties {
 181	if len(iProps) == 0 {
 182		return nil
 183	}
 184
 185	props := make(properties, len(iProps))
 186	for i, p := range iProps {
 187		props[i] = Property{
 188			key:      p.Key,
 189			value:    p.Value,
 190			hasValue: p.HasValue,
 191		}
 192	}
 193	return props
 194}
 195
 196func (p properties) asInternal() []baggage.Property {
 197	if len(p) == 0 {
 198		return nil
 199	}
 200
 201	iProps := make([]baggage.Property, len(p))
 202	for i, prop := range p {
 203		iProps[i] = baggage.Property{
 204			Key:      prop.key,
 205			Value:    prop.value,
 206			HasValue: prop.hasValue,
 207		}
 208	}
 209	return iProps
 210}
 211
 212func (p properties) Copy() properties {
 213	if len(p) == 0 {
 214		return nil
 215	}
 216
 217	props := make(properties, len(p))
 218	copy(props, p)
 219	return props
 220}
 221
 222// validate ensures each Property in p conforms to the W3C Baggage
 223// specification, returning an error otherwise.
 224func (p properties) validate() error {
 225	for _, prop := range p {
 226		if err := prop.validate(); err != nil {
 227			return err
 228		}
 229	}
 230	return nil
 231}
 232
 233// String encodes properties into a header string compliant with the W3C Baggage
 234// specification.
 235func (p properties) String() string {
 236	props := make([]string, 0, len(p))
 237	for _, prop := range p {
 238		s := prop.String()
 239
 240		// Ignored empty properties.
 241		if s != "" {
 242			props = append(props, s)
 243		}
 244	}
 245	return strings.Join(props, propertyDelimiter)
 246}
 247
 248// Member is a list-member of a baggage-string as defined by the W3C Baggage
 249// specification.
 250type Member struct {
 251	key, value string
 252	properties properties
 253
 254	// hasData indicates whether the created property contains data or not.
 255	// Properties that do not contain data are invalid with no other check
 256	// required.
 257	hasData bool
 258}
 259
 260// NewMember returns a new Member from the passed arguments.
 261//
 262// The passed key must be compliant with W3C Baggage specification.
 263// The passed value must be percent-encoded as defined in W3C Baggage specification.
 264//
 265// Notice: Consider using [NewMemberRaw] instead
 266// that does not require percent-encoding of the value.
 267func NewMember(key, value string, props ...Property) (Member, error) {
 268	if !validateKey(key) {
 269		return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
 270	}
 271
 272	if !validateValue(value) {
 273		return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
 274	}
 275	decodedValue, err := url.PathUnescape(value)
 276	if err != nil {
 277		return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
 278	}
 279	return NewMemberRaw(key, decodedValue, props...)
 280}
 281
 282// NewMemberRaw returns a new Member from the passed arguments.
 283//
 284// The passed key must be valid, non-empty UTF-8 string.
 285// The passed value must be valid UTF-8 string.
 286// However, the specific Propagators that are used to transmit baggage entries across
 287// component boundaries may impose their own restrictions on baggage key.
 288// For example, the W3C Baggage specification restricts the baggage keys to strings that
 289// satisfy the token definition from RFC7230, Section 3.2.6.
 290// For maximum compatibility, alphanumeric value are strongly recommended to be used as baggage key.
 291func NewMemberRaw(key, value string, props ...Property) (Member, error) {
 292	m := Member{
 293		key:        key,
 294		value:      value,
 295		properties: properties(props).Copy(),
 296		hasData:    true,
 297	}
 298	if err := m.validate(); err != nil {
 299		return newInvalidMember(), err
 300	}
 301	return m, nil
 302}
 303
 304func newInvalidMember() Member {
 305	return Member{}
 306}
 307
 308// parseMember attempts to decode a Member from the passed string. It returns
 309// an error if the input is invalid according to the W3C Baggage
 310// specification.
 311func parseMember(member string) (Member, error) {
 312	if n := len(member); n > maxBytesPerMembers {
 313		return newInvalidMember(), fmt.Errorf("%w: %d", errMemberBytes, n)
 314	}
 315
 316	var props properties
 317	keyValue, properties, found := strings.Cut(member, propertyDelimiter)
 318	if found {
 319		// Parse the member properties.
 320		for _, pStr := range strings.Split(properties, propertyDelimiter) {
 321			p, err := parseProperty(pStr)
 322			if err != nil {
 323				return newInvalidMember(), err
 324			}
 325			props = append(props, p)
 326		}
 327	}
 328	// Parse the member key/value pair.
 329
 330	// Take into account a value can contain equal signs (=).
 331	k, v, found := strings.Cut(keyValue, keyValueDelimiter)
 332	if !found {
 333		return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidMember, member)
 334	}
 335	// "Leading and trailing whitespaces are allowed but MUST be trimmed
 336	// when converting the header into a data structure."
 337	key := strings.TrimSpace(k)
 338	if !validateKey(key) {
 339		return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
 340	}
 341
 342	rawVal := strings.TrimSpace(v)
 343	if !validateValue(rawVal) {
 344		return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, v)
 345	}
 346
 347	// Decode a percent-encoded value.
 348	unescapeVal, err := url.PathUnescape(rawVal)
 349	if err != nil {
 350		return newInvalidMember(), fmt.Errorf("%w: %w", errInvalidValue, err)
 351	}
 352
 353	value := replaceInvalidUTF8Sequences(len(rawVal), unescapeVal)
 354	return Member{key: key, value: value, properties: props, hasData: true}, nil
 355}
 356
 357// replaceInvalidUTF8Sequences replaces invalid UTF-8 sequences with '�'.
 358func replaceInvalidUTF8Sequences(c int, unescapeVal string) string {
 359	if utf8.ValidString(unescapeVal) {
 360		return unescapeVal
 361	}
 362	// W3C baggage spec:
 363	// https://github.com/w3c/baggage/blob/8c215efbeebd3fa4b1aceb937a747e56444f22f3/baggage/HTTP_HEADER_FORMAT.md?plain=1#L69
 364
 365	var b strings.Builder
 366	b.Grow(c)
 367	for i := 0; i < len(unescapeVal); {
 368		r, size := utf8.DecodeRuneInString(unescapeVal[i:])
 369		if r == utf8.RuneError && size == 1 {
 370			// Invalid UTF-8 sequence found, replace it with '�'
 371			_, _ = b.WriteString("�")
 372		} else {
 373			_, _ = b.WriteRune(r)
 374		}
 375		i += size
 376	}
 377
 378	return b.String()
 379}
 380
 381// validate ensures m conforms to the W3C Baggage specification.
 382// A key must be an ASCII string, returning an error otherwise.
 383func (m Member) validate() error {
 384	if !m.hasData {
 385		return fmt.Errorf("%w: %q", errInvalidMember, m)
 386	}
 387
 388	if !validateBaggageName(m.key) {
 389		return fmt.Errorf("%w: %q", errInvalidKey, m.key)
 390	}
 391	if !validateBaggageValue(m.value) {
 392		return fmt.Errorf("%w: %q", errInvalidValue, m.value)
 393	}
 394	return m.properties.validate()
 395}
 396
 397// Key returns the Member key.
 398func (m Member) Key() string { return m.key }
 399
 400// Value returns the Member value.
 401func (m Member) Value() string { return m.value }
 402
 403// Properties returns a copy of the Member properties.
 404func (m Member) Properties() []Property { return m.properties.Copy() }
 405
 406// String encodes Member into a header string compliant with the W3C Baggage
 407// specification.
 408// It would return empty string if the key is invalid with the W3C Baggage
 409// specification. This could happen for a UTF-8 key, as it may contain
 410// invalid characters.
 411func (m Member) String() string {
 412	//  W3C Baggage specification does not allow percent-encoded keys.
 413	if !validateKey(m.key) {
 414		return ""
 415	}
 416
 417	s := m.key + keyValueDelimiter + valueEscape(m.value)
 418	if len(m.properties) > 0 {
 419		s += propertyDelimiter + m.properties.String()
 420	}
 421	return s
 422}
 423
 424// Baggage is a list of baggage members representing the baggage-string as
 425// defined by the W3C Baggage specification.
 426type Baggage struct { //nolint:golint
 427	list baggage.List
 428}
 429
 430// New returns a new valid Baggage. It returns an error if it results in a
 431// Baggage exceeding limits set in that specification.
 432//
 433// It expects all the provided members to have already been validated.
 434func New(members ...Member) (Baggage, error) {
 435	if len(members) == 0 {
 436		return Baggage{}, nil
 437	}
 438
 439	b := make(baggage.List)
 440	for _, m := range members {
 441		if !m.hasData {
 442			return Baggage{}, errInvalidMember
 443		}
 444
 445		// OpenTelemetry resolves duplicates by last-one-wins.
 446		b[m.key] = baggage.Item{
 447			Value:      m.value,
 448			Properties: m.properties.asInternal(),
 449		}
 450	}
 451
 452	// Check member numbers after deduplication.
 453	if len(b) > maxMembers {
 454		return Baggage{}, errMemberNumber
 455	}
 456
 457	bag := Baggage{b}
 458	if n := len(bag.String()); n > maxBytesPerBaggageString {
 459		return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
 460	}
 461
 462	return bag, nil
 463}
 464
 465// Parse attempts to decode a baggage-string from the passed string. It
 466// returns an error if the input is invalid according to the W3C Baggage
 467// specification.
 468//
 469// If there are duplicate list-members contained in baggage, the last one
 470// defined (reading left-to-right) will be the only one kept. This diverges
 471// from the W3C Baggage specification which allows duplicate list-members, but
 472// conforms to the OpenTelemetry Baggage specification.
 473func Parse(bStr string) (Baggage, error) {
 474	if bStr == "" {
 475		return Baggage{}, nil
 476	}
 477
 478	if n := len(bStr); n > maxBytesPerBaggageString {
 479		return Baggage{}, fmt.Errorf("%w: %d", errBaggageBytes, n)
 480	}
 481
 482	b := make(baggage.List)
 483	for _, memberStr := range strings.Split(bStr, listDelimiter) {
 484		m, err := parseMember(memberStr)
 485		if err != nil {
 486			return Baggage{}, err
 487		}
 488		// OpenTelemetry resolves duplicates by last-one-wins.
 489		b[m.key] = baggage.Item{
 490			Value:      m.value,
 491			Properties: m.properties.asInternal(),
 492		}
 493	}
 494
 495	// OpenTelemetry does not allow for duplicate list-members, but the W3C
 496	// specification does. Now that we have deduplicated, ensure the baggage
 497	// does not exceed list-member limits.
 498	if len(b) > maxMembers {
 499		return Baggage{}, errMemberNumber
 500	}
 501
 502	return Baggage{b}, nil
 503}
 504
 505// Member returns the baggage list-member identified by key.
 506//
 507// If there is no list-member matching the passed key the returned Member will
 508// be a zero-value Member.
 509// The returned member is not validated, as we assume the validation happened
 510// when it was added to the Baggage.
 511func (b Baggage) Member(key string) Member {
 512	v, ok := b.list[key]
 513	if !ok {
 514		// We do not need to worry about distinguishing between the situation
 515		// where a zero-valued Member is included in the Baggage because a
 516		// zero-valued Member is invalid according to the W3C Baggage
 517		// specification (it has an empty key).
 518		return newInvalidMember()
 519	}
 520
 521	return Member{
 522		key:        key,
 523		value:      v.Value,
 524		properties: fromInternalProperties(v.Properties),
 525		hasData:    true,
 526	}
 527}
 528
 529// Members returns all the baggage list-members.
 530// The order of the returned list-members is not significant.
 531//
 532// The returned members are not validated, as we assume the validation happened
 533// when they were added to the Baggage.
 534func (b Baggage) Members() []Member {
 535	if len(b.list) == 0 {
 536		return nil
 537	}
 538
 539	members := make([]Member, 0, len(b.list))
 540	for k, v := range b.list {
 541		members = append(members, Member{
 542			key:        k,
 543			value:      v.Value,
 544			properties: fromInternalProperties(v.Properties),
 545			hasData:    true,
 546		})
 547	}
 548	return members
 549}
 550
 551// SetMember returns a copy of the Baggage with the member included. If the
 552// baggage contains a Member with the same key, the existing Member is
 553// replaced.
 554//
 555// If member is invalid according to the W3C Baggage specification, an error
 556// is returned with the original Baggage.
 557func (b Baggage) SetMember(member Member) (Baggage, error) {
 558	if !member.hasData {
 559		return b, errInvalidMember
 560	}
 561
 562	n := len(b.list)
 563	if _, ok := b.list[member.key]; !ok {
 564		n++
 565	}
 566	list := make(baggage.List, n)
 567
 568	for k, v := range b.list {
 569		// Do not copy if we are just going to overwrite.
 570		if k == member.key {
 571			continue
 572		}
 573		list[k] = v
 574	}
 575
 576	list[member.key] = baggage.Item{
 577		Value:      member.value,
 578		Properties: member.properties.asInternal(),
 579	}
 580
 581	return Baggage{list: list}, nil
 582}
 583
 584// DeleteMember returns a copy of the Baggage with the list-member identified
 585// by key removed.
 586func (b Baggage) DeleteMember(key string) Baggage {
 587	n := len(b.list)
 588	if _, ok := b.list[key]; ok {
 589		n--
 590	}
 591	list := make(baggage.List, n)
 592
 593	for k, v := range b.list {
 594		if k == key {
 595			continue
 596		}
 597		list[k] = v
 598	}
 599
 600	return Baggage{list: list}
 601}
 602
 603// Len returns the number of list-members in the Baggage.
 604func (b Baggage) Len() int {
 605	return len(b.list)
 606}
 607
 608// String encodes Baggage into a header string compliant with the W3C Baggage
 609// specification.
 610// It would ignore members where the member key is invalid with the W3C Baggage
 611// specification. This could happen for a UTF-8 key, as it may contain
 612// invalid characters.
 613func (b Baggage) String() string {
 614	members := make([]string, 0, len(b.list))
 615	for k, v := range b.list {
 616		s := Member{
 617			key:        k,
 618			value:      v.Value,
 619			properties: fromInternalProperties(v.Properties),
 620		}.String()
 621
 622		// Ignored empty members.
 623		if s != "" {
 624			members = append(members, s)
 625		}
 626	}
 627	return strings.Join(members, listDelimiter)
 628}
 629
 630// parsePropertyInternal attempts to decode a Property from the passed string.
 631// It follows the spec at https://www.w3.org/TR/baggage/#definition.
 632func parsePropertyInternal(s string) (p Property, ok bool) {
 633	// For the entire function we will use "   key    =    value  " as an example.
 634	// Attempting to parse the key.
 635	// First skip spaces at the beginning "<   >key    =    value  " (they could be empty).
 636	index := skipSpace(s, 0)
 637
 638	// Parse the key: "   <key>    =    value  ".
 639	keyStart := index
 640	keyEnd := index
 641	for _, c := range s[keyStart:] {
 642		if !validateKeyChar(c) {
 643			break
 644		}
 645		keyEnd++
 646	}
 647
 648	// If we couldn't find any valid key character,
 649	// it means the key is either empty or invalid.
 650	if keyStart == keyEnd {
 651		return
 652	}
 653
 654	// Skip spaces after the key: "   key<    >=    value  ".
 655	index = skipSpace(s, keyEnd)
 656
 657	if index == len(s) {
 658		// A key can have no value, like: "   key    ".
 659		ok = true
 660		p.key = s[keyStart:keyEnd]
 661		return
 662	}
 663
 664	// If we have not reached the end and we can't find the '=' delimiter,
 665	// it means the property is invalid.
 666	if s[index] != keyValueDelimiter[0] {
 667		return
 668	}
 669
 670	// Attempting to parse the value.
 671	// Match: "   key    =<    >value  ".
 672	index = skipSpace(s, index+1)
 673
 674	// Match the value string: "   key    =    <value>  ".
 675	// A valid property can be: "   key    =".
 676	// Therefore, we don't have to check if the value is empty.
 677	valueStart := index
 678	valueEnd := index
 679	for _, c := range s[valueStart:] {
 680		if !validateValueChar(c) {
 681			break
 682		}
 683		valueEnd++
 684	}
 685
 686	// Skip all trailing whitespaces: "   key    =    value<  >".
 687	index = skipSpace(s, valueEnd)
 688
 689	// If after looking for the value and skipping whitespaces
 690	// we have not reached the end, it means the property is
 691	// invalid, something like: "   key    =    value  value1".
 692	if index != len(s) {
 693		return
 694	}
 695
 696	// Decode a percent-encoded value.
 697	rawVal := s[valueStart:valueEnd]
 698	unescapeVal, err := url.PathUnescape(rawVal)
 699	if err != nil {
 700		return
 701	}
 702	value := replaceInvalidUTF8Sequences(len(rawVal), unescapeVal)
 703
 704	ok = true
 705	p.key = s[keyStart:keyEnd]
 706	p.hasValue = true
 707
 708	p.value = value
 709	return
 710}
 711
 712func skipSpace(s string, offset int) int {
 713	i := offset
 714	for ; i < len(s); i++ {
 715		c := s[i]
 716		if c != ' ' && c != '\t' {
 717			break
 718		}
 719	}
 720	return i
 721}
 722
 723var safeKeyCharset = [utf8.RuneSelf]bool{
 724	// 0x23 to 0x27
 725	'#':  true,
 726	'$':  true,
 727	'%':  true,
 728	'&':  true,
 729	'\'': true,
 730
 731	// 0x30 to 0x39
 732	'0': true,
 733	'1': true,
 734	'2': true,
 735	'3': true,
 736	'4': true,
 737	'5': true,
 738	'6': true,
 739	'7': true,
 740	'8': true,
 741	'9': true,
 742
 743	// 0x41 to 0x5a
 744	'A': true,
 745	'B': true,
 746	'C': true,
 747	'D': true,
 748	'E': true,
 749	'F': true,
 750	'G': true,
 751	'H': true,
 752	'I': true,
 753	'J': true,
 754	'K': true,
 755	'L': true,
 756	'M': true,
 757	'N': true,
 758	'O': true,
 759	'P': true,
 760	'Q': true,
 761	'R': true,
 762	'S': true,
 763	'T': true,
 764	'U': true,
 765	'V': true,
 766	'W': true,
 767	'X': true,
 768	'Y': true,
 769	'Z': true,
 770
 771	// 0x5e to 0x7a
 772	'^': true,
 773	'_': true,
 774	'`': true,
 775	'a': true,
 776	'b': true,
 777	'c': true,
 778	'd': true,
 779	'e': true,
 780	'f': true,
 781	'g': true,
 782	'h': true,
 783	'i': true,
 784	'j': true,
 785	'k': true,
 786	'l': true,
 787	'm': true,
 788	'n': true,
 789	'o': true,
 790	'p': true,
 791	'q': true,
 792	'r': true,
 793	's': true,
 794	't': true,
 795	'u': true,
 796	'v': true,
 797	'w': true,
 798	'x': true,
 799	'y': true,
 800	'z': true,
 801
 802	// remainder
 803	'!': true,
 804	'*': true,
 805	'+': true,
 806	'-': true,
 807	'.': true,
 808	'|': true,
 809	'~': true,
 810}
 811
 812// validateBaggageName checks if the string is a valid OpenTelemetry Baggage name.
 813// Baggage name is a valid, non-empty UTF-8 string.
 814func validateBaggageName(s string) bool {
 815	if len(s) == 0 {
 816		return false
 817	}
 818
 819	return utf8.ValidString(s)
 820}
 821
 822// validateBaggageValue checks if the string is a valid OpenTelemetry Baggage value.
 823// Baggage value is a valid UTF-8 strings.
 824// Empty string is also a valid UTF-8 string.
 825func validateBaggageValue(s string) bool {
 826	return utf8.ValidString(s)
 827}
 828
 829// validateKey checks if the string is a valid W3C Baggage key.
 830func validateKey(s string) bool {
 831	if len(s) == 0 {
 832		return false
 833	}
 834
 835	for _, c := range s {
 836		if !validateKeyChar(c) {
 837			return false
 838		}
 839	}
 840
 841	return true
 842}
 843
 844func validateKeyChar(c int32) bool {
 845	return c >= 0 && c < int32(utf8.RuneSelf) && safeKeyCharset[c]
 846}
 847
 848// validateValue checks if the string is a valid W3C Baggage value.
 849func validateValue(s string) bool {
 850	for _, c := range s {
 851		if !validateValueChar(c) {
 852			return false
 853		}
 854	}
 855
 856	return true
 857}
 858
 859var safeValueCharset = [utf8.RuneSelf]bool{
 860	'!': true, // 0x21
 861
 862	// 0x23 to 0x2b
 863	'#':  true,
 864	'$':  true,
 865	'%':  true,
 866	'&':  true,
 867	'\'': true,
 868	'(':  true,
 869	')':  true,
 870	'*':  true,
 871	'+':  true,
 872
 873	// 0x2d to 0x3a
 874	'-': true,
 875	'.': true,
 876	'/': true,
 877	'0': true,
 878	'1': true,
 879	'2': true,
 880	'3': true,
 881	'4': true,
 882	'5': true,
 883	'6': true,
 884	'7': true,
 885	'8': true,
 886	'9': true,
 887	':': true,
 888
 889	// 0x3c to 0x5b
 890	'<': true, // 0x3C
 891	'=': true, // 0x3D
 892	'>': true, // 0x3E
 893	'?': true, // 0x3F
 894	'@': true, // 0x40
 895	'A': true, // 0x41
 896	'B': true, // 0x42
 897	'C': true, // 0x43
 898	'D': true, // 0x44
 899	'E': true, // 0x45
 900	'F': true, // 0x46
 901	'G': true, // 0x47
 902	'H': true, // 0x48
 903	'I': true, // 0x49
 904	'J': true, // 0x4A
 905	'K': true, // 0x4B
 906	'L': true, // 0x4C
 907	'M': true, // 0x4D
 908	'N': true, // 0x4E
 909	'O': true, // 0x4F
 910	'P': true, // 0x50
 911	'Q': true, // 0x51
 912	'R': true, // 0x52
 913	'S': true, // 0x53
 914	'T': true, // 0x54
 915	'U': true, // 0x55
 916	'V': true, // 0x56
 917	'W': true, // 0x57
 918	'X': true, // 0x58
 919	'Y': true, // 0x59
 920	'Z': true, // 0x5A
 921	'[': true, // 0x5B
 922
 923	// 0x5d to 0x7e
 924	']': true, // 0x5D
 925	'^': true, // 0x5E
 926	'_': true, // 0x5F
 927	'`': true, // 0x60
 928	'a': true, // 0x61
 929	'b': true, // 0x62
 930	'c': true, // 0x63
 931	'd': true, // 0x64
 932	'e': true, // 0x65
 933	'f': true, // 0x66
 934	'g': true, // 0x67
 935	'h': true, // 0x68
 936	'i': true, // 0x69
 937	'j': true, // 0x6A
 938	'k': true, // 0x6B
 939	'l': true, // 0x6C
 940	'm': true, // 0x6D
 941	'n': true, // 0x6E
 942	'o': true, // 0x6F
 943	'p': true, // 0x70
 944	'q': true, // 0x71
 945	'r': true, // 0x72
 946	's': true, // 0x73
 947	't': true, // 0x74
 948	'u': true, // 0x75
 949	'v': true, // 0x76
 950	'w': true, // 0x77
 951	'x': true, // 0x78
 952	'y': true, // 0x79
 953	'z': true, // 0x7A
 954	'{': true, // 0x7B
 955	'|': true, // 0x7C
 956	'}': true, // 0x7D
 957	'~': true, // 0x7E
 958}
 959
 960func validateValueChar(c int32) bool {
 961	return c >= 0 && c < int32(utf8.RuneSelf) && safeValueCharset[c]
 962}
 963
 964// valueEscape escapes the string so it can be safely placed inside a baggage value,
 965// replacing special characters with %XX sequences as needed.
 966//
 967// The implementation is based on:
 968// https://github.com/golang/go/blob/f6509cf5cdbb5787061b784973782933c47f1782/src/net/url/url.go#L285.
 969func valueEscape(s string) string {
 970	hexCount := 0
 971	for i := 0; i < len(s); i++ {
 972		c := s[i]
 973		if shouldEscape(c) {
 974			hexCount++
 975		}
 976	}
 977
 978	if hexCount == 0 {
 979		return s
 980	}
 981
 982	var buf [64]byte
 983	var t []byte
 984
 985	required := len(s) + 2*hexCount
 986	if required <= len(buf) {
 987		t = buf[:required]
 988	} else {
 989		t = make([]byte, required)
 990	}
 991
 992	j := 0
 993	for i := 0; i < len(s); i++ {
 994		c := s[i]
 995		if shouldEscape(s[i]) {
 996			const upperhex = "0123456789ABCDEF"
 997			t[j] = '%'
 998			t[j+1] = upperhex[c>>4]
 999			t[j+2] = upperhex[c&15]
1000			j += 3
1001		} else {
1002			t[j] = c
1003			j++
1004		}
1005	}
1006
1007	return string(t)
1008}
1009
1010// shouldEscape returns true if the specified byte should be escaped when
1011// appearing in a baggage value string.
1012func shouldEscape(c byte) bool {
1013	if c == '%' {
1014		// The percent character must be encoded so that percent-encoding can work.
1015		return true
1016	}
1017	return !validateValueChar(int32(c))
1018}