Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dd-java-agent/instrumentation/jetty-client/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
apply from: "$rootDir/gradle/java.gradle"
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
ext {
minJavaVersionForTests = JavaVersion.VERSION_11
}
muzzle {
pass {
group = "org.eclipse.jetty"
module = "jetty-client"
versions = "[10,12)"
javaVersion = "11"
}
fail {
group = "org.eclipse.jetty"
module = "jetty-client"
versions = "[,10)"
}
fail {
group = "org.eclipse.jetty"
module = "jetty-client"
versions = "[12,)"
javaVersion = "17"
}
}

apply from: "$rootDir/gradle/java.gradle"

addTestSuiteForDir('latestDepTest', 'test')
addTestSuiteForDir('jetty11Test', 'test')
addTestSuiteForDir('jetty10LatestDepTest', 'test')
compileMain_java11Java.configure {
it.sourceCompatibility = JavaVersion.VERSION_1_8
it.targetCompatibility = JavaVersion.VERSION_1_8
setJavaVersion(it, 11)
}
dependencies {
main_java11CompileOnly group: 'org.eclipse.jetty', name: 'jetty-client', version: '10.0.0'
main_java11Implementation(project(':dd-java-agent:instrumentation:jetty-client:jetty-client-common')) {
transitive = false
}
//because contains some instrumentation that still apply
testImplementation(project(':dd-java-agent:instrumentation:jetty-client:jetty-client-9.1'))
testImplementation(project(path:':dd-java-agent:testing', configuration:'shadow')) {
// explicitly declared below.
exclude group: 'org.eclipse.jetty'
}
testImplementation project(':dd-java-agent:instrumentation:jetty-util')
testImplementation group: 'org.eclipse.jetty', name: 'jetty-client', version: '10.0.0'
jetty10LatestDepTestImplementation group: 'org.eclipse.jetty', name: 'jetty-client', version: '10.+'
jetty11TestImplementation group: 'org.eclipse.jetty', name: 'jetty-client', version: '11.0.0'
latestDepTestImplementation group: 'org.eclipse.jetty', name: 'jetty-client', version: '11.+'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package datadog.trace.instrumentation.jetty_client10;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.namedOneOf;
import static datadog.trace.bootstrap.instrumentation.java.concurrent.ExcludeFilter.ExcludeType.RUNNABLE;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;

import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.ExcludeFilterProvider;
import datadog.trace.agent.tooling.Instrumenter;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.java.concurrent.ExcludeFilter;
import java.util.Collection;
import java.util.List;
import java.util.Map;

@AutoService(Instrumenter.class)
public class JettyClientInstrumentation extends Instrumenter.Tracing
implements Instrumenter.ForSingleType, ExcludeFilterProvider {
public JettyClientInstrumentation() {
super("jetty-client");
}

@Override
public String instrumentedType() {
return "org.eclipse.jetty.client.HttpClient";
}

@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".JettyClientDecorator",
"datadog.trace.instrumentation.jetty_client.HeadersInjectAdapter",
"datadog.trace.instrumentation.jetty_client.CallbackWrapper",
packageName + ".SpanFinishingCompleteListener"
};
}

@Override
public Map<String, String> contextStore() {
return singletonMap("org.eclipse.jetty.client.api.Request", AgentSpan.class.getName());
}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
isMethod()
.and(named("send"))
.and(
takesArgument(
0,
namedOneOf(
"org.eclipse.jetty.client.api.Request",
"org.eclipse.jetty.client.HttpRequest")))
.and(takesArgument(1, List.class)),
packageName + ".SendAdvice");
}

