Estoy intentando cancelar http nueva solicitud a través de Java 11 HttpClient.
Este es mi código de prueba:
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class App {
public static void main(String... args) throws InterruptedException {
HttpClient client = HttpClient.newBuilder().build();
URI uri = URI.create("http://releases.ubuntu.com/18.04.2/ubuntu-18.04.2-desktop-amd64.iso");
HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build();
var bodyHandler = HttpResponse.BodyHandlers.ofByteArrayConsumer(b -> System.out.println("#"));
var future = client.sendAsync(request, bodyHandler);
Thread.sleep(1000);
future.cancel(true);
System.out.println("\r\n----------CANCEL!!!------------");
System.out.println("\r\nisCancelled: " + future.isCancelled());
Thread.sleep(250);
}
}
Espero, que la tarea solicitud será cancelada inmediatamente después de future.cancel(true);
la línea invoca. Y, por lo tanto, la última línea impresa en la consola debe estarisCancelled: true
Sin embargo, cuando ejecuta este código, veo algo como esto:
################################################## ################################################## ---------- ------------ CANCELAR !!! #### isCancelled: verdadera ################# ################################################## ################################################## ##################################
Esto significa que las solicitudes de tarea aún en marcha después de que la cancele ... Así, que es el camino correcto para cancelar la solicitud?
UPD
Manera correcta de solicitud de cancelación es (como Daniel sugirió, + UPD2: evitar NPE en cancel()
método invoke):
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandler;
import java.net.http.HttpResponse.BodySubscriber;
import java.net.http.HttpResponse.ResponseInfo;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Flow.Subscription;
public class App {
private static class SubscriberWrapper implements BodySubscriber<Void> {
private final CountDownLatch latch;
private final BodySubscriber<Void> subscriber;
private Subscription subscription;
private SubscriberWrapper(BodySubscriber<Void> subscriber, CountDownLatch latch) {
this.subscriber = subscriber;
this.latch = latch;
}
@Override
public CompletionStage<Void> getBody() {
return subscriber.getBody();
}
@Override
public void onSubscribe(Subscription subscription) {
subscriber.onSubscribe(subscription);
this.subscription = subscription;
latch.countDown();
}
@Override
public void onNext(List<ByteBuffer> item) {
subscriber.onNext(item);
}
@Override
public void onError(Throwable throwable) {
subscriber.onError(throwable);
}
@Override
public void onComplete() {
subscriber.onComplete();
}
public void cancel() {
subscription.cancel();
System.out.println("\r\n----------CANCEL!!!------------");
}
}
private static class BodyHandlerWrapper implements BodyHandler<Void> {
private final CountDownLatch latch = new CountDownLatch(1);
private final BodyHandler<Void> handler;
private SubscriberWrapper subscriberWrapper;
private BodyHandlerWrapper(BodyHandler<Void> handler) {
this.handler = handler;
}
@Override
public BodySubscriber<Void> apply(ResponseInfo responseInfo) {
subscriberWrapper = new SubscriberWrapper(handler.apply(responseInfo), latch);
return subscriberWrapper;
}
public void cancel() {
CompletableFuture.runAsync(() -> {
try {
latch.await();
subscriberWrapper.cancel();
} catch (InterruptedException e) {}
});
}
}
public static void main(String... args) throws InterruptedException, ExecutionException {
HttpClient client = HttpClient.newBuilder().build();
URI uri = URI.create("http://releases.ubuntu.com/18.04.2/ubuntu-18.04.2-desktop-amd64.iso");
HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build();
var handler = HttpResponse.BodyHandlers.ofByteArrayConsumer(b -> System.out.print("#"));
BodyHandlerWrapper handlerWrapper = new BodyHandlerWrapper(handler);
client.sendAsync(request, handlerWrapper).thenAccept(b -> System.out.println(b.statusCode()));
Thread.sleep(1000);
handlerWrapper.cancel();
System.out.println("\r\n------Invoke cancel...---------");
Thread.sleep(2500);
}
}
Puede cancelar una solicitud HTTP utilizando la java.net.http.HttpClient
API cancelando el Flow.Subscription
objeto que se pasa a la respuesta de BodySubscriber
. Debería ser relativamente fácil para envolver trivialmente una de las proporcionadas BodyHandler
/ BodySubscriber
implementaciones con el fin de hacerse con el objeto de suscripción. Desafortunadamente no existe una relación entre el cancel
método de la CompletableFuture
devuelta por el cliente, y el cancel
método de la Flow.Subscription
pasada al BodySubscriber
. La forma correcta de cancelar una solicitud es a través del cancel
método de la suscripción.
Cancelación de la suscripción trabajará tanto con el síncrona ( HttpClient::send
) y (asíncronos HttpClient::sendAsync
) métodos. Se tendrá diferentes efectos sin embargo, dependiendo de si la solicitud fue enviada a través de HTTP / 1.1 o HTTP / 2.0 (con HTTP / 1.1 que causará que la conexión se cierra, con HTTP / 2.0 que hará que la corriente a poner a cero). Y por supuesto que podría tener ningún efecto en absoluto si el último byte de la respuesta ya fue entregado al BodySubscriber
.