From 5886d1ac487d0cb1fe127e6ee63b1de6f35627cc Mon Sep 17 00:00:00 2001 From: Gravita Date: Sat, 5 Jun 2021 06:30:56 +0700 Subject: [PATCH] [FEATURE] Downloader fix --- .../provider/AuthSupportUserBan.java | 10 +- .../launcher/BasicLauncherEventHandler.java | 28 +++++ .../pro/gravit/launcher/LauncherEngine.java | 1 + .../client/ClientLauncherEntryPoint.java | 6 +- .../events/ExtendedTokenRequestEvent.java | 7 ++ .../pro/gravit/launcher/request/Request.java | 17 +++ .../pro/gravit/launcher/AsyncDownloader.java | 2 + .../java/pro/gravit/utils/Downloader.java | 26 +++- .../java11/pro/gravit/utils/Downloader.java | 115 +++++++++++++++--- 9 files changed, 185 insertions(+), 27 deletions(-) create mode 100644 Launcher/src/main/java/pro/gravit/launcher/BasicLauncherEventHandler.java create mode 100644 LauncherAPI/src/main/java/pro/gravit/launcher/events/ExtendedTokenRequestEvent.java diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportUserBan.java b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportUserBan.java index 0a550043..1877471d 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportUserBan.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/auth/core/interfaces/provider/AuthSupportUserBan.java @@ -3,11 +3,13 @@ import pro.gravit.launchserver.auth.core.User; import pro.gravit.launchserver.auth.core.interfaces.user.UserSupportBanInfo; -public interface AuthSupportUserBan { - void banUser(User user, String reason); +import java.time.LocalDateTime; - default void banUser(User user) { - banUser(user, null); +public interface AuthSupportUserBan { + 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); diff --git a/Launcher/src/main/java/pro/gravit/launcher/BasicLauncherEventHandler.java b/Launcher/src/main/java/pro/gravit/launcher/BasicLauncherEventHandler.java new file mode 100644 index 00000000..dbdc6b78 --- /dev/null +++ b/Launcher/src/main/java/pro/gravit/launcher/BasicLauncherEventHandler.java @@ -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 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; + } +} diff --git a/Launcher/src/main/java/pro/gravit/launcher/LauncherEngine.java b/Launcher/src/main/java/pro/gravit/launcher/LauncherEngine.java index 23225ee5..63311a28 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/LauncherEngine.java +++ b/Launcher/src/main/java/pro/gravit/launcher/LauncherEngine.java @@ -192,6 +192,7 @@ public void start(String... args) throws Throwable { throw new RequestException("Connection failed", e); } }; + Request.service.registerEventHandler(new BasicLauncherEventHandler()); } Objects.requireNonNull(args, "args"); if (started.getAndSet(true)) diff --git a/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java b/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java index 7524f7ad..5aa63049 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java +++ b/Launcher/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java @@ -1,9 +1,6 @@ package pro.gravit.launcher.client; -import pro.gravit.launcher.Launcher; -import pro.gravit.launcher.LauncherAgent; -import pro.gravit.launcher.LauncherConfig; -import pro.gravit.launcher.LauncherEngine; +import pro.gravit.launcher.*; import pro.gravit.launcher.api.AuthService; import pro.gravit.launcher.api.ClientService; import pro.gravit.launcher.client.events.client.*; @@ -104,6 +101,7 @@ public static void main(String[] args) throws Throwable { LogHelper.info("Using Sessions"); Request.setSession(params.session); } + Request.service.registerEventHandler(new BasicLauncherEventHandler()); checkJVMBitsAndVersion(params.profile.getMinJavaVersion(), params.profile.getRecommendJavaVersion(), params.profile.getMaxJavaVersion(), params.profile.isWarnMissJavaVersion()); LauncherEngine.modulesManager.invokeEvent(new ClientProcessInitPhase(engine, params)); diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/events/ExtendedTokenRequestEvent.java b/LauncherAPI/src/main/java/pro/gravit/launcher/events/ExtendedTokenRequestEvent.java new file mode 100644 index 00000000..09df5190 --- /dev/null +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/events/ExtendedTokenRequestEvent.java @@ -0,0 +1,7 @@ +package pro.gravit.launcher.events; + +public interface ExtendedTokenRequestEvent { + String getExtendedTokenName(); + + String getExtendedToken(); +} diff --git a/LauncherAPI/src/main/java/pro/gravit/launcher/request/Request.java b/LauncherAPI/src/main/java/pro/gravit/launcher/request/Request.java index d64d4187..ec04d18b 100644 --- a/LauncherAPI/src/main/java/pro/gravit/launcher/request/Request.java +++ b/LauncherAPI/src/main/java/pro/gravit/launcher/request/Request.java @@ -14,6 +14,7 @@ import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.BiConsumer; public abstract class Request implements WebSocketRequest { public static StdWebSocketService service; @@ -23,6 +24,7 @@ public abstract class Request implements WebSocketRequ private static String authId; private static long tokenExpiredTime; private static List extendedTokenCallbacks = new ArrayList<>(4); + private static List> oauthChangeHandlers = new ArrayList<>(4); @LauncherNetworkAPI public final UUID requestUUID = UUID.randomUUID(); private transient final AtomicBoolean started = new AtomicBoolean(false); @@ -43,6 +45,9 @@ public static void setOAuth(String authId, AuthRequestEvent.OAuthRequestEvent ev } else { tokenExpiredTime = 0; } + for (BiConsumer handler : oauthChangeHandlers) { + handler.accept(authId, event); + } } public static AuthRequestEvent.OAuthRequestEvent getOAuth() { @@ -154,6 +159,18 @@ public void addExtendedTokenCallback(ExtendedTokenCallback cb) { extendedTokenCallbacks.add(cb); } + public void removeExtendedTokenCallback(ExtendedTokenCallback cb) { + extendedTokenCallbacks.remove(cb); + } + + public void addOAuthChangeHandler(BiConsumer eventHandler) { + oauthChangeHandlers.add(eventHandler); + } + + public void removeOAuthChangeHandler(BiConsumer eventHandler) { + oauthChangeHandlers.remove(eventHandler); + } + public R request() throws Exception { if (!started.compareAndSet(false, true)) throw new IllegalStateException("Request already started"); diff --git a/LauncherCore/src/main/java/pro/gravit/launcher/AsyncDownloader.java b/LauncherCore/src/main/java/pro/gravit/launcher/AsyncDownloader.java index dd5d73e3..b8d7e89d 100644 --- a/LauncherCore/src/main/java/pro/gravit/launcher/AsyncDownloader.java +++ b/LauncherCore/src/main/java/pro/gravit/launcher/AsyncDownloader.java @@ -32,6 +32,7 @@ public class AsyncDownloader { private static volatile SSLSocketFactory sslSocketFactory; private static volatile SSLContext sslContext; public final Callback callback; + public volatile boolean isClosed; public AsyncDownloader(Callback callback) { this.callback = callback; @@ -170,6 +171,7 @@ public void transfer(InputStream input, Path file, long size) throws IOException // Download with digest update byte[] bytes = IOHelper.newBuffer(); while (downloaded < size) { + if (isClosed) throw new IOException("Download interrupted"); int remaining = (int) Math.min(size - downloaded, bytes.length); int length = input.read(bytes, 0, remaining); if (length < 0) diff --git a/LauncherCore/src/main/java/pro/gravit/utils/Downloader.java b/LauncherCore/src/main/java/pro/gravit/utils/Downloader.java index cb76e722..2eb9e4ac 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/Downloader.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/Downloader.java @@ -13,7 +13,27 @@ public interface DownloadCallback { void apply(long fullDiff); } - public CompletableFuture downloadList(List files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception { + private final CompletableFuture future; + private final AsyncDownloader asyncDownloader; + + private Downloader(CompletableFuture future, AsyncDownloader downloader) { + this.future = future; + this.asyncDownloader = downloader; + } + + public CompletableFuture getFuture() { + return future; + } + + public void cancel() { + this.asyncDownloader.isClosed = true; + } + + public boolean isCanceled() { + return this.asyncDownloader.isClosed; + } + + public static Downloader downloadList(List files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception { final boolean closeExecutor; if (executor == null) { executor = Executors.newWorkStealingPool(4); @@ -30,10 +50,10 @@ public CompletableFuture downloadList(List file CompletableFuture future = CompletableFuture.allOf(asyncDownloader.runDownloadList(list, baseURL, targetDir, executor)); ExecutorService finalExecutor = executor; - return future.thenAccept(e -> { + return new Downloader(future.thenAccept(e -> { if (closeExecutor) { finalExecutor.shutdownNow(); } - }); + }), asyncDownloader); } } diff --git a/LauncherCore/src/main/java11/pro/gravit/utils/Downloader.java b/LauncherCore/src/main/java11/pro/gravit/utils/Downloader.java index 9ea80c9a..7c9a47d1 100644 --- a/LauncherCore/src/main/java11/pro/gravit/utils/Downloader.java +++ b/LauncherCore/src/main/java11/pro/gravit/utils/Downloader.java @@ -11,6 +11,7 @@ import java.nio.ByteBuffer; import java.nio.file.Path; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.concurrent.*; @@ -29,12 +30,55 @@ public interface DownloadCallback { void onComplete(Path path); } - public static CompletableFuture downloadList(List files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception { + protected final HttpClient client; + protected final ExecutorService executor; + protected CompletableFuture future; + protected final LinkedList 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 getFuture() { + return future; + } + + public static Downloader downloadList(List files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception { boolean closeExecutor = false; if (executor == null) { executor = Executors.newWorkStealingPool(Math.min(3, threads)); 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() .version(isNoHttp2 ? HttpClient.Version.HTTP_1_1 : HttpClient.Version.HTTP_2) .followRedirects(HttpClient.Redirect.NORMAL) @@ -46,21 +90,15 @@ public static CompletableFuture downloadList(List future = downloadList(builder.build(), files, baseURL, targetDir, callback, executor, threads); - if (closeExecutor) { - ExecutorService finalExecutor = executor; - future = future.thenAccept(e -> { - finalExecutor.shutdownNow(); - }); - } - return future; + HttpClient client = builder.build(); + return new Downloader(client, executor); } private static class ConsumerObject { Consumer> next = null; } - public static CompletableFuture downloadList(HttpClient client, List files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception { + public CompletableFuture downloadFiles(List files, String baseURL, Path targetDir, DownloadCallback callback, ExecutorService executor, int threads) throws Exception { // URI scheme URI baseUri = new URI(baseURL); Collections.shuffle(files); @@ -79,7 +117,8 @@ public static CompletableFuture downloadList(HttpClient client, List downloadList(HttpClient client, List> sendAsync(HttpClient client, AsyncDownloader.SizedFile file, URI baseUri, Path targetDir, DownloadCallback callback) throws Exception { - return client.sendAsync(makeHttpRequest(baseUri, file.urlPath), makeBodyHandler(targetDir.resolve(file.filePath), callback)); + public static class DownloadTask { + public final ProgressTrackingBodyHandler bodyHandler; + public final CompletableFuture> completableFuture; + + public DownloadTask(ProgressTrackingBodyHandler bodyHandler, CompletableFuture> 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 bodyHandler = makeBodyHandler(targetDir.resolve(file.filePath), callback); + CompletableFuture> 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 host = baseUri.getHost(); int port = baseUri.getPort(); @@ -109,13 +176,14 @@ private static HttpRequest makeHttpRequest(URI baseUri, String filePath) throws .build(); } - private static HttpResponse.BodyHandler makeBodyHandler(Path file, DownloadCallback callback) { + protected ProgressTrackingBodyHandler makeBodyHandler(Path file, DownloadCallback callback) { return new ProgressTrackingBodyHandler<>(HttpResponse.BodyHandlers.ofFile(file), callback); } public static class ProgressTrackingBodyHandler implements HttpResponse.BodyHandler { private final HttpResponse.BodyHandler delegate; private final DownloadCallback callback; + private ProgressTrackingBodySubscriber subscriber; public ProgressTrackingBodyHandler(HttpResponse.BodyHandler delegate, DownloadCallback callback) { this.delegate = delegate; @@ -124,11 +192,19 @@ public ProgressTrackingBodyHandler(HttpResponse.BodyHandler delegate, Downloa @Override public HttpResponse.BodySubscriber 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 { private final HttpResponse.BodySubscriber delegate; + private Flow.Subscription subscription; public ProgressTrackingBodySubscriber(HttpResponse.BodySubscriber delegate) { this.delegate = delegate; @@ -141,6 +217,7 @@ public CompletionStage getBody() { @Override public void onSubscribe(Flow.Subscription subscription) { + this.subscription = subscription; delegate.onSubscribe(subscription); } @@ -163,6 +240,12 @@ public void onError(Throwable throwable) { public void onComplete() { delegate.onComplete(); } + + public void cancel() { + if (subscription != null) { + subscription.cancel(); + } + } } } }