@Override
public Map<ExcludeFilter.ExcludeType, ? extends Collection<String>> excludedClasses() {
return singletonMap(RUNNABLE, singletonList("org.eclipse.jetty.util.SocketAddressResolver$1"));
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.instrumentation.jetty_client;
package datadog.trace.instrumentation.jetty_client10;

import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString;
import datadog.trace.bootstrap.instrumentation.decorator.HttpClientDecorator;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package datadog.trace.instrumentation.jetty_client10;

import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
import static datadog.trace.instrumentation.jetty_client.HeadersInjectAdapter.SETTER;
import static datadog.trace.instrumentation.jetty_client10.JettyClientDecorator.DECORATE;
import static datadog.trace.instrumentation.jetty_client10.JettyClientDecorator.HTTP_REQUEST;

import datadog.trace.bootstrap.InstrumentationContext;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.decorator.HttpClientDecorator;
import java.util.List;
import net.bytebuddy.asm.Advice;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;

public class SendAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static AgentSpan methodEnter(
@Advice.Argument(0) Request request,
@Advice.Argument(1) List<Response.ResponseListener> responseListeners) {
AgentSpan span = startSpan(HTTP_REQUEST);
InstrumentationContext.get(Request.class, AgentSpan.class).put(request, span);
// make sure the span is finished before onComplete callbacks execute
responseListeners.add(0, new SpanFinishingCompleteListener(span));
DECORATE.afterStart(span);
DECORATE.onRequest(span, request);
propagate().inject(span, request, SETTER);
propagate()
.injectPathwayContext(span, request, SETTER, HttpClientDecorator.CLIENT_PATHWAY_EDGE_TAGS);
return span;
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(
@Advice.Enter final AgentSpan span, @Advice.Thrown final Throwable throwable) {
if (throwable != null) {
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
span.finish();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package datadog.trace.instrumentation.jetty_client;
package datadog.trace.instrumentation.jetty_client10;

import static datadog.trace.instrumentation.jetty_client.JettyClientDecorator.DECORATE;
import static datadog.trace.instrumentation.jetty_client10.JettyClientDecorator.DECORATE;

import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import org.eclipse.jetty.client.api.Response;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import datadog.trace.agent.test.base.HttpClientTest
import datadog.trace.agent.test.naming.TestingGenericHttpNamingConventions
import org.eclipse.jetty.client.HttpClient
import org.eclipse.jetty.client.HttpProxy
import org.eclipse.jetty.client.HttpResponseException
import org.eclipse.jetty.client.api.Request
import org.eclipse.jetty.client.api.Response
import org.eclipse.jetty.client.api.Result
import org.eclipse.jetty.client.dynamic.HttpClientTransportDynamic
import org.eclipse.jetty.client.util.StringContentProvider
import org.eclipse.jetty.io.ClientConnector
import org.eclipse.jetty.util.ssl.SslContextFactory
import spock.lang.Shared
import spock.lang.Subject

import java.util.concurrent.ExecutionException

abstract class JettyClientTest extends HttpClientTest {

@Shared
@Subject
HttpClient client = createHttpClient()

@Shared
HttpClient proxiedClient = createHttpClient()

def createHttpClient() {
SslContextFactory.Client sslContextFactory = new SslContextFactory.Client(true)
ClientConnector clientConnector = new ClientConnector()
clientConnector.setSslContextFactory(sslContextFactory)
return new HttpClient(new HttpClientTransportDynamic(clientConnector))
}

def setupSpec() {
client.connectTimeout = CONNECT_TIMEOUT_MS
client.addressResolutionTimeout = CONNECT_TIMEOUT_MS
client.idleTimeout = READ_TIMEOUT_MS
client.start()

proxiedClient.proxyConfiguration.proxies.add(new HttpProxy("localhost", proxy.port))
proxiedClient.connectTimeout = CONNECT_TIMEOUT_MS
proxiedClient.addressResolutionTimeout = CONNECT_TIMEOUT_MS
proxiedClient.idleTimeout = READ_TIMEOUT_MS
proxiedClient.start()
}

@Override
int doRequest(String method, URI uri, Map<String, String> headers, String body, Closure callback) {
def proxy = uri.fragment != null && uri.fragment.equals("proxy")
Request req = (proxy ? proxiedClient : client).newRequest(uri).method(method)
headers.entrySet().each {
req.header(it.key, it.value)
}
if (body) {
req.content(new StringContentProvider(body))
}
if (callback) {
req.onComplete(new Response.CompleteListener() {
@Override
void onComplete(Result result) {
callback.call()
}
})
}
try {
def resp = req.send()
blockUntilChildSpansFinished(1)
return resp.status
} catch (ExecutionException ex) {
if (ex.cause instanceof HttpResponseException) {
return (ex.cause as HttpResponseException).response.status
}
throw ex
}
}

@Override
CharSequence component() {
return "jetty-client"
}

@Override
boolean testRedirects() {
false
}

@Override
boolean testRemoteConnection() {
false
}

@Override
boolean testSecure() {
true
}

@Override
boolean testProxy() {
false // doesn't produce CONNECT span.
}
}

class JettyClientV0Test extends JettyClientTest implements TestingGenericHttpNamingConventions.ClientV0 {
}

class JettyClientV1ForkedTest extends JettyClientTest implements TestingGenericHttpNamingConventions.ClientV1 {
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,60 @@ muzzle {
* is compiled with Java 11, so we can't validate with muzzle which uses Java 8.
*/
pass {
name = "client"
group = "org.eclipse.jetty"
module = "jetty-client"
versions = "[9.1,10)"
}
pass {
name = "listener"
group = "org.eclipse.jetty"
module = "jetty-client"
versions = "[9.1,10)"
}
fail {
name = "client"
group = "org.eclipse.jetty"
module = "jetty-client"
versions = "[10,12)"
javaVersion = "11"
}
pass {
name = "listener"
group = "org.eclipse.jetty"
module = "jetty-client"
versions = "[10,12)"
javaVersion = "11"
}
fail {
group = "org.eclipse.jetty"
module = "jetty-client"
versions = "[,9.1)"
}

fail {
group = "org.eclipse.jetty"
module = "jetty-client"
versions = "[12,)"
javaVersion = "17"
}
}

apply from: "$rootDir/gradle/java.gradle"

addTestSuiteForDir('latestDepTest', 'test')

dependencies {
compileOnly group: 'org.eclipse.jetty', name: 'jetty-client', version: '9.1.0.v20131115'

implementation(project(':dd-java-agent:instrumentation:jetty-client:jetty-client-common')) {
transitive = false
}
testImplementation(project(':dd-java-agent:testing')) {
// explicitly declared below.
exclude module: 'jetty-server'
exclude group: 'org.eclipse.jetty'
}
testImplementation project(':dd-java-agent:instrumentation:jetty-util')
testImplementation group: 'org.eclipse.jetty', name: 'jetty-client', version: '9.1.0.v20131115'
testImplementation group: 'org.eclipse.jetty', name: 'jetty-util', version: '9.1.0.v20131115'
latestDepTestImplementation group: 'org.eclipse.jetty', name: 'jetty-client', version: '9.+' // 10+ requires Java 11

// because there are shared dependencies between the client and server
// we have to be explicit about which version to use.
testImplementation group: 'org.eclipse.jetty', name: 'jetty-server', version: '9.1.0.v20131115'
latestDepTestImplementation group: 'org.eclipse.jetty', name: 'jetty-client', version: '9.+'
latestDepTestImplementation group: 'org.eclipse.jetty', name: 'jetty-server', version: '9.+'
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package datadog.trace.instrumentation.jetty_client;
package datadog.trace.instrumentation.jetty_client91;

import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
Expand Down Expand Up @@ -28,11 +28,6 @@ public String instrumentedType() {
return "org.eclipse.jetty.client.util.FutureResponseListener";
}

@Override
public String[] helperClassNames() {
return new String[] {packageName + ".JettyClientDecorator"};
}

@Override
public Map<String, String> contextStore() {
Map<String, String> contextStore = new HashMap<>(4);
Expand All @@ -43,6 +38,11 @@ public Map<String, String> contextStore() {
return contextStore;
}

@Override
public String muzzleDirective() {
return "listener";
}

@Override
public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
Expand Down
Loading