Describe the bug
Asynchronous HTTP requests using the JDK-supplied java.net.http.HttpRequest support cancellation. However, if the application is run with the instrumentation agent, the request can no longer be cancelled correctly and instead always runs to completion.
Steps to reproduce
In a new Gradle application, prepare the following files along with file opentelemetry-javaagent.jar:
settings.gradle
rootProject.name = 'req-close-issue'
build.gradle
plugins {
id 'application'
}
application {
mainClass = 'org.example.ReqCloseIssue'
applicationDefaultJvmArgs = ['-javaagent:opentelemetry-javaagent.jar',
'-Dotel.resource.attributes=service.name=req-close-issue-app',
'-Dotel.traces.exporter=none',
'-Dotel.metrics.exporter=none',
'-Dotel.logs.exporter=none']
}
repositories {
mavenCentral()
}
src/main/java/org/example/ReqCloseIssue.java
package org.example;
import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
public class ReqCloseIssue {
static synchronized void log(String format, Object... args) {
System.out.printf(format + "%n", args);
}
public static void main(String[] args) throws Exception {
HttpServer slowServer = HttpServer.create(new InetSocketAddress(8080), 0);
slowServer.createContext("/", exchange -> {
try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {
}
String response = "** message body **";
exchange.sendResponseHeaders(200, response.length());
exchange.getResponseBody().write(response.getBytes());
exchange.close();
});
slowServer.setExecutor(Executors.newCachedThreadPool(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}));
slowServer.start();
try (HttpClient client = HttpClient.newHttpClient()) {
HttpRequest request = HttpRequest.newBuilder(new URI("http://localhost:8080/")).build();
CompletableFuture<String> future =
client.sendAsync(request, BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.whenComplete((res, ex) -> {
if (ex == null) {
log("Request was not cancelled; got response %s", res);
} else {
log("Request was cancelled with %s", ex.getCause());
}
})
.exceptionally(ex -> "** cancelled **");
Thread.sleep(200); // request will still be in progress at end of sleep
future.cancel(true);
log("Got response: %s", future.get()); // throws exception when agent present
} finally {
slowServer.stop(0);
}
}
}
Build and run the application with the following command:
Expected behavior
The following should be printed to the terminal:
Request was cancelled with java.util.concurrent.CancellationException: Request cancelled
Got response: ** cancelled **
Actual behavior
The following is printed to the terminal:
Request was not cancelled; got response ** message body **
Exception in thread "main" java.util.concurrent.CancellationException
at java.base/java.util.concurrent.CompletableFuture.cancel(CompletableFuture.java:2510)
at org.example.ReqCloseIssue.main(ReqCloseIssue.java:55)
Javaagent or library instrumentation version
opentelemetry-javaagent version 2.20.0
Environment
JDK: OpenJDK Runtime Environment Temurin-21.0.1+12
OS: Ubuntu 22.04.5 LTS
Additional context
The above shows that adding the agent changes the application behaviour
Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.
Describe the bug
Asynchronous HTTP requests using the JDK-supplied
java.net.http.HttpRequestsupport cancellation. However, if the application is run with the instrumentation agent, the request can no longer be cancelled correctly and instead always runs to completion.Steps to reproduce
In a new Gradle application, prepare the following files along with file
opentelemetry-javaagent.jar:settings.gradlebuild.gradleplugins { id 'application' } application { mainClass = 'org.example.ReqCloseIssue' applicationDefaultJvmArgs = ['-javaagent:opentelemetry-javaagent.jar', '-Dotel.resource.attributes=service.name=req-close-issue-app', '-Dotel.traces.exporter=none', '-Dotel.metrics.exporter=none', '-Dotel.logs.exporter=none'] } repositories { mavenCentral() }src/main/java/org/example/ReqCloseIssue.javaBuild and run the application with the following command:
Expected behavior
The following should be printed to the terminal:
Actual behavior
The following is printed to the terminal:
Javaagent or library instrumentation version
opentelemetry-javaagent version 2.20.0
Environment
JDK: OpenJDK Runtime Environment Temurin-21.0.1+12
OS: Ubuntu 22.04.5 LTS
Additional context
The above shows that adding the agent changes the application behaviour
Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding
+1orme too, to help us triage it. Learn more here.