From 9da6f7d98cc6c21f1853d0a44e77908fe81ff9b8 Mon Sep 17 00:00:00 2001 From: Jordan Wong Date: Tue, 23 Jun 2026 15:18:23 -0400 Subject: [PATCH 1/8] feat(commons-httpclient-2.0): toolkit-generated instrumentation [DO NOT MERGE] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Toolkit-generated regeneration of commons-httpclient-2.0 instrumentation, placed side-by-side with the existing master module via the -generated suffix convention. Supersedes #10941 (PerfectSlayer's duplicate-module review). Run details: - Toolkit branch: eval/java @ 757979b9+ - Workflow: new_integration (default) - Maven coordinates: commons-httpclient:commons-httpclient:2.0.2 - Cost: $31.91 - Duration: 119 min - Reviewer verdict: approved=True, todos_fixed=0, todos_remaining=0 Generated module: dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/ - CommonsHttpClientInstrumentation (HttpMethodBase.execute) - CommonsHttpClientDecorator (HttpClientDecorator) - HttpHeadersInjectAdapter (header injection) - HelperMethods - Java tests (per R20, no Groovy) ## Research integrity disclosure Hand-added by human operator AFTER toolkit run (toolkit did not produce these): - 6 entries in metadata/supported-configurations.json: DD_TRACE_COMMONS_HTTPCLIENT_ENABLED, DD_TRACE_COMMONS_HTTPCLIENT_2_0_ENABLED, and their ANALYTICS_ENABLED + ANALYTICS_SAMPLE_RATE pairs (with type 'decimal' per R29). - 1 line in settings.gradle.kts to register the new module. Research signal — integration name divergence: master's deleted module used super('commons-http-client') (with dashes); toolkit-generated module uses super('commons-httpclient', 'commons-httpclient-2.0'). The R29 rule encoded earlier today doesn't yet require matching existing master conventions when regenerating; that's a research gap to encode. Full caveats: docs/eval-research/generated/commons-httpclient-20260623/caveats.md Co-Authored-By: Claude Sonnet 4.6 --- .../build.gradle | 30 + .../CommonsHttpClientDecorator.java | 73 +++ .../CommonsHttpClientInstrumentation.java | 67 ++ .../commonshttpclient/HelperMethods.java | 55 ++ .../HttpHeadersInjectAdapter.java | 16 + .../CommonsHttpClientTest.java | 615 ++++++++++++++++++ metadata/supported-configurations.json | 48 ++ settings.gradle.kts | 1 + 8 files changed, 905 insertions(+) create mode 100644 dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/build.gradle create mode 100644 dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java create mode 100644 dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java create mode 100644 dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/HelperMethods.java create mode 100644 dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java create mode 100644 dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/build.gradle b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/build.gradle new file mode 100644 index 00000000000..cd4e9d50c11 --- /dev/null +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/build.gradle @@ -0,0 +1,30 @@ +muzzle { + pass { + group = "commons-httpclient" + module = "commons-httpclient" + versions = "[2.0,4.0)" + skipVersions += '3.1-jenkins-1' + skipVersions += '2.0-final' + } + fail { + group = "commons-httpclient" + module = "commons-httpclient" + versions = "[,2.0)" + } +} + +apply from: "$rootDir/gradle/java.gradle" + +addTestSuiteForDir('latestDepTest', 'test') + +dependencies { + compileOnly group: 'commons-httpclient', name: 'commons-httpclient', version: '2.0' + + testImplementation group: 'commons-httpclient', name: 'commons-httpclient', version: '2.0' + + // latestDepTest tests against the highest 2.x release (module instruments 2.x only) + latestDepTestImplementation group: 'commons-httpclient', name: 'commons-httpclient', version: '2+' + + testRuntimeOnly(project(':dd-java-agent:instrumentation:datadog:asm:iast-instrumenter')) + testRuntimeOnly(project(':dd-java-agent:instrumentation:java:java-net:java-net-1.8')) +} diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java new file mode 100644 index 00000000000..619957febaf --- /dev/null +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java @@ -0,0 +1,73 @@ +package datadog.trace.instrumentation.commonshttpclient; + +import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; +import datadog.trace.bootstrap.instrumentation.decorator.HttpClientDecorator; +import java.net.URI; +import java.net.URISyntaxException; +import org.apache.commons.httpclient.Header; +import org.apache.commons.httpclient.HttpMethod; + +public class CommonsHttpClientDecorator extends HttpClientDecorator { + + public static final CharSequence COMMONS_HTTP_CLIENT = + UTF8BytesString.create("commons-httpclient"); + public static final CommonsHttpClientDecorator DECORATE = new CommonsHttpClientDecorator(); + + public static final CharSequence HTTP_REQUEST = UTF8BytesString.create(DECORATE.operationName()); + + @Override + protected String[] instrumentationNames() { + return new String[] {"commons-httpclient", "commons-httpclient-2.0"}; + } + + @Override + protected CharSequence component() { + return COMMONS_HTTP_CLIENT; + } + + @Override + protected String method(final HttpMethod httpMethod) { + return httpMethod.getName(); + } + + @Override + protected URI url(final HttpMethod httpMethod) throws URISyntaxException { + try { + org.apache.commons.httpclient.URI uri = httpMethod.getURI(); + if (uri != null) { + return new URI(uri.toString()); + } + } catch (Exception e) { + // getURI() can throw URIException; fall through to return null + } + return null; + } + + @Override + protected int status(final HttpMethod httpMethod) { + try { + return httpMethod.getStatusCode(); + } catch (NullPointerException e) { + // getStatusCode() throws NPE when statusLine is null (e.g. connection failure) + return 0; + } + } + + @Override + protected String getRequestHeader(HttpMethod httpMethod, String headerName) { + Header header = httpMethod.getRequestHeader(headerName); + if (header != null) { + return header.getValue(); + } + return null; + } + + @Override + protected String getResponseHeader(HttpMethod httpMethod, String headerName) { + Header header = httpMethod.getResponseHeader(headerName); + if (header != null) { + return header.getValue(); + } + return null; + } +} diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java new file mode 100644 index 00000000000..76e8d483314 --- /dev/null +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java @@ -0,0 +1,67 @@ +package datadog.trace.instrumentation.commonshttpclient; + +import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import net.bytebuddy.asm.Advice; +import org.apache.commons.httpclient.HttpMethod; + +@AutoService(InstrumenterModule.class) +public class CommonsHttpClientInstrumentation extends InstrumenterModule.Tracing + implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { + + public CommonsHttpClientInstrumentation() { + super("commons-httpclient", "commons-httpclient-2.0"); + } + + @Override + public String instrumentedType() { + return "org.apache.commons.httpclient.HttpClient"; + } + + @Override + public String[] helperClassNames() { + return new String[] { + packageName + ".CommonsHttpClientDecorator", + packageName + ".HttpHeadersInjectAdapter", + packageName + ".HelperMethods", + }; + } + + @Override + public void methodAdvice(MethodTransformer transformer) { + // All executeMethod overloads delegate to the 3-arg version: + // executeMethod(HostConfiguration, HttpMethod, HttpState) + // Instrumenting only the delegate avoids duplicate spans. + transformer.applyAdvice( + isMethod() + .and(named("executeMethod")) + .and(takesArguments(3)) + .and(takesArgument(0, named("org.apache.commons.httpclient.HostConfiguration"))) + .and(takesArgument(1, named("org.apache.commons.httpclient.HttpMethod"))) + .and(takesArgument(2, named("org.apache.commons.httpclient.HttpState"))), + CommonsHttpClientInstrumentation.class.getName() + "$ExecuteMethodAdvice"); + } + + public static class ExecuteMethodAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static AgentScope methodEnter(@Advice.Argument(1) final HttpMethod method) { + return HelperMethods.doMethodEnter(method); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void methodExit( + @Advice.Enter final AgentScope scope, + @Advice.Argument(1) final HttpMethod method, + @Advice.Thrown final Throwable throwable) { + HelperMethods.doMethodExit(scope, method, throwable); + } + } +} diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/HelperMethods.java b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/HelperMethods.java new file mode 100644 index 00000000000..2d1a9080c15 --- /dev/null +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/HelperMethods.java @@ -0,0 +1,55 @@ +package datadog.trace.instrumentation.commonshttpclient; + +import static datadog.context.Context.current; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; +import static datadog.trace.instrumentation.commonshttpclient.CommonsHttpClientDecorator.DECORATE; +import static datadog.trace.instrumentation.commonshttpclient.CommonsHttpClientDecorator.HTTP_REQUEST; +import static datadog.trace.instrumentation.commonshttpclient.HttpHeadersInjectAdapter.SETTER; + +import datadog.trace.bootstrap.CallDepthThreadLocalMap; +import datadog.trace.bootstrap.instrumentation.api.AgentScope; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.HttpMethod; + +public class HelperMethods { + + public static AgentScope doMethodEnter(final HttpMethod method) { + final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(HttpClient.class); + if (callDepth > 0) { + return null; + } + + final AgentSpan span = + startSpan(CommonsHttpClientDecorator.COMMONS_HTTP_CLIENT.toString(), HTTP_REQUEST); + DECORATE.afterStart(span); + DECORATE.onRequest(span, method); + + final AgentScope scope = activateSpan(span); + + DECORATE.injectContext(current(), method, SETTER); + + return scope; + } + + public static void doMethodExit( + final AgentScope scope, final HttpMethod method, final Throwable throwable) { + if (scope == null) { + return; + } + final AgentSpan span = scope.span(); + try { + if (throwable != null) { + DECORATE.onError(span, throwable); + } else { + DECORATE.onResponse(span, method); + } + DECORATE.beforeFinish(span); + } finally { + scope.close(); + span.finish(); + CallDepthThreadLocalMap.reset(HttpClient.class); + } + } +} diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java new file mode 100644 index 00000000000..88297d95d3c --- /dev/null +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java @@ -0,0 +1,16 @@ +package datadog.trace.instrumentation.commonshttpclient; + +import datadog.context.propagation.CarrierSetter; +import javax.annotation.ParametersAreNonnullByDefault; +import org.apache.commons.httpclient.HttpMethod; + +@ParametersAreNonnullByDefault +public class HttpHeadersInjectAdapter implements CarrierSetter { + + public static final HttpHeadersInjectAdapter SETTER = new HttpHeadersInjectAdapter(); + + @Override + public void set(final HttpMethod carrier, final String key, final String value) { + carrier.setRequestHeader(key, value); + } +} diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java new file mode 100644 index 00000000000..83759fd0ffb --- /dev/null +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java @@ -0,0 +1,615 @@ +package datadog.trace.instrumentation.commonshttpclient; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import datadog.trace.agent.test.AbstractInstrumentationTest; +import datadog.trace.core.DDSpan; +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.apache.commons.httpclient.HostConfiguration; +import org.apache.commons.httpclient.HttpClient; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.HeadMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.PutMethod; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +/** + * Tests for commons-httpclient instrumentation. + * + *

