Skip to content

Commit 2cf870d

Browse files
amarzialimcculls
andauthored
Apache http client 4: do not copy all request headers on redirect (#7483)
* Apache httpclient: do not copy all request headers on redirect * Break the propagation if we cannot safely copy * Update internal-api/src/main/java/datadog/trace/util/PropagationUtils.java Co-authored-by: Stuart McCulloch <[email protected]> * review * document * add propagationutils to jacoco excludes --------- Co-authored-by: Stuart McCulloch <[email protected]>
1 parent 44f9079 commit 2cf870d

File tree

5 files changed

+58
-31
lines changed

5 files changed

+58
-31
lines changed

dd-java-agent/agent-otel/otel-shim/src/main/java/datadog/opentelemetry/shim/context/propagation/AgentTextMapPropagator.java

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -11,46 +11,24 @@
1111
import datadog.trace.bootstrap.instrumentation.api.AgentSpan.Context.Extracted;
1212
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
1313
import datadog.trace.bootstrap.instrumentation.api.TagContext;
14+
import datadog.trace.util.PropagationUtils;
1415
import io.opentelemetry.api.trace.Span;
1516
import io.opentelemetry.api.trace.SpanContext;
1617
import io.opentelemetry.api.trace.TraceState;
1718
import io.opentelemetry.context.Context;
1819
import io.opentelemetry.context.propagation.TextMapGetter;
1920
import io.opentelemetry.context.propagation.TextMapPropagator;
2021
import io.opentelemetry.context.propagation.TextMapSetter;
21-
import java.util.Arrays;
2222
import java.util.Collection;
2323
import javax.annotation.Nullable;
2424
import javax.annotation.ParametersAreNonnullByDefault;
2525

2626
@ParametersAreNonnullByDefault
2727
public class AgentTextMapPropagator implements TextMapPropagator {
28-
private final Collection<String> fields;
29-
30-
public AgentTextMapPropagator() {
31-
// Cannot suppose the current injector from AgentTracer so declare them all
32-
this.fields =
33-
Arrays.asList(
34-
// W3C headers
35-
"traceparent",
36-
"tracestate",
37-
// DD headers
38-
"x-datadog-trace-id",
39-
"x-datadog-parent-id",
40-
"x-datadog-sampling-priority",
41-
"x-datadog-origin",
42-
"x-datadog-tags",
43-
// B3 single headers
44-
"X-B3-TraceId",
45-
"X-B3-SpanId",
46-
"X-B3-Sampled",
47-
// B3 multi header
48-
"b3");
49-
}
5028

5129
@Override
5230
public Collection<String> fields() {
53-
return this.fields;
31+
return PropagationUtils.KNOWN_PROPAGATION_HEADERS;
5432
}
5533

5634
@Override

dd-java-agent/instrumentation/apache-httpasyncclient-4/src/main/java/datadog/trace/instrumentation/apachehttpasyncclient/ApacheHttpClientRedirectInstrumentation.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
import com.google.auto.service.AutoService;
99
import datadog.trace.agent.tooling.Instrumenter;
1010
import datadog.trace.agent.tooling.InstrumenterModule;
11+
import datadog.trace.util.PropagationUtils;
1112
import java.util.Locale;
1213
import net.bytebuddy.asm.Advice;
1314
import net.bytebuddy.description.type.TypeDescription;
1415
import net.bytebuddy.implementation.bytecode.assign.Assigner;
1516
import net.bytebuddy.matcher.ElementMatcher;
1617
import org.apache.http.Header;
1718
import org.apache.http.HttpRequest;
19+
import org.apache.http.client.methods.HttpRequestWrapper;
20+
import org.apache.http.protocol.HttpContext;
1821

1922
/**
2023
* Early versions don't copy headers over on redirect. This instrumentation copies our headers over
@@ -26,7 +29,7 @@ public class ApacheHttpClientRedirectInstrumentation extends InstrumenterModule.
2629
implements Instrumenter.ForTypeHierarchy {
2730

2831
public ApacheHttpClientRedirectInstrumentation() {
29-
super("httpasyncclient", "apache-httpasyncclient");
32+
super("httpasyncclient", "apache-httpasyncclient", "httpclient-redirect");
3033
}
3134

3235
@Override
@@ -44,18 +47,24 @@ public void methodAdvice(MethodTransformer transformer) {
4447
transformer.applyAdvice(
4548
isMethod()
4649
.and(named("getRedirect"))
47-
.and(takesArgument(0, named("org.apache.http.HttpRequest"))),
50+
.and(takesArgument(2, named("org.apache.http.protocol.HttpContext"))),
4851
ApacheHttpClientRedirectInstrumentation.class.getName() + "$ClientRedirectAdvice");
4952
}
5053

5154
public static class ClientRedirectAdvice {
5255
@Advice.OnMethodExit(suppress = Throwable.class)
5356
private static void onAfterExecute(
54-
@Advice.Argument(value = 0) final HttpRequest original,
57+
@Advice.Argument(value = 2) final HttpContext context,
5558
@Advice.Return(typing = Assigner.Typing.DYNAMIC) final HttpRequest redirect) {
5659
if (redirect == null) {
5760
return;
5861
}
62+
Object originalRequest = context.getAttribute("http.request");
63+
if (!(originalRequest instanceof HttpRequest)) {
64+
return;
65+
}
66+
HttpRequest original = (HttpRequest) originalRequest;
67+
5968
// Apache HttpClient 4.0.1+ copies headers from original to redirect only
6069
// if redirect headers are empty. Because we add headers
6170
// "x-datadog-" and "x-b3-" to redirect: it means redirect headers never
@@ -65,11 +74,17 @@ private static void onAfterExecute(
6574
if (!redirect.headerIterator().hasNext()) {
6675
// redirect didn't have other headers besides tracing, so we need to do copy
6776
// (same work as Apache HttpClient 4.0.1+ does w/o instrumentation)
68-
redirect.setHeaders(original.getAllHeaders());
77+
if (original instanceof HttpRequestWrapper) {
78+
// We should use the initial request because the wrapped one might contain more headers
79+
// (i.e. Host) we do not want to copy
80+
// if we cannot access the original request we cannot safely copy.
81+
// At this point we break the propagation not to corrupt the customer request
82+
redirect.setHeaders(((HttpRequestWrapper) original).getOriginal().getAllHeaders());
83+
}
6984
} else {
7085
for (final Header header : original.getAllHeaders()) {
71-
final String name = header.getName().toLowerCase(Locale.ROOT);
72-
if (name.startsWith("x-datadog-") || name.startsWith("x-b3-")) {
86+
if (PropagationUtils.KNOWN_PROPAGATION_HEADERS.contains(
87+
header.getName().toLowerCase(Locale.ROOT))) {
7388
if (!redirect.containsHeader(header.getName())) {
7489
redirect.setHeader(header.getName(), header.getValue());
7590
}

dd-java-agent/instrumentation/apache-httpclient-4/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ dependencies {
3434
compileOnly group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.0'
3535
testImplementation(testFixtures(project(':dd-java-agent:agent-iast')))
3636
testImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.0'
37-
37+
testImplementation(project(':dd-java-agent:instrumentation:apache-httpasyncclient-4'))
3838
// to instrument the integration test
3939
iastIntegrationTestImplementation(testFixtures(project(':dd-java-agent:agent-iast')))
4040
iastIntegrationTestImplementation group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.0'

internal-api/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ excludedClassesCoverage += [
140140
"datadog.trace.util.PidHelper",
141141
"datadog.trace.util.PidHelper.Fallback",
142142
"datadog.trace.util.ProcessUtils",
143+
"datadog.trace.util.PropagationUtils",
143144
"datadog.trace.util.UnsafeUtils",
144145
"datadog.trace.api.ConfigCollector",
145146
"datadog.trace.api.Config.HostNameHolder",
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package datadog.trace.util;
2+
3+
import java.util.Arrays;
4+
import java.util.Collection;
5+
import java.util.Collections;
6+
import java.util.LinkedHashSet;
7+
8+
public class PropagationUtils {
9+
private PropagationUtils() {
10+
// avoid constructing instances of this class.
11+
}
12+
13+
/** The list of the known propagation headers. They must be lowercased. */
14+
public static final Collection<String> KNOWN_PROPAGATION_HEADERS =
15+
Collections.unmodifiableCollection(
16+
new LinkedHashSet<>(
17+
Arrays.asList(
18+
// W3C headers
19+
"traceparent",
20+
"tracestate",
21+
// DD headers
22+
"x-datadog-trace-id",
23+
"x-datadog-parent-id",
24+
"x-datadog-sampling-priority",
25+
"x-datadog-origin",
26+
"x-datadog-tags",
27+
// B3 single headers
28+
"x-b3-traceid",
29+
"x-b3-spanid",
30+
"x-b3-sampled",
31+
// B3 multi header
32+
"b3")));
33+
}

0 commit comments

Comments
 (0)