Skip to content

Commit 4cbe4d7

Browse files
trentmbasepi
andauthored
fix: "tracestate" parsing must allow whitespace around list-members (#1728)
* fix: "tracestate" parsing must allow whitespace around list-members Per https://w3c.github.io/trace-context/#tracestate-header-field-values the tracestate header can have whitespace around list-members. E.g. 'vendor1=foo, vendor2=bar ,\t vendor3=baz ' Refs: elastic/apm-agent-rum-js#1334 * I think this is what the pre-commit test failure in CI is complaining about. * Remove superfluous question mark * Use the same regex for tracestate reconstruction * Strip optional whitespace around tracestate * Revert my removal of the `*?` in Trent's original regex * CHANGELOG --------- Co-authored-by: Colton Myers <[email protected]>
1 parent 2af4266 commit 4cbe4d7

File tree

3 files changed

+11
-5
lines changed

3 files changed

+11
-5
lines changed

CHANGELOG.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ endif::[]
4343
===== Bug fixes
4444
4545
* Fix an async issue with long elasticsearch queries {pull}1725[#1725]
46+
* Fix a minor inconsistency with the W3C tracestate spec {pull}1728[#1728]
4647
4748
4849
[[release-notes-6.x]]

elasticapm/utils/disttracing.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,11 @@ def _parse_tracestate(self, tracestate) -> Dict[str, str]:
199199
made up of key:value pairs, separated by semicolons. It is meant to
200200
be parsed into a dict.
201201
202-
tracestate: es=key:value;key:value...,othervendor=<opaque>
202+
tracestate: es=key:value;key:value... , othervendor=<opaque>
203+
204+
Per https://w3c.github.io/trace-context/#tracestate-header-field-values
205+
there can be optional whitespace (OWS) between the comma-separated
206+
list-members.
203207
"""
204208
if not tracestate:
205209
return {}
@@ -208,7 +212,7 @@ def _parse_tracestate(self, tracestate) -> Dict[str, str]:
208212

209213
ret = {}
210214
try:
211-
state = re.search(r"(?:,|^)es=([^,]*)", tracestate).group(1).split(";")
215+
state = re.search(r"(?:,|^)\s*es=([^,]*?)\s*(?:,|$)", tracestate).group(1).split(";")
212216
except IndexError:
213217
return {}
214218
for keyval in state:
@@ -230,8 +234,9 @@ def _set_tracestate(self):
230234
return elastic_state
231235
else:
232236
# Remove es=<stuff> from the tracestate, and add the new es state to the end
233-
otherstate = re.sub(r"(?:,|^)es=([^,]*)", "", self.tracestate)
234-
otherstate = otherstate.lstrip(",")
237+
otherstate = re.sub(r"(?:,|^)\s*es=([^,]*?)\s*(?:,|$)", "", self.tracestate)
238+
otherstate = otherstate.lstrip(",") # in case `es=` was the first entry
239+
otherstate = re.sub(r",,", ",", otherstate) # remove potential double commas
235240
# No validation of `otherstate` required, since we're downstream. We only need to check `es=`
236241
# since we introduced it, and that validation has already been done at this point.
237242
if otherstate:

tests/utils/test_disttracing_header.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def test_trace_parent_binary_invalid_length(caplog):
125125
"state_header",
126126
[
127127
"es=foo:bar;baz:qux,othervendor=<opaque>",
128-
"snes=x:y,es=foo:bar;baz:qux,othervendor=<opaque>",
128+
"snes=x:y, es=foo:bar;baz:qux ,\tothervendor=<opaque>", # whitespace (OWS)
129129
"othervendor=<opaque>,es=foo:bar;baz:qux",
130130
],
131131
)

0 commit comments

Comments
 (0)