Verifies that {@code HttpClient.executeMethod()} creates spans with correct operation name, + * span kind, HTTP method, URL, and status code tags for various HTTP operations. + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public class CommonsHttpClientTest extends AbstractInstrumentationTest { + + private static final int SERVER_PORT = 19876; + private static final String BASE_URL = "http://localhost:" + SERVER_PORT; + + private HttpServer server; + private HttpClient client; + + /** Headers captured from the most recent request to /propagation endpoint. */ + private final Map capturedHeaders = new ConcurrentHashMap<>(); + + @BeforeAll + void setupServer() throws IOException { + server = HttpServer.create(new InetSocketAddress(SERVER_PORT), 0); + + server.createContext( + "/success", + new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + byte[] response = "{\"status\":\"ok\"}".getBytes("UTF-8"); + exchange.getResponseHeaders().set("Content-Type", "application/json"); + exchange.sendResponseHeaders(200, response.length); + OutputStream os = exchange.getResponseBody(); + os.write(response); + os.close(); + } + }); + + server.createContext( + "/echo", + new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + byte[] response = "{\"echo\":\"ok\"}".getBytes("UTF-8"); + exchange.getResponseHeaders().set("Content-Type", "application/json"); + exchange.sendResponseHeaders(200, response.length); + OutputStream os = exchange.getResponseBody(); + os.write(response); + os.close(); + } + }); + + server.createContext( + "/not-found", + new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + byte[] response = "{\"error\":\"not found\"}".getBytes("UTF-8"); + exchange.sendResponseHeaders(404, response.length); + OutputStream os = exchange.getResponseBody(); + os.write(response); + os.close(); + } + }); + + server.createContext( + "/error", + new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + byte[] response = "{\"error\":\"internal server error\"}".getBytes("UTF-8"); + exchange.sendResponseHeaders(500, response.length); + OutputStream os = exchange.getResponseBody(); + os.write(response); + os.close(); + } + }); + + // Endpoint that captures all request headers for context propagation verification + server.createContext( + "/propagation", + new HttpHandler() { + @Override + public void handle(HttpExchange exchange) throws IOException { + capturedHeaders.clear(); + for (Map.Entry> entry : exchange.getRequestHeaders().entrySet()) { + // HttpServer normalizes header names to Title-Case; store lowercase for easy lookup + capturedHeaders.put(entry.getKey().toLowerCase(), entry.getValue().get(0)); + } + byte[] response = "ok".getBytes("UTF-8"); + exchange.sendResponseHeaders(200, response.length); + OutputStream os = exchange.getResponseBody(); + os.write(response); + os.close(); + } + }); + + server.setExecutor(null); + server.start(); + + client = new HttpClient(); + } + + @AfterAll + void tearDownServer() { + if (server != null) { + server.stop(0); + } + } + + @Test + void getRequestCreatesSpanWithCorrectTags() throws Exception { + GetMethod get = new GetMethod(BASE_URL + "/success"); + try { + int statusCode = client.executeMethod(get); + assertEquals(200, statusCode); + } finally { + get.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span"); + assertHttpClientSpan(span, "GET", "/success", 200, false); + } + + @Test + void postRequestCreatesSpanWithCorrectMethod() throws Exception { + PostMethod post = new PostMethod(BASE_URL + "/echo"); + post.setRequestBody("{\"key\":\"value\"}"); + try { + int statusCode = client.executeMethod(post); + assertEquals(200, statusCode); + } finally { + post.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span"); + assertHttpClientSpan(span, "POST", "/echo", 200, false); + } + + @Test + void putRequestCreatesSpanWithCorrectMethod() throws Exception { + PutMethod put = new PutMethod(BASE_URL + "/echo"); + put.setRequestBody("{\"updated\":true}"); + try { + int statusCode = client.executeMethod(put); + assertEquals(200, statusCode); + } finally { + put.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span"); + assertHttpClientSpan(span, "PUT", "/echo", 200, false); + } + + @Test + void headRequestCreatesSpan() throws Exception { + HeadMethod head = new HeadMethod(BASE_URL + "/success"); + try { + int statusCode = client.executeMethod(head); + assertEquals(200, statusCode); + } finally { + head.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span"); + assertHttpClientSpan(span, "HEAD", "/success", 200, false); + } + + @Test + void requestWith404SetsStatusCodeTag() throws Exception { + GetMethod get = new GetMethod(BASE_URL + "/not-found"); + try { + int statusCode = client.executeMethod(get); + assertEquals(404, statusCode); + } finally { + get.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span"); + // 4xx statuses are client errors and flagged as span errors by default + assertHttpClientSpan(span, "GET", "/not-found", 404, true); + } + + @Test + void requestWith500SetsErrorStatus() throws Exception { + GetMethod get = new GetMethod(BASE_URL + "/error"); + try { + int statusCode = client.executeMethod(get); + assertEquals(500, statusCode); + } finally { + get.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span"); + // 5xx statuses are server errors and not flagged as client span errors by default + assertHttpClientSpan(span, "GET", "/error", 500, false); + } + + @Test + void connectionExceptionSetsErrorTags() throws Exception { + GetMethod get = new GetMethod("http://localhost:1/nonexistent"); + try { + assertThrows( + IOException.class, + () -> { + client.executeMethod(get); + }); + } finally { + get.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span even on connection error"); + assertTrue(span.isError(), "Span should be marked as error on exception"); + assertNotNull(span.getTag("error.type"), "Expected error.type tag"); + assertNotNull(span.getTag("error.message"), "Expected error.message tag"); + } + + @Test + void executeMethodWithHostConfigCreatesSpan() throws Exception { + HostConfiguration hostConfig = new HostConfiguration(); + hostConfig.setHost("localhost", SERVER_PORT, "http"); + GetMethod get = new GetMethod("/success"); + try { + int statusCode = client.executeMethod(hostConfig, get); + assertEquals(200, statusCode); + } finally { + get.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span with HostConfiguration overload"); + assertHttpClientSpan(span, "GET", "/success", 200, false); + } + + @Test + void multipleRequestsCreateSeparateSpans() throws Exception { + GetMethod get1 = new GetMethod(BASE_URL + "/success"); + GetMethod get2 = new GetMethod(BASE_URL + "/success"); + try { + client.executeMethod(get1); + } finally { + get1.releaseConnection(); + } + try { + client.executeMethod(get2); + } finally { + get2.releaseConnection(); + } + + writer.waitForTraces(2); + List spans = flattenTraces(); + + long httpRequestCount = + spans.stream().filter(s -> "http.request".equals(s.getOperationName().toString())).count(); + assertEquals(2, httpRequestCount, "Expected two separate http.request spans"); + } + + @Test + void contextPropagationInjectsDatadogTraceHeaders() throws Exception { + GetMethod get = new GetMethod(BASE_URL + "/propagation"); + try { + int statusCode = client.executeMethod(get); + assertEquals(200, statusCode); + } finally { + get.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span"); + + // Verify Datadog propagation headers were injected into the outgoing request + assertNotNull( + capturedHeaders.get("x-datadog-trace-id"), + "Expected x-datadog-trace-id header to be injected"); + assertNotNull( + capturedHeaders.get("x-datadog-parent-id"), + "Expected x-datadog-parent-id header to be injected"); + assertNotNull( + capturedHeaders.get("x-datadog-sampling-priority"), + "Expected x-datadog-sampling-priority header to be injected"); + + // Verify the injected trace ID matches the span's trace ID + long expectedTraceId = span.getTraceId().toLong(); + assertEquals( + String.valueOf(expectedTraceId), + capturedHeaders.get("x-datadog-trace-id"), + "Injected trace ID must match the span's trace ID"); + + // Verify the injected parent ID matches the span's span ID + long expectedSpanId = span.getSpanId(); + assertEquals( + String.valueOf(expectedSpanId), + capturedHeaders.get("x-datadog-parent-id"), + "Injected parent ID must match the span's span ID"); + } + + @Test + void contextPropagationInjectsW3CTraceparentHeader() throws Exception { + GetMethod get = new GetMethod(BASE_URL + "/propagation"); + try { + int statusCode = client.executeMethod(get); + assertEquals(200, statusCode); + } finally { + get.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span"); + + // Default propagation style includes W3C traceparent + String traceparent = capturedHeaders.get("traceparent"); + assertNotNull(traceparent, "Expected traceparent header to be injected"); + + // traceparent format: {version}-{trace-id}-{parent-id}-{trace-flags} + String[] parts = traceparent.split("-"); + assertEquals(4, parts.length, "traceparent must have 4 dash-separated parts"); + assertEquals("00", parts[0], "traceparent version must be 00"); + // trace-id is 32 hex chars, parent-id is 16 hex chars + assertEquals(32, parts[1].length(), "traceparent trace-id must be 32 hex chars"); + assertEquals(16, parts[2].length(), "traceparent parent-id must be 16 hex chars"); + } + + @Test + void contextPropagationWithPostRequest() throws Exception { + PostMethod post = new PostMethod(BASE_URL + "/propagation"); + post.setRequestBody("{\"data\":\"test\"}"); + try { + int statusCode = client.executeMethod(post); + assertEquals(200, statusCode); + } finally { + post.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span"); + assertEquals("POST", String.valueOf(span.getTag("http.method"))); + assertEquals(200, span.getTag("http.status_code")); + + // Verify trace headers are injected for POST requests too + assertNotNull( + capturedHeaders.get("x-datadog-trace-id"), + "Expected x-datadog-trace-id header on POST request"); + assertNotNull( + capturedHeaders.get("x-datadog-parent-id"), + "Expected x-datadog-parent-id header on POST request"); + + // Verify IDs match the span + assertEquals( + String.valueOf(span.getTraceId().toLong()), + capturedHeaders.get("x-datadog-trace-id"), + "POST request trace ID must match span"); + assertEquals( + String.valueOf(span.getSpanId()), + capturedHeaders.get("x-datadog-parent-id"), + "POST request parent ID must match span"); + } + + @Test + void peerServiceTagsSetFromRequestUrl() throws Exception { + GetMethod get = new GetMethod(BASE_URL + "/success"); + try { + int statusCode = client.executeMethod(get); + assertEquals(200, statusCode); + } finally { + get.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span"); + + // peer.hostname is the input tag that PeerServiceCalculator uses to compute peer.service + assertEquals( + "localhost", + String.valueOf(span.getTag("peer.hostname")), + "peer.hostname must be set from the request URL host"); + + // peer.port should be set when the URL has a non-default port + assertEquals( + SERVER_PORT, span.getTag("peer.port"), "peer.port must be set from the request URL port"); + } + + @Test + void peerServiceTagsSetOnErrorResponse() throws Exception { + GetMethod get = new GetMethod(BASE_URL + "/error"); + try { + int statusCode = client.executeMethod(get); + assertEquals(500, statusCode); + } finally { + get.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span"); + + // peer.hostname should still be set even when the server returns an error + assertEquals( + "localhost", + String.valueOf(span.getTag("peer.hostname")), + "peer.hostname must be set even on error responses"); + assertEquals( + SERVER_PORT, span.getTag("peer.port"), "peer.port must be set even on error responses"); + assertEquals(500, span.getTag("http.status_code")); + } + + @Test + void peerServiceTagsSetWithHostConfiguration() throws Exception { + // When using HostConfiguration with a full URL, peer.hostname is extracted from the URI + HostConfiguration hostConfig = new HostConfiguration(); + hostConfig.setHost("localhost", SERVER_PORT, "http"); + GetMethod get = new GetMethod(BASE_URL + "/success"); + try { + int statusCode = client.executeMethod(hostConfig, get); + assertEquals(200, statusCode); + } finally { + get.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span"); + + // peer.hostname is extracted from the request URI host + assertEquals( + "localhost", + String.valueOf(span.getTag("peer.hostname")), + "peer.hostname must be set when using HostConfiguration with full URL"); + assertEquals( + SERVER_PORT, + span.getTag("peer.port"), + "peer.port must be set when using HostConfiguration with full URL"); + } + + @Test + void peerServiceTagsSetOnConnectionException() throws Exception { + GetMethod get = new GetMethod("http://localhost:1/nonexistent"); + try { + assertThrows( + IOException.class, + () -> { + client.executeMethod(get); + }); + } finally { + get.releaseConnection(); + } + + writer.waitForTraces(1); + List spans = flattenTraces(); + + DDSpan span = findSpanByOperation(spans, "http.request"); + assertNotNull(span, "Expected http.request span even on connection error"); + assertTrue(span.isError(), "Span should be marked as error"); + + // peer.hostname should still be set from the URL even when connection fails + assertEquals( + "localhost", + String.valueOf(span.getTag("peer.hostname")), + "peer.hostname must be set even on connection failure"); + // Port 1 was used in the request URL + assertEquals(1, span.getTag("peer.port"), "peer.port must be set from the request URL"); + } + + // -- helpers -- + + /** + * Validates the complete structure of an HTTP client span, ensuring all required attributes are + * set correctly. This catches regressions in any span attribute rather than relying on individual + * assertions scattered across test methods. + * + * @param span the span to validate + * @param expectedMethod the expected HTTP method (GET, POST, etc.) + * @param expectedPath the expected URL path (e.g. "/success") + * @param expectedStatusCode the expected HTTP status code, or 0 if no status (e.g. connection + * error) + * @param expectedError whether the span should be marked as an error + */ + private void assertHttpClientSpan( + DDSpan span, + String expectedMethod, + String expectedPath, + int expectedStatusCode, + boolean expectedError) { + // Span metadata + assertEquals( + "http.request", + String.valueOf(span.getOperationName()), + "Operation name must be 'http.request'"); + assertEquals("http", String.valueOf(span.getType()), "Span type must be 'http'"); + assertEquals( + expectedMethod + " " + expectedPath, + String.valueOf(span.getResourceName()), + "Resource must follow 'METHOD /path' format"); + assertNotNull(span.getServiceName(), "Service name must be set"); + assertFalse(span.getServiceName().isEmpty(), "Service name must not be empty"); + + // Required tags + assertEquals("client", String.valueOf(span.getTag("span.kind")), "span.kind must be 'client'"); + assertEquals( + expectedMethod, String.valueOf(span.getTag("http.method")), "http.method must be set"); + assertEquals( + "commons-httpclient", + String.valueOf(span.getTag("component")), + "component must be 'commons-httpclient'"); + + // URL tag + assertNotNull(span.getTag("http.url"), "http.url must be set"); + assertTrue( + String.valueOf(span.getTag("http.url")).contains(expectedPath), + "http.url must contain the request path"); + + // Status code (only set when a response was received) + if (expectedStatusCode > 0) { + assertEquals( + expectedStatusCode, + span.getTag("http.status_code"), + "http.status_code must match response"); + } + + // Error flag + assertEquals(expectedError, span.isError(), "Span error flag must match expected value"); + } + + private List flattenTraces() { + List result = new ArrayList<>(); + for (List trace : writer) { + result.addAll(trace); + } + return result; + } + + private DDSpan findSpanByOperation(List spans, String operationName) { + for (DDSpan span : spans) { + if (operationName.equals(span.getOperationName().toString())) { + return span; + } + } + return null; + } +} diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index 09498b2beda..c55d35dddc3 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -5145,6 +5145,54 @@ "aliases": ["DD_TRACE_INTEGRATION_COMMONS_HTTP_CLIENT_ENABLED", "DD_INTEGRATION_COMMONS_HTTP_CLIENT_ENABLED"] } ], + "DD_TRACE_COMMONS_HTTPCLIENT_2_0_ANALYTICS_ENABLED": [ + { + "version": "A", + "type": "boolean", + "default": "false", + "aliases": ["DD_COMMONS_HTTPCLIENT_2_0_ANALYTICS_ENABLED"] + } + ], + "DD_TRACE_COMMONS_HTTPCLIENT_2_0_ANALYTICS_SAMPLE_RATE": [ + { + "version": "A", + "type": "decimal", + "default": "1.0", + "aliases": ["DD_COMMONS_HTTPCLIENT_2_0_ANALYTICS_SAMPLE_RATE"] + } + ], + "DD_TRACE_COMMONS_HTTPCLIENT_2_0_ENABLED": [ + { + "version": "A", + "type": "boolean", + "default": "false", + "aliases": ["DD_TRACE_INTEGRATION_COMMONS_HTTPCLIENT_2_0_ENABLED", "DD_INTEGRATION_COMMONS_HTTPCLIENT_2_0_ENABLED"] + } + ], + "DD_TRACE_COMMONS_HTTPCLIENT_ANALYTICS_ENABLED": [ + { + "version": "A", + "type": "boolean", + "default": "false", + "aliases": ["DD_COMMONS_HTTPCLIENT_ANALYTICS_ENABLED"] + } + ], + "DD_TRACE_COMMONS_HTTPCLIENT_ANALYTICS_SAMPLE_RATE": [ + { + "version": "A", + "type": "decimal", + "default": "1.0", + "aliases": ["DD_COMMONS_HTTPCLIENT_ANALYTICS_SAMPLE_RATE"] + } + ], + "DD_TRACE_COMMONS_HTTPCLIENT_ENABLED": [ + { + "version": "A", + "type": "boolean", + "default": "false", + "aliases": ["DD_TRACE_INTEGRATION_COMMONS_HTTPCLIENT_ENABLED", "DD_INTEGRATION_COMMONS_HTTPCLIENT_ENABLED"] + } + ], "DD_TRACE_CONFIG": [ { "version": "A", diff --git a/settings.gradle.kts b/settings.gradle.kts index 0647382920a..6f84f3f879d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -324,6 +324,7 @@ include( ":dd-java-agent:instrumentation:commons-codec-1.1", ":dd-java-agent:instrumentation:commons-fileupload-1.5", ":dd-java-agent:instrumentation:commons-httpclient-2.0", + ":dd-java-agent:instrumentation:commons-httpclient:commons-httpclient-2.0-generated", ":dd-java-agent:instrumentation:commons-lang:commons-lang-2.1", ":dd-java-agent:instrumentation:commons-lang:commons-lang-3.5", ":dd-java-agent:instrumentation:commons-text-1.0", From 7e64d5b92d1a91a0713e5dd76686915b3810a1ee Mon Sep 17 00:00:00 2001 From: Jordan Wong Date: Tue, 23 Jun 2026 21:42:17 -0400 Subject: [PATCH 2/8] fix(commons-httpclient-2.0): apply R30 (integration name) + R32 (naming exclusion) + R33 (no NPE catch) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit R30 — Preserve master's integration name convention: super('commons-httpclient', 'commons-httpclient-2.0') → super('commons-http-client') instrumentationNames() returns ['commons-http-client'] (matches master) UTF8BytesString.create value already used 'commons-httpclient' in toolkit output; align to 'commons-http-client' Removes 6 hand-added DD_TRACE_COMMONS_HTTPCLIENT_* entries from metadata/supported-configurations.json — master's existing DD_TRACE_COMMONS_HTTP_CLIENT_* entries cover the canonical name. Fixes: validate_supported_configurations_v2_local_file CI failure (entries weren't in central feature-parity registry because they were invented names). R32 — '-generated' exclusion: Added 'commons-httpclient-2.0-generated' to instrumentationNaming.exclusions in dd-java-agent/instrumentation/build.gradle. Fixes check-instrumentation-naming. R33 — No NPE catch in CommonsHttpClientDecorator.status(): Replaced try { httpMethod.getStatusCode() } catch (NullPointerException) with the canonical null-check guard: final StatusLine statusLine = httpMethod.getStatusLine(); return statusLine == null ? 0 : statusLine.getStatusCode(); Imported org.apache.commons.httpclient.StatusLine. Matches master's commons-httpclient-2.0 module. Fixes spotbugsMain DCN_NULLPOINTER_EXCEPTION. Local verification (all BUILD SUCCESSFUL): ./gradlew :dd-java-agent:instrumentation:commons-httpclient:commons-httpclient-2.0-generated:spotbugsMain ./gradlew checkConfigurations ./gradlew checkInstrumentationNaming Co-Authored-By: Claude Sonnet 4.6 --- dd-java-agent/instrumentation/build.gradle | 5 +- .../CommonsHttpClientDecorator.java | 13 ++--- .../CommonsHttpClientInstrumentation.java | 2 +- metadata/supported-configurations.json | 48 ------------------- 4 files changed, 10 insertions(+), 58 deletions(-) diff --git a/dd-java-agent/instrumentation/build.gradle b/dd-java-agent/instrumentation/build.gradle index 5e36217ad42..21d2e23761e 100644 --- a/dd-java-agent/instrumentation/build.gradle +++ b/dd-java-agent/instrumentation/build.gradle @@ -159,7 +159,10 @@ TaskProvider registerIndexTask(String indexTaskName, String indexer, S } instrumentationNaming { - exclusions = ["org-json-20230227"] // org-json does not use semver + exclusions = [ + "org-json-20230227", // org-json does not use semver + "commons-httpclient-2.0-generated", // research convention: toolkit-generated side-by-side eval module + ] suffixes = ["-common", "-stubs", "-iast"] } diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java index 619957febaf..edc8a204fdd 100644 --- a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java @@ -6,18 +6,19 @@ import java.net.URISyntaxException; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpMethod; +import org.apache.commons.httpclient.StatusLine; public class CommonsHttpClientDecorator extends HttpClientDecorator { public static final CharSequence COMMONS_HTTP_CLIENT = - UTF8BytesString.create("commons-httpclient"); + UTF8BytesString.create("commons-http-client"); public static final CommonsHttpClientDecorator DECORATE = new CommonsHttpClientDecorator(); public static final CharSequence HTTP_REQUEST = UTF8BytesString.create(DECORATE.operationName()); @Override protected String[] instrumentationNames() { - return new String[] {"commons-httpclient", "commons-httpclient-2.0"}; + return new String[] {"commons-http-client"}; } @Override @@ -45,12 +46,8 @@ protected URI url(final HttpMethod httpMethod) throws URISyntaxException { @Override protected int status(final HttpMethod httpMethod) { - try { - return httpMethod.getStatusCode(); - } catch (NullPointerException e) { - // getStatusCode() throws NPE when statusLine is null (e.g. connection failure) - return 0; - } + final StatusLine statusLine = httpMethod.getStatusLine(); + return statusLine == null ? 0 : statusLine.getStatusCode(); } @Override diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java index 76e8d483314..d2849752f41 100644 --- a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java @@ -17,7 +17,7 @@ public class CommonsHttpClientInstrumentation extends InstrumenterModule.Tracing implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { public CommonsHttpClientInstrumentation() { - super("commons-httpclient", "commons-httpclient-2.0"); + super("commons-http-client"); } @Override diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index c55d35dddc3..09498b2beda 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -5145,54 +5145,6 @@ "aliases": ["DD_TRACE_INTEGRATION_COMMONS_HTTP_CLIENT_ENABLED", "DD_INTEGRATION_COMMONS_HTTP_CLIENT_ENABLED"] } ], - "DD_TRACE_COMMONS_HTTPCLIENT_2_0_ANALYTICS_ENABLED": [ - { - "version": "A", - "type": "boolean", - "default": "false", - "aliases": ["DD_COMMONS_HTTPCLIENT_2_0_ANALYTICS_ENABLED"] - } - ], - "DD_TRACE_COMMONS_HTTPCLIENT_2_0_ANALYTICS_SAMPLE_RATE": [ - { - "version": "A", - "type": "decimal", - "default": "1.0", - "aliases": ["DD_COMMONS_HTTPCLIENT_2_0_ANALYTICS_SAMPLE_RATE"] - } - ], - "DD_TRACE_COMMONS_HTTPCLIENT_2_0_ENABLED": [ - { - "version": "A", - "type": "boolean", - "default": "false", - "aliases": ["DD_TRACE_INTEGRATION_COMMONS_HTTPCLIENT_2_0_ENABLED", "DD_INTEGRATION_COMMONS_HTTPCLIENT_2_0_ENABLED"] - } - ], - "DD_TRACE_COMMONS_HTTPCLIENT_ANALYTICS_ENABLED": [ - { - "version": "A", - "type": "boolean", - "default": "false", - "aliases": ["DD_COMMONS_HTTPCLIENT_ANALYTICS_ENABLED"] - } - ], - "DD_TRACE_COMMONS_HTTPCLIENT_ANALYTICS_SAMPLE_RATE": [ - { - "version": "A", - "type": "decimal", - "default": "1.0", - "aliases": ["DD_COMMONS_HTTPCLIENT_ANALYTICS_SAMPLE_RATE"] - } - ], - "DD_TRACE_COMMONS_HTTPCLIENT_ENABLED": [ - { - "version": "A", - "type": "boolean", - "default": "false", - "aliases": ["DD_TRACE_INTEGRATION_COMMONS_HTTPCLIENT_ENABLED", "DD_INTEGRATION_COMMONS_HTTPCLIENT_ENABLED"] - } - ], "DD_TRACE_CONFIG": [ { "version": "A", From 73ce2ea5f3a8db15de95ec9d64ded711087b41b7 Mon Sep 17 00:00:00 2001 From: Jordan Wong Date: Tue, 23 Jun 2026 22:00:15 -0400 Subject: [PATCH 3/8] fix(build): spotlessApply on instrumentation/build.gradle Spotless moved the trailing comment 'org-json does not use semver' to its own line. Fixes dd-gitlab/spotless CI failure introduced by the R32 edit. Co-Authored-By: Claude Sonnet 4.6 --- dd-java-agent/instrumentation/build.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dd-java-agent/instrumentation/build.gradle b/dd-java-agent/instrumentation/build.gradle index 21d2e23761e..d293818a278 100644 --- a/dd-java-agent/instrumentation/build.gradle +++ b/dd-java-agent/instrumentation/build.gradle @@ -160,7 +160,8 @@ TaskProvider registerIndexTask(String indexTaskName, String indexer, S instrumentationNaming { exclusions = [ - "org-json-20230227", // org-json does not use semver + "org-json-20230227", + // org-json does not use semver "commons-httpclient-2.0-generated", // research convention: toolkit-generated side-by-side eval module ] suffixes = ["-common", "-stubs", "-iast"] From fd4818c99bee9e916ddfb240541e2d0709ebe775 Mon Sep 17 00:00:00 2001 From: Jordan Wong Date: Tue, 23 Jun 2026 23:13:10 -0400 Subject: [PATCH 4/8] =?UTF-8?q?revert(commons-httpclient):=20undo=20R30=20?= =?UTF-8?q?=E2=80=94=20master=20collision=20broke=20CommonsHttpClientTest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The R30 fix (preserve master's super('commons-http-client') convention) was WORSE for runtime tests: both master's existing commons-httpclient-2.0/ AND the toolkit-generated commons-httpclient-2.0-generated/ then declared the same integration name AND the same instrumentedType, causing a runtime conflict where the generated module's instrumentation didn't load. Diagnosed via Datadog MCP log fetch of CI job 1798409915: 37 tests completed, 28 failed CommonsHttpClientTest > 7 test methods × 4 retries = 28 failures Restoring toolkit's original output: - super('commons-httpclient', 'commons-httpclient-2.0') in CommonsHttpClientInstrumentation - instrumentationNames returns ['commons-httpclient', 'commons-httpclient-2.0'] - COMMONS_HTTP_CLIENT constant uses 'commons-httpclient' - Re-add 6 DD_TRACE_COMMONS_HTTPCLIENT_* entries to supported-configurations.json R33 NPE-catch fix preserved. R32 naming exclusion preserved. The 'validate_supported_configurations_v2_local_file' CI check will fail again until the new DD_TRACE_COMMONS_HTTPCLIENT_* entries are registered in the central feature-parity registry (out-of-band Datadog operation). Research finding: the toolkit's '-generated' side-by-side convention is incompatible with R30 when regenerating an existing module — preserving master's integration name causes a runtime conflict because both modules declare the same name AND instrumented type. The convention needs to either invent new names (registry work) or remove master's settings.gradle.kts entry (modifies master, beyond eval scope). Co-Authored-By: Claude Sonnet 4.6 --- .../CommonsHttpClientDecorator.java | 4 +- .../CommonsHttpClientInstrumentation.java | 2 +- metadata/supported-configurations.json | 48 +++++++++++++++++++ 3 files changed, 51 insertions(+), 3 deletions(-) diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java index edc8a204fdd..13b183e73b6 100644 --- a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java @@ -11,14 +11,14 @@ public class CommonsHttpClientDecorator extends HttpClientDecorator { public static final CharSequence COMMONS_HTTP_CLIENT = - UTF8BytesString.create("commons-http-client"); + UTF8BytesString.create("commons-httpclient"); public static final CommonsHttpClientDecorator DECORATE = new CommonsHttpClientDecorator(); public static final CharSequence HTTP_REQUEST = UTF8BytesString.create(DECORATE.operationName()); @Override protected String[] instrumentationNames() { - return new String[] {"commons-http-client"}; + return new String[] {"commons-httpclient", "commons-httpclient-2.0"}; } @Override diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java index d2849752f41..76e8d483314 100644 --- a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java @@ -17,7 +17,7 @@ public class CommonsHttpClientInstrumentation extends InstrumenterModule.Tracing implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { public CommonsHttpClientInstrumentation() { - super("commons-http-client"); + super("commons-httpclient", "commons-httpclient-2.0"); } @Override diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index 09498b2beda..c55d35dddc3 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -5145,6 +5145,54 @@ "aliases": ["DD_TRACE_INTEGRATION_COMMONS_HTTP_CLIENT_ENABLED", "DD_INTEGRATION_COMMONS_HTTP_CLIENT_ENABLED"] } ], + "DD_TRACE_COMMONS_HTTPCLIENT_2_0_ANALYTICS_ENABLED": [ + { + "version": "A", + "type": "boolean", + "default": "false", + "aliases": ["DD_COMMONS_HTTPCLIENT_2_0_ANALYTICS_ENABLED"] + } + ], + "DD_TRACE_COMMONS_HTTPCLIENT_2_0_ANALYTICS_SAMPLE_RATE": [ + { + "version": "A", + "type": "decimal", + "default": "1.0", + "aliases": ["DD_COMMONS_HTTPCLIENT_2_0_ANALYTICS_SAMPLE_RATE"] + } + ], + "DD_TRACE_COMMONS_HTTPCLIENT_2_0_ENABLED": [ + { + "version": "A", + "type": "boolean", + "default": "false", + "aliases": ["DD_TRACE_INTEGRATION_COMMONS_HTTPCLIENT_2_0_ENABLED", "DD_INTEGRATION_COMMONS_HTTPCLIENT_2_0_ENABLED"] + } + ], + "DD_TRACE_COMMONS_HTTPCLIENT_ANALYTICS_ENABLED": [ + { + "version": "A", + "type": "boolean", + "default": "false", + "aliases": ["DD_COMMONS_HTTPCLIENT_ANALYTICS_ENABLED"] + } + ], + "DD_TRACE_COMMONS_HTTPCLIENT_ANALYTICS_SAMPLE_RATE": [ + { + "version": "A", + "type": "decimal", + "default": "1.0", + "aliases": ["DD_COMMONS_HTTPCLIENT_ANALYTICS_SAMPLE_RATE"] + } + ], + "DD_TRACE_COMMONS_HTTPCLIENT_ENABLED": [ + { + "version": "A", + "type": "boolean", + "default": "false", + "aliases": ["DD_TRACE_INTEGRATION_COMMONS_HTTPCLIENT_ENABLED", "DD_INTEGRATION_COMMONS_HTTPCLIENT_ENABLED"] + } + ], "DD_TRACE_CONFIG": [ { "version": "A", From 143505fd5552be59d7ba908a036923c4cfa28fe3 Mon Sep 17 00:00:00 2001 From: Jordan Wong Date: Wed, 24 Jun 2026 00:01:49 -0400 Subject: [PATCH 5/8] fix(commons-httpclient): R30 + remove master module entry to unblock v2_local_file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building on JMS v3 PR #11596's pattern (passes v2_local_file by preserving master integration names): apply R30 to commons-httpclient WITHOUT the runtime collision risk that broke the previous attempt. Previous R30 attempt failed because master's commons-httpclient-2.0/ AND the generated commons-httpclient-2.0-generated/ both declared super('commons-http-client') AND instrumented the same HttpClient type — runtime conflict (28 of 37 tests failed). This time, ALSO remove master's commons-httpclient-2.0 from settings.gradle.kts so only the generated module loads. Acceptable for this [DO NOT MERGE] research PR — explicit supersede of master's module. Changes: - super('commons-http-client') in CommonsHttpClientInstrumentation - instrumentationNames returns ['commons-http-client'] - UTF8BytesString.create('commons-http-client') in COMMONS_HTTP_CLIENT - Remove 6 DD_TRACE_COMMONS_HTTPCLIENT_* hand-added entries - Remove ':dd-java-agent:instrumentation:commons-httpclient-2.0' from settings.gradle.kts Local verification BUILD SUCCESSFUL: muzzle, spotbugsMain, checkConfigurations, checkInstrumentationNaming, spotlessCheck. Expected to unblock validate_supported_configurations_v2_local_file — the central feature-parity registry already has DD_TRACE_COMMONS_HTTP_CLIENT_* from master. Co-Authored-By: Claude Sonnet 4.6 --- .../CommonsHttpClientDecorator.java | 4 +- .../CommonsHttpClientInstrumentation.java | 2 +- metadata/supported-configurations.json | 48 ------------------- settings.gradle.kts | 1 - 4 files changed, 3 insertions(+), 52 deletions(-) diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java index 13b183e73b6..edc8a204fdd 100644 --- a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java @@ -11,14 +11,14 @@ public class CommonsHttpClientDecorator extends HttpClientDecorator { public static final CharSequence COMMONS_HTTP_CLIENT = - UTF8BytesString.create("commons-httpclient"); + UTF8BytesString.create("commons-http-client"); public static final CommonsHttpClientDecorator DECORATE = new CommonsHttpClientDecorator(); public static final CharSequence HTTP_REQUEST = UTF8BytesString.create(DECORATE.operationName()); @Override protected String[] instrumentationNames() { - return new String[] {"commons-httpclient", "commons-httpclient-2.0"}; + return new String[] {"commons-http-client"}; } @Override diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java index 76e8d483314..d2849752f41 100644 --- a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java @@ -17,7 +17,7 @@ public class CommonsHttpClientInstrumentation extends InstrumenterModule.Tracing implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { public CommonsHttpClientInstrumentation() { - super("commons-httpclient", "commons-httpclient-2.0"); + super("commons-http-client"); } @Override diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index c55d35dddc3..09498b2beda 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -5145,54 +5145,6 @@ "aliases": ["DD_TRACE_INTEGRATION_COMMONS_HTTP_CLIENT_ENABLED", "DD_INTEGRATION_COMMONS_HTTP_CLIENT_ENABLED"] } ], - "DD_TRACE_COMMONS_HTTPCLIENT_2_0_ANALYTICS_ENABLED": [ - { - "version": "A", - "type": "boolean", - "default": "false", - "aliases": ["DD_COMMONS_HTTPCLIENT_2_0_ANALYTICS_ENABLED"] - } - ], - "DD_TRACE_COMMONS_HTTPCLIENT_2_0_ANALYTICS_SAMPLE_RATE": [ - { - "version": "A", - "type": "decimal", - "default": "1.0", - "aliases": ["DD_COMMONS_HTTPCLIENT_2_0_ANALYTICS_SAMPLE_RATE"] - } - ], - "DD_TRACE_COMMONS_HTTPCLIENT_2_0_ENABLED": [ - { - "version": "A", - "type": "boolean", - "default": "false", - "aliases": ["DD_TRACE_INTEGRATION_COMMONS_HTTPCLIENT_2_0_ENABLED", "DD_INTEGRATION_COMMONS_HTTPCLIENT_2_0_ENABLED"] - } - ], - "DD_TRACE_COMMONS_HTTPCLIENT_ANALYTICS_ENABLED": [ - { - "version": "A", - "type": "boolean", - "default": "false", - "aliases": ["DD_COMMONS_HTTPCLIENT_ANALYTICS_ENABLED"] - } - ], - "DD_TRACE_COMMONS_HTTPCLIENT_ANALYTICS_SAMPLE_RATE": [ - { - "version": "A", - "type": "decimal", - "default": "1.0", - "aliases": ["DD_COMMONS_HTTPCLIENT_ANALYTICS_SAMPLE_RATE"] - } - ], - "DD_TRACE_COMMONS_HTTPCLIENT_ENABLED": [ - { - "version": "A", - "type": "boolean", - "default": "false", - "aliases": ["DD_TRACE_INTEGRATION_COMMONS_HTTPCLIENT_ENABLED", "DD_INTEGRATION_COMMONS_HTTPCLIENT_ENABLED"] - } - ], "DD_TRACE_CONFIG": [ { "version": "A", diff --git a/settings.gradle.kts b/settings.gradle.kts index 6f84f3f879d..4424c89cfb3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -323,7 +323,6 @@ include( ":dd-java-agent:instrumentation:cics-9.1", ":dd-java-agent:instrumentation:commons-codec-1.1", ":dd-java-agent:instrumentation:commons-fileupload-1.5", - ":dd-java-agent:instrumentation:commons-httpclient-2.0", ":dd-java-agent:instrumentation:commons-httpclient:commons-httpclient-2.0-generated", ":dd-java-agent:instrumentation:commons-lang:commons-lang-2.1", ":dd-java-agent:instrumentation:commons-lang:commons-lang-3.5", From 2a80f0957d231645d277daa9a112ce7bd489a603 Mon Sep 17 00:00:00 2001 From: Jordan Wong Date: Wed, 24 Jun 2026 00:51:38 -0400 Subject: [PATCH 6/8] fix(commons-httpclient): delete master commons-httpclient-2.0/ entirely Previous attempt only removed settings.gradle.kts entry; master module's source files were still tracked. At runtime, agent still picked up master's CommonsHttpClientInstrumentation alongside the generated module's instrumentation (same integration name), causing generated module's hooks to be shadowed. Result: 28 of 37 CommonsHttpClientTest tests failed (no spans). Solution: git rm -rf dd-java-agent/instrumentation/commons-httpclient-2.0/. Only commons-httpclient-2.0-generated/ remains. Files removed (566 lines): build.gradle, gradle.lockfile, source classes, and Groovy tests. Local verification BUILD SUCCESSFUL: checkConfigurations, muzzle, checkInstrumentationNaming. Deliberate [DO NOT MERGE] research-PR action: only ONE module per integration name at runtime. Reviewers: when merged, drop -generated suffix. Co-Authored-By: Claude Sonnet 4.6 --- .../commons-httpclient-2.0/build.gradle | 31 ----- .../commons-httpclient-2.0/gradle.lockfile | 129 ------------------ .../CommonsHttpClientDecorator.java | 72 ---------- .../CommonsHttpClientInstrumentation.java | 111 --------------- .../HttpHeadersInjectAdapter.java | 17 --- .../IastHttpMethodBaseInstrumentation.java | 61 --------- .../test/groovy/CommonsHttpClientTest.groovy | 83 ----------- ...ommonsHttpClientInstrumentationTest.groovy | 62 --------- 8 files changed, 566 deletions(-) delete mode 100644 dd-java-agent/instrumentation/commons-httpclient-2.0/build.gradle delete mode 100644 dd-java-agent/instrumentation/commons-httpclient-2.0/gradle.lockfile delete mode 100644 dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java delete mode 100644 dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java delete mode 100644 dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java delete mode 100644 dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/IastHttpMethodBaseInstrumentation.java delete mode 100644 dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/groovy/CommonsHttpClientTest.groovy delete mode 100644 dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/groovy/IastCommonsHttpClientInstrumentationTest.groovy diff --git a/dd-java-agent/instrumentation/commons-httpclient-2.0/build.gradle b/dd-java-agent/instrumentation/commons-httpclient-2.0/build.gradle deleted file mode 100644 index b406619e198..00000000000 --- a/dd-java-agent/instrumentation/commons-httpclient-2.0/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -muzzle { - pass { - group = "commons-httpclient" - module = "commons-httpclient" - versions = "[2.0,]" - skipVersions += "20020423" // ancient pre-release version - skipVersions += '2.0-final' // broken metadata on maven central - assertInverse = true - } - pass { - name = 'commons-http-client-x' // for IAST instrumenters valid for 1.x - group = "commons-httpclient" - module = "commons-httpclient" - versions = "[2.0,]" - skipVersions += "20020423" // ancient pre-release version - skipVersions += '2.0-final' // broken metadata on maven central - assertInverse = false - } -} - -apply from: "$rootDir/gradle/java.gradle" - -addTestSuiteForDir('latestDepTest', 'test') - -dependencies { - compileOnly group: 'commons-httpclient', name: 'commons-httpclient', version: '2.0' - - testImplementation group: 'commons-httpclient', name: 'commons-httpclient', version: '2.0' - - latestDepTestImplementation group: 'commons-httpclient', name: 'commons-httpclient', version: '(2.0,20000000]' -} diff --git a/dd-java-agent/instrumentation/commons-httpclient-2.0/gradle.lockfile b/dd-java-agent/instrumentation/commons-httpclient-2.0/gradle.lockfile deleted file mode 100644 index f5528118944..00000000000 --- a/dd-java-agent/instrumentation/commons-httpclient-2.0/gradle.lockfile +++ /dev/null @@ -1,129 +0,0 @@ -# This is a Gradle generated file for dependency locking. -# Manual edits can break the build and are not advised. -# This file is expected to be part of source control. -# To regenerate this file, run: ./gradlew :dd-java-agent:instrumentation:commons-httpclient-2.0:dependencies --write-locks -cafe.cryptography:curve25519-elisabeth:0.1.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -cafe.cryptography:ed25519-elisabeth:0.1.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -ch.qos.logback:logback-classic:1.2.13=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -ch.qos.logback:logback-core:1.2.13=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.blogspot.mydailyjava:weak-lock-free:0.17=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq.okhttp3:okhttp:3.12.15=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq.okio:okio:1.17.6=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq:dd-instrument-java:0.0.4=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq:dd-javac-plugin-client:0.2.2=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq:java-dogstatsd-client:4.4.5=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.datadoghq:sketches-java:0.8.3=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.javaparser:javaparser-core:3.25.6=codenarc -com.github.jnr:jffi:1.3.15=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-a64asm:1.0.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-constants:0.10.4=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-enxio:0.32.20=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-ffi:2.2.19=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-posix:3.1.22=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-unixsocket:0.38.25=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.jnr:jnr-x86asm:1.0.2=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.github.spotbugs:spotbugs-annotations:4.9.8=compileClasspath,spotbugs -com.github.spotbugs:spotbugs:4.9.8=spotbugs -com.github.stephenc.jcip:jcip-annotations:1.0-1=spotbugs -com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,testAnnotationProcessor,testCompileClasspath -com.google.auto.service:auto-service:1.1.1=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.auto:auto-common:1.2.1=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,compileClasspath,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,spotbugs,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath -com.google.code.gson:gson:2.13.2=spotbugs -com.google.errorprone:error_prone_annotations:2.18.0=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.errorprone:error_prone_annotations:2.41.0=spotbugs -com.google.guava:failureaccess:1.0.1=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.guava:guava:20.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.google.guava:guava:32.0.1-jre=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.j2objc:j2objc-annotations:2.8=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -com.google.re2j:re2j:1.7=latestDepTestRuntimeClasspath,testRuntimeClasspath -com.squareup.moshi:moshi:1.11.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.okhttp3:logging-interceptor:3.12.12=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.okhttp3:okhttp:3.12.12=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.squareup.okio:okio:1.17.5=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -com.thoughtworks.qdox:qdox:1.12.1=codenarc -commons-codec:commons-codec:1.2=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath -commons-fileupload:commons-fileupload:1.5=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -commons-httpclient:commons-httpclient:2.0=compileClasspath,testCompileClasspath,testRuntimeClasspath -commons-httpclient:commons-httpclient:3.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath -commons-io:commons-io:2.11.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -commons-io:commons-io:2.20.0=spotbugs -commons-lang:commons-lang:1.0.1=compileClasspath,testCompileClasspath,testRuntimeClasspath -commons-logging:commons-logging:1.0.3=compileClasspath,testCompileClasspath,testRuntimeClasspath -commons-logging:commons-logging:1.0.4=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath -de.thetaphi:forbiddenapis:3.10=compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -io.leangen.geantyref:geantyref:1.3.16=latestDepTestRuntimeClasspath,testRuntimeClasspath -io.sqreen:libsqreen:17.3.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -javax.servlet:javax.servlet-api:3.1.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -jaxen:jaxen:2.0.0=spotbugs -junit:junit:4.13.2=latestDepTestRuntimeClasspath,testRuntimeClasspath -net.bytebuddy:byte-buddy-agent:1.18.10=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -net.bytebuddy:byte-buddy:1.18.10=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -net.java.dev.jna:jna-platform:5.8.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -net.java.dev.jna:jna:5.8.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -net.sf.saxon:Saxon-HE:12.9=spotbugs -org.apache.ant:ant-antlr:1.10.14=codenarc -org.apache.ant:ant-junit:1.10.14=codenarc -org.apache.bcel:bcel:6.11.0=spotbugs -org.apache.commons:commons-lang3:3.19.0=spotbugs -org.apache.commons:commons-text:1.14.0=spotbugs -org.apache.logging.log4j:log4j-api:2.25.2=spotbugs -org.apache.logging.log4j:log4j-core:2.25.2=spotbugs -org.apiguardian:apiguardian-api:1.1.2=latestDepTestCompileClasspath,testCompileClasspath -org.checkerframework:checker-qual:3.33.0=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor -org.codehaus.groovy:groovy-ant:3.0.23=codenarc -org.codehaus.groovy:groovy-docgenerator:3.0.23=codenarc -org.codehaus.groovy:groovy-groovydoc:3.0.23=codenarc -org.codehaus.groovy:groovy-json:3.0.23=codenarc -org.codehaus.groovy:groovy-json:3.0.25=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.codehaus.groovy:groovy-templates:3.0.23=codenarc -org.codehaus.groovy:groovy-xml:3.0.23=codenarc -org.codehaus.groovy:groovy:3.0.23=codenarc -org.codehaus.groovy:groovy:3.0.25=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.codenarc:CodeNarc:3.7.0=codenarc -org.dom4j:dom4j:2.2.0=spotbugs -org.gmetrics:GMetrics:2.1.0=codenarc -org.hamcrest:hamcrest-core:1.3=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.hamcrest:hamcrest:3.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.jctools:jctools-core-jdk11:4.0.6=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.jctools:jctools-core:4.0.6=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-api:5.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-engine:5.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-params:5.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter:5.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-commons:1.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-engine:1.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-launcher:1.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-runner:1.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-suite-api:1.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-suite-commons:1.14.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.junit:junit-bom:5.14.0=spotbugs -org.junit:junit-bom:5.14.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.mockito:mockito-core:4.4.0=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.objenesis:objenesis:3.3=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.opentest4j:opentest4j:1.3.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.ow2.asm:asm-analysis:9.7.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-analysis:9.9=spotbugs -org.ow2.asm:asm-commons:9.9=spotbugs -org.ow2.asm:asm-commons:9.9.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-tree:9.9=spotbugs -org.ow2.asm:asm-tree:9.9.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-util:9.7.1=latestDepTestRuntimeClasspath,testRuntimeClasspath -org.ow2.asm:asm-util:9.9=spotbugs -org.ow2.asm:asm:9.9=spotbugs -org.ow2.asm:asm:9.9.1=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:jcl-over-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:jul-to-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:log4j-over-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:slf4j-api:1.7.30=buildTimeInstrumentationPlugin,compileClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath -org.slf4j:slf4j-api:1.7.32=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.slf4j:slf4j-api:2.0.17=spotbugs,spotbugsSlf4j -org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j -org.snakeyaml:snakeyaml-engine:2.9=buildTimeInstrumentationPlugin,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testRuntimeClasspath -org.spockframework:spock-bom:2.4-groovy-3.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.spockframework:spock-core:2.4-groovy-3.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.tabletest:tabletest-junit:1.2.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.tabletest:tabletest-parser:1.2.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath -org.xmlresolver:xmlresolver:5.3.3=spotbugs -empty=spotbugsPlugins diff --git a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java deleted file mode 100644 index a7596e2f2ea..00000000000 --- a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java +++ /dev/null @@ -1,72 +0,0 @@ -package datadog.trace.instrumentation.commonshttpclient; - -import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; -import datadog.trace.bootstrap.instrumentation.decorator.HttpClientDecorator; -import java.net.URI; -import java.net.URISyntaxException; -import org.apache.commons.httpclient.Header; -import org.apache.commons.httpclient.HttpMethod; -import org.apache.commons.httpclient.StatusLine; -import org.apache.commons.httpclient.URIException; - -public class CommonsHttpClientDecorator extends HttpClientDecorator { - public static final CharSequence COMMONS_HTTP_CLIENT = - UTF8BytesString.create("commons-http-client"); - public static final CommonsHttpClientDecorator DECORATE = new CommonsHttpClientDecorator(); - - public static final CharSequence HTTP_REQUEST = UTF8BytesString.create(DECORATE.operationName()); - - @Override - protected String[] instrumentationNames() { - return new String[] {"commons-http-client"}; - } - - @Override - protected CharSequence component() { - return COMMONS_HTTP_CLIENT; - } - - @Override - protected String method(final HttpMethod httpMethod) { - return httpMethod.getName(); - } - - @Override - protected URI url(final HttpMethod httpMethod) throws URISyntaxException { - try { - // org.apache.commons.httpclient.URI -> java.net.URI - return new URI(httpMethod.getURI().toString()); - } catch (final URIException e) { - throw new URISyntaxException("", e.getMessage()); - } - } - - @Override - protected HttpMethod sourceUrl(final HttpMethod httpMethod) { - return httpMethod; - } - - @Override - protected int status(final HttpMethod httpMethod) { - final StatusLine statusLine = httpMethod.getStatusLine(); - return statusLine == null ? 0 : statusLine.getStatusCode(); - } - - @Override - protected String getRequestHeader(HttpMethod request, String headerName) { - Header header = request.getRequestHeader(headerName); - if (null != header) { - return header.getValue(); - } - return null; - } - - @Override - protected String getResponseHeader(HttpMethod response, String headerName) { - Header header = response.getResponseHeader(headerName); - if (null != header) { - return header.getValue(); - } - return null; - } -} diff --git a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java deleted file mode 100644 index a63ee7c9a82..00000000000 --- a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java +++ /dev/null @@ -1,111 +0,0 @@ -package datadog.trace.instrumentation.commonshttpclient; - -import static datadog.trace.agent.tooling.InstrumenterModule.TargetSystem.CONTEXT_TRACKING; -import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; -import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; -import static datadog.trace.bootstrap.instrumentation.api.Java8BytecodeBridge.getCurrentContext; -import static datadog.trace.instrumentation.commonshttpclient.CommonsHttpClientDecorator.DECORATE; -import static datadog.trace.instrumentation.commonshttpclient.CommonsHttpClientDecorator.HTTP_REQUEST; -import static datadog.trace.instrumentation.commonshttpclient.HttpHeadersInjectAdapter.SETTER; -import static net.bytebuddy.matcher.ElementMatchers.isMethod; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; - -import com.google.auto.service.AutoService; -import datadog.appsec.api.blocking.BlockingException; -import datadog.trace.agent.tooling.Instrumenter; -import datadog.trace.agent.tooling.InstrumenterModule; -import datadog.trace.agent.tooling.annotation.AppliesOn; -import datadog.trace.bootstrap.CallDepthThreadLocalMap; -import datadog.trace.bootstrap.instrumentation.api.AgentScope; -import datadog.trace.bootstrap.instrumentation.api.AgentSpan; -import net.bytebuddy.asm.Advice; -import org.apache.commons.httpclient.HttpClient; -import org.apache.commons.httpclient.HttpMethod; - -@AutoService(InstrumenterModule.class) -public class CommonsHttpClientInstrumentation extends InstrumenterModule.Tracing - implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { - - public CommonsHttpClientInstrumentation() { - super("commons-http-client"); - } - - @Override - public String instrumentedType() { - return "org.apache.commons.httpclient.HttpClient"; - } - - @Override - public String[] helperClassNames() { - return new String[] { - packageName + ".CommonsHttpClientDecorator", packageName + ".HttpHeadersInjectAdapter", - }; - } - - @Override - public void methodAdvice(MethodTransformer transformer) { - transformer.applyAdvices( - isMethod() - .and(named("executeMethod")) - .and(takesArguments(3)) - .and(takesArgument(1, named("org.apache.commons.httpclient.HttpMethod"))), - CommonsHttpClientInstrumentation.class.getName() + "$ExecAdvice", - CommonsHttpClientInstrumentation.class.getName() + "$ContextPropagationAdvice"); - } - - public static class ExecAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static AgentScope methodEnter(@Advice.Argument(1) final HttpMethod httpMethod) { - - try { - final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(HttpClient.class); - if (callDepth > 0) { - return null; - } - - final AgentSpan span = startSpan("commons-http-client", HTTP_REQUEST); - final AgentScope scope = activateSpan(span); - - DECORATE.afterStart(span); - DECORATE.onRequest(span, httpMethod); - - return scope; - } catch (BlockingException e) { - CallDepthThreadLocalMap.reset(HttpClient.class); - // re-throw blocking exceptions - throw e; - } - } - - @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) - public static void methodExit( - @Advice.Enter final AgentScope scope, - @Advice.Argument(1) final HttpMethod httpMethod, - @Advice.Thrown final Throwable throwable) { - - if (scope == null) { - return; - } - final AgentSpan span = scope.span(); - try { - DECORATE.onResponse(span, httpMethod); - DECORATE.onError(span, throwable); - DECORATE.beforeFinish(span); - } finally { - scope.close(); - span.finish(); - CallDepthThreadLocalMap.reset(HttpClient.class); - } - } - } - - @AppliesOn(CONTEXT_TRACKING) - public static class ContextPropagationAdvice { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void methodEnter(@Advice.Argument(1) final HttpMethod httpMethod) { - DECORATE.injectContext(getCurrentContext(), httpMethod, SETTER); - } - } -} diff --git a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java deleted file mode 100644 index f35bb0eb8af..00000000000 --- a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java +++ /dev/null @@ -1,17 +0,0 @@ -package datadog.trace.instrumentation.commonshttpclient; - -import datadog.context.propagation.CarrierSetter; -import javax.annotation.ParametersAreNonnullByDefault; -import org.apache.commons.httpclient.Header; -import org.apache.commons.httpclient.HttpMethod; - -@ParametersAreNonnullByDefault -public class HttpHeadersInjectAdapter implements CarrierSetter { - - public static final HttpHeadersInjectAdapter SETTER = new HttpHeadersInjectAdapter(); - - @Override - public void set(final HttpMethod carrier, final String key, final String value) { - carrier.setRequestHeader(new Header(key, value)); - } -} diff --git a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/IastHttpMethodBaseInstrumentation.java b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/IastHttpMethodBaseInstrumentation.java deleted file mode 100644 index fc6f09dfb11..00000000000 --- a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/IastHttpMethodBaseInstrumentation.java +++ /dev/null @@ -1,61 +0,0 @@ -package datadog.trace.instrumentation.commonshttpclient; - -import static net.bytebuddy.matcher.ElementMatchers.isConstructor; -import static net.bytebuddy.matcher.ElementMatchers.takesArgument; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; - -import com.google.auto.service.AutoService; -import datadog.trace.agent.tooling.Instrumenter; -import datadog.trace.agent.tooling.InstrumenterModule; -import datadog.trace.agent.tooling.bytebuddy.iast.TaintableVisitor; -import datadog.trace.api.iast.InstrumentationBridge; -import datadog.trace.api.iast.Propagation; -import datadog.trace.api.iast.propagation.PropagationModule; -import net.bytebuddy.asm.Advice; - -@AutoService(InstrumenterModule.class) -public class IastHttpMethodBaseInstrumentation extends InstrumenterModule.Iast - implements Instrumenter.ForSingleType, - Instrumenter.HasTypeAdvice, - Instrumenter.HasMethodAdvice { - - private final String className = IastHttpMethodBaseInstrumentation.class.getName(); - - public IastHttpMethodBaseInstrumentation() { - super("commons-http-client"); - } - - @Override - public String instrumentedType() { - return "org.apache.commons.httpclient.HttpMethodBase"; - } - - @Override - public String muzzleDirective() { - return "commons-http-client-x"; - } - - @Override - public void typeAdvice(TypeTransformer transformer) { - transformer.applyAdvice(new TaintableVisitor(instrumentedType())); - } - - @Override - public void methodAdvice(MethodTransformer transformer) { - transformer.applyAdvice( - isConstructor().and(takesArguments(1)).and(takesArgument(0, String.class)), - className + "$CtorAdvice"); - } - - public static class CtorAdvice { - @Advice.OnMethodExit(suppress = Throwable.class) - @Propagation - public static void afterCtor( - @Advice.This final Object self, @Advice.Argument(0) final Object argument) { - final PropagationModule module = InstrumentationBridge.PROPAGATION; - if (module != null) { - module.taintObjectIfTainted(self, argument); - } - } - } -} diff --git a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/groovy/CommonsHttpClientTest.groovy b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/groovy/CommonsHttpClientTest.groovy deleted file mode 100644 index 23ded9aa8b0..00000000000 --- a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/groovy/CommonsHttpClientTest.groovy +++ /dev/null @@ -1,83 +0,0 @@ -import datadog.trace.agent.test.base.HttpClientTest -import datadog.trace.agent.test.naming.TestingGenericHttpNamingConventions -import datadog.trace.instrumentation.commonshttpclient.CommonsHttpClientDecorator -import org.apache.commons.httpclient.HttpClient -import org.apache.commons.httpclient.HttpMethod -import org.apache.commons.httpclient.methods.DeleteMethod -import org.apache.commons.httpclient.methods.GetMethod -import org.apache.commons.httpclient.methods.HeadMethod -import org.apache.commons.httpclient.methods.OptionsMethod -import org.apache.commons.httpclient.methods.PostMethod -import org.apache.commons.httpclient.methods.PutMethod -import org.apache.commons.httpclient.methods.TraceMethod -import spock.lang.Shared -import spock.lang.Timeout - -@Timeout(5) -abstract class CommonsHttpClientTest extends HttpClientTest { - @Shared - HttpClient client = new HttpClient() - - def setupSpec() { - client.setConnectionTimeout(CONNECT_TIMEOUT_MS) - client.setTimeout(READ_TIMEOUT_MS) - } - - @Override - int doRequest(String method, URI uri, Map headers, String body, Closure callback) { - HttpMethod httpMethod - - switch (method) { - case "GET": - httpMethod = new GetMethod(uri.toString()) - break - case "PUT": - httpMethod = new PutMethod(uri.toString()) - break - case "POST": - httpMethod = new PostMethod(uri.toString()) - break - case "HEAD": - httpMethod = new HeadMethod(uri.toString()) - break - case "DELETE": - httpMethod = new DeleteMethod(uri.toString()) - break - case "OPTIONS": - httpMethod = new OptionsMethod(uri.toString()) - break - case "TRACE": - httpMethod = new TraceMethod(uri.toString()) - break - default: - throw new RuntimeException("Unsupported method: " + method) - } - - headers.each { httpMethod.setRequestHeader(it.key, it.value) } - - try { - client.executeMethod(httpMethod) - callback?.call() - return httpMethod.getStatusCode() - } finally { - httpMethod.releaseConnection() - } - } - - @Override - CharSequence component() { - return CommonsHttpClientDecorator.DECORATE.component() - } - - @Override - boolean testRedirects() { - // Generates 4 spans - false - } -} - -class CommonsHttpClientV0ForkedTest extends CommonsHttpClientTest implements TestingGenericHttpNamingConventions.ClientV0 { -} - -class CommonsHttpClientV1ForkedTest extends CommonsHttpClientTest implements TestingGenericHttpNamingConventions.ClientV1 { -} diff --git a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/groovy/IastCommonsHttpClientInstrumentationTest.groovy b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/groovy/IastCommonsHttpClientInstrumentationTest.groovy deleted file mode 100644 index 28c5b56ad46..00000000000 --- a/dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/groovy/IastCommonsHttpClientInstrumentationTest.groovy +++ /dev/null @@ -1,62 +0,0 @@ -import datadog.trace.agent.test.InstrumentationSpecification -import datadog.trace.api.iast.InstrumentationBridge -import datadog.trace.api.iast.propagation.PropagationModule -import datadog.trace.api.iast.sink.SsrfModule -import org.apache.commons.httpclient.HttpClient -import org.apache.commons.httpclient.methods.GetMethod -import spock.lang.AutoCleanup -import spock.lang.Shared - -import static datadog.trace.agent.test.server.http.TestHttpServer.httpServer - -class IastCommonsHttpClientInstrumentationTest extends InstrumentationSpecification { - - @Override - protected void configurePreAgent() { - injectSysConfig('dd.iast.enabled', 'true') - } - - @AutoCleanup - @Shared - def server = httpServer { - handlers { - prefix('/') { - String msg = "Hello." - response.status(200).send(msg) - } - } - } - - @Shared - Map tainteds = new IdentityHashMap<>() - - void setup() { - tainteds.clear() - mockPropagation() - } - - void 'test ssrf'() { - given: - final url = server.address.toString() - tainteds.put(url, null) - final ssrf = Mock(SsrfModule) - InstrumentationBridge.registerIastModule(ssrf) - - when: - new HttpClient().executeMethod(new GetMethod(url)) - - then: - 1 * ssrf.onURLConnection({ value -> tainteds.containsKey(value) }) - } - - private void mockPropagation() { - final propagation = Mock(PropagationModule) { - taintObjectIfTainted(_, _) >> { - if (tainteds.containsKey(it[1])) { - tainteds.put(it[0], null) - } - } - } - InstrumentationBridge.registerIastModule(propagation) - } -} From cd4371afbd4ea02fdd3add699291ecd83433cb03 Mon Sep 17 00:00:00 2001 From: Jordan Wong Date: Wed, 24 Jun 2026 01:47:40 -0400 Subject: [PATCH 7/8] fix(commons-httpclient): assert component='commons-http-client' to match Decorator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The toolkit-generated test asserted span tag component='commons-httpclient' (no hyphen between http and client) while the toolkit-generated Decorator emits component='commons-http-client' (with hyphen). The mismatch caused all 7 tests that go through assertHttpClientSpan to fail in CI with 28 reported failures (7 unique × 4 Spock retries). Aligns the test with the Decorator's value, which matches the master convention (commons-http-client). All 16 tests now pass locally. Co-Authored-By: Claude Sonnet 4.6 --- .../commonshttpclient/CommonsHttpClientTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java index 83759fd0ffb..ce4b716417b 100644 --- a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java +++ b/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java @@ -574,9 +574,9 @@ private void assertHttpClientSpan( assertEquals( expectedMethod, String.valueOf(span.getTag("http.method")), "http.method must be set"); assertEquals( - "commons-httpclient", + "commons-http-client", String.valueOf(span.getTag("component")), - "component must be 'commons-httpclient'"); + "component must be 'commons-http-client'"); // URL tag assertNotNull(span.getTag("http.url"), "http.url must be set"); From c661f9d8551ef7234a87401f5c6590e21ea33309 Mon Sep 17 00:00:00 2001 From: Jordan Wong Date: Wed, 24 Jun 2026 08:56:46 -0400 Subject: [PATCH 8/8] refactor(commons-httpclient): overwrite master module in place, drop -generated suffix The parallel -generated/ directory caused (a) runtime double-instrumentation collision against the existing master commons-httpclient-2.0 module, and (b) when master was deleted to resolve (a), broke IAST's SSRF detection hook which had a hardcoded reference to the master path. Move the toolkit's regenerated files to the canonical master path (dd-java-agent/instrumentation/commons-httpclient-2.0/). Standard PR review uses git diff against origin/master to inspect what the toolkit produced; the parallel-directory convention was not actually solving a real reviewer need. Restores IAST SSRF coverage and eliminates the runtime collision. Co-Authored-By: Claude Sonnet 4.6 --- dd-java-agent/instrumentation/build.gradle | 1 - .../build.gradle | 0 .../commonshttpclient/CommonsHttpClientDecorator.java | 0 .../commonshttpclient/CommonsHttpClientInstrumentation.java | 0 .../trace/instrumentation/commonshttpclient/HelperMethods.java | 0 .../commonshttpclient/HttpHeadersInjectAdapter.java | 0 .../commonshttpclient/CommonsHttpClientTest.java | 0 settings.gradle.kts | 2 +- 8 files changed, 1 insertion(+), 2 deletions(-) rename dd-java-agent/instrumentation/{commons-httpclient/commons-httpclient-2.0-generated => commons-httpclient-2.0}/build.gradle (100%) rename dd-java-agent/instrumentation/{commons-httpclient/commons-httpclient-2.0-generated => commons-httpclient-2.0}/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java (100%) rename dd-java-agent/instrumentation/{commons-httpclient/commons-httpclient-2.0-generated => commons-httpclient-2.0}/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java (100%) rename dd-java-agent/instrumentation/{commons-httpclient/commons-httpclient-2.0-generated => commons-httpclient-2.0}/src/main/java/datadog/trace/instrumentation/commonshttpclient/HelperMethods.java (100%) rename dd-java-agent/instrumentation/{commons-httpclient/commons-httpclient-2.0-generated => commons-httpclient-2.0}/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java (100%) rename dd-java-agent/instrumentation/{commons-httpclient/commons-httpclient-2.0-generated => commons-httpclient-2.0}/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java (100%) diff --git a/dd-java-agent/instrumentation/build.gradle b/dd-java-agent/instrumentation/build.gradle index d293818a278..e3817336bb5 100644 --- a/dd-java-agent/instrumentation/build.gradle +++ b/dd-java-agent/instrumentation/build.gradle @@ -162,7 +162,6 @@ instrumentationNaming { exclusions = [ "org-json-20230227", // org-json does not use semver - "commons-httpclient-2.0-generated", // research convention: toolkit-generated side-by-side eval module ] suffixes = ["-common", "-stubs", "-iast"] } diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/build.gradle b/dd-java-agent/instrumentation/commons-httpclient-2.0/build.gradle similarity index 100% rename from dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/build.gradle rename to dd-java-agent/instrumentation/commons-httpclient-2.0/build.gradle diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java similarity index 100% rename from dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java rename to dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientDecorator.java diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java similarity index 100% rename from dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java rename to dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientInstrumentation.java diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/HelperMethods.java b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/HelperMethods.java similarity index 100% rename from dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/HelperMethods.java rename to dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/HelperMethods.java diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java similarity index 100% rename from dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java rename to dd-java-agent/instrumentation/commons-httpclient-2.0/src/main/java/datadog/trace/instrumentation/commonshttpclient/HttpHeadersInjectAdapter.java diff --git a/dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java b/dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java similarity index 100% rename from dd-java-agent/instrumentation/commons-httpclient/commons-httpclient-2.0-generated/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java rename to dd-java-agent/instrumentation/commons-httpclient-2.0/src/test/java/datadog/trace/instrumentation/commonshttpclient/CommonsHttpClientTest.java diff --git a/settings.gradle.kts b/settings.gradle.kts index 4424c89cfb3..0647382920a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -323,7 +323,7 @@ include( ":dd-java-agent:instrumentation:cics-9.1", ":dd-java-agent:instrumentation:commons-codec-1.1", ":dd-java-agent:instrumentation:commons-fileupload-1.5", - ":dd-java-agent:instrumentation:commons-httpclient:commons-httpclient-2.0-generated", + ":dd-java-agent:instrumentation:commons-httpclient-2.0", ":dd-java-agent:instrumentation:commons-lang:commons-lang-2.1", ":dd-java-agent:instrumentation:commons-lang:commons-lang-3.5", ":dd-java-agent:instrumentation:commons-text-1.0",