baggage.go raw

   1  // Copyright The OpenTelemetry Authors
   2  // SPDX-License-Identifier: Apache-2.0
   3  
   4  package baggage // import "go.opentelemetry.io/otel/baggage"
   5  
   6  import (
   7  	"errors"
   8  	"fmt"
   9  	"net/url"
  10  	"strings"
  11  	"unicode/utf8"
  12  
  13  	"go.opentelemetry.io/otel/internal/baggage"
  14  )
  15  
  16  const (
  17  	maxMembers               = 180
  18  	maxBytesPerMembers       = 4096
  19  	maxBytesPerBaggageString = 8192
  20  
  21  	listDelimiter     = ","
  22  	keyValueDelimiter = "="
  23  	propertyDelimiter = ";"
  24  )
  25  
  26  var (
  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.
  37  type 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.
  54  func 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.
  70  func 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.
  94  func 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  
 110  func 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.
 117  func 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.
 132  func (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.
 150  func (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.
 157  func (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.
 166  func (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  
 178  type properties []Property
 179  
 180  func 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  
 196  func (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  
 212  func (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.
 224  func (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.
 235  func (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.
 250  type 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.
 267  func 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.
 291  func 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  
 304  func 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.
 311  func 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 '�'.
 358  func 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.
 383  func (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.
 398  func (m Member) Key() string { return m.key }
 399  
 400  // Value returns the Member value.
 401  func (m Member) Value() string { return m.value }
 402  
 403  // Properties returns a copy of the Member properties.
 404  func (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.
 411  func (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.
 426  type 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.
 434  func 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.
 473  func 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.
 511  func (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.
 534  func (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.
 557  func (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.
 586  func (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.
 604  func (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.
 613  func (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.
 632  func 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  
 712  func 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  
 723  var 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.
 814  func validateBaggageName(s string) bool {
 815  	if s == "" {
 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.
 825  func validateBaggageValue(s string) bool {
 826  	return utf8.ValidString(s)
 827  }
 828  
 829  // validateKey checks if the string is a valid W3C Baggage key.
 830  func validateKey(s string) bool {
 831  	if s == "" {
 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  
 844  func 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.
 849  func validateValue(s string) bool {
 850  	for _, c := range s {
 851  		if !validateValueChar(c) {
 852  			return false
 853  		}
 854  	}
 855  
 856  	return true
 857  }
 858  
 859  var 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  
 960  func 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.
 969  func 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.
1012  func 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  }
1019