Skip to content

Commit 32b3326

Browse files
authored
Apache http client 4.3 low level instrumentation (#10253)
1 parent 4f0baff commit 32b3326

File tree

2 files changed

+24
-87
lines changed

2 files changed

+24
-87
lines changed

instrumentation/apache-httpclient/apache-httpclient-4.3/library/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/TracingProtocolExec.java

+10-81
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,22 @@
99
import io.opentelemetry.context.Scope;
1010
import io.opentelemetry.context.propagation.ContextPropagators;
1111
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
12+
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientRequestResendCount;
1213
import java.io.IOException;
13-
import javax.annotation.Nullable;
1414
import org.apache.http.HttpException;
1515
import org.apache.http.HttpHost;
1616
import org.apache.http.HttpResponse;
17-
import org.apache.http.ProtocolException;
18-
import org.apache.http.client.ClientProtocolException;
1917
import org.apache.http.client.methods.CloseableHttpResponse;
2018
import org.apache.http.client.methods.HttpExecutionAware;
2119
import org.apache.http.client.methods.HttpRequestWrapper;
2220
import org.apache.http.client.protocol.HttpClientContext;
2321
import org.apache.http.conn.routing.HttpRoute;
24-
import org.apache.http.impl.client.DefaultRedirectStrategy;
25-
import org.apache.http.impl.client.RedirectLocations;
2622
import org.apache.http.impl.execchain.ClientExecChain;
2723

2824
final class TracingProtocolExec implements ClientExecChain {
2925

30-
private static final String REQUEST_CONTEXT_ATTRIBUTE_ID =
26+
private static final String REQUEST_PARENT_CONTEXT_ATTRIBUTE_ID =
3127
TracingProtocolExec.class.getName() + ".context";
32-
private static final String REQUEST_WRAPPER_ATTRIBUTE_ID =
33-
TracingProtocolExec.class.getName() + ".requestWrapper";
34-
private static final String REDIRECT_COUNT_ATTRIBUTE_ID =
35-
TracingProtocolExec.class.getName() + ".redirectCount";
3628

3729
private final Instrumenter<ApacheHttpClientRequest, HttpResponse> instrumenter;
3830
private final ContextPropagators propagators;
@@ -54,14 +46,12 @@ public CloseableHttpResponse execute(
5446
HttpClientContext httpContext,
5547
HttpExecutionAware httpExecutionAware)
5648
throws IOException, HttpException {
57-
Context context = httpContext.getAttribute(REQUEST_CONTEXT_ATTRIBUTE_ID, Context.class);
58-
if (context != null) {
59-
ApacheHttpClientRequest instrumenterRequest =
60-
httpContext.getAttribute(REQUEST_WRAPPER_ATTRIBUTE_ID, ApacheHttpClientRequest.class);
61-
// Request already had a context so a redirect. Don't create a new span just inject and
62-
// execute.
63-
propagators.getTextMapPropagator().inject(context, request, HttpHeaderSetter.INSTANCE);
64-
return execute(route, request, instrumenterRequest, httpContext, httpExecutionAware, context);
49+
50+
Context parentContext =
51+
httpContext.getAttribute(REQUEST_PARENT_CONTEXT_ATTRIBUTE_ID, Context.class);
52+
if (parentContext == null) {
53+
parentContext = HttpClientRequestResendCount.initialize(Context.current());
54+
httpContext.setAttribute(REQUEST_PARENT_CONTEXT_ATTRIBUTE_ID, parentContext);
6555
}
6656

6757
HttpHost host = null;
@@ -81,16 +71,11 @@ public CloseableHttpResponse execute(
8171
}
8272
ApacheHttpClientRequest instrumenterRequest = new ApacheHttpClientRequest(host, request);
8373

84-
Context parentContext = Context.current();
8574
if (!instrumenter.shouldStart(parentContext, instrumenterRequest)) {
8675
return exec.execute(route, request, httpContext, httpExecutionAware);
8776
}
8877

89-
context = instrumenter.start(parentContext, instrumenterRequest);
90-
httpContext.setAttribute(REQUEST_CONTEXT_ATTRIBUTE_ID, context);
91-
httpContext.setAttribute(REQUEST_WRAPPER_ATTRIBUTE_ID, instrumenterRequest);
92-
httpContext.setAttribute(REDIRECT_COUNT_ATTRIBUTE_ID, 0);
93-
78+
Context context = instrumenter.start(parentContext, instrumenterRequest);
9479
propagators.getTextMapPropagator().inject(context, request, HttpHeaderSetter.INSTANCE);
9580

9681
return execute(route, request, instrumenterRequest, httpContext, httpExecutionAware, context);
@@ -113,63 +98,7 @@ private CloseableHttpResponse execute(
11398
error = e;
11499
throw e;
115100
} finally {
116-
if (!pendingRedirect(context, httpContext, request, instrumenterRequest, response)) {
117-
instrumenter.end(context, instrumenterRequest, response, error);
118-
}
119-
}
120-
}
121-
122-
private boolean pendingRedirect(
123-
Context context,
124-
HttpClientContext httpContext,
125-
HttpRequestWrapper request,
126-
ApacheHttpClientRequest instrumenterRequest,
127-
@Nullable CloseableHttpResponse response) {
128-
if (response == null) {
129-
return false;
130-
}
131-
if (!httpContext.getRequestConfig().isRedirectsEnabled()) {
132-
return false;
101+
instrumenter.end(context, instrumenterRequest, response, error);
133102
}
134-
135-
// TODO(anuraaga): Support redirect strategies other than the default. There is no way to get
136-
// the user defined redirect strategy without some tricks, but it's very rare to override
137-
// the strategy, usually it is either on or off as checked above. We can add support for this
138-
// later if needed.
139-
try {
140-
if (!DefaultRedirectStrategy.INSTANCE.isRedirected(request, response, httpContext)) {
141-
return false;
142-
}
143-
} catch (ProtocolException e) {
144-
// DefaultRedirectStrategy.isRedirected cannot throw this so just return a default.
145-
return false;
146-
}
147-
148-
// Very hacky and a bit slow, but the only way to determine whether the client will fail with
149-
// a circular redirect, which happens before exec decorators run.
150-
RedirectLocations redirectLocations =
151-
(RedirectLocations) httpContext.getAttribute(HttpClientContext.REDIRECT_LOCATIONS);
152-
if (redirectLocations != null) {
153-
RedirectLocations copy = new RedirectLocations();
154-
copy.addAll(redirectLocations);
155-
156-
try {
157-
DefaultRedirectStrategy.INSTANCE.getLocationURI(request, response, httpContext);
158-
} catch (ProtocolException e) {
159-
// We will not be returning to the Exec, finish the span.
160-
instrumenter.end(context, instrumenterRequest, response, new ClientProtocolException(e));
161-
return true;
162-
} finally {
163-
httpContext.setAttribute(HttpClientContext.REDIRECT_LOCATIONS, copy);
164-
}
165-
}
166-
167-
int redirectCount = httpContext.getAttribute(REDIRECT_COUNT_ATTRIBUTE_ID, Integer.class);
168-
if (++redirectCount > httpContext.getRequestConfig().getMaxRedirects()) {
169-
return false;
170-
}
171-
172-
httpContext.setAttribute(REDIRECT_COUNT_ATTRIBUTE_ID, redirectCount);
173-
return true;
174103
}
175104
}

instrumentation/apache-httpclient/apache-httpclient-4.3/testing/src/main/java/io/opentelemetry/instrumentation/apachehttpclient/v4_3/AbstractApacheHttpClientTest.java

+14-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
99
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest;
1010
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult;
11+
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions;
1112
import java.io.IOException;
1213
import java.io.UncheckedIOException;
1314
import java.net.URI;
@@ -54,8 +55,15 @@ CloseableHttpClient getClient(URI uri) {
5455
return client;
5556
}
5657

58+
abstract static class ApacheHttpClientTest<T> extends AbstractHttpClientTest<T> {
59+
@Override
60+
protected void configure(HttpClientTestOptions.Builder optionsBuilder) {
61+
optionsBuilder.markAsLowLevelInstrumentation();
62+
}
63+
}
64+
5765
@Nested
58-
class ApacheClientHostRequestTest extends AbstractHttpClientTest<BasicHttpRequest> {
66+
class ApacheClientHostRequestTest extends ApacheHttpClientTest<BasicHttpRequest> {
5967

6068
@Override
6169
public BasicHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
@@ -92,7 +100,7 @@ public void sendRequestWithCallback(
92100
}
93101

94102
@Nested
95-
class ApacheClientHostRequestContextTest extends AbstractHttpClientTest<BasicHttpRequest> {
103+
class ApacheClientHostRequestContextTest extends ApacheHttpClientTest<BasicHttpRequest> {
96104

97105
@Override
98106
public BasicHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
@@ -133,7 +141,7 @@ public void sendRequestWithCallback(
133141
}
134142

135143
@Nested
136-
class ApacheClientHostAbsoluteUriRequestTest extends AbstractHttpClientTest<BasicHttpRequest> {
144+
class ApacheClientHostAbsoluteUriRequestTest extends ApacheHttpClientTest<BasicHttpRequest> {
137145

138146
@Override
139147
public BasicHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
@@ -170,7 +178,7 @@ public void sendRequestWithCallback(
170178

171179
@Nested
172180
class ApacheClientHostAbsoluteUriRequestContextTest
173-
extends AbstractHttpClientTest<BasicHttpRequest> {
181+
extends ApacheHttpClientTest<BasicHttpRequest> {
174182

175183
@Override
176184
public BasicHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
@@ -210,7 +218,7 @@ public void sendRequestWithCallback(
210218
}
211219

212220
@Nested
213-
class ApacheClientUriRequestTest extends AbstractHttpClientTest<HttpUriRequest> {
221+
class ApacheClientUriRequestTest extends ApacheHttpClientTest<HttpUriRequest> {
214222

215223
@Override
216224
public HttpUriRequest buildRequest(String method, URI uri, Map<String, String> headers) {
@@ -241,7 +249,7 @@ public void sendRequestWithCallback(
241249
}
242250

243251
@Nested
244-
class ApacheClientUriRequestContextTest extends AbstractHttpClientTest<HttpUriRequest> {
252+
class ApacheClientUriRequestContextTest extends ApacheHttpClientTest<HttpUriRequest> {
245253

246254
@Override
247255
public HttpUriRequest buildRequest(String method, URI uri, Map<String, String> headers) {

0 commit comments

Comments
 (0)