diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/B3HttpCodec.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/B3HttpCodec.java index 7d6d132541a..d18d1ebce88 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/B3HttpCodec.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/B3HttpCodec.java @@ -222,6 +222,7 @@ public boolean accept(final String key, final String value) { log.debug("Header: {}", key); } try { + handleTags(key, value); char first = Character.toLowerCase(key.charAt(0)); switch (first) { case 'x': @@ -250,10 +251,7 @@ public boolean accept(final String key, final String value) { break; default: } - if (handledIpHeaders(key, value)) { - return true; - } - handleTags(key, value); + handledIpHeaders(key, value); } catch (RuntimeException e) { invalidateContext(); log.debug("Exception when extracting context", e); @@ -282,6 +280,7 @@ public boolean accept(String key, String value) { if (LOG_EXTRACT_HEADER_NAMES) { log.debug("Header: {}", key); } + handleTags(key, value); if (B3_KEY.equalsIgnoreCase(key)) { return extractB3(firstHeaderValue(value)); } else { @@ -304,10 +303,7 @@ public boolean accept(String key, String value) { break; } } - if (handledIpHeaders(key, value)) { - return true; - } - handleTags(key, value); + handledIpHeaders(key, value); } catch (RuntimeException e) { invalidateContext(); log.debug("Exception when extracting context", e); diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/DatadogHttpCodec.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/DatadogHttpCodec.java index f2db43cdb6c..a8bbe883907 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/DatadogHttpCodec.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/DatadogHttpCodec.java @@ -121,6 +121,9 @@ public boolean accept(String key, String value) { if (LOG_EXTRACT_HEADER_NAMES) { log.debug("Header: {}", key); } + if (!handleTags(key, value)) { + handleMappedBaggage(key, value); + } String lowerCaseKey = null; int classification = IGNORE; char first = Character.toLowerCase(key.charAt(0)); @@ -198,13 +201,7 @@ public boolean accept(String key, String value) { return false; } } else { - if (handledIpHeaders(key, value)) { - return true; - } - if (handleTags(key, value)) { - return true; - } - handleMappedBaggage(key, value); + handledIpHeaders(key, value); } return true; } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/HaystackHttpCodec.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/HaystackHttpCodec.java index 61e9a40e897..c1030d17c08 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/HaystackHttpCodec.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/HaystackHttpCodec.java @@ -157,6 +157,9 @@ public boolean accept(String key, String value) { if (LOG_EXTRACT_HEADER_NAMES) { log.debug("Header: {}", key); } + if (!handleTags(key, value)) { + handleMappedBaggage(key, value); + } char first = Character.toLowerCase(key.charAt(0)); String lowerCaseKey = null; int classification = IGNORE; @@ -230,13 +233,7 @@ public boolean accept(String key, String value) { return false; } } else { - if (handledIpHeaders(key, value)) { - return true; - } - if (handleTags(key, value)) { - return true; - } - handleMappedBaggage(key, value); + handledIpHeaders(key, value); } return true; } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/NoneCodec.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/NoneCodec.java index 5d6924ea69e..7b959df7e5b 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/NoneCodec.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/NoneCodec.java @@ -30,6 +30,9 @@ public boolean accept(String key, String value) { if (LOG_EXTRACT_HEADER_NAMES) { log.debug("Header: {}", key); } + if (!handleTags(key, value)) { + handleMappedBaggage(key, value); + } char first = Character.toLowerCase(key.charAt(0)); switch (first) { case 'x': @@ -50,13 +53,7 @@ public boolean accept(String key, String value) { default: } - if (handledIpHeaders(key, value)) { - return true; - } - if (handleTags(key, value)) { - return true; - } - handleMappedBaggage(key, value); + handledIpHeaders(key, value); return true; } } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/W3CHttpCodec.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/W3CHttpCodec.java index a6dd7e7b65f..c0f97f6d495 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/W3CHttpCodec.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/W3CHttpCodec.java @@ -153,6 +153,9 @@ public boolean accept(String key, String value) { if (LOG_EXTRACT_HEADER_NAMES) { log.debug("Header: {}", key); } + if (!handleTags(key, value)) { + handleMappedBaggage(key, value); + } String lowerCaseKey = null; int classification = IGNORE; char first = Character.toLowerCase(key.charAt(0)); @@ -213,13 +216,7 @@ public boolean accept(String key, String value) { return false; } } else { - if (handledIpHeaders(key, value)) { - return true; - } - if (handleTags(key, value)) { - return true; - } - handleMappedBaggage(key, value); + handledIpHeaders(key, value); } return true; } diff --git a/dd-trace-core/src/main/java/datadog/trace/core/propagation/XRayHttpCodec.java b/dd-trace-core/src/main/java/datadog/trace/core/propagation/XRayHttpCodec.java index bce004eb054..7098bbaf361 100644 --- a/dd-trace-core/src/main/java/datadog/trace/core/propagation/XRayHttpCodec.java +++ b/dd-trace-core/src/main/java/datadog/trace/core/propagation/XRayHttpCodec.java @@ -151,6 +151,8 @@ public boolean accept(String key, String value) { log.debug("Header: {}", key); } try { + handleTags(key, value); + handleMappedBaggage(key, value); char first = Character.toLowerCase(key.charAt(0)); switch (first) { case 'x': @@ -174,18 +176,7 @@ public boolean accept(String key, String value) { default: } - if (handledIpHeaders(key, value)) { - return true; - } else { - handleTags(key, value); - } - - if (!baggageMapping.isEmpty()) { - String mappedKey = baggageMapping.get(toLowerCase(key)); - if (null != mappedKey) { - addBaggageItem(mappedKey, value); - } - } + handledIpHeaders(key, value); return true; } catch (RuntimeException e) { invalidateContext(); diff --git a/dd-trace-core/src/test/java/datadog/trace/core/propagation/ConsumedHeaderTagMappingTest.java b/dd-trace-core/src/test/java/datadog/trace/core/propagation/ConsumedHeaderTagMappingTest.java new file mode 100644 index 00000000000..ccaa756be71 --- /dev/null +++ b/dd-trace-core/src/test/java/datadog/trace/core/propagation/ConsumedHeaderTagMappingTest.java @@ -0,0 +1,242 @@ +package datadog.trace.core.propagation; + +import static datadog.trace.api.config.TracerConfig.PROPAGATION_EXTRACT_LOG_HEADER_NAMES_ENABLED; +import static datadog.trace.api.config.TracerConfig.TRACE_CLIENT_IP_HEADER; +import static datadog.trace.bootstrap.ActiveSubsystems.APPSEC_ACTIVE; +import static datadog.trace.bootstrap.instrumentation.api.ContextVisitors.stringValuesMap; +import static datadog.trace.core.propagation.HttpCodecTestHelper.headers; +import static java.util.Collections.emptyMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import datadog.trace.api.Config; +import datadog.trace.api.DynamicConfig; +import datadog.trace.api.TraceConfig; +import datadog.trace.bootstrap.instrumentation.api.TagContext; +import datadog.trace.test.junit.utils.config.WithConfig; +import datadog.trace.test.util.DDJavaSpecification; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Supplier; +import java.util.stream.Stream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * Verifies that a {@code DD_TRACE_REQUEST_HEADER_TAGS} mapping onto a header that the {@link + * ContextInterpreter} also consumes for another purpose (user-agent, forwarding, client-IP) is + * still honoured. This is the general form of the APMS-20654 bug, which was fixed for {@code + * X-Amzn-Trace-Id} in PR #12662: the {@code handled*} helpers short-circuit {@code accept()} with + * an early {@code return true} before {@code handleTags} runs, so the mapped tag is silently + * dropped. + * + *
The behaviour lives in {@link ContextInterpreter} and is duplicated across every propagation
+ * style, so each style is exercised. AppSec is enabled so that {@code collectIpHeaders} is true and
+ * the forwarding / client-IP helpers actually consume their headers.
+ */
+@WithConfig(key = PROPAGATION_EXTRACT_LOG_HEADER_NAMES_ENABLED, value = "true")
+class ConsumedHeaderTagMappingTest extends DDJavaSpecification {
+ private boolean origAppSecActive;
+ private HttpCodec.Extractor extractor;
+
+ @BeforeEach
+ void enableAppSec() {
+ this.origAppSecActive = APPSEC_ACTIVE;
+ APPSEC_ACTIVE = true;
+ }
+
+ @AfterEach
+ void restoreAppSec() {
+ if (this.extractor != null) {
+ this.extractor.cleanup();
+ }
+ APPSEC_ACTIVE = this.origAppSecActive;
+ }
+
+ static Stream