baggage.go 25 KB

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