mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-11-15 11:39:11 +03:00
[FEATURE] Downloader fix
This commit is contained in:
parent
314eb8c09e
commit
5886d1ac48
9 changed files with 185 additions and 27 deletions
|
@ -3,11 +3,13 @@
|
||||||
import pro.gravit.launchserver.auth.core.User;
|
import pro.gravit.launchserver.auth.core.User;
|
||||||
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportBanInfo;
|
import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportBanInfo;
|
||||||
|
|
||||||
public interface AuthSupportUserBan {
|
import java.time.LocalDateTime;
|
||||||
void banUser(User user, String reason);
|
|
||||||
|
|
||||||
default void banUser(User user) {
|
public interface AuthSupportUserBan {
|
||||||
banUser(user, null);
|
UserSupportBanInfo.UserBanInfo banUser(User user, String reason, String moderator, LocalDateTime startTime, LocalDateTime endTime);
|
||||||
|
|
||||||
|
default UserSupportBanInfo.UserBanInfo banUser(User user) {
|
||||||
|
return banUser(user, null, null, LocalDateTime.now(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unbanUser(User user);
|
void unbanUser(User user);
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package pro.gravit.launcher;
|
||||||
|
|
||||||
|
import pro.gravit.launcher.events.ExtendedTokenRequestEvent;
|
||||||
|
import pro.gravit.launcher.events.request.SecurityReportRequestEvent;
|
||||||
|
import pro.gravit.launcher.request.Request;
|
||||||
|
import pro.gravit.launcher.request.WebSocketEvent;
|
||||||
|
import pro.gravit.launcher.request.websockets.ClientWebSocketService;
|
||||||
|
|
||||||
|
public class BasicLauncherEventHandler implements ClientWebSocketService.EventHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends WebSocketEvent> boolean eventHandle(T event) {
|
||||||
|
if (event instanceof SecurityReportRequestEvent) {
|
||||||
|
SecurityReportRequestEvent event1 = (SecurityReportRequestEvent) event;
|
||||||
|
if (event1.action == SecurityReportRequestEvent.ReportAction.CRASH) {
|
||||||
|
LauncherEngine.exitLauncher(80);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event instanceof ExtendedTokenRequestEvent) {
|
||||||
|
ExtendedTokenRequestEvent event1 = (ExtendedTokenRequestEvent) event;
|
||||||
|
String token = event1.getExtendedToken();
|
||||||
|
if (token != null) {
|
||||||
|
Request.addExtendedToken(event1.getExtendedTokenName(), token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -192,6 +192,7 @@ public void start(String... args) throws Throwable {
|
||||||
throw new RequestException("Connection failed", e);
|
throw new RequestException("Connection failed", e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Request.service.registerEventHandler(new BasicLauncherEventHandler());
|
||||||
}
|
}
|
||||||
Objects.requireNonNull(args, "args");
|
Objects.requireNonNull(args, "args");
|
||||||
if (started.getAndSet(true))
|
if (started.getAndSet(true))
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
package pro.gravit.launcher.client;
|
package pro.gravit.launcher.client;
|
||||||
|
|
||||||
import pro.gravit.launcher.Launcher;
|
import pro.gravit.launcher.*;
|
||||||
import pro.gravit.launcher.LauncherAgent;
|
|
||||||
import pro.gravit.launcher.LauncherConfig;
|
|
||||||
import pro.gravit.launcher.LauncherEngine;
|
|
||||||
import pro.gravit.launcher.api.AuthService;
|
import pro.gravit.launcher.api.AuthService;
|
||||||
import pro.gravit.launcher.api.ClientService;
|
import pro.gravit.launcher.api.ClientService;
|
||||||
import pro.gravit.launcher.client.events.client.*;
|
import pro.gravit.launcher.client.events.client.*;
|
||||||
|
@ -104,6 +101,7 @@ public static void main(String[] args) throws Throwable {
|
||||||
LogHelper.info("Using Sessions");
|
LogHelper.info("Using Sessions");
|
||||||
Request.setSession(params.session);
|
Request.setSession(params.session);
|
||||||
}
|
}
|
||||||
|
Request.service.registerEventHandler(new BasicLauncherEventHandler());
|
||||||
checkJVMBitsAndVersion(params.profile.getMinJavaVersion(), params.profile.getRecommendJavaVersion(), params.profile.getMaxJavaVersion(), params.profile.isWarnMissJavaVersion());
|
checkJVMBitsAndVersion(params.profile.getMinJavaVersion(), params.profile.getRecommendJavaVersion(), params.profile.getMaxJavaVersion(), params.profile.isWarnMissJavaVersion());
|
||||||
LauncherEngine.modulesManager.invokeEvent(new ClientProcessInitPhase(engine, params));
|
LauncherEngine.modulesManager.invokeEvent(new ClientProcessInitPhase(engine, params));
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package pro.gravit.launcher.events;
|
||||||
|
|
||||||
|
public interface ExtendedTokenRequestEvent {
|
||||||
|
String getExtendedTokenName();
|
||||||
|
|
||||||
|
String getExtendedToken();
|
||||||
|
}
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
public abstract class Request<R extends WebSocketEvent> implements WebSocketRequest {
|
public abstract class Request<R extends WebSocketEvent> implements WebSocketRequest {
|
||||||
public static StdWebSocketService service;
|
public static StdWebSocketService service;
|
||||||
|
@ -23,6 +24,7 @@ public abstract class Request<R extends WebSocketEvent> implements WebSocketRequ
|
||||||
private static String authId;
|
private static String authId;
|
||||||
private static long tokenExpiredTime;
|
private static long tokenExpiredTime;
|
||||||
private static List<ExtendedTokenCallback> extendedTokenCallbacks = new ArrayList<>(4);
|
private static List<ExtendedTokenCallback> extendedTokenCallbacks = new ArrayList<>(4);
|
||||||
|
private static List<BiConsumer<String, AuthRequestEvent.OAuthRequestEvent>> oauthChangeHandlers = new ArrayList<>(4);
|
||||||
@LauncherNetworkAPI
|
@LauncherNetworkAPI
|
||||||
public final UUID requestUUID = UUID.randomUUID();
|
public final UUID requestUUID = UUID.randomUUID();
|
||||||
private transient final AtomicBoolean started = new AtomicBoolean(false);
|
private transient final AtomicBoolean started = new AtomicBoolean(false);
|
||||||
|
@ -43,6 +45,9 @@ public static void setOAuth(String authId, AuthRequestEvent.OAuthRequestEvent ev
|
||||||
} else {
|
} else {
|
||||||
tokenExpiredTime = 0;
|
tokenExpiredTime = 0;
|
||||||
}
|
}
|
||||||
|
for (BiConsumer<String, AuthRequestEvent.OAuthRequestEvent> handler : oauthChangeHandlers) {
|
||||||
|
handler.accept(authId, event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AuthRequestEvent.OAuthRequestEvent getOAuth() {
|
public static AuthRequestEvent.OAuthRequestEvent getOAuth() {
|
||||||
|
@ -154,6 +159,18 @@ public void addExtendedTokenCallback(ExtendedTokenCallback cb) {
|
||||||
extendedTokenCallbacks.add(cb);
|
extendedTokenCallbacks.add(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void removeExtendedTokenCallback(ExtendedTokenCallback cb) {
|
||||||
|
extendedTokenCallbacks.remove(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addOAuthChangeHandler(BiConsumer<String, AuthRequestEvent.OAuthRequestEvent> eventHandler) {
|
||||||
|
oauthChangeHandlers.add(eventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOAuthChangeHandler(BiConsumer<String, AuthRequestEvent.OAuthRequestEvent> eventHandler) {
|
||||||
|
oauthChangeHandlers.remove(eventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
public R request() throws Exception {
|
public R request() throws Exception {
|
||||||
if (!started.compareAndSet(false, true))
|
if (!started.compareAndSet(false, true))
|
||||||
throw new IllegalStateException("Request already started");
|
throw new IllegalStateException("Request already started");
|
||||||
|
|
|
@ -32,6 +32,7 @@ public class AsyncDownloader {
|
||||||
private static volatile SSLSocketFactory sslSocketFactory;
|
private static volatile SSLSocketFactory sslSocketFactory;
|
||||||
private static volatile SSLContext sslContext;
|
private static volatile SSLContext sslContext;
|
||||||
public final Callback callback;
|
public final Callback callback;
|
||||||
|
public volatile boolean isClosed;
|
||||||
|
|
||||||
public AsyncDownloader(Callback callback) {
|
public AsyncDownloader(Callback callback) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
|
@ -170,6 +171,7 @@ public void transfer(InputStream input, Path file, long size) throws IOException
|
||||||
// Download with digest update
|
// Download with digest update
|
||||||
byte[] bytes = IOHelper.newBuffer();
|
byte[] bytes = IOHelper.newBuffer();
|
||||||
while (downloaded < size) {
|
while (downloaded < size) {
|
||||||
|
if (isClosed) throw new IOException("Download interrupted");
|
||||||
int remaining = (int) Math.min(size - downloaded, bytes.length);
|
int remaining = (int) Math.min(size - downloaded, bytes.length);
|
||||||
int length = input.read(bytes, 0, remaining);
|
int length = input.read(bytes, 0, remaining);
|
||||||
if (length < 0)
|
if (length < 0)
|
||||||
|
|
|
@ -13,7 +13,27 @@ public interface DownloadCallback {
|
||||||
void apply(long fullDiff);
|
void apply(long fullDiff);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Void> downloadList(List<AsyncDownloader.SizedFile> files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception {
|
private final CompletableFuture<Void> future;
|
||||||
|
private final AsyncDownloader asyncDownloader;
|
||||||
|
|
||||||
|
private Downloader(CompletableFuture<Void> future, AsyncDownloader downloader) {
|
||||||
|
this.future = future;
|
||||||
|
this.asyncDownloader = downloader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Void> getFuture() {
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
this.asyncDownloader.isClosed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCanceled() {
|
||||||
|
return this.asyncDownloader.isClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Downloader downloadList(List<AsyncDownloader.SizedFile> files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception {
|
||||||
final boolean closeExecutor;
|
final boolean closeExecutor;
|
||||||
if (executor == null) {
|
if (executor == null) {
|
||||||
executor = Executors.newWorkStealingPool(4);
|
executor = Executors.newWorkStealingPool(4);
|
||||||
|
@ -30,10 +50,10 @@ public CompletableFuture<Void> downloadList(List<AsyncDownloader.SizedFile> file
|
||||||
CompletableFuture<Void> future = CompletableFuture.allOf(asyncDownloader.runDownloadList(list, baseURL, targetDir, executor));
|
CompletableFuture<Void> future = CompletableFuture.allOf(asyncDownloader.runDownloadList(list, baseURL, targetDir, executor));
|
||||||
|
|
||||||
ExecutorService finalExecutor = executor;
|
ExecutorService finalExecutor = executor;
|
||||||
return future.thenAccept(e -> {
|
return new Downloader(future.thenAccept(e -> {
|
||||||
if (closeExecutor) {
|
if (closeExecutor) {
|
||||||
finalExecutor.shutdownNow();
|
finalExecutor.shutdownNow();
|
||||||
}
|
}
|
||||||
});
|
}), asyncDownloader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
@ -29,12 +30,55 @@ public interface DownloadCallback {
|
||||||
void onComplete(Path path);
|
void onComplete(Path path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompletableFuture<Void> downloadList(List<AsyncDownloader.SizedFile> files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception {
|
protected final HttpClient client;
|
||||||
|
protected final ExecutorService executor;
|
||||||
|
protected CompletableFuture<Void> future;
|
||||||
|
protected final LinkedList<DownloadTask> tasks = new LinkedList<>();
|
||||||
|
|
||||||
|
protected Downloader(HttpClient client, ExecutorService executor) {
|
||||||
|
this.client = client;
|
||||||
|
this.executor = executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
for (DownloadTask task : tasks) {
|
||||||
|
if (!task.isCompleted()) {
|
||||||
|
task.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks.clear();
|
||||||
|
executor.shutdownNow();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCanceled() {
|
||||||
|
return executor.isTerminated();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Void> getFuture() {
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Downloader downloadList(List<AsyncDownloader.SizedFile> files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception {
|
||||||
boolean closeExecutor = false;
|
boolean closeExecutor = false;
|
||||||
if (executor == null) {
|
if (executor == null) {
|
||||||
executor = Executors.newWorkStealingPool(Math.min(3, threads));
|
executor = Executors.newWorkStealingPool(Math.min(3, threads));
|
||||||
closeExecutor = true;
|
closeExecutor = true;
|
||||||
}
|
}
|
||||||
|
Downloader downloader = newDownloader(executor);
|
||||||
|
downloader.future = downloader.downloadFiles(files, baseURL, targetDir, callback, executor, threads);
|
||||||
|
if (closeExecutor) {
|
||||||
|
ExecutorService finalExecutor = executor;
|
||||||
|
downloader.future = downloader.future.thenAccept(e -> {
|
||||||
|
finalExecutor.shutdownNow();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return downloader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Downloader newDownloader(ExecutorService executor) {
|
||||||
|
if (executor == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
HttpClient.Builder builder = HttpClient.newBuilder()
|
HttpClient.Builder builder = HttpClient.newBuilder()
|
||||||
.version(isNoHttp2 ? HttpClient.Version.HTTP_1_1 : HttpClient.Version.HTTP_2)
|
.version(isNoHttp2 ? HttpClient.Version.HTTP_1_1 : HttpClient.Version.HTTP_2)
|
||||||
.followRedirects(HttpClient.Redirect.NORMAL)
|
.followRedirects(HttpClient.Redirect.NORMAL)
|
||||||
|
@ -46,21 +90,15 @@ public static CompletableFuture<Void> downloadList(List<AsyncDownloader.SizedFil
|
||||||
throw new SecurityException(e);
|
throw new SecurityException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CompletableFuture<Void> future = downloadList(builder.build(), files, baseURL, targetDir, callback, executor, threads);
|
HttpClient client = builder.build();
|
||||||
if (closeExecutor) {
|
return new Downloader(client, executor);
|
||||||
ExecutorService finalExecutor = executor;
|
|
||||||
future = future.thenAccept(e -> {
|
|
||||||
finalExecutor.shutdownNow();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return future;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ConsumerObject {
|
private static class ConsumerObject {
|
||||||
Consumer<HttpResponse<Path>> next = null;
|
Consumer<HttpResponse<Path>> next = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompletableFuture<Void> downloadList(HttpClient client, List<AsyncDownloader.SizedFile> files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception {
|
public CompletableFuture<Void> downloadFiles(List<AsyncDownloader.SizedFile> files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception {
|
||||||
// URI scheme
|
// URI scheme
|
||||||
URI baseUri = new URI(baseURL);
|
URI baseUri = new URI(baseURL);
|
||||||
Collections.shuffle(files);
|
Collections.shuffle(files);
|
||||||
|
@ -79,7 +117,8 @@ public static CompletableFuture<Void> downloadList(HttpClient client, List<Async
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
sendAsync(client, file, baseUri, targetDir, callback).thenAccept(consumerObject.next);
|
DownloadTask task = sendAsync(file, baseUri, targetDir, callback);
|
||||||
|
task.completableFuture.thenAccept(consumerObject.next);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
future.completeExceptionally(exception);
|
future.completeExceptionally(exception);
|
||||||
}
|
}
|
||||||
|
@ -91,11 +130,39 @@ public static CompletableFuture<Void> downloadList(HttpClient client, List<Async
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CompletableFuture<HttpResponse<Path>> sendAsync(HttpClient client, AsyncDownloader.SizedFile file, URI baseUri, Path targetDir, DownloadCallback callback) throws Exception {
|
public static class DownloadTask {
|
||||||
return client.sendAsync(makeHttpRequest(baseUri, file.urlPath), makeBodyHandler(targetDir.resolve(file.filePath), callback));
|
public final ProgressTrackingBodyHandler<Path> bodyHandler;
|
||||||
|
public final CompletableFuture<HttpResponse<Path>> completableFuture;
|
||||||
|
|
||||||
|
public DownloadTask(ProgressTrackingBodyHandler<Path> bodyHandler, CompletableFuture<HttpResponse<Path>> completableFuture) {
|
||||||
|
this.bodyHandler = bodyHandler;
|
||||||
|
this.completableFuture = completableFuture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompleted() {
|
||||||
|
return completableFuture.isDone() | completableFuture.isCompletedExceptionally();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
bodyHandler.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpRequest makeHttpRequest(URI baseUri, String filePath) throws URISyntaxException {
|
protected DownloadTask sendAsync(AsyncDownloader.SizedFile file, URI baseUri, Path targetDir, DownloadCallback callback) throws Exception {
|
||||||
|
ProgressTrackingBodyHandler<Path> bodyHandler = makeBodyHandler(targetDir.resolve(file.filePath), callback);
|
||||||
|
CompletableFuture<HttpResponse<Path>> future = client.sendAsync(makeHttpRequest(baseUri, file.urlPath), bodyHandler);
|
||||||
|
var ref = new Object() {
|
||||||
|
DownloadTask task = null;
|
||||||
|
};
|
||||||
|
ref.task = new DownloadTask(bodyHandler, future.thenApply((e) -> {
|
||||||
|
tasks.remove(ref.task);
|
||||||
|
return e;
|
||||||
|
}));
|
||||||
|
tasks.add(ref.task);
|
||||||
|
return ref.task;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected HttpRequest makeHttpRequest(URI baseUri, String filePath) throws URISyntaxException {
|
||||||
String scheme = baseUri.getScheme();
|
String scheme = baseUri.getScheme();
|
||||||
String host = baseUri.getHost();
|
String host = baseUri.getHost();
|
||||||
int port = baseUri.getPort();
|
int port = baseUri.getPort();
|
||||||
|
@ -109,13 +176,14 @@ private static HttpRequest makeHttpRequest(URI baseUri, String filePath) throws
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpResponse.BodyHandler<Path> makeBodyHandler(Path file, DownloadCallback callback) {
|
protected ProgressTrackingBodyHandler<Path> makeBodyHandler(Path file, DownloadCallback callback) {
|
||||||
return new ProgressTrackingBodyHandler<>(HttpResponse.BodyHandlers.ofFile(file), callback);
|
return new ProgressTrackingBodyHandler<>(HttpResponse.BodyHandlers.ofFile(file), callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ProgressTrackingBodyHandler<T> implements HttpResponse.BodyHandler<T> {
|
public static class ProgressTrackingBodyHandler<T> implements HttpResponse.BodyHandler<T> {
|
||||||
private final HttpResponse.BodyHandler<T> delegate;
|
private final HttpResponse.BodyHandler<T> delegate;
|
||||||
private final DownloadCallback callback;
|
private final DownloadCallback callback;
|
||||||
|
private ProgressTrackingBodySubscriber subscriber;
|
||||||
|
|
||||||
public ProgressTrackingBodyHandler(HttpResponse.BodyHandler<T> delegate, DownloadCallback callback) {
|
public ProgressTrackingBodyHandler(HttpResponse.BodyHandler<T> delegate, DownloadCallback callback) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
|
@ -124,11 +192,19 @@ public ProgressTrackingBodyHandler(HttpResponse.BodyHandler<T> delegate, Downloa
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpResponse.BodySubscriber<T> apply(HttpResponse.ResponseInfo responseInfo) {
|
public HttpResponse.BodySubscriber<T> apply(HttpResponse.ResponseInfo responseInfo) {
|
||||||
return delegate.apply(responseInfo);
|
subscriber = new ProgressTrackingBodySubscriber(delegate.apply(responseInfo));
|
||||||
|
return subscriber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
if (subscriber != null) {
|
||||||
|
subscriber.cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ProgressTrackingBodySubscriber implements HttpResponse.BodySubscriber<T> {
|
private class ProgressTrackingBodySubscriber implements HttpResponse.BodySubscriber<T> {
|
||||||
private final HttpResponse.BodySubscriber<T> delegate;
|
private final HttpResponse.BodySubscriber<T> delegate;
|
||||||
|
private Flow.Subscription subscription;
|
||||||
|
|
||||||
public ProgressTrackingBodySubscriber(HttpResponse.BodySubscriber<T> delegate) {
|
public ProgressTrackingBodySubscriber(HttpResponse.BodySubscriber<T> delegate) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
|
@ -141,6 +217,7 @@ public CompletionStage<T> getBody() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSubscribe(Flow.Subscription subscription) {
|
public void onSubscribe(Flow.Subscription subscription) {
|
||||||
|
this.subscription = subscription;
|
||||||
delegate.onSubscribe(subscription);
|
delegate.onSubscribe(subscription);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +240,12 @@ public void onError(Throwable throwable) {
|
||||||
public void onComplete() {
|
public void onComplete() {
|
||||||
delegate.onComplete();
|
delegate.onComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void cancel() {
|
||||||
|
if (subscription != null) {
|
||||||
|
subscription.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue