diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/DatadogPTagsCodec.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/DatadogPTagsCodec.java index ec7a9a05feb..3ac0c7ad712 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/DatadogPTagsCodec.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/DatadogPTagsCodec.java @@ -176,10 +176,8 @@ private static int validateCharsUntilSeparatorOrEnd( pos++; if (pos < end) { c = s.charAt(pos); - // It's not allowed to have the separator as the last character so only check - // if there is something after the separator - if (pos < end - 1 && c == separator) { - break; + if (c == separator) { + break; // trailing separator allowed; caller resumes parsing from here } } } while (pos < end); @@ -189,7 +187,10 @@ private static int validateCharsUntilSeparatorOrEnd( private static boolean isAllowedKeyChar(int c) { // space (MIN_ALLOWED_CHAR) is not allowed - return c > MIN_ALLOWED_CHAR && c <= MAX_ALLOWED_CHAR && c != TAGS_SEPARATOR; + return c > MIN_ALLOWED_CHAR + && c <= MAX_ALLOWED_CHAR + && c != TAG_KEY_SEPARATOR + && c != TAGS_SEPARATOR; } private static boolean isAllowedValueChar(int c) { diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java index 0430bbc3a2c..c0018544188 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/ptags/W3CPTagsCodec.java @@ -100,6 +100,10 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) { CharSequence lastParentId = null; TagValue orgPropagationMarkerTagValue = null; while (tagPos < ddMemberValueEnd) { + tagPos = skipEmptyElements(value, tagPos, ddMemberValueEnd); + if (tagPos >= ddMemberValueEnd) { + break; + } int tagKeyEndsAt = validateCharsUntilSeparatorOrEnd( value, @@ -108,10 +112,11 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) { KEY_VALUE_SEPARATOR, false, W3CPTagsCodec::isAllowedKeyChar); - if (tagKeyEndsAt < 0 || tagKeyEndsAt == ddMemberValueEnd) { - log.warn("Invalid datadog tags header value: '{}' at {}", value, tagPos); - // TODO drop parts? - return empty(tagsFactory, value, firstMemberStart, ddMemberStart, ddMemberValueEnd); + if (tagKeyEndsAt < 0 || tagKeyEndsAt >= ddMemberValueEnd) { + int nextTagPos = skipMalformedElement(value, tagPos, ddMemberValueEnd); + maxUnknownSize += (nextTagPos - tagPos); // still relay malformed elements + tagPos = nextTagPos; + continue; } int tagValuePos = tagKeyEndsAt + 1; int tagValueEndsAt = @@ -123,9 +128,10 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) { true, W3CPTagsCodec::isAllowedValueChar); if (tagValueEndsAt < 0) { - log.warn("Invalid datadog tags header value: '{}' at {}", value, tagKeyEndsAt); - // TODO drop parts? - return empty(tagsFactory, value, firstMemberStart, ddMemberStart, ddMemberValueEnd); + int nextTagPos = skipMalformedElement(value, tagValuePos, ddMemberValueEnd); + maxUnknownSize += (nextTagPos - tagPos); // still relay malformed elements + tagPos = nextTagPos; + continue; } int nextTagPos = tagValueEndsAt + 1; if (tagValueEndsAt == ddMemberValueEnd) { @@ -152,7 +158,6 @@ PropagationTags fromHeaderValue(PTagsFactory tagsFactory, String value) { if (tagKey.equals(TRACE_ID_TAG)) { return tagsFactory.createInvalid(PROPAGATION_ERROR_MALFORMED_TID + tagValue); } - // TODO drop parts? return empty(tagsFactory, value, firstMemberStart, ddMemberStart, ddMemberValueEnd); } if (tagKey.equals(DECISION_MAKER_TAG)) { @@ -351,10 +356,8 @@ private static int validateCharsUntilSeparatorOrEnd( pos++; if (pos < end) { c = s.charAt(pos); - // It's not allowed to have the separator as the last character so only check - // if there is something after the separator - if (pos < end - 1 && c == separator) { - break; + if (c == separator) { + break; // trailing separator allowed; caller resumes parsing from here } } } while (pos < end); @@ -365,7 +368,10 @@ private static int validateCharsUntilSeparatorOrEnd( private static boolean isAllowedKeyChar(int c) { // We already know that the segments have been validated against the valid chars for // the general tracestate header - return c > MIN_ALLOWED_CHAR && c <= MAX_ALLOWED_CHAR && c != KEY_VALUE_SEPARATOR; + return c > MIN_ALLOWED_CHAR + && c <= MAX_ALLOWED_CHAR + && c != KEY_VALUE_SEPARATOR + && c != ELEMENT_SEPARATOR; } private static boolean isAllowedValueChar(int c) { @@ -613,12 +619,37 @@ private static boolean isOWC(char c) { return c == ' ' || c == '\t'; } - private static int stripTrailingOWC(String original, int start, int end) { - char c = original.charAt(--end); - while (isOWC(c) && end > start) { - c = original.charAt(--end); + private static int stripTrailingOWC(String value, int start, int end) { + while (end > start + 1 && isOWC(value.charAt(end - 1))) { + end--; } - return ++end; + return end; + } + + private static int skipMalformedElement(String value, int start, int end) { + log.warn( + "Invalid datadog tags header value: '{}' dropping malformed element at {}", value, start); + int pos = start; + while (pos < end) { + char c = value.charAt(pos++); + if (c == ELEMENT_SEPARATOR) { + return pos; + } + } + return end; + } + + private static int skipEmptyElements(String value, int start, int end) { + int pos = start; + while (pos < end) { + char c = value.charAt(pos++); + if (c == ELEMENT_SEPARATOR) { + start = pos; + } else if (!isOWC(c)) { + return start; + } + } + return end; } private static int cleanUpAndAppendUnknown(StringBuilder sb, W3CPTags w3CPTags, int size) { @@ -631,16 +662,21 @@ private static int cleanUpAndAppendUnknown(StringBuilder sb, W3CPTags w3CPTags, int elementStart = w3CPTags.ddMemberStart + EMPTY_SIZE; // skip over 'dd=' int okSize = size; while (elementStart < w3CPTags.ddMemberValueEnd && size < MAX_HEADER_SIZE) { + elementStart = skipEmptyElements(original, elementStart, w3CPTags.ddMemberValueEnd); + if (elementStart == w3CPTags.ddMemberValueEnd) { + break; + } okSize = size; int elementEnd = original.indexOf(ELEMENT_SEPARATOR, elementStart); - if (elementEnd < 0) { + if (elementEnd < 0 || elementEnd > w3CPTags.ddMemberValueEnd) { elementEnd = w3CPTags.ddMemberValueEnd; } if (!original.startsWith(Encoding.W3C.getPrefix(), elementStart)) { char first = original.charAt(elementStart); - char second = original.charAt(elementStart + 1); - if (second != KEY_VALUE_SEPARATOR || (first != 'o' && first != 's')) { - // only append elements that we don't know about or are not tags + // ignore known o:, s:, and p: elements because we always add them back in appendPrefix + if ((first != 'o' && first != 's' && first != 'p') + || elementStart + 1 >= elementEnd + || original.charAt(elementStart + 1) != KEY_VALUE_SEPARATOR) { if (sb.length() > EMPTY_SIZE) { sb.append(ELEMENT_SEPARATOR); size++; diff --git a/dd-trace-core/src/test/java/datadog/trace/core/propagation/DatadogPropagationTagsTest.java b/dd-trace-core/src/test/java/datadog/trace/core/propagation/DatadogPropagationTagsTest.java index 22332709f62..73c6c8af9b9 100644 --- a/dd-trace-core/src/test/java/datadog/trace/core/propagation/DatadogPropagationTagsTest.java +++ b/dd-trace-core/src/test/java/datadog/trace/core/propagation/DatadogPropagationTagsTest.java @@ -27,6 +27,7 @@ class DatadogPropagationTagsTest extends DDJavaSpecification { "valid dm tag 2-digit | '_dd.p.dm=934086a686-10' | '_dd.p.dm=934086a686-10' | [_dd.p.dm: '934086a686-10'] ", "valid dm tag 3-digit | '_dd.p.dm=934086a686-102' | '_dd.p.dm=934086a686-102' | [_dd.p.dm: '934086a686-102'] ", "dm tag minus only | '_dd.p.dm=-1' | '_dd.p.dm=-1' | [_dd.p.dm: '-1'] ", + "dm tag with trailing separator | '_dd.p.dm=-4,' | '_dd.p.dm=-4' | [_dd.p.dm: '-4'] ", "any p tag | '_dd.p.anytag=value' | '_dd.p.anytag=value' | [_dd.p.anytag: 'value'] ", "non p tag dropped | '_dd.b.somekey=value' | | [:] ", "upstream services alone dropped | '_dd.p.upstream_services=bWNudWx0eS13ZWI|0|1|0.1' | | [:] ", diff --git a/dd-trace-core/src/test/java/datadog/trace/core/propagation/W3CHttpExtractorTest.java b/dd-trace-core/src/test/java/datadog/trace/core/propagation/W3CHttpExtractorTest.java index d1d80827e74..511ffe97ac7 100644 --- a/dd-trace-core/src/test/java/datadog/trace/core/propagation/W3CHttpExtractorTest.java +++ b/dd-trace-core/src/test/java/datadog/trace/core/propagation/W3CHttpExtractorTest.java @@ -108,15 +108,22 @@ void checkMaxFromW3CTraceIds(@ConvertWith(TraceIdTestConverter.class) DDTraceId } @TableTest({ - "scenario | traceparent | tracestate | priority | decisionMaker | origin", - "keep empty state | '00-00000000000000000000000000000001-123456789abcdef0-01' | '' | SAMPLER_KEEP | SamplingMechanism.DEFAULT | ", - "drop empty state | '00-00000000000000000000000000000001-123456789abcdef0-00' | '' | SAMPLER_DROP | | ", - "keep with user keep state | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=s:2;o:some' | USER_KEEP | | some ", - "keep with user keep state and manual dm | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=s:2;o:some;t.dm:-4' | USER_KEEP | SamplingMechanism.MANUAL | some ", - "drop with user keep state and manual dm | '00-00000000000000000000000000000001-123456789abcdef0-00' | 'dd=s:2;o:some;t.dm:-4' | SAMPLER_DROP | | some ", - "drop with user drop state | '00-00000000000000000000000000000001-123456789abcdef0-00' | 'dd=s:-1;o:some' | USER_DROP | | some ", - "drop with user drop state and manual dm | '00-00000000000000000000000000000001-123456789abcdef0-00' | 'dd=s:-1;o:some;t.dm:-4' | USER_DROP | SamplingMechanism.MANUAL | some ", - "keep overrides user drop state with manual dm | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=s:-1;o:some;t.dm:-4' | SAMPLER_KEEP | SamplingMechanism.DEFAULT | some " + "scenario | traceparent | tracestate | priority | decisionMaker | origin", + "keep empty state | '00-00000000000000000000000000000001-123456789abcdef0-01' | '' | SAMPLER_KEEP | SamplingMechanism.DEFAULT | ", + "drop empty state | '00-00000000000000000000000000000001-123456789abcdef0-00' | '' | SAMPLER_DROP | | ", + "keep with user keep state | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=s:2;o:some' | USER_KEEP | | some ", + "keep with trailing element separator | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=s:2;o:some;' | USER_KEEP | | some ", + "keep with trailing separator and OWS | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=s:2;o:some; \t' | USER_KEEP | | some ", + "keep with trailing separator OWS before comma | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=s:2;o:some; ,x=y' | USER_KEEP | | some ", + "skip empty element in middle | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=s:2;;o:some' | USER_KEEP | | some ", + "skip leading separator | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=;s:2;o:some' | USER_KEEP | | some ", + "skip bare element in middle | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=s:2;flag;o:some' | USER_KEEP | | some ", + "skip multiple bare elements in middle | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=s:2;flag1;flag2;o:some' | USER_KEEP | | some ", + "keep with user keep state and manual dm | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=s:2;o:some;t.dm:-4' | USER_KEEP | SamplingMechanism.MANUAL | some ", + "drop with user keep state and manual dm | '00-00000000000000000000000000000001-123456789abcdef0-00' | 'dd=s:2;o:some;t.dm:-4' | SAMPLER_DROP | | some ", + "drop with user drop state | '00-00000000000000000000000000000001-123456789abcdef0-00' | 'dd=s:-1;o:some' | USER_DROP | | some ", + "drop with user drop state and manual dm | '00-00000000000000000000000000000001-123456789abcdef0-00' | 'dd=s:-1;o:some;t.dm:-4' | USER_DROP | SamplingMechanism.MANUAL | some ", + "keep overrides user drop state with manual dm | '00-00000000000000000000000000000001-123456789abcdef0-01' | 'dd=s:-1;o:some;t.dm:-4' | SAMPLER_KEEP | SamplingMechanism.DEFAULT | some " }) void extractTraceparentTracestateAndHttpHeaders( String traceparent, diff --git a/dd-trace-core/src/test/java/datadog/trace/core/propagation/W3CPropagationTagsTest.java b/dd-trace-core/src/test/java/datadog/trace/core/propagation/W3CPropagationTagsTest.java index f59c8d36004..491b8fb32ab 100644 --- a/dd-trace-core/src/test/java/datadog/trace/core/propagation/W3CPropagationTagsTest.java +++ b/dd-trace-core/src/test/java/datadog/trace/core/propagation/W3CPropagationTagsTest.java @@ -230,35 +230,81 @@ static IntStream memberCountArguments() { } @TableTest({ - "scenario | headerValue | expectedHeaderValue | tags ", - "null | | | [:] ", - "empty | '' | | [:] ", - "dd only with dm | 'dd=s:0;t.dm:934086a686-4' | 'dd=s:0;t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", - "dd only with ts | 'dd=s:0;t.ts:02' | 'dd=s:0;t.ts:02' | [_dd.p.ts: 02] ", - "dd only with ts zero | 'dd=s:0;t.ts:00' | 'dd=s:0' | [:] ", - "dd only with dm and ts | 'dd=s:0;t.dm:934086a686-4;t.ts:02' | 'dd=s:0;t.dm:934086a686-4;t.ts:02' | [_dd.p.dm: 934086a686-4, _dd.p.ts: 02] ", - "other before dd | 'other=whatever,dd=s:0;t.dm:934086a686-4' | 'dd=s:0;t.dm:934086a686-4,other=whatever' | [_dd.p.dm: 934086a686-4] ", - "dd before other | 'dd=s:0;t.dm:934086a687-3,other=whatever' | 'dd=s:0;t.dm:934086a687-3,other=whatever' | [_dd.p.dm: 934086a687-3] ", - "some before dd before other | 'some=thing,dd=s:0;t.dm:934086a687-3,other=whatever' | 'dd=s:0;t.dm:934086a687-3,some=thing,other=whatever' | [_dd.p.dm: 934086a687-3] ", - "no dd | 'some=thing,other=whatever' | 'some=thing,other=whatever' | [:] ", - "dd with origin and dm | 'dd=s:0;o:some;t.dm:934086a686-4' | 'dd=s:0;o:some;t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", - "dd with unknown key | 'dd=s:0;x:unknown;t.dm:934086a686-4' | 'dd=s:0;t.dm:934086a686-4;x:unknown' | [_dd.p.dm: 934086a686-4] ", - "other before dd with unknown | 'other=whatever,dd=s:0;x:unknown;t.dm:934086a686-4' | 'dd=s:0;t.dm:934086a686-4;x:unknown,other=whatever' | [_dd.p.dm: 934086a686-4] ", - "dd with xyz instead of s | 'other=whatever,dd=xyz:unknown;t.dm:934086a686-4' | 'dd=t.dm:934086a686-4;xyz:unknown,other=whatever' | [_dd.p.dm: 934086a686-4] ", - "dd with trailing whitespace | 'other=whatever,dd=t.dm:934086a686-4;xyz:unknown ' | 'dd=t.dm:934086a686-4;xyz:unknown,other=whatever' | [_dd.p.dm: 934086a686-4] ", - "ws and tabs around members | '\tsome=thing \t , dd=s:0;t.dm:934086a687-3\t\t, other=whatever\t\t ' | 'dd=s:0;t.dm:934086a687-3,some=thing,other=whatever' | [_dd.p.dm: 934086a687-3] ", - "dd with two t. tags | 'dd=s:0;t.a:b;t.x:y' | 'dd=s:0;t.a:b;t.x:y' | [_dd.p.a: b, _dd.p.x: y] ", - "dd with two t. tags trailing whitespace | 'dd=s:0;t.a:b;t.x:y \t' | 'dd=s:0;t.a:b;t.x:y' | [_dd.p.a: b, _dd.p.x: y] ", - "dd with two t. tags inner whitespace | 'dd=s:0;t.a:b ;t.x:y \t' | 'dd=s:0;t.a:b ;t.x:y' | ['_dd.p.a': 'b ', _dd.p.x: y] ", - "dd with two t. tags invalid whitespace | 'dd=s:0;t.a:b \t;t.x:y \t' | | [:] ", - "dd with tid | 'dd=s:0;t.tid:123456789abcdef0' | 'dd=s:0;t.tid:123456789abcdef0' | [_dd.p.tid: 123456789abcdef0] ", - "tid empty value | 'dd=t.tid:' | | [:] ", - "tid too short length 1 | 'dd=t.tid:1' | | ['_dd.propagation_error': 'malformed_tid 1'] ", - "tid too short length 15 | 'dd=t.tid:111111111111111' | | ['_dd.propagation_error': 'malformed_tid 111111111111111'] ", - "tid too long length 17 | 'dd=t.tid:11111111111111111' | | ['_dd.propagation_error': 'malformed_tid 11111111111111111']", - "tid uppercase | 'dd=t.tid:123456789ABCDEF0' | | ['_dd.propagation_error': 'malformed_tid 123456789ABCDEF0'] ", - "tid non-hex character | 'dd=t.tid:123456789abcdefg' | | ['_dd.propagation_error': 'malformed_tid 123456789abcdefg'] ", - "tid negative | 'dd=t.tid:-123456789abcdef' | | ['_dd.propagation_error': 'malformed_tid -123456789abcdef'] " + "scenario | headerValue | expectedHeaderValue | tags ", + "null | | | [:] ", + "empty | '' | | [:] ", + "dd only with dm | 'dd=s:0;t.dm:934086a686-4' | 'dd=s:0;t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "dd only with ts | 'dd=s:0;t.ts:02' | 'dd=s:0;t.ts:02' | [_dd.p.ts: 02] ", + "dd only with ts zero | 'dd=s:0;t.ts:00' | 'dd=s:0' | [:] ", + "dd only with dm and ts | 'dd=s:0;t.dm:934086a686-4;t.ts:02' | 'dd=s:0;t.dm:934086a686-4;t.ts:02' | [_dd.p.dm: 934086a686-4, _dd.p.ts: 02] ", + "other before dd | 'other=whatever,dd=s:0;t.dm:934086a686-4' | 'dd=s:0;t.dm:934086a686-4,other=whatever' | [_dd.p.dm: 934086a686-4] ", + "dd before other | 'dd=s:0;t.dm:934086a687-3,other=whatever' | 'dd=s:0;t.dm:934086a687-3,other=whatever' | [_dd.p.dm: 934086a687-3] ", + "some before dd before other | 'some=thing,dd=s:0;t.dm:934086a687-3,other=whatever' | 'dd=s:0;t.dm:934086a687-3,some=thing,other=whatever' | [_dd.p.dm: 934086a687-3] ", + "no dd | 'some=thing,other=whatever' | 'some=thing,other=whatever' | [:] ", + "dd with origin and dm | 'dd=s:0;o:some;t.dm:934086a686-4' | 'dd=s:0;o:some;t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "dd with unknown key | 'dd=s:0;x:unknown;t.dm:934086a686-4' | 'dd=s:0;t.dm:934086a686-4;x:unknown' | [_dd.p.dm: 934086a686-4] ", + "other before dd with unknown | 'other=whatever,dd=s:0;x:unknown;t.dm:934086a686-4' | 'dd=s:0;t.dm:934086a686-4;x:unknown,other=whatever' | [_dd.p.dm: 934086a686-4] ", + "dd with xyz instead of s | 'other=whatever,dd=xyz:unknown;t.dm:934086a686-4' | 'dd=t.dm:934086a686-4;xyz:unknown,other=whatever' | [_dd.p.dm: 934086a686-4] ", + "dd with trailing whitespace | 'other=whatever,dd=t.dm:934086a686-4;xyz:unknown ' | 'dd=t.dm:934086a686-4;xyz:unknown,other=whatever' | [_dd.p.dm: 934086a686-4] ", + "ws and tabs around members | '\tsome=thing \t , dd=s:0;t.dm:934086a687-3\t\t, other=whatever\t\t ' | 'dd=s:0;t.dm:934086a687-3,some=thing,other=whatever' | [_dd.p.dm: 934086a687-3] ", + "dd with two t. tags | 'dd=s:0;t.a:b;t.x:y' | 'dd=s:0;t.a:b;t.x:y' | [_dd.p.a: b, _dd.p.x: y] ", + "dd with two t. tags trailing whitespace | 'dd=s:0;t.a:b;t.x:y \t' | 'dd=s:0;t.a:b;t.x:y' | [_dd.p.a: b, _dd.p.x: y] ", + "dd with two t. tags inner whitespace | 'dd=s:0;t.a:b ;t.x:y \t' | 'dd=s:0;t.a:b ;t.x:y' | ['_dd.p.a': 'b ', _dd.p.x: y] ", + "dd with two t. tags invalid whitespace | 'dd=s:0;t.a:b \t;t.x:y \t' | | [:] ", + "dd with tid | 'dd=s:0;t.tid:123456789abcdef0' | 'dd=s:0;t.tid:123456789abcdef0' | [_dd.p.tid: 123456789abcdef0] ", + "tid empty value | 'dd=t.tid:' | | [:] ", + "tid too short length 1 | 'dd=t.tid:1' | | ['_dd.propagation_error': 'malformed_tid 1'] ", + "tid too short length 15 | 'dd=t.tid:111111111111111' | | ['_dd.propagation_error': 'malformed_tid 111111111111111'] ", + "tid too long length 17 | 'dd=t.tid:11111111111111111' | | ['_dd.propagation_error': 'malformed_tid 11111111111111111']", + "tid uppercase | 'dd=t.tid:123456789ABCDEF0' | | ['_dd.propagation_error': 'malformed_tid 123456789ABCDEF0'] ", + "tid non-hex character | 'dd=t.tid:123456789abcdefg' | | ['_dd.propagation_error': 'malformed_tid 123456789abcdefg'] ", + "tid negative | 'dd=t.tid:-123456789abcdef' | | ['_dd.propagation_error': 'malformed_tid -123456789abcdef'] ", + "dd with trailing separator | 'dd=s:0;t.dm:934086a686-4;' | 'dd=s:0;t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "dd single tag trailing separator | 'dd=t.dm:934086a686-4;' | 'dd=t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "dd before other trailing separator | 'dd=s:0;t.dm:934086a687-3;,other=whatever' | 'dd=s:0;t.dm:934086a687-3,other=whatever' | [_dd.p.dm: 934086a687-3] ", + "dd trailing separator then ws | 'dd=s:0;t.dm:934086a686-4; ' | 'dd=s:0;t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "dd trailing separator then tab | 'dd=s:0;t.dm:934086a686-4;\t' | 'dd=s:0;t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "dd trailing separator ws then comma | 'dd=s:0;t.dm:934086a687-3; ,other=whatever' | 'dd=s:0;t.dm:934086a687-3,other=whatever' | [_dd.p.dm: 934086a687-3] ", + "unknown trailing separator | 'dd=x:y;' | 'dd=x:y' | [:] ", + "unknown trailing separator then space | 'dd=x:y; ' | 'dd=x:y' | [:] ", + "unknown trailing separator then tab | 'dd=x:y;\t' | 'dd=x:y' | [:] ", + "unknown two trailing separators | 'dd=x:y;;' | 'dd=x:y' | [:] ", + "unknown three trailing separators | 'dd=x:y;;;' | 'dd=x:y' | [:] ", + "leading empty element | 'dd=;x:y' | 'dd=x:y' | [:] ", + "leading double empty element | 'dd=;;x:y' | 'dd=x:y' | [:] ", + "double separator between two unknowns | 'dd=x:y;;z:w' | 'dd=x:y;z:w' | [:] ", + "double separator between known tags | 'dd=s:0;;t.dm:934086a686-4' | 'dd=s:0;t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "unknown then double sep then known tag | 'dd=s:0;x:y;;t.dm:934086a686-4' | 'dd=s:0;t.dm:934086a686-4;x:y' | [_dd.p.dm: 934086a686-4] ", + "leading/interior/trailing empty elements | 'dd=x:y;;;z:w;;' | 'dd=x:y;z:w' | [:] ", + "only empty elements, no tags | 'dd=;;;;' | | [:] ", + "only empty elements before other member | 'other=whatever,dd=;;;;' | 'other=whatever' | [:] ", + "double sep then trailing space | 'dd=x:y;; ' | 'dd=x:y' | [:] ", + "double sep then trailing tab | 'dd=x:y;;\t' | 'dd=x:y' | [:] ", + "triple sep then trailing space | 'dd=x:y;;; ' | 'dd=x:y' | [:] ", + "sep then ws then sep is empty element ws | 'dd=x:y; ;z:w' | 'dd=x:y;z:w' | [:] ", + "2 seps then ws then sep is empty elem ws | 'dd=x:y;; ;z:w' | 'dd=x:y;z:w' | [:] ", + "sampling unknown trailing sep then ws | 'dd=s:0;x:y; ' | 'dd=s:0;x:y' | [:] ", + "sep then ws then sep is empty elem | 'dd=s:0; ;t.dm:934086a686-4' | 'dd=s:0;t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "dd interior ws between submembers | 'dd=s:0;t.dm:934086a686-4; t.x:y' | 'dd=s:0;t.dm:934086a686-4; t.x:y' | [_dd.p.dm: 934086a686-4] ", + "dd interior single space submember | 'dd=s:0; t.dm:934086a686-4' | 'dd=s:0; t.dm:934086a686-4' | [:] ", + "sampling priority non-numeric dropped | 'dd=s:abc;t.dm:934086a686-4' | 'dd=t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "sampling priority overflow dropped | 'dd=s:99999999999;t.dm:934086a686-4' | 'dd=t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "sampling priority min int retained | 'dd=s:-2147483648;t.dm:934086a686-4' | 'dd=s:-2147483648;t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "sampling priority leading plus | 'dd=s:+5;t.dm:934086a686-4' | 'dd=s:5;t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "sampling priority lone minus dropped | 'dd=s:-;t.dm:934086a686-4' | 'dd=t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "sampling priority empty value invalid | 'dd=s:;t.dm:934086a686-4' | 'dd=t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "sampling priority ws only value dropped | 'dd=s: ;t.dm:934086a686-4' | 'dd=t.dm:934086a686-4' | [_dd.p.dm: 934086a686-4] ", + "sampling priority just above max int | 'dd=s:2147483648' | | [:] ", + "sampling priority just below min int | 'dd=s:-2147483649' | | [:] ", + "bare element after known unknown | 'dd=x:y;z' | 'dd=x:y;z' | [:] ", + "bare element before known unknown | 'dd=z;x:y' | 'dd=z;x:y' | [:] ", + "bare element between known unknowns | 'dd=x:y;z;a:b' | 'dd=x:y;z;a:b' | [:] ", + "parent id not duplicated as unknown | 'dd=p:b6241412414a;x:y' | 'dd=p:b6241412414a;x:y' | [:] ", + "origin not duplicated as unknown | 'dd=o:rum;x:y' | 'dd=o:rum;x:y' | [:] ", + "sampling priority not duplicated as unknown | 'dd=s:1;x:y' | 'dd=s:1;x:y' | [:] ", + "malformed element with leading space key | 'dd=x:y; z:w' | 'dd=x:y; z:w' | [:] ", + "malformed element with interior space key | 'dd=x:y;a b:c' | 'dd=x:y;a b:c' | [:] ", + "malformed element between known tags | 'dd=s:1; z:w;o:rum' | 'dd=s:1;o:rum; z:w' | [:] " }) void createPropagationTagsFromHeaderValue( String headerValue, String expectedHeaderValue, Map tags) { @@ -268,6 +314,21 @@ void createPropagationTagsFromHeaderValue( assertEquals(tags, propagationTags.createTagMap()); } + @TableTest({ + "scenario | headerValue | expectedLastParentId | expectedHeaderValue ", + "parent id | 'dd=p:b6241412414a' | b6241412414a | 'dd=p:b6241412414a' ", + "parent id with trailing separator | 'dd=p:b6241412414a;' | b6241412414a | 'dd=p:b6241412414a' ", + "sampling then parent id trailing | 'dd=s:1;p:b6241412414a;' | b6241412414a | 'dd=s:1;p:b6241412414a'", + "parent id trailing separator ws | 'dd=p:b6241412414a; ' | b6241412414a | 'dd=p:b6241412414a' " + }) + void extractsLastParentIdWithTrailingSeparator( + String headerValue, String expectedLastParentId, String expectedHeaderValue) { + PropagationTags propagationTags = factory().fromHeaderValue(W3C, headerValue); + + assertEquals(expectedLastParentId, propagationTags.getLastParentId().toString()); + assertEquals(expectedHeaderValue, propagationTags.headerValue(W3C)); + } + @TableTest({ "scenario | headerValue | expectedHeaderValue | tags ", "single dm | 'dd=s:0;t.dm:934086a686-4' | '_dd.p.dm=934086a686-4' | [_dd.p.dm: 934086a686-4] ",