Skip to content

Commit

Permalink
jetty-httpclient-12: send method must pass along implemented response…
Browse files Browse the repository at this point in the history
… listeners

The super HttpRequest.send call says:

> The listener passed to this method may implement not only Response.CompleteListener
> but also other response listener interfaces, and all the events implemented will be notified.

The current implementation passes through a lambda that implements CompleteListener only, and
does not delegate-in-scope to any other callback methods you might provide
  • Loading branch information
stevenschlansker committed Sep 27, 2024
1 parent 54693b6 commit 24bb28e
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import io.opentelemetry.context.Scope;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.jetty.httpclient.v12_0.internal.JettyClientTracingListener;
import io.opentelemetry.instrumentation.jetty.httpclient.v12_0.internal.JettyClientWrapUtil;
import java.net.URI;
import java.nio.ByteBuffer;
import org.eclipse.jetty.client.HttpClient;
Expand All @@ -34,14 +35,9 @@ public TracingHttpRequest(
@Override
public void send(Response.CompleteListener listener) {
parentContext = Context.current();
// start span and attach listeners.
// start span and attach listeners - must handle all listeners, not just CompleteListener.
JettyClientTracingListener.handleRequest(parentContext, this, instrumenter);
super.send(
result -> {
try (Scope scope = openScope()) {
listener.onComplete(result);
}
});
super.send(JettyClientWrapUtil.wrapTheListener(listener, parentContext));
}

private Scope openScope() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.jetty.httpclient.v12_0.internal;

import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.client.Response;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class JettyClientWrapUtil {
private static final Class<?>[] LISTENER_INTERFACES = {
Response.CompleteListener.class,
Response.FailureListener.class,
Response.SuccessListener.class,
Response.AsyncContentListener.class,
Response.ContentSourceListener.class,
Response.ContentListener.class,
Response.HeadersListener.class,
Response.HeaderListener.class,
Response.BeginListener.class
};

private JettyClientWrapUtil() {}

/**
* Utility to wrap the response listeners only, this includes the important CompleteListener.
*
* @param context top level context that is above the Jetty client span context
* @param listener listener passed to Jetty client send() method
* @return wrapped listener
*/
public static Response.CompleteListener wrapTheListener(
Response.CompleteListener listener, Context context) {
if (listener == null) {
return listener;
}

Class<?> listenerClass = listener.getClass();
List<Class<?>> interfaces = new ArrayList<>();
for (Class<?> type : LISTENER_INTERFACES) {
if (type.isInstance(listener)) {
interfaces.add(type);
}
}
if (interfaces.isEmpty()) {
return listener;
}

return (Response.CompleteListener)
Proxy.newProxyInstance(
listenerClass.getClassLoader(),
interfaces.toArray(new Class<?>[0]),
(proxy, method, args) -> {
try (Scope ignored = context.makeCurrent()) {
return method.invoke(listener, args);
} catch (InvocationTargetException exception) {
throw exception.getCause();
}
});
}
}

0 comments on commit 24bb28e

Please # to comment.