将代码中的 URL 替换为 URI (#4131)
This commit is contained in:
@@ -53,7 +53,7 @@ import java.io.File;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.AccessDeniedException;
|
import java.nio.file.AccessDeniedException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
@@ -267,27 +267,27 @@ public final class LauncherHelper {
|
|||||||
if (ex.getCause() instanceof ResponseCodeException) {
|
if (ex.getCause() instanceof ResponseCodeException) {
|
||||||
ResponseCodeException rce = (ResponseCodeException) ex.getCause();
|
ResponseCodeException rce = (ResponseCodeException) ex.getCause();
|
||||||
int responseCode = rce.getResponseCode();
|
int responseCode = rce.getResponseCode();
|
||||||
URL url = rce.getUrl();
|
URI uri = rce.getUri();
|
||||||
if (responseCode == 404)
|
if (responseCode == 404)
|
||||||
message += i18n("download.code.404", url);
|
message += i18n("download.code.404", uri);
|
||||||
else
|
else
|
||||||
message += i18n("download.failed", url, responseCode);
|
message += i18n("download.failed", uri, responseCode);
|
||||||
} else {
|
} else {
|
||||||
message += StringUtils.getStackTrace(ex.getCause());
|
message += StringUtils.getStackTrace(ex.getCause());
|
||||||
}
|
}
|
||||||
} else if (ex instanceof DownloadException) {
|
} else if (ex instanceof DownloadException) {
|
||||||
URL url = ((DownloadException) ex).getUrl();
|
URI uri = ((DownloadException) ex).getUri();
|
||||||
if (ex.getCause() instanceof SocketTimeoutException) {
|
if (ex.getCause() instanceof SocketTimeoutException) {
|
||||||
message = i18n("install.failed.downloading.timeout", url);
|
message = i18n("install.failed.downloading.timeout", uri);
|
||||||
} else if (ex.getCause() instanceof ResponseCodeException) {
|
} else if (ex.getCause() instanceof ResponseCodeException) {
|
||||||
ResponseCodeException responseCodeException = (ResponseCodeException) ex.getCause();
|
ResponseCodeException responseCodeException = (ResponseCodeException) ex.getCause();
|
||||||
if (I18n.hasKey("download.code." + responseCodeException.getResponseCode())) {
|
if (I18n.hasKey("download.code." + responseCodeException.getResponseCode())) {
|
||||||
message = i18n("download.code." + responseCodeException.getResponseCode(), url);
|
message = i18n("download.code." + responseCodeException.getResponseCode(), uri);
|
||||||
} else {
|
} else {
|
||||||
message = i18n("install.failed.downloading.detail", url) + "\n" + StringUtils.getStackTrace(ex.getCause());
|
message = i18n("install.failed.downloading.detail", uri) + "\n" + StringUtils.getStackTrace(ex.getCause());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
message = i18n("install.failed.downloading.detail", url) + "\n" + StringUtils.getStackTrace(ex.getCause());
|
message = i18n("install.failed.downloading.detail", uri) + "\n" + StringUtils.getStackTrace(ex.getCause());
|
||||||
}
|
}
|
||||||
} else if (ex instanceof GameAssetIndexDownloadTask.GameAssetIndexMalformedException) {
|
} else if (ex instanceof GameAssetIndexDownloadTask.GameAssetIndexMalformedException) {
|
||||||
message = i18n("assets.index.malformed");
|
message = i18n("assets.index.malformed");
|
||||||
@@ -298,11 +298,11 @@ public final class LauncherHelper {
|
|||||||
} else if (ex instanceof ResponseCodeException) {
|
} else if (ex instanceof ResponseCodeException) {
|
||||||
ResponseCodeException rce = (ResponseCodeException) ex;
|
ResponseCodeException rce = (ResponseCodeException) ex;
|
||||||
int responseCode = rce.getResponseCode();
|
int responseCode = rce.getResponseCode();
|
||||||
URL url = rce.getUrl();
|
URI uri = rce.getUri();
|
||||||
if (responseCode == 404)
|
if (responseCode == 404)
|
||||||
message = i18n("download.code.404", url);
|
message = i18n("download.code.404", uri);
|
||||||
else
|
else
|
||||||
message = i18n("download.failed", url, responseCode);
|
message = i18n("download.failed", uri, responseCode);
|
||||||
} else if (ex instanceof CommandTooLongException) {
|
} else if (ex instanceof CommandTooLongException) {
|
||||||
message = i18n("launch.failed.command_too_long");
|
message = i18n("launch.failed.command_too_long");
|
||||||
} else if (ex instanceof ExecutionPolicyLimitException) {
|
} else if (ex instanceof ExecutionPolicyLimitException) {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -110,7 +110,7 @@ public final class TexturesLoader {
|
|||||||
if (!Files.isRegularFile(file)) {
|
if (!Files.isRegularFile(file)) {
|
||||||
// download it
|
// download it
|
||||||
try {
|
try {
|
||||||
new FileDownloadTask(new URL(texture.getUrl()), file.toFile()).run();
|
new FileDownloadTask(URI.create(texture.getUrl()), file).run();
|
||||||
LOG.info("Texture downloaded: " + texture.getUrl());
|
LOG.info("Texture downloaded: " + texture.getUrl());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (Files.isRegularFile(file)) {
|
if (Files.isRegularFile(file)) {
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import org.jackhuang.hmcl.util.io.ResponseCodeException;
|
|||||||
import javax.net.ssl.SSLHandshakeException;
|
import javax.net.ssl.SSLHandshakeException;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.AccessDeniedException;
|
import java.nio.file.AccessDeniedException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -137,26 +137,26 @@ public final class DownloadProviders {
|
|||||||
|
|
||||||
public static String localizeErrorMessage(Throwable exception) {
|
public static String localizeErrorMessage(Throwable exception) {
|
||||||
if (exception instanceof DownloadException) {
|
if (exception instanceof DownloadException) {
|
||||||
URL url = ((DownloadException) exception).getUrl();
|
URI uri = ((DownloadException) exception).getUri();
|
||||||
if (exception.getCause() instanceof SocketTimeoutException) {
|
if (exception.getCause() instanceof SocketTimeoutException) {
|
||||||
return i18n("install.failed.downloading.timeout", url);
|
return i18n("install.failed.downloading.timeout", uri);
|
||||||
} else if (exception.getCause() instanceof ResponseCodeException) {
|
} else if (exception.getCause() instanceof ResponseCodeException) {
|
||||||
ResponseCodeException responseCodeException = (ResponseCodeException) exception.getCause();
|
ResponseCodeException responseCodeException = (ResponseCodeException) exception.getCause();
|
||||||
if (I18n.hasKey("download.code." + responseCodeException.getResponseCode())) {
|
if (I18n.hasKey("download.code." + responseCodeException.getResponseCode())) {
|
||||||
return i18n("download.code." + responseCodeException.getResponseCode(), url);
|
return i18n("download.code." + responseCodeException.getResponseCode(), uri);
|
||||||
} else {
|
} else {
|
||||||
return i18n("install.failed.downloading.detail", url) + "\n" + StringUtils.getStackTrace(exception.getCause());
|
return i18n("install.failed.downloading.detail", uri) + "\n" + StringUtils.getStackTrace(exception.getCause());
|
||||||
}
|
}
|
||||||
} else if (exception.getCause() instanceof FileNotFoundException) {
|
} else if (exception.getCause() instanceof FileNotFoundException) {
|
||||||
return i18n("download.code.404", url);
|
return i18n("download.code.404", uri);
|
||||||
} else if (exception.getCause() instanceof AccessDeniedException) {
|
} else if (exception.getCause() instanceof AccessDeniedException) {
|
||||||
return i18n("install.failed.downloading.detail", url) + "\n" + i18n("exception.access_denied", ((AccessDeniedException) exception.getCause()).getFile());
|
return i18n("install.failed.downloading.detail", uri) + "\n" + i18n("exception.access_denied", ((AccessDeniedException) exception.getCause()).getFile());
|
||||||
} else if (exception.getCause() instanceof ArtifactMalformedException) {
|
} else if (exception.getCause() instanceof ArtifactMalformedException) {
|
||||||
return i18n("install.failed.downloading.detail", url) + "\n" + i18n("exception.artifact_malformed");
|
return i18n("install.failed.downloading.detail", uri) + "\n" + i18n("exception.artifact_malformed");
|
||||||
} else if (exception.getCause() instanceof SSLHandshakeException) {
|
} else if (exception.getCause() instanceof SSLHandshakeException) {
|
||||||
return i18n("install.failed.downloading.detail", url) + "\n" + i18n("exception.ssl_handshake");
|
return i18n("install.failed.downloading.detail", uri) + "\n" + i18n("exception.ssl_handshake");
|
||||||
} else {
|
} else {
|
||||||
return i18n("install.failed.downloading.detail", url) + "\n" + StringUtils.getStackTrace(exception.getCause());
|
return i18n("install.failed.downloading.detail", uri) + "\n" + StringUtils.getStackTrace(exception.getCause());
|
||||||
}
|
}
|
||||||
} else if (exception instanceof ArtifactMalformedException) {
|
} else if (exception instanceof ArtifactMalformedException) {
|
||||||
return i18n("exception.artifact_malformed");
|
return i18n("exception.artifact_malformed");
|
||||||
|
|||||||
@@ -18,13 +18,15 @@
|
|||||||
package org.jackhuang.hmcl.setting;
|
package org.jackhuang.hmcl.setting;
|
||||||
|
|
||||||
import javafx.beans.InvalidationListener;
|
import javafx.beans.InvalidationListener;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
@@ -32,7 +34,10 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
|||||||
public final class ProxyManager {
|
public final class ProxyManager {
|
||||||
|
|
||||||
private static final ProxySelector NO_PROXY = new SimpleProxySelector(Proxy.NO_PROXY);
|
private static final ProxySelector NO_PROXY = new SimpleProxySelector(Proxy.NO_PROXY);
|
||||||
private static final ProxySelector SYSTEM_DEFAULT = Lang.requireNonNullElse(ProxySelector.getDefault(), NO_PROXY);
|
private static final ProxySelector SYSTEM_DEFAULT = Objects.requireNonNullElse(ProxySelector.getDefault(), NO_PROXY);
|
||||||
|
|
||||||
|
private static volatile @NotNull ProxySelector defaultProxySelector = SYSTEM_DEFAULT;
|
||||||
|
private static volatile @Nullable SimpleAuthenticator defaultAuthenticator = null;
|
||||||
|
|
||||||
private static ProxySelector getProxySelector() {
|
private static ProxySelector getProxySelector() {
|
||||||
if (config().hasProxy()) {
|
if (config().hasProxy()) {
|
||||||
@@ -53,7 +58,7 @@ public final class ProxyManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Authenticator getAuthenticator() {
|
private static SimpleAuthenticator getAuthenticator() {
|
||||||
if (config().hasProxy() && config().hasProxyAuth()) {
|
if (config().hasProxy() && config().hasProxyAuth()) {
|
||||||
String username = config().getProxyUser();
|
String username = config().getProxyUser();
|
||||||
String password = config().getProxyPass();
|
String password = config().getProxyPass();
|
||||||
@@ -67,15 +72,34 @@ public final class ProxyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void init() {
|
static void init() {
|
||||||
ProxySelector.setDefault(getProxySelector());
|
ProxySelector.setDefault(new ProxySelector() {
|
||||||
InvalidationListener updateProxySelector = observable -> ProxySelector.setDefault(getProxySelector());
|
@Override
|
||||||
|
public List<Proxy> select(URI uri) {
|
||||||
|
return defaultProxySelector.select(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
|
||||||
|
defaultProxySelector.connectFailed(uri, sa, ioe);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Authenticator.setDefault(new Authenticator() {
|
||||||
|
@Override
|
||||||
|
protected PasswordAuthentication getPasswordAuthentication() {
|
||||||
|
var defaultAuthenticator = ProxyManager.defaultAuthenticator;
|
||||||
|
return defaultAuthenticator != null ? defaultAuthenticator.getPasswordAuthentication() : null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
defaultProxySelector = getProxySelector();
|
||||||
|
InvalidationListener updateProxySelector = observable -> defaultProxySelector = getProxySelector();
|
||||||
config().proxyTypeProperty().addListener(updateProxySelector);
|
config().proxyTypeProperty().addListener(updateProxySelector);
|
||||||
config().proxyHostProperty().addListener(updateProxySelector);
|
config().proxyHostProperty().addListener(updateProxySelector);
|
||||||
config().proxyPortProperty().addListener(updateProxySelector);
|
config().proxyPortProperty().addListener(updateProxySelector);
|
||||||
config().hasProxyProperty().addListener(updateProxySelector);
|
config().hasProxyProperty().addListener(updateProxySelector);
|
||||||
|
|
||||||
Authenticator.setDefault(getAuthenticator());
|
defaultAuthenticator = getAuthenticator();
|
||||||
InvalidationListener updateAuthenticator = observable -> Authenticator.setDefault(getAuthenticator());
|
InvalidationListener updateAuthenticator = observable -> defaultAuthenticator = getAuthenticator();
|
||||||
config().hasProxyProperty().addListener(updateAuthenticator);
|
config().hasProxyProperty().addListener(updateAuthenticator);
|
||||||
config().hasProxyAuthProperty().addListener(updateAuthenticator);
|
config().hasProxyAuthProperty().addListener(updateAuthenticator);
|
||||||
config().proxyUserProperty().addListener(updateAuthenticator);
|
config().proxyUserProperty().addListener(updateAuthenticator);
|
||||||
@@ -124,7 +148,7 @@ public final class ProxyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PasswordAuthentication getPasswordAuthentication() {
|
public PasswordAuthentication getPasswordAuthentication() {
|
||||||
return getRequestorType() == RequestorType.PROXY ? new PasswordAuthentication(username, password) : null;
|
return getRequestorType() == RequestorType.PROXY ? new PasswordAuthentication(username, password) : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -844,14 +844,14 @@ public final class FXUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Image loadImage(URL url) throws Exception {
|
public static Image loadImage(URI uri) throws Exception {
|
||||||
URLConnection connection = NetworkUtils.createConnection(url);
|
URLConnection connection = NetworkUtils.createConnection(uri);
|
||||||
if (connection instanceof HttpURLConnection) {
|
if (connection instanceof HttpURLConnection) {
|
||||||
connection = NetworkUtils.resolveConnection((HttpURLConnection) connection);
|
connection = NetworkUtils.resolveConnection((HttpURLConnection) connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
try (InputStream input = connection.getInputStream()) {
|
try (InputStream input = connection.getInputStream()) {
|
||||||
String path = url.getPath();
|
String path = uri.getPath();
|
||||||
if (path != null && "webp".equalsIgnoreCase(StringUtils.substringAfterLast(path, '.')))
|
if (path != null && "webp".equalsIgnoreCase(StringUtils.substringAfterLast(path, '.')))
|
||||||
return loadWebPImage(input);
|
return loadWebPImage(input);
|
||||||
else {
|
else {
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ import org.jackhuang.hmcl.ui.construct.DialogAware;
|
|||||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||||
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed;
|
||||||
@@ -196,7 +196,7 @@ public final class AddAuthlibInjectorServerPane extends TransitionPane implement
|
|||||||
lblServerName.setText(serverBeingAdded.getName());
|
lblServerName.setText(serverBeingAdded.getName());
|
||||||
lblServerUrl.setText(serverBeingAdded.getUrl());
|
lblServerUrl.setText(serverBeingAdded.getUrl());
|
||||||
|
|
||||||
lblServerWarning.setVisible("http".equals(NetworkUtils.toURL(serverBeingAdded.getUrl()).getProtocol()));
|
lblServerWarning.setVisible("http".equals(URI.create(serverBeingAdded.getUrl()).getScheme()));
|
||||||
|
|
||||||
this.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT);
|
this.setContent(confirmServerPane, ContainerAnimations.SWIPE_LEFT);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ import javafx.scene.control.ScrollPane;
|
|||||||
import javafx.scene.layout.BorderPane;
|
import javafx.scene.layout.BorderPane;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.*;
|
||||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
|
||||||
import org.jackhuang.hmcl.task.TaskListener;
|
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -43,7 +41,7 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
|||||||
public class TaskExecutorDialogPane extends BorderPane {
|
public class TaskExecutorDialogPane extends BorderPane {
|
||||||
private TaskExecutor executor;
|
private TaskExecutor executor;
|
||||||
private TaskCancellationAction onCancel;
|
private TaskCancellationAction onCancel;
|
||||||
private final Consumer<FileDownloadTask.SpeedEvent> speedEventHandler;
|
private final Consumer<FetchTask.SpeedEvent> speedEventHandler;
|
||||||
|
|
||||||
private final Label lblTitle;
|
private final Label lblTitle;
|
||||||
private final Label lblProgress;
|
private final Label lblProgress;
|
||||||
@@ -108,7 +106,7 @@ public class TaskExecutorDialogPane extends BorderPane {
|
|||||||
String finalUnit = unit;
|
String finalUnit = unit;
|
||||||
Platform.runLater(() -> lblProgress.setText(String.format("%.1f %s", finalSpeed, finalUnit)));
|
Platform.runLater(() -> lblProgress.setText(String.format("%.1f %s", finalSpeed, finalUnit)));
|
||||||
};
|
};
|
||||||
FileDownloadTask.speedEvent.channel(FileDownloadTask.SpeedEvent.class).registerWeak(speedEventHandler);
|
FileDownloadTask.speedEvent.channel(FetchTask.SpeedEvent.class).registerWeak(speedEventHandler);
|
||||||
|
|
||||||
onEscPressed(this, btnCancel::fire);
|
onEscPressed(this, btnCancel::fire);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@@ -214,7 +214,7 @@ public class DecoratorController {
|
|||||||
String backgroundImageUrl = config().getBackgroundImageUrl();
|
String backgroundImageUrl = config().getBackgroundImageUrl();
|
||||||
if (backgroundImageUrl != null) {
|
if (backgroundImageUrl != null) {
|
||||||
try {
|
try {
|
||||||
image = FXUtils.loadImage(new URL(backgroundImageUrl));
|
image = FXUtils.loadImage(URI.create(backgroundImageUrl));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warning("Couldn't load background image", e);
|
LOG.warning("Couldn't load background image", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ import org.jackhuang.hmcl.ui.wizard.Navigation;
|
|||||||
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
import org.jackhuang.hmcl.ui.wizard.WizardController;
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
||||||
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
|
||||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -156,7 +156,7 @@ public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage
|
|||||||
Path dest = runDirectory.resolve(subdirectoryName).resolve(result);
|
Path dest = runDirectory.resolve(subdirectoryName).resolve(result);
|
||||||
|
|
||||||
Controllers.taskDialog(Task.composeAsync(() -> {
|
Controllers.taskDialog(Task.composeAsync(() -> {
|
||||||
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(file.getFile().getUrl()), dest.toFile());
|
var task = new FileDownloadTask(URI.create(file.getFile().getUrl()), dest);
|
||||||
task.setName(file.getName());
|
task.setName(file.getName());
|
||||||
return task;
|
return task;
|
||||||
}).whenComplete(Schedulers.javafx(), exception -> {
|
}).whenComplete(Schedulers.javafx(), exception -> {
|
||||||
|
|||||||
@@ -29,9 +29,7 @@ import javafx.scene.shape.SVGPath;
|
|||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import org.jackhuang.hmcl.game.ModpackHelper;
|
import org.jackhuang.hmcl.game.ModpackHelper;
|
||||||
import org.jackhuang.hmcl.mod.server.ServerModpackManifest;
|
import org.jackhuang.hmcl.mod.server.ServerModpackManifest;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.*;
|
||||||
import org.jackhuang.hmcl.task.GetTask;
|
|
||||||
import org.jackhuang.hmcl.task.Schedulers;
|
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.SVG;
|
import org.jackhuang.hmcl.ui.SVG;
|
||||||
@@ -43,7 +41,7 @@ import org.jackhuang.hmcl.util.gson.JsonUtils;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -128,7 +126,7 @@ public final class ModpackSelectionPage extends VBox implements WizardPage {
|
|||||||
private void onChooseRemoteFile() {
|
private void onChooseRemoteFile() {
|
||||||
Controllers.prompt(i18n("modpack.choose.remote.tooltip"), (urlString, resolve, reject) -> {
|
Controllers.prompt(i18n("modpack.choose.remote.tooltip"), (urlString, resolve, reject) -> {
|
||||||
try {
|
try {
|
||||||
URL url = new URL(urlString);
|
URI url = URI.create(urlString);
|
||||||
if (urlString.endsWith("server-manifest.json")) {
|
if (urlString.endsWith("server-manifest.json")) {
|
||||||
// if urlString ends with .json, we assume that the url is server-manifest.json
|
// if urlString ends with .json, we assume that the url is server-manifest.json
|
||||||
Controllers.taskDialog(new GetTask(url).whenComplete(Schedulers.javafx(), (result, e) -> {
|
Controllers.taskDialog(new GetTask(url).whenComplete(Schedulers.javafx(), (result, e) -> {
|
||||||
@@ -150,7 +148,7 @@ public final class ModpackSelectionPage extends VBox implements WizardPage {
|
|||||||
resolve.run();
|
resolve.run();
|
||||||
|
|
||||||
Controllers.taskDialog(
|
Controllers.taskDialog(
|
||||||
new FileDownloadTask(url, modpack.toFile(), null)
|
new FileDownloadTask(url, modpack, null)
|
||||||
.whenComplete(Schedulers.javafx(), e -> {
|
.whenComplete(Schedulers.javafx(), e -> {
|
||||||
if (e == null) {
|
if (e == null) {
|
||||||
resolve.run();
|
resolve.run();
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.net.SocketTimeoutException;
|
import java.net.SocketTimeoutException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -133,28 +133,28 @@ public final class UpdateInstallerWizardProvider implements WizardProvider {
|
|||||||
if (exception.getCause() instanceof ResponseCodeException) {
|
if (exception.getCause() instanceof ResponseCodeException) {
|
||||||
ResponseCodeException rce = (ResponseCodeException) exception.getCause();
|
ResponseCodeException rce = (ResponseCodeException) exception.getCause();
|
||||||
int responseCode = rce.getResponseCode();
|
int responseCode = rce.getResponseCode();
|
||||||
URL url = rce.getUrl();
|
URI uri = rce.getUri();
|
||||||
if (responseCode == 404)
|
if (responseCode == 404)
|
||||||
message += i18n("download.code.404", url);
|
message += i18n("download.code.404", uri);
|
||||||
else
|
else
|
||||||
message += i18n("download.failed", url, responseCode);
|
message += i18n("download.failed", uri, responseCode);
|
||||||
} else {
|
} else {
|
||||||
message += StringUtils.getStackTrace(exception.getCause());
|
message += StringUtils.getStackTrace(exception.getCause());
|
||||||
}
|
}
|
||||||
Controllers.dialog(message, i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR, next);
|
Controllers.dialog(message, i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR, next);
|
||||||
} else if (exception instanceof DownloadException) {
|
} else if (exception instanceof DownloadException) {
|
||||||
URL url = ((DownloadException) exception).getUrl();
|
URI uri = ((DownloadException) exception).getUri();
|
||||||
if (exception.getCause() instanceof SocketTimeoutException) {
|
if (exception.getCause() instanceof SocketTimeoutException) {
|
||||||
Controllers.dialog(i18n("install.failed.downloading.timeout", url), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR, next);
|
Controllers.dialog(i18n("install.failed.downloading.timeout", uri), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR, next);
|
||||||
} else if (exception.getCause() instanceof ResponseCodeException) {
|
} else if (exception.getCause() instanceof ResponseCodeException) {
|
||||||
ResponseCodeException responseCodeException = (ResponseCodeException) exception.getCause();
|
ResponseCodeException responseCodeException = (ResponseCodeException) exception.getCause();
|
||||||
if (I18n.hasKey("download.code." + responseCodeException.getResponseCode())) {
|
if (I18n.hasKey("download.code." + responseCodeException.getResponseCode())) {
|
||||||
Controllers.dialog(i18n("download.code." + responseCodeException.getResponseCode(), url), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR, next);
|
Controllers.dialog(i18n("download.code." + responseCodeException.getResponseCode(), uri), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR, next);
|
||||||
} else {
|
} else {
|
||||||
Controllers.dialog(i18n("install.failed.downloading.detail", url) + "\n" + StringUtils.getStackTrace(exception.getCause()), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR, next);
|
Controllers.dialog(i18n("install.failed.downloading.detail", uri) + "\n" + StringUtils.getStackTrace(exception.getCause()), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR, next);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Controllers.dialog(i18n("install.failed.downloading.detail", url) + "\n" + StringUtils.getStackTrace(exception.getCause()), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR, next);
|
Controllers.dialog(i18n("install.failed.downloading.detail", uri) + "\n" + StringUtils.getStackTrace(exception.getCause()), i18n("install.failed.downloading"), MessageDialogPane.MessageType.ERROR, next);
|
||||||
}
|
}
|
||||||
} else if (exception instanceof UnsupportedInstallationException) {
|
} else if (exception instanceof UnsupportedInstallationException) {
|
||||||
switch (((UnsupportedInstallationException) exception).getReason()) {
|
switch (((UnsupportedInstallationException) exception).getReason()) {
|
||||||
|
|||||||
@@ -363,7 +363,7 @@ public final class JavaDownloadDialog extends StackPane {
|
|||||||
return getIntegrityCheck
|
return getIntegrityCheck
|
||||||
.thenComposeAsync(integrityCheck ->
|
.thenComposeAsync(integrityCheck ->
|
||||||
new FileDownloadTask(downloadProvider.injectURLWithCandidates(fileInfo.getDirectDownloadUri()),
|
new FileDownloadTask(downloadProvider.injectURLWithCandidates(fileInfo.getDirectDownloadUri()),
|
||||||
targetFile, integrityCheck).setName(fileInfo.getFileName()))
|
targetFile.toPath(), integrityCheck).setName(fileInfo.getFileName()))
|
||||||
.thenSupplyAsync(targetFile::toPath);
|
.thenSupplyAsync(targetFile::toPath);
|
||||||
})
|
})
|
||||||
.whenComplete(Schedulers.javafx(), ((result, exception) -> {
|
.whenComplete(Schedulers.javafx(), ((result, exception) -> {
|
||||||
|
|||||||
@@ -49,12 +49,12 @@ import org.jackhuang.hmcl.ui.construct.*;
|
|||||||
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||||
import org.jackhuang.hmcl.util.*;
|
import org.jackhuang.hmcl.util.*;
|
||||||
import org.jackhuang.hmcl.util.i18n.I18n;
|
import org.jackhuang.hmcl.util.i18n.I18n;
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
|
||||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.net.URI;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@@ -180,7 +180,7 @@ public class DownloadPage extends Control implements DecoratorPage {
|
|||||||
|
|
||||||
Controllers.taskDialog(
|
Controllers.taskDialog(
|
||||||
Task.composeAsync(() -> {
|
Task.composeAsync(() -> {
|
||||||
FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(file.getFile().getUrl()), dest, file.getFile().getIntegrityCheck());
|
var task = new FileDownloadTask(URI.create(file.getFile().getUrl()), dest.toPath(), file.getFile().getIntegrityCheck());
|
||||||
task.setName(file.getName());
|
task.setName(file.getName());
|
||||||
return task;
|
return task;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import org.jackhuang.hmcl.util.Pair;
|
|||||||
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
import org.jackhuang.hmcl.util.TaskCancellationAction;
|
||||||
import org.jackhuang.hmcl.util.io.CSVTable;
|
import org.jackhuang.hmcl.util.io.CSVTable;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@@ -293,9 +293,9 @@ public class ModUpdatesPage extends BorderPane implements DecoratorPage {
|
|||||||
if (isDisabled)
|
if (isDisabled)
|
||||||
fileName += ModManager.DISABLED_EXTENSION;
|
fileName += ModManager.DISABLED_EXTENSION;
|
||||||
|
|
||||||
FileDownloadTask task = new FileDownloadTask(
|
var task = new FileDownloadTask(
|
||||||
new URL(remote.getFile().getUrl()),
|
URI.create(remote.getFile().getUrl()),
|
||||||
modManager.getModsDirectory().resolve(fileName).toFile());
|
modManager.getModsDirectory().resolve(fileName));
|
||||||
|
|
||||||
task.setName(remote.getName());
|
task.setName(remote.getName());
|
||||||
return task;
|
return task;
|
||||||
|
|||||||
@@ -28,10 +28,7 @@ import org.jackhuang.hmcl.game.GameRepository;
|
|||||||
import org.jackhuang.hmcl.game.LauncherHelper;
|
import org.jackhuang.hmcl.game.LauncherHelper;
|
||||||
import org.jackhuang.hmcl.mod.RemoteMod;
|
import org.jackhuang.hmcl.mod.RemoteMod;
|
||||||
import org.jackhuang.hmcl.setting.*;
|
import org.jackhuang.hmcl.setting.*;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.*;
|
||||||
import org.jackhuang.hmcl.task.Schedulers;
|
|
||||||
import org.jackhuang.hmcl.task.Task;
|
|
||||||
import org.jackhuang.hmcl.task.TaskExecutor;
|
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.account.CreateAccountPane;
|
import org.jackhuang.hmcl.ui.account.CreateAccountPane;
|
||||||
@@ -47,7 +44,8 @@ import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
@@ -75,18 +73,18 @@ public final class Versions {
|
|||||||
|
|
||||||
public static void downloadModpackImpl(Profile profile, String version, RemoteMod.Version file) {
|
public static void downloadModpackImpl(Profile profile, String version, RemoteMod.Version file) {
|
||||||
Path modpack;
|
Path modpack;
|
||||||
URL downloadURL;
|
URI downloadURL;
|
||||||
try {
|
try {
|
||||||
modpack = Files.createTempFile("modpack", ".zip");
|
modpack = Files.createTempFile("modpack", ".zip");
|
||||||
downloadURL = new URL(file.getFile().getUrl());
|
downloadURL = new URI(file.getFile().getUrl());
|
||||||
} catch (IOException e) {
|
} catch (IOException | URISyntaxException e) {
|
||||||
Controllers.dialog(
|
Controllers.dialog(
|
||||||
i18n("install.failed.downloading.detail", file.getFile().getUrl()) + "\n" + StringUtils.getStackTrace(e),
|
i18n("install.failed.downloading.detail", file.getFile().getUrl()) + "\n" + StringUtils.getStackTrace(e),
|
||||||
i18n("download.failed.no_code"), MessageDialogPane.MessageType.ERROR);
|
i18n("download.failed.no_code"), MessageDialogPane.MessageType.ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Controllers.taskDialog(
|
Controllers.taskDialog(
|
||||||
new FileDownloadTask(downloadURL, modpack.toFile())
|
new FileDownloadTask(downloadURL, modpack)
|
||||||
.whenComplete(Schedulers.javafx(), e -> {
|
.whenComplete(Schedulers.javafx(), e -> {
|
||||||
if (e == null) {
|
if (e == null) {
|
||||||
if (version != null) {
|
if (version != null) {
|
||||||
|
|||||||
@@ -19,21 +19,21 @@ package org.jackhuang.hmcl.upgrade;
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
import org.jackhuang.hmcl.util.Pack200Utils;
|
import org.jackhuang.hmcl.util.Pack200Utils;
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
|
||||||
import org.tukaani.xz.XZInputStream;
|
import org.tukaani.xz.XZInputStream;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.jar.JarOutputStream;
|
import java.util.jar.JarOutputStream;
|
||||||
|
|
||||||
class HMCLDownloadTask extends FileDownloadTask {
|
final class HMCLDownloadTask extends FileDownloadTask {
|
||||||
|
|
||||||
private RemoteVersion.Type archiveFormat;
|
private final RemoteVersion.Type archiveFormat;
|
||||||
|
|
||||||
public HMCLDownloadTask(RemoteVersion version, Path target) {
|
public HMCLDownloadTask(RemoteVersion version, Path target) {
|
||||||
super(NetworkUtils.toURL(version.getUrl()), target.toFile(), version.getIntegrityCheck());
|
super(URI.create(version.getUrl()), target, version.getIntegrityCheck());
|
||||||
archiveFormat = version.getType();
|
archiveFormat = version.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,8 +42,7 @@ class HMCLDownloadTask extends FileDownloadTask {
|
|||||||
super.execute();
|
super.execute();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Path target = getFile().toPath();
|
Path target = getPath();
|
||||||
|
|
||||||
switch (archiveFormat) {
|
switch (archiveFormat) {
|
||||||
case JAR:
|
case JAR:
|
||||||
break;
|
break;
|
||||||
@@ -51,7 +50,7 @@ class HMCLDownloadTask extends FileDownloadTask {
|
|||||||
case PACK_XZ:
|
case PACK_XZ:
|
||||||
byte[] raw = Files.readAllBytes(target);
|
byte[] raw = Files.readAllBytes(target);
|
||||||
try (InputStream in = new XZInputStream(new ByteArrayInputStream(raw));
|
try (InputStream in = new XZInputStream(new ByteArrayInputStream(raw));
|
||||||
JarOutputStream out = new JarOutputStream(Files.newOutputStream(target))) {
|
JarOutputStream out = new JarOutputStream(Files.newOutputStream(target))) {
|
||||||
Pack200Utils.unpack(in, out);
|
Pack200Utils.unpack(in, out);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -60,7 +59,11 @@ class HMCLDownloadTask extends FileDownloadTask {
|
|||||||
throw new IllegalArgumentException("Unknown format: " + archiveFormat);
|
throw new IllegalArgumentException("Unknown format: " + archiveFormat);
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
getFile().delete();
|
try {
|
||||||
|
Files.deleteIfExists(getPath());
|
||||||
|
} catch (Throwable e2) {
|
||||||
|
e.addSuppressed(e2);
|
||||||
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,14 @@ import org.jackhuang.hmcl.util.gson.JsonUtils;
|
|||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class RemoteVersion {
|
public class RemoteVersion {
|
||||||
|
|
||||||
public static RemoteVersion fetch(UpdateChannel channel, String url) throws IOException {
|
public static RemoteVersion fetch(UpdateChannel channel, String url) throws IOException {
|
||||||
try {
|
try {
|
||||||
JsonObject response = JsonUtils.fromNonNullJson(NetworkUtils.doGet(NetworkUtils.toURL(url)), JsonObject.class);
|
JsonObject response = JsonUtils.fromNonNullJson(NetworkUtils.doGet(URI.create(url)), JsonObject.class);
|
||||||
String version = Optional.ofNullable(response.get("version")).map(JsonElement::getAsString).orElseThrow(() -> new IOException("version is missing"));
|
String version = Optional.ofNullable(response.get("version")).map(JsonElement::getAsString).orElseThrow(() -> new IOException("version is missing"));
|
||||||
String jarUrl = Optional.ofNullable(response.get("jar")).map(JsonElement::getAsString).orElse(null);
|
String jarUrl = Optional.ofNullable(response.get("jar")).map(JsonElement::getAsString).orElse(null);
|
||||||
String jarHash = Optional.ofNullable(response.get("jarsha1")).map(JsonElement::getAsString).orElse(null);
|
String jarHash = Optional.ofNullable(response.get("jarsha1")).map(JsonElement::getAsString).orElse(null);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import org.jackhuang.hmcl.upgrade.UpdateChecker;
|
|||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
@@ -122,7 +123,7 @@ public final class CrashReporter implements Thread.UncaughtExceptionHandler {
|
|||||||
map.put("version", Metadata.VERSION);
|
map.put("version", Metadata.VERSION);
|
||||||
map.put("log", LOG.getLogs());
|
map.put("log", LOG.getLogs());
|
||||||
try {
|
try {
|
||||||
String response = NetworkUtils.doPost(NetworkUtils.toURL(Metadata.PUBLISH_URL + "/hmcl/crash.php"), map);
|
String response = NetworkUtils.doPost(URI.create(Metadata.PUBLISH_URL + "/hmcl/crash.php"), map);
|
||||||
if (StringUtils.isNotBlank(response))
|
if (StringUtils.isNotBlank(response))
|
||||||
LOG.error("Crash server response: " + response);
|
LOG.error("Crash server response: " + response);
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
|||||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -96,7 +96,7 @@ public class AuthlibInjectorDownloader implements AuthlibInjectorArtifactProvide
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
new FileDownloadTask(downloadProvider.get().injectURLWithCandidates(latest.downloadUrl), artifactLocation.toFile(),
|
new FileDownloadTask(downloadProvider.get().injectURLWithCandidates(latest.downloadUrl), artifactLocation,
|
||||||
Optional.ofNullable(latest.checksums.get("sha256"))
|
Optional.ofNullable(latest.checksums.get("sha256"))
|
||||||
.map(checksum -> new IntegrityCheck("SHA-256", checksum))
|
.map(checksum -> new IntegrityCheck("SHA-256", checksum))
|
||||||
.orElse(null))
|
.orElse(null))
|
||||||
@@ -110,9 +110,9 @@ public class AuthlibInjectorDownloader implements AuthlibInjectorArtifactProvide
|
|||||||
|
|
||||||
private AuthlibInjectorVersionInfo getLatestArtifactInfo() throws IOException {
|
private AuthlibInjectorVersionInfo getLatestArtifactInfo() throws IOException {
|
||||||
IOException exception = null;
|
IOException exception = null;
|
||||||
for (URL url : downloadProvider.get().injectURLWithCandidates(LATEST_BUILD_URL)) {
|
for (URI url : downloadProvider.get().injectURLWithCandidates(LATEST_BUILD_URL)) {
|
||||||
try {
|
try {
|
||||||
return HttpRequest.GET(url.toExternalForm()).getJson(AuthlibInjectorVersionInfo.class);
|
return HttpRequest.GET(url.toString()).getJson(AuthlibInjectorVersionInfo.class);
|
||||||
} catch (IOException | JsonParseException e) {
|
} catch (IOException | JsonParseException e) {
|
||||||
if (exception == null) {
|
if (exception == null) {
|
||||||
exception = new IOException("Failed to fetch authlib-injector artifact info");
|
exception = new IOException("Failed to fetch authlib-injector artifact info");
|
||||||
|
|||||||
@@ -17,13 +17,11 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.auth.authlibinjector;
|
package org.jackhuang.hmcl.auth.authlibinjector;
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.io.NetworkUtils.toURL;
|
|
||||||
|
|
||||||
import org.jackhuang.hmcl.auth.AuthenticationException;
|
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilProvider;
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilProvider;
|
||||||
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
|
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class AuthlibInjectorProvider implements YggdrasilProvider {
|
public class AuthlibInjectorProvider implements YggdrasilProvider {
|
||||||
@@ -35,33 +33,33 @@ public class AuthlibInjectorProvider implements YggdrasilProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getAuthenticationURL() throws AuthenticationException {
|
public URI getAuthenticationURL() throws AuthenticationException {
|
||||||
return toURL(apiRoot + "authserver/authenticate");
|
return URI.create(apiRoot + "authserver/authenticate");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getRefreshmentURL() throws AuthenticationException {
|
public URI getRefreshmentURL() throws AuthenticationException {
|
||||||
return toURL(apiRoot + "authserver/refresh");
|
return URI.create(apiRoot + "authserver/refresh");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getValidationURL() throws AuthenticationException {
|
public URI getValidationURL() throws AuthenticationException {
|
||||||
return toURL(apiRoot + "authserver/validate");
|
return URI.create(apiRoot + "authserver/validate");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getInvalidationURL() throws AuthenticationException {
|
public URI getInvalidationURL() throws AuthenticationException {
|
||||||
return toURL(apiRoot + "authserver/invalidate");
|
return URI.create(apiRoot + "authserver/invalidate");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getSkinUploadURL(UUID uuid) throws UnsupportedOperationException {
|
public URI getSkinUploadURL(UUID uuid) throws UnsupportedOperationException {
|
||||||
return toURL(apiRoot + "api/user/profile/" + UUIDTypeAdapter.fromUUID(uuid) + "/skin");
|
return URI.create(apiRoot + "api/user/profile/" + UUIDTypeAdapter.fromUUID(uuid) + "/skin");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URL getProfilePropertiesURL(UUID uuid) throws AuthenticationException {
|
public URI getProfilePropertiesURL(UUID uuid) throws AuthenticationException {
|
||||||
return toURL(apiRoot + "sessionserver/session/minecraft/profile/" + UUIDTypeAdapter.fromUUID(uuid));
|
return URI.create(apiRoot + "sessionserver/session/minecraft/profile/" + UUIDTypeAdapter.fromUUID(uuid));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.auth.authlibinjector;
|
package org.jackhuang.hmcl.auth.authlibinjector;
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
@@ -25,14 +24,15 @@ import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
|
||||||
import org.jackhuang.hmcl.util.io.HttpRequest;
|
import org.jackhuang.hmcl.util.io.HttpRequest;
|
||||||
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
import org.jackhuang.hmcl.util.javafx.ObservableHelper;
|
import org.jackhuang.hmcl.util.javafx.ObservableHelper;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -58,17 +58,14 @@ public class AuthlibInjectorServer implements Observable {
|
|||||||
public static AuthlibInjectorServer locateServer(String url) throws IOException {
|
public static AuthlibInjectorServer locateServer(String url) throws IOException {
|
||||||
try {
|
try {
|
||||||
url = addHttpsIfMissing(url);
|
url = addHttpsIfMissing(url);
|
||||||
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
HttpURLConnection conn = NetworkUtils.createHttpConnection(URI.create(url));
|
||||||
conn.setRequestProperty("Accept-Language", Locale.getDefault().toLanguageTag());
|
|
||||||
|
|
||||||
String ali = conn.getHeaderField("x-authlib-injector-api-location");
|
String ali = conn.getHeaderField("x-authlib-injector-api-location");
|
||||||
if (ali != null) {
|
if (ali != null) {
|
||||||
URL absoluteAli = new URL(conn.getURL(), ali);
|
URI absoluteAli = conn.getURL().toURI().resolve(ali);
|
||||||
if (!urlEqualsIgnoreSlash(url, absoluteAli.toString())) {
|
if (!urlEqualsIgnoreSlash(url, absoluteAli.toString())) {
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
url = absoluteAli.toString();
|
url = absoluteAli.toString();
|
||||||
conn = (HttpURLConnection) absoluteAli.openConnection();
|
conn = NetworkUtils.createHttpConnection(absoluteAli);
|
||||||
conn.setRequestProperty("Accept-Language", Locale.getDefault().toLanguageTag());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,22 +74,26 @@ public class AuthlibInjectorServer implements Observable {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
AuthlibInjectorServer server = new AuthlibInjectorServer(url);
|
AuthlibInjectorServer server = new AuthlibInjectorServer(url);
|
||||||
server.refreshMetadata(new String(conn.getInputStream().readAllBytes(), UTF_8));
|
server.refreshMetadata(NetworkUtils.readFullyAsString(conn));
|
||||||
return server;
|
return server;
|
||||||
} finally {
|
} finally {
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
}
|
}
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException | URISyntaxException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String addHttpsIfMissing(String url) {
|
private static String addHttpsIfMissing(String url) throws IOException {
|
||||||
String lowercased = url.toLowerCase(Locale.ROOT);
|
URI uri = URI.create(url);
|
||||||
if (!lowercased.startsWith("http://") && !lowercased.startsWith("https://")) {
|
|
||||||
url = "https://" + url;
|
if (uri.getScheme() == null) {
|
||||||
|
return "https://" + url;
|
||||||
|
} else if (!NetworkUtils.isHttpUri(uri)) {
|
||||||
|
throw new IOException("Yggdrasil server should be an HTTP or HTTPS URI, but got: " + url);
|
||||||
|
} else {
|
||||||
|
return url;
|
||||||
}
|
}
|
||||||
return url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean urlEqualsIgnoreSlash(String a, String b) {
|
private static boolean urlEqualsIgnoreSlash(String a, String b) {
|
||||||
@@ -103,7 +104,7 @@ public class AuthlibInjectorServer implements Observable {
|
|||||||
return a.equals(b);
|
return a.equals(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String url;
|
private final String url;
|
||||||
@Nullable
|
@Nullable
|
||||||
private String metadataResponse;
|
private String metadataResponse;
|
||||||
private long metadataTimestamp;
|
private long metadataTimestamp;
|
||||||
|
|||||||
@@ -34,7 +34,8 @@ import org.jackhuang.hmcl.util.javafx.ObservableOptionalCache;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -164,7 +165,7 @@ public class MicrosoftService {
|
|||||||
.accept("application/json").createConnection();
|
.accept("application/json").createConnection();
|
||||||
|
|
||||||
if (request.getResponseCode() != 200) {
|
if (request.getResponseCode() != 200) {
|
||||||
throw new ResponseCodeException(new URL("https://api.minecraftservices.com/entitlements/mcstore"), request.getResponseCode());
|
throw new ResponseCodeException(URI.create("https://api.minecraftservices.com/entitlements/mcstore"), request.getResponseCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Minecraft Account UUID
|
// Get Minecraft Account UUID
|
||||||
@@ -247,22 +248,22 @@ public class MicrosoftService {
|
|||||||
if (responseCode == HTTP_NOT_FOUND) {
|
if (responseCode == HTTP_NOT_FOUND) {
|
||||||
throw new NoMinecraftJavaEditionProfileException();
|
throw new NoMinecraftJavaEditionProfileException();
|
||||||
} else if (responseCode != 200) {
|
} else if (responseCode != 200) {
|
||||||
throw new ResponseCodeException(new URL("https://api.minecraftservices.com/minecraft/profile"), responseCode);
|
throw new ResponseCodeException(URI.create("https://api.minecraftservices.com/minecraft/profile"), responseCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
String result = NetworkUtils.readData(conn);
|
String result = NetworkUtils.readFullyAsString(conn);
|
||||||
return JsonUtils.fromNonNullJson(result, MinecraftProfileResponse.class);
|
return JsonUtils.fromNonNullJson(result, MinecraftProfileResponse.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<CompleteGameProfile> getCompleteGameProfile(UUID uuid) throws AuthenticationException {
|
public Optional<CompleteGameProfile> getCompleteGameProfile(UUID uuid) throws AuthenticationException {
|
||||||
Objects.requireNonNull(uuid);
|
Objects.requireNonNull(uuid);
|
||||||
|
|
||||||
return Optional.ofNullable(GSON.fromJson(request(NetworkUtils.toURL("https://sessionserver.mojang.com/session/minecraft/profile/" + UUIDTypeAdapter.fromUUID(uuid)), null), CompleteGameProfile.class));
|
return Optional.ofNullable(GSON.fromJson(request(URI.create("https://sessionserver.mojang.com/session/minecraft/profile/" + UUIDTypeAdapter.fromUUID(uuid)), null), CompleteGameProfile.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void uploadSkin(String accessToken, boolean isSlim, Path file) throws AuthenticationException, UnsupportedOperationException {
|
public void uploadSkin(String accessToken, boolean isSlim, Path file) throws AuthenticationException, UnsupportedOperationException {
|
||||||
try {
|
try {
|
||||||
HttpURLConnection con = NetworkUtils.createHttpConnection(NetworkUtils.toURL("https://api.minecraftservices.com/minecraft/profile/skins"));
|
HttpURLConnection con = NetworkUtils.createHttpConnection(URI.create("https://api.minecraftservices.com/minecraft/profile/skins"));
|
||||||
con.setRequestMethod("POST");
|
con.setRequestMethod("POST");
|
||||||
con.setRequestProperty("Authorization", "Bearer " + accessToken);
|
con.setRequestProperty("Authorization", "Bearer " + accessToken);
|
||||||
con.setDoOutput(true);
|
con.setDoOutput(true);
|
||||||
@@ -273,21 +274,21 @@ public class MicrosoftService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String response = NetworkUtils.readData(con);
|
String response = NetworkUtils.readFullyAsString(con);
|
||||||
if (StringUtils.isBlank(response)) {
|
if (StringUtils.isBlank(response)) {
|
||||||
if (con.getResponseCode() / 100 != 2)
|
if (con.getResponseCode() / 100 != 2)
|
||||||
throw new ResponseCodeException(con.getURL(), con.getResponseCode());
|
throw new ResponseCodeException(con.getURL().toURI(), con.getResponseCode());
|
||||||
} else {
|
} else {
|
||||||
MinecraftErrorResponse profileResponse = GSON.fromJson(response, MinecraftErrorResponse.class);
|
MinecraftErrorResponse profileResponse = GSON.fromJson(response, MinecraftErrorResponse.class);
|
||||||
if (StringUtils.isNotBlank(profileResponse.errorMessage) || con.getResponseCode() / 100 != 2)
|
if (StringUtils.isNotBlank(profileResponse.errorMessage) || con.getResponseCode() / 100 != 2)
|
||||||
throw new AuthenticationException("Failed to upload skin, response code: " + con.getResponseCode() + ", response: " + response);
|
throw new AuthenticationException("Failed to upload skin, response code: " + con.getResponseCode() + ", response: " + response);
|
||||||
}
|
}
|
||||||
} catch (IOException | JsonParseException e) {
|
} catch (IOException | JsonParseException | URISyntaxException e) {
|
||||||
throw new AuthenticationException(e);
|
throw new AuthenticationException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String request(URL url, Object payload) throws AuthenticationException {
|
private static String request(URI url, Object payload) throws AuthenticationException {
|
||||||
try {
|
try {
|
||||||
if (payload == null)
|
if (payload == null)
|
||||||
return NetworkUtils.doGet(url);
|
return NetworkUtils.doGet(url);
|
||||||
|
|||||||
@@ -33,14 +33,11 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||||
@@ -169,7 +166,7 @@ public class Skin {
|
|||||||
String realCslApi = type == Type.LITTLE_SKIN
|
String realCslApi = type == Type.LITTLE_SKIN
|
||||||
? "https://littleskin.cn/csl"
|
? "https://littleskin.cn/csl"
|
||||||
: StringUtils.removeSuffix(Lang.requireNonNullElse(cslApi, ""), "/");
|
: StringUtils.removeSuffix(Lang.requireNonNullElse(cslApi, ""), "/");
|
||||||
return Task.composeAsync(() -> new GetTask(new URL(String.format("%s/%s.json", realCslApi, username))))
|
return Task.composeAsync(() -> new GetTask(URI.create(String.format("%s/%s.json", realCslApi, username))))
|
||||||
.thenComposeAsync(json -> {
|
.thenComposeAsync(json -> {
|
||||||
SkinJson result = JsonUtils.GSON.fromJson(json, SkinJson.class);
|
SkinJson result = JsonUtils.GSON.fromJson(json, SkinJson.class);
|
||||||
|
|
||||||
@@ -179,8 +176,8 @@ public class Skin {
|
|||||||
|
|
||||||
return Task.allOf(
|
return Task.allOf(
|
||||||
Task.supplyAsync(result::getModel),
|
Task.supplyAsync(result::getModel),
|
||||||
result.getHash() == null ? Task.supplyAsync(() -> null) : new FetchBytesTask(new URL(String.format("%s/textures/%s", realCslApi, result.getHash())), 3),
|
result.getHash() == null ? Task.supplyAsync(() -> null) : new FetchBytesTask(URI.create(String.format("%s/textures/%s", realCslApi, result.getHash())), 3),
|
||||||
result.getCapeHash() == null ? Task.supplyAsync(() -> null) : new FetchBytesTask(new URL(String.format("%s/textures/%s", realCslApi, result.getCapeHash())), 3)
|
result.getCapeHash() == null ? Task.supplyAsync(() -> null) : new FetchBytesTask(URI.create(String.format("%s/textures/%s", realCslApi, result.getCapeHash())), 3)
|
||||||
);
|
);
|
||||||
}).thenApplyAsync(result -> {
|
}).thenApplyAsync(result -> {
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
@@ -232,8 +229,8 @@ public class Skin {
|
|||||||
|
|
||||||
private static class FetchBytesTask extends FetchTask<InputStream> {
|
private static class FetchBytesTask extends FetchTask<InputStream> {
|
||||||
|
|
||||||
public FetchBytesTask(URL url, int retry) {
|
public FetchBytesTask(URI uri, int retry) {
|
||||||
super(Collections.singletonList(url), retry);
|
super(List.of(uri), retry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -247,7 +244,7 @@ public class Skin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Context getContext(URLConnection conn, boolean checkETag) throws IOException {
|
protected Context getContext(URLConnection connection, boolean checkETag) throws IOException {
|
||||||
return new Context() {
|
return new Context() {
|
||||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
@@ -263,7 +260,7 @@ public class Skin {
|
|||||||
setResult(new ByteArrayInputStream(baos.toByteArray()));
|
setResult(new ByteArrayInputStream(baos.toByteArray()));
|
||||||
|
|
||||||
if (checkETag) {
|
if (checkETag) {
|
||||||
repository.cacheBytes(baos.toByteArray(), conn);
|
repository.cacheBytes(connection, baos.toByteArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ package org.jackhuang.hmcl.auth.yggdrasil;
|
|||||||
|
|
||||||
import org.jackhuang.hmcl.auth.AuthenticationException;
|
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,13 +27,13 @@ import java.util.UUID;
|
|||||||
*/
|
*/
|
||||||
public interface YggdrasilProvider {
|
public interface YggdrasilProvider {
|
||||||
|
|
||||||
URL getAuthenticationURL() throws AuthenticationException;
|
URI getAuthenticationURL() throws AuthenticationException;
|
||||||
|
|
||||||
URL getRefreshmentURL() throws AuthenticationException;
|
URI getRefreshmentURL() throws AuthenticationException;
|
||||||
|
|
||||||
URL getValidationURL() throws AuthenticationException;
|
URI getValidationURL() throws AuthenticationException;
|
||||||
|
|
||||||
URL getInvalidationURL() throws AuthenticationException;
|
URI getInvalidationURL() throws AuthenticationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL to upload skin.
|
* URL to upload skin.
|
||||||
@@ -51,8 +51,8 @@ public interface YggdrasilProvider {
|
|||||||
* @throws AuthenticationException if url cannot be generated. e.g. some parameter or query is malformed.
|
* @throws AuthenticationException if url cannot be generated. e.g. some parameter or query is malformed.
|
||||||
* @throws UnsupportedOperationException if the Yggdrasil provider does not support third-party skin uploading.
|
* @throws UnsupportedOperationException if the Yggdrasil provider does not support third-party skin uploading.
|
||||||
*/
|
*/
|
||||||
URL getSkinUploadURL(UUID uuid) throws AuthenticationException, UnsupportedOperationException;
|
URI getSkinUploadURL(UUID uuid) throws AuthenticationException, UnsupportedOperationException;
|
||||||
|
|
||||||
URL getProfilePropertiesURL(UUID uuid) throws AuthenticationException;
|
URI getProfilePropertiesURL(UUID uuid) throws AuthenticationException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import org.jackhuang.hmcl.util.javafx.ObservableOptionalCache;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -160,7 +160,7 @@ public class YggdrasilService {
|
|||||||
request.file("file", FileUtils.getName(file), "image/" + FileUtils.getExtension(file), fis);
|
request.file("file", FileUtils.getName(file), "image/" + FileUtils.getExtension(file), fis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requireEmpty(NetworkUtils.readData(con));
|
requireEmpty(NetworkUtils.readFullyAsString(con));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AuthenticationException(e);
|
throw new AuthenticationException(e);
|
||||||
}
|
}
|
||||||
@@ -227,12 +227,12 @@ public class YggdrasilService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String request(URL url, Object payload) throws AuthenticationException {
|
private static String request(URI uri, Object payload) throws AuthenticationException {
|
||||||
try {
|
try {
|
||||||
if (payload == null)
|
if (payload == null)
|
||||||
return NetworkUtils.doGet(url);
|
return NetworkUtils.doGet(uri);
|
||||||
else
|
else
|
||||||
return NetworkUtils.doPost(url, payload instanceof String ? (String) payload : GSON.toJson(payload), "application/json");
|
return NetworkUtils.doPost(uri, payload instanceof String ? (String) payload : GSON.toJson(payload), "application/json");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new ServerDisconnectException(e);
|
throw new ServerDisconnectException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download;
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -59,21 +59,21 @@ public class AdaptedDownloadProvider implements DownloadProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<URL> getAssetObjectCandidates(String assetObjectLocation) {
|
public List<URI> getAssetObjectCandidates(String assetObjectLocation) {
|
||||||
return downloadProviderCandidates.stream()
|
return downloadProviderCandidates.stream()
|
||||||
.flatMap(d -> d.getAssetObjectCandidates(assetObjectLocation).stream())
|
.flatMap(d -> d.getAssetObjectCandidates(assetObjectLocation).stream())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<URL> injectURLWithCandidates(String baseURL) {
|
public List<URI> injectURLWithCandidates(String baseURL) {
|
||||||
return downloadProviderCandidates.stream()
|
return downloadProviderCandidates.stream()
|
||||||
.flatMap(d -> d.injectURLWithCandidates(baseURL).stream())
|
.flatMap(d -> d.injectURLWithCandidates(baseURL).stream())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<URL> injectURLsWithCandidates(List<String> urls) {
|
public List<URI> injectURLsWithCandidates(List<String> urls) {
|
||||||
return downloadProviderCandidates.stream()
|
return downloadProviderCandidates.stream()
|
||||||
.flatMap(d -> d.injectURLsWithCandidates(urls).stream())
|
.flatMap(d -> d.injectURLsWithCandidates(urls).stream())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download;
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,17 +51,17 @@ public class AutoDownloadProvider implements DownloadProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<URL> getAssetObjectCandidates(String assetObjectLocation) {
|
public List<URI> getAssetObjectCandidates(String assetObjectLocation) {
|
||||||
return fileProvider.getAssetObjectCandidates(assetObjectLocation);
|
return fileProvider.getAssetObjectCandidates(assetObjectLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<URL> injectURLWithCandidates(String baseURL) {
|
public List<URI> injectURLWithCandidates(String baseURL) {
|
||||||
return fileProvider.injectURLWithCandidates(baseURL);
|
return fileProvider.injectURLWithCandidates(baseURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<URL> injectURLsWithCandidates(List<String> urls) {
|
public List<URI> injectURLsWithCandidates(List<String> urls) {
|
||||||
return fileProvider.injectURLsWithCandidates(urls);
|
return fileProvider.injectURLsWithCandidates(urls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.download;
|
package org.jackhuang.hmcl.download;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
import java.net.URI;
|
||||||
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -35,8 +32,8 @@ public interface DownloadProvider {
|
|||||||
|
|
||||||
String getAssetBaseURL();
|
String getAssetBaseURL();
|
||||||
|
|
||||||
default List<URL> getAssetObjectCandidates(String assetObjectLocation) {
|
default List<URI> getAssetObjectCandidates(String assetObjectLocation) {
|
||||||
return Collections.singletonList(NetworkUtils.toURL(getAssetBaseURL() + assetObjectLocation));
|
return List.of(URI.create(getAssetBaseURL() + assetObjectLocation));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,11 +56,11 @@ public interface DownloadProvider {
|
|||||||
* @param baseURL original URL provided by Mojang and Forge.
|
* @param baseURL original URL provided by Mojang and Forge.
|
||||||
* @return the URL that is equivalent to [baseURL], but belongs to your own service provider.
|
* @return the URL that is equivalent to [baseURL], but belongs to your own service provider.
|
||||||
*/
|
*/
|
||||||
default List<URL> injectURLWithCandidates(String baseURL) {
|
default List<URI> injectURLWithCandidates(String baseURL) {
|
||||||
return Collections.singletonList(NetworkUtils.toURL(injectURL(baseURL)));
|
return List.of(URI.create(injectURL(baseURL)));
|
||||||
}
|
}
|
||||||
|
|
||||||
default List<URL> injectURLsWithCandidates(List<String> urls) {
|
default List<URI> injectURLsWithCandidates(List<String> urls) {
|
||||||
return urls.stream().flatMap(url -> injectURLWithCandidates(url).stream()).collect(Collectors.toList());
|
return urls.stream().flatMap(url -> injectURLWithCandidates(url).stream()).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import org.jackhuang.hmcl.task.FileDownloadTask;
|
|||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -59,8 +59,8 @@ public final class FabricAPIInstallTask extends Task<Version> {
|
|||||||
@Override
|
@Override
|
||||||
public void execute() throws IOException {
|
public void execute() throws IOException {
|
||||||
dependencies.add(new FileDownloadTask(
|
dependencies.add(new FileDownloadTask(
|
||||||
new URL(remote.getVersion().getFile().getUrl()),
|
URI.create(remote.getVersion().getFile().getUrl()),
|
||||||
dependencyManager.getGameRepository().getRunDirectory(version.getId()).toPath().resolve("mods").resolve("fabric-api-" + remote.getVersion().getVersion() + ".jar").toFile(),
|
dependencyManager.getGameRepository().getRunDirectory(version.getId()).toPath().resolve("mods").resolve("fabric-api-" + remote.getVersion().getVersion() + ".jar"),
|
||||||
remote.getVersion().getFile().getIntegrityCheck())
|
remote.getVersion().getFile().getIntegrityCheck())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public final class ForgeInstallTask extends Task<Version> {
|
|||||||
|
|
||||||
dependent = new FileDownloadTask(
|
dependent = new FileDownloadTask(
|
||||||
dependencyManager.getDownloadProvider().injectURLsWithCandidates(remote.getUrls()),
|
dependencyManager.getDownloadProvider().injectURLsWithCandidates(remote.getUrls()),
|
||||||
installer.toFile(), null);
|
installer, null);
|
||||||
dependent.setCacheRepository(dependencyManager.getCacheRepository());
|
dependent.setCacheRepository(dependencyManager.getCacheRepository());
|
||||||
dependent.setCaching(true);
|
dependent.setCaching(true);
|
||||||
dependent.addIntegrityCheckHandler(FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER);
|
dependent.addIntegrityCheckHandler(FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER);
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import org.jackhuang.hmcl.game.Library;
|
|||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
|
||||||
import org.jackhuang.hmcl.util.DigestUtils;
|
import org.jackhuang.hmcl.util.DigestUtils;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
import org.jackhuang.hmcl.util.function.ExceptionalFunction;
|
||||||
@@ -48,7 +47,7 @@ import java.io.File;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.FileSystem;
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -346,12 +345,12 @@ public class ForgeNewInstallTask extends Task<Version> {
|
|||||||
throw new Exception("client_mappings download info not found");
|
throw new Exception("client_mappings download info not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<URL> mappingsUrl = dependencyManager.getDownloadProvider()
|
List<URI> mappingsUrl = dependencyManager.getDownloadProvider()
|
||||||
.injectURLWithCandidates(mappings.getUrl());
|
.injectURLWithCandidates(mappings.getUrl());
|
||||||
FileDownloadTask mappingsTask = new FileDownloadTask(
|
var mappingsTask = new FileDownloadTask(
|
||||||
mappingsUrl,
|
mappingsUrl,
|
||||||
new File(output),
|
Path.of(output),
|
||||||
IntegrityCheck.of("SHA-1", mappings.getSha1()));
|
FileDownloadTask.IntegrityCheck.of("SHA-1", mappings.getSha1()));
|
||||||
mappingsTask.setCaching(true);
|
mappingsTask.setCaching(true);
|
||||||
mappingsTask.setCacheRepository(dependencyManager.getCacheRepository());
|
mappingsTask.setCacheRepository(dependencyManager.getCacheRepository());
|
||||||
return mappingsTask;
|
return mappingsTask;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import org.jackhuang.hmcl.util.CacheRepository;
|
|||||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -102,9 +102,9 @@ public final class GameAssetDownloadTask extends Task<Void> {
|
|||||||
LOG.warning("Unable to calc hash value of file " + file, e);
|
LOG.warning("Unable to calc hash value of file " + file, e);
|
||||||
}
|
}
|
||||||
if (download) {
|
if (download) {
|
||||||
List<URL> urls = dependencyManager.getDownloadProvider().getAssetObjectCandidates(assetObject.getLocation());
|
List<URI> uris = dependencyManager.getDownloadProvider().getAssetObjectCandidates(assetObject.getLocation());
|
||||||
|
|
||||||
FileDownloadTask task = new FileDownloadTask(urls, file.toFile(), new FileDownloadTask.IntegrityCheck("SHA-1", assetObject.getHash()));
|
var task = new FileDownloadTask(uris, file, new FileDownloadTask.IntegrityCheck("SHA-1", assetObject.getHash()));
|
||||||
task.setName(assetObject.getHash());
|
task.setName(assetObject.getHash());
|
||||||
task.setCandidate(dependencyManager.getCacheRepository().getCommonDirectory()
|
task.setCandidate(dependencyManager.getCacheRepository().getCommonDirectory()
|
||||||
.resolve("assets").resolve("objects").resolve(assetObject.getLocation()));
|
.resolve("assets").resolve("objects").resolve(assetObject.getLocation()));
|
||||||
|
|||||||
@@ -94,9 +94,9 @@ public final class GameAssetIndexDownloadTask extends Task<Void> {
|
|||||||
|
|
||||||
// We should not check the hash code of asset index file since this file is not consistent
|
// We should not check the hash code of asset index file since this file is not consistent
|
||||||
// And Mojang will modify this file anytime. So assetIndex.hash might be outdated.
|
// And Mojang will modify this file anytime. So assetIndex.hash might be outdated.
|
||||||
FileDownloadTask task = new FileDownloadTask(
|
var task = new FileDownloadTask(
|
||||||
dependencyManager.getDownloadProvider().injectURLWithCandidates(assetIndexInfo.getUrl()),
|
dependencyManager.getDownloadProvider().injectURLWithCandidates(assetIndexInfo.getUrl()),
|
||||||
assetIndexFile.toFile(),
|
assetIndexFile,
|
||||||
verifyHashCode ? new FileDownloadTask.IntegrityCheck("SHA-1", assetIndexInfo.getSha1()) : null
|
verifyHashCode ? new FileDownloadTask.IntegrityCheck("SHA-1", assetIndexInfo.getSha1()) : null
|
||||||
);
|
);
|
||||||
task.setCacheRepository(dependencyManager.getCacheRepository());
|
task.setCacheRepository(dependencyManager.getCacheRepository());
|
||||||
|
|||||||
@@ -20,11 +20,10 @@ package org.jackhuang.hmcl.download.game;
|
|||||||
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
import org.jackhuang.hmcl.download.DefaultDependencyManager;
|
||||||
import org.jackhuang.hmcl.game.Version;
|
import org.jackhuang.hmcl.game.Version;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.util.CacheRepository;
|
import org.jackhuang.hmcl.util.CacheRepository;
|
||||||
|
|
||||||
import java.io.File;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -54,12 +53,12 @@ public final class GameDownloadTask extends Task<Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
File jar = dependencyManager.getGameRepository().getVersionJar(version);
|
Path jar = dependencyManager.getGameRepository().getVersionJar(version).toPath();
|
||||||
|
|
||||||
FileDownloadTask task = new FileDownloadTask(
|
var task = new FileDownloadTask(
|
||||||
dependencyManager.getDownloadProvider().injectURLWithCandidates(version.getDownloadInfo().getUrl()),
|
dependencyManager.getDownloadProvider().injectURLWithCandidates(version.getDownloadInfo().getUrl()),
|
||||||
jar,
|
jar,
|
||||||
IntegrityCheck.of(CacheRepository.SHA1, version.getDownloadInfo().getSha1()));
|
FileDownloadTask.IntegrityCheck.of(CacheRepository.SHA1, version.getDownloadInfo().getSha1()));
|
||||||
task.setCaching(true);
|
task.setCaching(true);
|
||||||
task.setCacheRepository(dependencyManager.getCacheRepository());
|
task.setCacheRepository(dependencyManager.getCacheRepository());
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import org.jackhuang.hmcl.util.io.FileUtils;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -116,8 +116,8 @@ public class LibraryDownloadTask extends Task<Void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
List<URL> urls = dependencyManager.getDownloadProvider().injectURLWithCandidates(url);
|
List<URI> uris = dependencyManager.getDownloadProvider().injectURLWithCandidates(url);
|
||||||
task = new FileDownloadTask(urls, jar,
|
task = new FileDownloadTask(uris, jar.toPath(),
|
||||||
library.getDownload().getSha1() != null ? new IntegrityCheck("SHA-1", library.getDownload().getSha1()) : null);
|
library.getDownload().getSha1() != null ? new IntegrityCheck("SHA-1", library.getDownload().getSha1()) : null);
|
||||||
task.setCacheRepository(cacheRepository);
|
task.setCacheRepository(cacheRepository);
|
||||||
task.setCaching(true);
|
task.setCaching(true);
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public final class MojangJavaDownloadTask extends Task<MojangJavaDownloadTask.Re
|
|||||||
if (file.getDownloads().containsKey("lzma")) {
|
if (file.getDownloads().containsKey("lzma")) {
|
||||||
DownloadInfo download = file.getDownloads().get("lzma");
|
DownloadInfo download = file.getDownloads().get("lzma");
|
||||||
File tempFile = target.resolve(entry.getKey() + ".lzma").toFile();
|
File tempFile = target.resolve(entry.getKey() + ".lzma").toFile();
|
||||||
FileDownloadTask task = new FileDownloadTask(downloadProvider.injectURLWithCandidates(download.getUrl()), tempFile, new FileDownloadTask.IntegrityCheck("SHA-1", download.getSha1()));
|
var task = new FileDownloadTask(downloadProvider.injectURLWithCandidates(download.getUrl()), tempFile.toPath(), new FileDownloadTask.IntegrityCheck("SHA-1", download.getSha1()));
|
||||||
task.setName(entry.getKey());
|
task.setName(entry.getKey());
|
||||||
dependencies.add(task.thenRunAsync(() -> {
|
dependencies.add(task.thenRunAsync(() -> {
|
||||||
Path decompressed = target.resolve(entry.getKey() + ".tmp");
|
Path decompressed = target.resolve(entry.getKey() + ".tmp");
|
||||||
@@ -121,7 +121,7 @@ public final class MojangJavaDownloadTask extends Task<MojangJavaDownloadTask.Re
|
|||||||
}));
|
}));
|
||||||
} else if (file.getDownloads().containsKey("raw")) {
|
} else if (file.getDownloads().containsKey("raw")) {
|
||||||
DownloadInfo download = file.getDownloads().get("raw");
|
DownloadInfo download = file.getDownloads().get("raw");
|
||||||
FileDownloadTask task = new FileDownloadTask(downloadProvider.injectURLWithCandidates(download.getUrl()), dest.toFile(), new FileDownloadTask.IntegrityCheck("SHA-1", download.getSha1()));
|
var task = new FileDownloadTask(downloadProvider.injectURLWithCandidates(download.getUrl()), dest, new FileDownloadTask.IntegrityCheck("SHA-1", download.getSha1()));
|
||||||
task.setName(entry.getKey());
|
task.setName(entry.getKey());
|
||||||
if (file.isExecutable()) {
|
if (file.isExecutable()) {
|
||||||
dependencies.add(task.thenRunAsync(() -> dest.toFile().setExecutable(true)));
|
dependencies.add(task.thenRunAsync(() -> dest.toFile().setExecutable(true)));
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public final class NeoForgeInstallTask extends Task<Version> {
|
|||||||
|
|
||||||
dependent = new FileDownloadTask(
|
dependent = new FileDownloadTask(
|
||||||
dependencyManager.getDownloadProvider().injectURLsWithCandidates(remoteVersion.getUrls()),
|
dependencyManager.getDownloadProvider().injectURLsWithCandidates(remoteVersion.getUrls()),
|
||||||
installer.toFile(), null
|
installer, null
|
||||||
);
|
);
|
||||||
dependent.setCacheRepository(dependencyManager.getCacheRepository());
|
dependent.setCacheRepository(dependencyManager.getCacheRepository());
|
||||||
dependent.setCaching(true);
|
dependent.setCaching(true);
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import org.jackhuang.hmcl.download.game.GameLibrariesTask;
|
|||||||
import org.jackhuang.hmcl.download.game.VersionJsonDownloadTask;
|
import org.jackhuang.hmcl.download.game.VersionJsonDownloadTask;
|
||||||
import org.jackhuang.hmcl.game.*;
|
import org.jackhuang.hmcl.game.*;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask;
|
import org.jackhuang.hmcl.task.FileDownloadTask;
|
||||||
import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck;
|
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.util.DigestUtils;
|
import org.jackhuang.hmcl.util.DigestUtils;
|
||||||
import org.jackhuang.hmcl.util.StringUtils;
|
import org.jackhuang.hmcl.util.StringUtils;
|
||||||
@@ -44,7 +43,7 @@ import java.io.File;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.FileSystem;
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -342,12 +341,12 @@ public class NeoForgeOldInstallTask extends Task<Version> {
|
|||||||
throw new Exception("client_mappings download info not found");
|
throw new Exception("client_mappings download info not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<URL> mappingsUrl = dependencyManager.getDownloadProvider()
|
List<URI> mappingsUri = dependencyManager.getDownloadProvider()
|
||||||
.injectURLWithCandidates(mappings.getUrl());
|
.injectURLWithCandidates(mappings.getUrl());
|
||||||
FileDownloadTask mappingsTask = new FileDownloadTask(
|
var mappingsTask = new FileDownloadTask(
|
||||||
mappingsUrl,
|
mappingsUri,
|
||||||
new File(output),
|
Path.of(output),
|
||||||
IntegrityCheck.of("SHA-1", mappings.getSha1()));
|
FileDownloadTask.IntegrityCheck.of("SHA-1", mappings.getSha1()));
|
||||||
mappingsTask.setCaching(true);
|
mappingsTask.setCaching(true);
|
||||||
mappingsTask.setCacheRepository(dependencyManager.getCacheRepository());
|
mappingsTask.setCacheRepository(dependencyManager.getCacheRepository());
|
||||||
return mappingsTask;
|
return mappingsTask;
|
||||||
|
|||||||
@@ -96,9 +96,9 @@ public final class OptiFineInstallTask extends Task<Version> {
|
|||||||
dest = Files.createTempFile("optifine-installer", ".jar");
|
dest = Files.createTempFile("optifine-installer", ".jar");
|
||||||
|
|
||||||
if (installer == null) {
|
if (installer == null) {
|
||||||
FileDownloadTask task = new FileDownloadTask(
|
var task = new FileDownloadTask(
|
||||||
dependencyManager.getDownloadProvider().injectURLsWithCandidates(remote.getUrls()),
|
dependencyManager.getDownloadProvider().injectURLsWithCandidates(remote.getUrls()),
|
||||||
dest.toFile(), null);
|
dest, null);
|
||||||
task.setCacheRepository(dependencyManager.getCacheRepository());
|
task.setCacheRepository(dependencyManager.getCacheRepository());
|
||||||
task.setCaching(true);
|
task.setCaching(true);
|
||||||
dependents.add(task);
|
dependents.add(task);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import org.jackhuang.hmcl.task.FileDownloadTask;
|
|||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -59,8 +59,8 @@ public final class QuiltAPIInstallTask extends Task<Version> {
|
|||||||
@Override
|
@Override
|
||||||
public void execute() throws IOException {
|
public void execute() throws IOException {
|
||||||
dependencies.add(new FileDownloadTask(
|
dependencies.add(new FileDownloadTask(
|
||||||
new URL(remote.getVersion().getFile().getUrl()),
|
URI.create(remote.getVersion().getFile().getUrl()),
|
||||||
dependencyManager.getGameRepository().getRunDirectory(version.getId()).toPath().resolve("mods").resolve("quilt-api-" + remote.getVersion().getVersion() + ".jar").toFile(),
|
dependencyManager.getGameRepository().getRunDirectory(version.getId()).toPath().resolve("mods").resolve("quilt-api-" + remote.getVersion().getVersion() + ".jar"),
|
||||||
remote.getVersion().getFile().getIntegrityCheck())
|
remote.getVersion().getFile().getIntegrityCheck())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ public final class CurseCompletionTask extends Task<Void> {
|
|||||||
return Stream.empty();
|
return Stream.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDownloadTask task = new FileDownloadTask(f.getUrl(), path);
|
var task = new FileDownloadTask(f.getUrl(), path.toPath());
|
||||||
task.setCacheRepository(dependency.getCacheRepository());
|
task.setCacheRepository(dependency.getCacheRepository());
|
||||||
task.setCaching(true);
|
task.setCaching(true);
|
||||||
return Stream.of(task.withCounter("hmcl.modpack.download"));
|
return Stream.of(task.withCounter("hmcl.modpack.download"));
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import org.jackhuang.hmcl.util.gson.Validation;
|
|||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,15 +84,15 @@ public final class CurseManifestFile implements Validation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public URL getUrl() {
|
public URI getUrl() {
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
if (fileName != null) {
|
if (fileName != null) {
|
||||||
return NetworkUtils.toURL(NetworkUtils.encodeLocation(String.format("https://edge.forgecdn.net/files/%d/%d/%s", fileID / 1000, fileID % 1000, fileName)));
|
return URI.create(NetworkUtils.encodeLocation(String.format("https://edge.forgecdn.net/files/%d/%d/%s", fileID / 1000, fileID % 1000, fileName)));
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return NetworkUtils.toURL(NetworkUtils.encodeLocation(url));
|
return URI.create(NetworkUtils.encodeLocation(url));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -102,7 +102,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
|||||||
throw new CustomException();
|
throw new CustomException();
|
||||||
}
|
}
|
||||||
})).thenComposeAsync(wrap(unused1 -> {
|
})).thenComposeAsync(wrap(unused1 -> {
|
||||||
return executor.one(new GetTask(new URL(manifest.getFileApi() + "/manifest.json")));
|
return executor.one(new GetTask(URI.create(manifest.getFileApi() + "/manifest.json")));
|
||||||
})).thenComposeAsync(wrap(remoteManifestJson -> {
|
})).thenComposeAsync(wrap(remoteManifestJson -> {
|
||||||
McbbsModpackManifest remoteManifest;
|
McbbsModpackManifest remoteManifest;
|
||||||
// We needs to update modpack from online server.
|
// We needs to update modpack from online server.
|
||||||
@@ -204,7 +204,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
|||||||
return file.withFileName(NetworkUtils.detectFileName(file.getUrl()));
|
return file.withFileName(NetworkUtils.detectFileName(file.getUrl()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
try {
|
try {
|
||||||
String result = NetworkUtils.doGet(NetworkUtils.toURL(String.format("https://cursemeta.dries007.net/%d/%d.json", file.getProjectID(), file.getFileID())));
|
String result = NetworkUtils.doGet(URI.create(String.format("https://cursemeta.dries007.net/%d/%d.json", file.getProjectID(), file.getFileID())));
|
||||||
CurseMetaMod mod = JsonUtils.fromNonNullJson(result, CurseMetaMod.class);
|
CurseMetaMod mod = JsonUtils.fromNonNullJson(result, CurseMetaMod.class);
|
||||||
return file.withFileName(mod.getFileNameOnDisk()).withURL(mod.getDownloadURL());
|
return file.withFileName(mod.getFileNameOnDisk()).withURL(mod.getDownloadURL());
|
||||||
} catch (FileNotFoundException fof) {
|
} catch (FileNotFoundException fof) {
|
||||||
@@ -213,7 +213,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
|||||||
return file;
|
return file;
|
||||||
} catch (IOException | JsonParseException e2) {
|
} catch (IOException | JsonParseException e2) {
|
||||||
try {
|
try {
|
||||||
String result = NetworkUtils.doGet(NetworkUtils.toURL(String.format("https://addons-ecs.forgesvc.net/api/v2/addon/%d/file/%d", file.getProjectID(), file.getFileID())));
|
String result = NetworkUtils.doGet(URI.create(String.format("https://addons-ecs.forgesvc.net/api/v2/addon/%d/file/%d", file.getProjectID(), file.getFileID())));
|
||||||
CurseMetaMod mod = JsonUtils.fromNonNullJson(result, CurseMetaMod.class);
|
CurseMetaMod mod = JsonUtils.fromNonNullJson(result, CurseMetaMod.class);
|
||||||
return file.withFileName(mod.getFileName()).withURL(mod.getDownloadURL());
|
return file.withFileName(mod.getFileName()).withURL(mod.getDownloadURL());
|
||||||
} catch (FileNotFoundException fof) {
|
} catch (FileNotFoundException fof) {
|
||||||
@@ -247,7 +247,7 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
|||||||
McbbsModpackManifest.CurseFile curseFile = (McbbsModpackManifest.CurseFile) file;
|
McbbsModpackManifest.CurseFile curseFile = (McbbsModpackManifest.CurseFile) file;
|
||||||
if (StringUtils.isNotBlank(curseFile.getFileName())) {
|
if (StringUtils.isNotBlank(curseFile.getFileName())) {
|
||||||
if (!modManager.hasSimpleMod(curseFile.getFileName())) {
|
if (!modManager.hasSimpleMod(curseFile.getFileName())) {
|
||||||
FileDownloadTask task = new FileDownloadTask(curseFile.getUrl(), modManager.getSimpleModPath(curseFile.getFileName()).toFile());
|
var task = new FileDownloadTask(curseFile.getUrl(), modManager.getSimpleModPath(curseFile.getFileName()));
|
||||||
task.setCacheRepository(dependency.getCacheRepository());
|
task.setCacheRepository(dependency.getCacheRepository());
|
||||||
task.setCaching(true);
|
task.setCaching(true);
|
||||||
dependencies.add(task.withCounter("hmcl.modpack.download"));
|
dependencies.add(task.withCounter("hmcl.modpack.download"));
|
||||||
@@ -297,8 +297,8 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
|
|||||||
if (file instanceof McbbsModpackManifest.AddonFile) {
|
if (file instanceof McbbsModpackManifest.AddonFile) {
|
||||||
McbbsModpackManifest.AddonFile addonFile = (McbbsModpackManifest.AddonFile) file;
|
McbbsModpackManifest.AddonFile addonFile = (McbbsModpackManifest.AddonFile) file;
|
||||||
return new FileDownloadTask(
|
return new FileDownloadTask(
|
||||||
new URL(remoteManifest.getFileApi() + "/overrides/" + NetworkUtils.encodeLocation(addonFile.getPath())),
|
URI.create(remoteManifest.getFileApi() + "/overrides/" + NetworkUtils.encodeLocation(addonFile.getPath())),
|
||||||
modManager.getSimpleModPath(addonFile.getPath()).toFile(),
|
modManager.getSimpleModPath(addonFile.getPath()),
|
||||||
addonFile.getHash() != null ? new FileDownloadTask.IntegrityCheck("SHA-1", addonFile.getHash()) : null);
|
addonFile.getHash() != null ? new FileDownloadTask.IntegrityCheck("SHA-1", addonFile.getHash()) : null);
|
||||||
} else if (file instanceof McbbsModpackManifest.CurseFile) {
|
} else if (file instanceof McbbsModpackManifest.CurseFile) {
|
||||||
// we download it later.
|
// we download it later.
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ import org.jackhuang.hmcl.util.io.NetworkUtils;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -329,9 +329,9 @@ public class McbbsModpackManifest implements ModpackManifest, Validation {
|
|||||||
return fileName;
|
return fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public URL getUrl() {
|
public URI getUrl() {
|
||||||
return url == null ? NetworkUtils.toURL("https://www.curseforge.com/minecraft/mc-mods/" + projectID + "/download/" + fileID + "/file")
|
return url == null ? URI.create("https://www.curseforge.com/minecraft/mc-mods/" + projectID + "/download/" + fileID + "/file")
|
||||||
: NetworkUtils.toURL(NetworkUtils.encodeLocation(url));
|
: URI.create(NetworkUtils.encodeLocation(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CurseFile withFileName(String fileName) {
|
public CurseFile withFileName(String fileName) {
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ public class ModrinthCompletionTask extends Task<Void> {
|
|||||||
if (modsDirectory.equals(filePath.getParent()) && this.modManager.hasSimpleMod(FileUtils.getName(filePath)))
|
if (modsDirectory.equals(filePath.getParent()) && this.modManager.hasSimpleMod(FileUtils.getName(filePath)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
FileDownloadTask task = new FileDownloadTask(file.getDownloads(), filePath.toFile());
|
var task = new FileDownloadTask(file.getDownloads(), filePath);
|
||||||
task.setCacheRepository(dependency.getCacheRepository());
|
task.setCacheRepository(dependency.getCacheRepository());
|
||||||
task.setCaching(true);
|
task.setCaching(true);
|
||||||
dependencies.add(task.withCounter("hmcl.modpack.download"));
|
dependencies.add(task.withCounter("hmcl.modpack.download"));
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import org.jackhuang.hmcl.util.gson.TolerableValidationException;
|
|||||||
import org.jackhuang.hmcl.util.gson.Validation;
|
import org.jackhuang.hmcl.util.gson.Validation;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -98,10 +98,10 @@ public class ModrinthManifest implements ModpackManifest, Validation {
|
|||||||
private final String path;
|
private final String path;
|
||||||
private final Map<String, String> hashes;
|
private final Map<String, String> hashes;
|
||||||
private final Map<String, String> env;
|
private final Map<String, String> env;
|
||||||
private final List<URL> downloads;
|
private final List<URI> downloads;
|
||||||
private final int fileSize;
|
private final int fileSize;
|
||||||
|
|
||||||
public File(String path, Map<String, String> hashes, Map<String, String> env, List<URL> downloads, int fileSize) {
|
public File(String path, Map<String, String> hashes, Map<String, String> env, List<URI> downloads, int fileSize) {
|
||||||
this.path = path;
|
this.path = path;
|
||||||
this.hashes = hashes;
|
this.hashes = hashes;
|
||||||
this.env = env;
|
this.env = env;
|
||||||
@@ -121,7 +121,7 @@ public class ModrinthManifest implements ModpackManifest, Validation {
|
|||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<URL> getDownloads() {
|
public List<URI> getDownloads() {
|
||||||
return downloads;
|
return downloads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
package org.jackhuang.hmcl.mod.multimc;
|
package org.jackhuang.hmcl.mod.multimc;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
import org.jackhuang.hmcl.download.LibraryAnalyzer;
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -46,7 +45,7 @@ public final class MultiMCComponents {
|
|||||||
return PAIRS;
|
return PAIRS;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static URL getMetaURL(String componentID, String version) {
|
public static URI getMetaURL(String componentID, String version) {
|
||||||
return NetworkUtils.toURL(String.format("https://meta.multimc.org/v1/%s/%s.json", componentID, version));
|
return URI.create(String.format("https://meta.multimc.org/v1/%s/%s.json", componentID, version));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import org.jackhuang.hmcl.util.io.NetworkUtils;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -85,7 +85,7 @@ public class ServerModpackCompletionTask extends Task<Void> {
|
|||||||
@Override
|
@Override
|
||||||
public void preExecute() throws Exception {
|
public void preExecute() throws Exception {
|
||||||
if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return;
|
if (manifest == null || StringUtils.isBlank(manifest.getManifest().getFileApi())) return;
|
||||||
dependent = new GetTask(new URL(manifest.getManifest().getFileApi() + "/server-manifest.json"));
|
dependent = new GetTask(URI.create(manifest.getManifest().getFileApi() + "/server-manifest.json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -153,8 +153,8 @@ public class ServerModpackCompletionTask extends Task<Void> {
|
|||||||
if (download) {
|
if (download) {
|
||||||
total++;
|
total++;
|
||||||
dependencies.add(new FileDownloadTask(
|
dependencies.add(new FileDownloadTask(
|
||||||
new URL(remoteManifest.getFileApi() + "/overrides/" + NetworkUtils.encodeLocation(file.getPath())),
|
URI.create(remoteManifest.getFileApi() + "/overrides/" + NetworkUtils.encodeLocation(file.getPath())),
|
||||||
actualPath.toFile(),
|
actualPath,
|
||||||
new FileDownloadTask.IntegrityCheck("SHA-1", file.getHash()))
|
new FileDownloadTask.IntegrityCheck("SHA-1", file.getHash()))
|
||||||
.withCounter("hmcl.modpack.download"));
|
.withCounter("hmcl.modpack.download"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,21 +20,20 @@ package org.jackhuang.hmcl.task;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
public class DownloadException extends IOException {
|
public class DownloadException extends IOException {
|
||||||
|
|
||||||
private final URL url;
|
private final URI uri;
|
||||||
|
|
||||||
public DownloadException(URL url, @NotNull Throwable cause) {
|
public DownloadException(URI uri, @NotNull Throwable cause) {
|
||||||
super("Unable to download " + url + ", " + cause.getMessage(), requireNonNull(cause));
|
super("Unable to download " + uri + ", " + cause.getMessage(), requireNonNull(cause));
|
||||||
|
this.uri = uri;
|
||||||
this.url = url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public URL getUrl() {
|
public URI getUri() {
|
||||||
return url;
|
return uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,36 +24,38 @@ import org.jackhuang.hmcl.util.ToStringBuilder;
|
|||||||
import org.jackhuang.hmcl.util.io.IOUtils;
|
import org.jackhuang.hmcl.util.io.IOUtils;
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
import org.jackhuang.hmcl.util.io.ResponseCodeException;
|
import org.jackhuang.hmcl.util.io.ResponseCodeException;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.threadPool;
|
import static org.jackhuang.hmcl.util.Lang.threadPool;
|
||||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
|
|
||||||
public abstract class FetchTask<T> extends Task<T> {
|
public abstract class FetchTask<T> extends Task<T> {
|
||||||
protected final List<URL> urls;
|
protected final List<URI> uris;
|
||||||
protected final int retry;
|
protected final int retry;
|
||||||
protected boolean caching;
|
protected boolean caching;
|
||||||
protected CacheRepository repository = CacheRepository.getInstance();
|
protected CacheRepository repository = CacheRepository.getInstance();
|
||||||
|
|
||||||
public FetchTask(List<URL> urls, int retry) {
|
public FetchTask(@NotNull List<@NotNull URI> uris, int retry) {
|
||||||
Objects.requireNonNull(urls);
|
Objects.requireNonNull(uris);
|
||||||
|
|
||||||
this.urls = urls.stream().filter(Objects::nonNull).collect(Collectors.toList());
|
this.uris = List.copyOf(uris);
|
||||||
this.retry = retry;
|
this.retry = retry;
|
||||||
|
|
||||||
if (this.urls.isEmpty())
|
if (this.uris.isEmpty())
|
||||||
throw new IllegalArgumentException("At least one URL is required");
|
throw new IllegalArgumentException("At least one URL is required");
|
||||||
|
|
||||||
setExecutor(download());
|
setExecutor(download());
|
||||||
@@ -67,18 +69,19 @@ public abstract class FetchTask<T> extends Task<T> {
|
|||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void beforeDownload(URL url) throws IOException {}
|
protected void beforeDownload(URI uri) throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract void useCachedResult(Path cachedFile) throws IOException;
|
protected abstract void useCachedResult(Path cachedFile) throws IOException;
|
||||||
|
|
||||||
protected abstract EnumCheckETag shouldCheckETag();
|
protected abstract EnumCheckETag shouldCheckETag();
|
||||||
|
|
||||||
protected abstract Context getContext(URLConnection conn, boolean checkETag) throws IOException;
|
protected abstract Context getContext(URLConnection connection, boolean checkETag) throws IOException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() throws Exception {
|
public void execute() throws Exception {
|
||||||
Exception exception = null;
|
Exception exception = null;
|
||||||
URL failedURL = null;
|
URI failedURI = null;
|
||||||
boolean checkETag;
|
boolean checkETag;
|
||||||
switch (shouldCheckETag()) {
|
switch (shouldCheckETag()) {
|
||||||
case CHECK_E_TAG: checkETag = true; break;
|
case CHECK_E_TAG: checkETag = true; break;
|
||||||
@@ -87,7 +90,7 @@ public abstract class FetchTask<T> extends Task<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int repeat = 0;
|
int repeat = 0;
|
||||||
download: for (URL url : urls) {
|
download: for (URI uri : uris) {
|
||||||
for (int retryTime = 0; retryTime < retry; retryTime++) {
|
for (int retryTime = 0; retryTime < retry; retryTime++) {
|
||||||
if (isCancelled()) {
|
if (isCancelled()) {
|
||||||
break download;
|
break download;
|
||||||
@@ -95,11 +98,11 @@ public abstract class FetchTask<T> extends Task<T> {
|
|||||||
|
|
||||||
List<String> redirects = null;
|
List<String> redirects = null;
|
||||||
try {
|
try {
|
||||||
beforeDownload(url);
|
beforeDownload(uri);
|
||||||
|
|
||||||
updateProgress(0);
|
updateProgress(0);
|
||||||
|
|
||||||
URLConnection conn = NetworkUtils.createConnection(url);
|
URLConnection conn = NetworkUtils.createConnection(uri);
|
||||||
if (checkETag) repository.injectConnection(conn);
|
if (checkETag) repository.injectConnection(conn);
|
||||||
|
|
||||||
if (conn instanceof HttpURLConnection) {
|
if (conn instanceof HttpURLConnection) {
|
||||||
@@ -111,21 +114,21 @@ public abstract class FetchTask<T> extends Task<T> {
|
|||||||
if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
if (responseCode == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
||||||
// Handle cache
|
// Handle cache
|
||||||
try {
|
try {
|
||||||
Path cache = repository.getCachedRemoteFile(conn);
|
Path cache = repository.getCachedRemoteFile(conn.getURL().toURI());
|
||||||
useCachedResult(cache);
|
useCachedResult(cache);
|
||||||
return;
|
return;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.warning("Unable to use cached file, redownload " + url, e);
|
LOG.warning("Unable to use cached file, redownload " + uri, e);
|
||||||
repository.removeRemoteEntry(conn);
|
repository.removeRemoteEntry(conn.getURL().toURI());
|
||||||
// Now we must reconnect the server since 304 may result in empty content,
|
// Now we must reconnect the server since 304 may result in empty content,
|
||||||
// if we want to redownload the file, we must reconnect the server without etag settings.
|
// if we want to redownload the file, we must reconnect the server without etag settings.
|
||||||
retryTime--;
|
retryTime--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else if (responseCode / 100 == 4) {
|
} else if (responseCode / 100 == 4) {
|
||||||
throw new FileNotFoundException(url.toString());
|
throw new FileNotFoundException(uri.toString());
|
||||||
} else if (responseCode / 100 != 2) {
|
} else if (responseCode / 100 != 2) {
|
||||||
throw new ResponseCodeException(url, responseCode);
|
throw new ResponseCodeException(uri, responseCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,21 +167,21 @@ public abstract class FetchTask<T> extends Task<T> {
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
} catch (FileNotFoundException ex) {
|
} catch (FileNotFoundException ex) {
|
||||||
failedURL = url;
|
failedURI = uri;
|
||||||
exception = ex;
|
exception = ex;
|
||||||
LOG.warning("Failed to download " + url + ", not found" + ((redirects == null || redirects.isEmpty()) ? "" : ", redirects: " + redirects), ex);
|
LOG.warning("Failed to download " + uri + ", not found" + ((redirects == null || redirects.isEmpty()) ? "" : ", redirects: " + redirects), ex);
|
||||||
|
|
||||||
break; // we will not try this URL again
|
break; // we will not try this URL again
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
failedURL = url;
|
failedURI = uri;
|
||||||
exception = ex;
|
exception = ex;
|
||||||
LOG.warning("Failed to download " + url + ", repeat times: " + (++repeat) + ((redirects == null || redirects.isEmpty()) ? "" : ", redirects: " + redirects), ex);
|
LOG.warning("Failed to download " + uri + ", repeat times: " + (++repeat) + ((redirects == null || redirects.isEmpty()) ? "" : ", redirects: " + redirects), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exception != null)
|
if (exception != null)
|
||||||
throw new DownloadException(failedURL, exception);
|
throw new DownloadException(failedURI, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Timer timer = new Timer("DownloadSpeedRecorder", true);
|
private static final Timer timer = new Timer("DownloadSpeedRecorder", true);
|
||||||
@@ -209,6 +212,7 @@ public abstract class FetchTask<T> extends Task<T> {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Download speed in byte/sec.
|
* Download speed in byte/sec.
|
||||||
|
*
|
||||||
* @return download speed
|
* @return download speed
|
||||||
*/
|
*/
|
||||||
public int getSpeed() {
|
public int getSpeed() {
|
||||||
@@ -240,7 +244,7 @@ public abstract class FetchTask<T> extends Task<T> {
|
|||||||
NOT_CHECK_E_TAG,
|
NOT_CHECK_E_TAG,
|
||||||
CACHED
|
CACHED
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static final class DownloadState {
|
protected static final class DownloadState {
|
||||||
private final int startPosition;
|
private final int startPosition;
|
||||||
private final int endPosition;
|
private final int endPosition;
|
||||||
|
|||||||
@@ -22,16 +22,20 @@ import org.jackhuang.hmcl.util.io.ChecksumMismatchException;
|
|||||||
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
import org.jackhuang.hmcl.util.io.CompressingUtils;
|
||||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.OutputStream;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.nio.file.FileSystem;
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static org.jackhuang.hmcl.util.DigestUtils.getDigest;
|
import static org.jackhuang.hmcl.util.DigestUtils.getDigest;
|
||||||
@@ -78,73 +82,76 @@ public class FileDownloadTask extends FetchTask<Void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final File file;
|
private final Path file;
|
||||||
private final IntegrityCheck integrityCheck;
|
private final IntegrityCheck integrityCheck;
|
||||||
private Path candidate;
|
private Path candidate;
|
||||||
private final ArrayList<IntegrityCheckHandler> integrityCheckHandlers = new ArrayList<>();
|
private final ArrayList<IntegrityCheckHandler> integrityCheckHandlers = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param url the URL of remote file.
|
* @param uri the URI of remote file.
|
||||||
* @param file the location that download to.
|
* @param path the location that download to.
|
||||||
*/
|
*/
|
||||||
public FileDownloadTask(URL url, File file) {
|
public FileDownloadTask(URI uri, Path path) {
|
||||||
this(url, file, null);
|
this(uri, path, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param url the URL of remote file.
|
* @param uri the URI of remote file.
|
||||||
* @param file the location that download to.
|
* @param path the location that download to.
|
||||||
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
||||||
*/
|
*/
|
||||||
public FileDownloadTask(URL url, File file, IntegrityCheck integrityCheck) {
|
public FileDownloadTask(URI uri, Path path, IntegrityCheck integrityCheck) {
|
||||||
this(Collections.singletonList(url), file, integrityCheck);
|
this(List.of(uri), path, integrityCheck);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param url the URL of remote file.
|
* @param uri the URI of remote file.
|
||||||
* @param file the location that download to.
|
* @param path the location that download to.
|
||||||
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
||||||
* @param retry the times for retrying if downloading fails.
|
* @param retry the times for retrying if downloading fails.
|
||||||
*/
|
*/
|
||||||
public FileDownloadTask(URL url, File file, IntegrityCheck integrityCheck, int retry) {
|
public FileDownloadTask(URI uri, Path path, IntegrityCheck integrityCheck, int retry) {
|
||||||
this(Collections.singletonList(url), file, integrityCheck, retry);
|
this(List.of(uri), path, integrityCheck, retry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* @param urls urls of remote file, will be attempted in order.
|
*
|
||||||
|
* @param uris uris of remote file, will be attempted in order.
|
||||||
* @param file the location that download to.
|
* @param file the location that download to.
|
||||||
*/
|
*/
|
||||||
public FileDownloadTask(List<URL> urls, File file) {
|
public FileDownloadTask(List<URI> uris, Path file) {
|
||||||
this(urls, file, null);
|
this(uris, file, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* @param urls urls of remote file, will be attempted in order.
|
*
|
||||||
* @param file the location that download to.
|
* @param uris uris of remote file, will be attempted in order.
|
||||||
|
* @param path the location that download to.
|
||||||
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
||||||
*/
|
*/
|
||||||
public FileDownloadTask(List<URL> urls, File file, IntegrityCheck integrityCheck) {
|
public FileDownloadTask(List<URI> uris, Path path, IntegrityCheck integrityCheck) {
|
||||||
this(urls, file, integrityCheck, 3);
|
this(uris, path, integrityCheck, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* @param urls urls of remote file, will be attempted in order.
|
*
|
||||||
* @param file the location that download to.
|
* @param uris uris of remote file, will be attempted in order.
|
||||||
|
* @param path the location that download to.
|
||||||
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
* @param integrityCheck the integrity check to perform, null if no integrity check is to be performed
|
||||||
* @param retry the times for retrying if downloading fails.
|
* @param retry the times for retrying if downloading fails.
|
||||||
*/
|
*/
|
||||||
public FileDownloadTask(List<URL> urls, File file, IntegrityCheck integrityCheck, int retry) {
|
public FileDownloadTask(List<URI> uris, Path path, IntegrityCheck integrityCheck, int retry) {
|
||||||
super(urls, retry);
|
super(uris, retry);
|
||||||
this.file = file;
|
this.file = path;
|
||||||
this.integrityCheck = integrityCheck;
|
this.integrityCheck = integrityCheck;
|
||||||
|
|
||||||
setName(file.getName());
|
setName(path.getFileName().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getFile() {
|
public Path getPath() {
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,8 +171,8 @@ public class FileDownloadTask extends FetchTask<Void> {
|
|||||||
Optional<Path> cache = repository.checkExistentFile(candidate, integrityCheck.getAlgorithm(), integrityCheck.getChecksum());
|
Optional<Path> cache = repository.checkExistentFile(candidate, integrityCheck.getAlgorithm(), integrityCheck.getChecksum());
|
||||||
if (cache.isPresent()) {
|
if (cache.isPresent()) {
|
||||||
try {
|
try {
|
||||||
FileUtils.copyFile(cache.get().toFile(), file);
|
FileUtils.copyFile(cache.get(), file);
|
||||||
LOG.trace("Successfully verified file " + file + " from " + urls.get(0));
|
LOG.trace("Successfully verified file " + file + " from " + uris.get(0));
|
||||||
return EnumCheckETag.CACHED;
|
return EnumCheckETag.CACHED;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.warning("Failed to copy cache files", e);
|
LOG.warning("Failed to copy cache files", e);
|
||||||
@@ -178,20 +185,20 @@ public class FileDownloadTask extends FetchTask<Void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void beforeDownload(URL url) {
|
protected void beforeDownload(URI uri) {
|
||||||
LOG.trace("Downloading " + url + " to " + file);
|
LOG.trace("Downloading " + uri + " to " + file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void useCachedResult(Path cache) throws IOException {
|
protected void useCachedResult(Path cache) throws IOException {
|
||||||
FileUtils.copyFile(cache.toFile(), file);
|
FileUtils.copyFile(cache, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Context getContext(URLConnection conn, boolean checkETag) throws IOException {
|
protected Context getContext(URLConnection connection, boolean checkETag) throws IOException {
|
||||||
Path temp = Files.createTempFile(null, null);
|
Path temp = Files.createTempFile(null, null);
|
||||||
RandomAccessFile rFile = new RandomAccessFile(temp.toFile(), "rw");
|
|
||||||
MessageDigest digest = integrityCheck == null ? null : integrityCheck.createDigest();
|
MessageDigest digest = integrityCheck == null ? null : integrityCheck.createDigest();
|
||||||
|
OutputStream fileOutput = Files.newOutputStream(temp);
|
||||||
|
|
||||||
return new Context() {
|
return new Context() {
|
||||||
@Override
|
@Override
|
||||||
@@ -200,36 +207,34 @@ public class FileDownloadTask extends FetchTask<Void> {
|
|||||||
digest.update(buffer, offset, len);
|
digest.update(buffer, offset, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
rFile.write(buffer, offset, len);
|
fileOutput.write(buffer, offset, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
try {
|
try {
|
||||||
rFile.close();
|
fileOutput.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.warning("Failed to close file: " + rFile, e);
|
LOG.warning("Failed to close file: " + temp, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSuccess()) {
|
if (!isSuccess()) {
|
||||||
try {
|
try {
|
||||||
Files.delete(temp);
|
Files.deleteIfExists(temp);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.warning("Failed to delete file: " + rFile, e);
|
LOG.warning("Failed to delete file: " + temp, e);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (IntegrityCheckHandler handler : integrityCheckHandlers) {
|
for (IntegrityCheckHandler handler : integrityCheckHandlers) {
|
||||||
handler.checkIntegrity(temp, file.toPath());
|
handler.checkIntegrity(temp, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
Files.deleteIfExists(file.toPath());
|
Files.createDirectories(file.toAbsolutePath().getParent());
|
||||||
if (!FileUtils.makeDirectory(file.getAbsoluteFile().getParentFile()))
|
|
||||||
throw new IOException("Unable to make parent directory " + file);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
FileUtils.moveFile(temp.toFile(), file);
|
Files.move(temp, file, StandardCopyOption.REPLACE_EXISTING);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IOException("Unable to move temp file from " + temp + " to " + file, e);
|
throw new IOException("Unable to move temp file from " + temp + " to " + file, e);
|
||||||
}
|
}
|
||||||
@@ -241,14 +246,14 @@ public class FileDownloadTask extends FetchTask<Void> {
|
|||||||
|
|
||||||
if (caching && integrityCheck != null) {
|
if (caching && integrityCheck != null) {
|
||||||
try {
|
try {
|
||||||
repository.cacheFile(file.toPath(), integrityCheck.getAlgorithm(), integrityCheck.getChecksum());
|
repository.cacheFile(file, integrityCheck.getAlgorithm(), integrityCheck.getChecksum());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.warning("Failed to cache file", e);
|
LOG.warning("Failed to cache file", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkETag) {
|
if (checkETag) {
|
||||||
repository.cacheRemoteFile(file.toPath(), conn);
|
repository.cacheRemoteFile(connection, file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -257,7 +262,8 @@ public class FileDownloadTask extends FetchTask<Void> {
|
|||||||
public interface IntegrityCheckHandler {
|
public interface IntegrityCheckHandler {
|
||||||
/**
|
/**
|
||||||
* Check whether the file is corrupted or not.
|
* Check whether the file is corrupted or not.
|
||||||
* @param filePath the file locates in (maybe in temp directory)
|
*
|
||||||
|
* @param filePath the file locates in (maybe in temp directory)
|
||||||
* @param destinationPath for real file name
|
* @param destinationPath for real file name
|
||||||
* @throws IOException if the file is corrupted
|
* @throws IOException if the file is corrupted
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ package org.jackhuang.hmcl.task;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
|
||||||
@@ -34,25 +34,27 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
|||||||
*/
|
*/
|
||||||
public final class GetTask extends FetchTask<String> {
|
public final class GetTask extends FetchTask<String> {
|
||||||
|
|
||||||
|
private static final int DEFAULT_RETRY = 3;
|
||||||
|
|
||||||
private final Charset charset;
|
private final Charset charset;
|
||||||
|
|
||||||
public GetTask(URL url) {
|
public GetTask(URI url) {
|
||||||
this(url, UTF_8);
|
this(url, UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetTask(URL url, Charset charset) {
|
public GetTask(URI url, Charset charset) {
|
||||||
this(url, charset, 3);
|
this(url, charset, DEFAULT_RETRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetTask(URL url, Charset charset, int retry) {
|
public GetTask(URI url, Charset charset, int retry) {
|
||||||
this(Collections.singletonList(url), charset, retry);
|
this(List.of(url), charset, retry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetTask(List<URL> url) {
|
public GetTask(List<URI> url) {
|
||||||
this(url, UTF_8, 3);
|
this(url, UTF_8, DEFAULT_RETRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetTask(List<URL> urls, Charset charset, int retry) {
|
public GetTask(List<URI> urls, Charset charset, int retry) {
|
||||||
super(urls, retry);
|
super(urls, retry);
|
||||||
this.charset = charset;
|
this.charset = charset;
|
||||||
|
|
||||||
@@ -70,7 +72,7 @@ public final class GetTask extends FetchTask<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Context getContext(URLConnection conn, boolean checkETag) {
|
protected Context getContext(URLConnection connection, boolean checkETag) {
|
||||||
return new Context() {
|
return new Context() {
|
||||||
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
@@ -87,7 +89,7 @@ public final class GetTask extends FetchTask<String> {
|
|||||||
setResult(result);
|
setResult(result);
|
||||||
|
|
||||||
if (checkETag) {
|
if (checkETag) {
|
||||||
repository.cacheText(result, conn);
|
repository.cacheText(connection, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import org.jackhuang.hmcl.util.io.FileUtils;
|
|||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.Channels;
|
import java.nio.channels.Channels;
|
||||||
@@ -157,12 +158,11 @@ public class CacheRepository {
|
|||||||
return cache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getCachedRemoteFile(URLConnection conn) throws IOException {
|
public Path getCachedRemoteFile(URI uri) throws IOException {
|
||||||
String url = conn.getURL().toString();
|
|
||||||
lock.readLock().lock();
|
lock.readLock().lock();
|
||||||
ETagItem eTagItem;
|
ETagItem eTagItem;
|
||||||
try {
|
try {
|
||||||
eTagItem = index.get(url);
|
eTagItem = index.get(uri.toString());
|
||||||
} finally {
|
} finally {
|
||||||
lock.readLock().unlock();
|
lock.readLock().unlock();
|
||||||
}
|
}
|
||||||
@@ -177,11 +177,10 @@ public class CacheRepository {
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeRemoteEntry(URLConnection conn) {
|
public void removeRemoteEntry(URI uri) {
|
||||||
String url = conn.getURL().toString();
|
|
||||||
lock.readLock().lock();
|
lock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
index.remove(url);
|
index.remove(uri.toString());
|
||||||
} finally {
|
} finally {
|
||||||
lock.readLock().unlock();
|
lock.readLock().unlock();
|
||||||
}
|
}
|
||||||
@@ -203,34 +202,34 @@ public class CacheRepository {
|
|||||||
// conn.setRequestProperty("If-Modified-Since", eTagItem.getRemoteLastModified());
|
// conn.setRequestProperty("If-Modified-Since", eTagItem.getRemoteLastModified());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cacheRemoteFile(Path downloaded, URLConnection conn) throws IOException {
|
public void cacheRemoteFile(URLConnection connection, Path downloaded) throws IOException {
|
||||||
cacheData(() -> {
|
cacheData(connection, () -> {
|
||||||
String hash = DigestUtils.digestToString(SHA1, downloaded);
|
String hash = DigestUtils.digestToString(SHA1, downloaded);
|
||||||
Path cached = cacheFile(downloaded, SHA1, hash);
|
Path cached = cacheFile(downloaded, SHA1, hash);
|
||||||
return new CacheResult(hash, cached);
|
return new CacheResult(hash, cached);
|
||||||
}, conn);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cacheText(String text, URLConnection conn) throws IOException {
|
public void cacheText(URLConnection connection, String text) throws IOException {
|
||||||
cacheBytes(text.getBytes(UTF_8), conn);
|
cacheBytes(connection, text.getBytes(UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cacheBytes(byte[] bytes, URLConnection conn) throws IOException {
|
public void cacheBytes(URLConnection connection, byte[] bytes) throws IOException {
|
||||||
cacheData(() -> {
|
cacheData(connection, () -> {
|
||||||
String hash = DigestUtils.digestToString(SHA1, bytes);
|
String hash = DigestUtils.digestToString(SHA1, bytes);
|
||||||
Path cached = getFile(SHA1, hash);
|
Path cached = getFile(SHA1, hash);
|
||||||
FileUtils.writeBytes(cached, bytes);
|
FileUtils.writeBytes(cached, bytes);
|
||||||
return new CacheResult(hash, cached);
|
return new CacheResult(hash, cached);
|
||||||
}, conn);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void cacheData(ExceptionalSupplier<CacheResult, IOException> cacheSupplier, URLConnection conn) throws IOException {
|
private void cacheData(URLConnection connection, ExceptionalSupplier<CacheResult, IOException> cacheSupplier) throws IOException {
|
||||||
String eTag = conn.getHeaderField("ETag");
|
String eTag = connection.getHeaderField("ETag");
|
||||||
if (eTag == null) return;
|
if (eTag == null || eTag.isEmpty()) return;
|
||||||
String url = conn.getURL().toString();
|
String uri = connection.getURL().toString();
|
||||||
String lastModified = conn.getHeaderField("Last-Modified");
|
String lastModified = connection.getHeaderField("Last-Modified");
|
||||||
CacheResult cacheResult = cacheSupplier.get();
|
CacheResult cacheResult = cacheSupplier.get();
|
||||||
ETagItem eTagItem = new ETagItem(url, eTag, cacheResult.hash, Files.getLastModifiedTime(cacheResult.cachedFile).toMillis(), lastModified);
|
ETagItem eTagItem = new ETagItem(uri, eTag, cacheResult.hash, Files.getLastModifiedTime(cacheResult.cachedFile).toMillis(), lastModified);
|
||||||
Lock writeLock = lock.writeLock();
|
Lock writeLock = lock.writeLock();
|
||||||
writeLock.lock();
|
writeLock.lock();
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.util.io;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Glavo
|
||||||
|
*/
|
||||||
|
public enum ContentEncoding {
|
||||||
|
NONE {
|
||||||
|
@Override
|
||||||
|
public InputStream wrap(InputStream inputStream) {
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GZIP {
|
||||||
|
@Override
|
||||||
|
public InputStream wrap(InputStream inputStream) throws IOException {
|
||||||
|
return new GZIPInputStream(inputStream);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static @NotNull ContentEncoding fromConnection(URLConnection connection) throws IOException {
|
||||||
|
String encoding = connection.getContentEncoding();
|
||||||
|
if (encoding == null || encoding.isEmpty()) {
|
||||||
|
return NONE;
|
||||||
|
} else if ("gzip".equalsIgnoreCase(encoding)) {
|
||||||
|
return GZIP;
|
||||||
|
} else {
|
||||||
|
throw new IOException("Unsupported content encoding: " + encoding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract InputStream wrap(InputStream inputStream) throws IOException;
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -113,18 +114,13 @@ public abstract class HttpRequest {
|
|||||||
return getStringAsync().thenApplyAsync(jsonString -> JsonUtils.fromNonNullJson(jsonString, type));
|
return getStringAsync().thenApplyAsync(jsonString -> JsonUtils.fromNonNullJson(jsonString, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpRequest filter(ExceptionalBiConsumer<URL, Integer, IOException> responseCodeTester) {
|
|
||||||
this.responseCodeTester = responseCodeTester;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpRequest ignoreHttpErrorCode(int code) {
|
public HttpRequest ignoreHttpErrorCode(int code) {
|
||||||
toleratedHttpCodes.add(code);
|
toleratedHttpCodes.add(code);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpURLConnection createConnection() throws IOException {
|
public HttpURLConnection createConnection() throws IOException {
|
||||||
HttpURLConnection con = createHttpConnection(new URL(url));
|
HttpURLConnection con = createHttpConnection(URI.create(url));
|
||||||
con.setRequestMethod(method);
|
con.setRequestMethod(method);
|
||||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||||
con.setRequestProperty(entry.getKey(), entry.getValue());
|
con.setRequestProperty(entry.getKey(), entry.getValue());
|
||||||
@@ -133,7 +129,7 @@ public abstract class HttpRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class HttpGetRequest extends HttpRequest {
|
public static class HttpGetRequest extends HttpRequest {
|
||||||
public HttpGetRequest(String url) {
|
protected HttpGetRequest(String url) {
|
||||||
super(url, "GET");
|
super(url, "GET");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,7 +145,7 @@ public abstract class HttpRequest {
|
|||||||
public static final class HttpPostRequest extends HttpRequest {
|
public static final class HttpPostRequest extends HttpRequest {
|
||||||
private byte[] bytes;
|
private byte[] bytes;
|
||||||
|
|
||||||
public HttpPostRequest(String url) {
|
private HttpPostRequest(String url) {
|
||||||
super(url, "POST");
|
super(url, "POST");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,21 +185,17 @@ public abstract class HttpRequest {
|
|||||||
|
|
||||||
URL url = new URL(this.url);
|
URL url = new URL(this.url);
|
||||||
|
|
||||||
if (responseCodeTester != null) {
|
if (con.getResponseCode() / 100 != 2) {
|
||||||
responseCodeTester.accept(url, con.getResponseCode());
|
if (!ignoreHttpCode && !toleratedHttpCodes.contains(con.getResponseCode())) {
|
||||||
} else {
|
try {
|
||||||
if (con.getResponseCode() / 100 != 2) {
|
throw new ResponseCodeException(NetworkUtils.toURI(url), con.getResponseCode(), NetworkUtils.readFullyAsString(con));
|
||||||
if (!ignoreHttpCode && !toleratedHttpCodes.contains(con.getResponseCode())) {
|
} catch (IOException e) {
|
||||||
try {
|
throw new ResponseCodeException(NetworkUtils.toURI(url), con.getResponseCode(), e);
|
||||||
throw new ResponseCodeException(url, con.getResponseCode(), NetworkUtils.readData(con));
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ResponseCodeException(url, con.getResponseCode(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NetworkUtils.readData(con);
|
return NetworkUtils.readFullyAsString(con);
|
||||||
}, retryTimes);
|
}, retryTimes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,16 @@ import org.jackhuang.hmcl.util.Pair;
|
|||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
import static org.jackhuang.hmcl.util.StringUtils.*;
|
import static org.jackhuang.hmcl.util.StringUtils.*;
|
||||||
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
@@ -39,6 +43,10 @@ public final class NetworkUtils {
|
|||||||
private NetworkUtils() {
|
private NetworkUtils() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isHttpUri(URI uri) {
|
||||||
|
return "http".equals(uri.getScheme()) || "https".equals(uri.getScheme());
|
||||||
|
}
|
||||||
|
|
||||||
public static String withQuery(String baseUrl, Map<String, String> params) {
|
public static String withQuery(String baseUrl, Map<String, String> params) {
|
||||||
StringBuilder sb = new StringBuilder(baseUrl);
|
StringBuilder sb = new StringBuilder(baseUrl);
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
@@ -73,7 +81,7 @@ public final class NetworkUtils {
|
|||||||
scanner.useDelimiter("&");
|
scanner.useDelimiter("&");
|
||||||
while (scanner.hasNext()) {
|
while (scanner.hasNext()) {
|
||||||
String[] nameValue = scanner.next().split(NAME_VALUE_SEPARATOR);
|
String[] nameValue = scanner.next().split(NAME_VALUE_SEPARATOR);
|
||||||
if (nameValue.length <= 0 || nameValue.length > 2) {
|
if (nameValue.length == 0 || nameValue.length > 2) {
|
||||||
throw new IllegalArgumentException("bad query string");
|
throw new IllegalArgumentException("bad query string");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,8 +93,8 @@ public final class NetworkUtils {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static URLConnection createConnection(URL url) throws IOException {
|
public static URLConnection createConnection(URI uri) throws IOException {
|
||||||
URLConnection connection = url.openConnection();
|
URLConnection connection = uri.toURL().openConnection();
|
||||||
connection.setUseCaches(false);
|
connection.setUseCaches(false);
|
||||||
connection.setConnectTimeout(TIME_OUT);
|
connection.setConnectTimeout(TIME_OUT);
|
||||||
connection.setReadTimeout(TIME_OUT);
|
connection.setReadTimeout(TIME_OUT);
|
||||||
@@ -94,15 +102,15 @@ public final class NetworkUtils {
|
|||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HttpURLConnection createHttpConnection(URL url) throws IOException {
|
public static HttpURLConnection createHttpConnection(URI url) throws IOException {
|
||||||
return (HttpURLConnection) createConnection(url);
|
return (HttpURLConnection) createConnection(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see <a href=
|
|
||||||
* "https://github.com/curl/curl/blob/3f7b1bb89f92c13e69ee51b710ac54f775aab320/lib/transfer.c#L1427-L1461">Curl</a>
|
|
||||||
* @param location the url to be URL encoded
|
* @param location the url to be URL encoded
|
||||||
* @return encoded URL
|
* @return encoded URL
|
||||||
|
* @see <a href=
|
||||||
|
* "https://github.com/curl/curl/blob/3f7b1bb89f92c13e69ee51b710ac54f775aab320/lib/transfer.c#L1427-L1461">Curl</a>
|
||||||
*/
|
*/
|
||||||
public static String encodeLocation(String location) {
|
public static String encodeLocation(String location) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
@@ -138,10 +146,10 @@ public final class NetworkUtils {
|
|||||||
* This method is a work-around that aims to solve problem when "Location" in
|
* This method is a work-around that aims to solve problem when "Location" in
|
||||||
* stupid server's response is not encoded.
|
* stupid server's response is not encoded.
|
||||||
*
|
*
|
||||||
* @see <a href="https://github.com/curl/curl/issues/473">Issue with libcurl</a>
|
|
||||||
* @param conn the stupid http connection.
|
* @param conn the stupid http connection.
|
||||||
* @return manually redirected http connection.
|
* @return manually redirected http connection.
|
||||||
* @throws IOException if an I/O error occurs.
|
* @throws IOException if an I/O error occurs.
|
||||||
|
* @see <a href="https://github.com/curl/curl/issues/473">Issue with libcurl</a>
|
||||||
*/
|
*/
|
||||||
public static HttpURLConnection resolveConnection(HttpURLConnection conn, List<String> redirects) throws IOException {
|
public static HttpURLConnection resolveConnection(HttpURLConnection conn, List<String> redirects) throws IOException {
|
||||||
int redirect = 0;
|
int redirect = 0;
|
||||||
@@ -153,7 +161,7 @@ public final class NetworkUtils {
|
|||||||
Map<String, List<String>> properties = conn.getRequestProperties();
|
Map<String, List<String>> properties = conn.getRequestProperties();
|
||||||
String method = conn.getRequestMethod();
|
String method = conn.getRequestMethod();
|
||||||
int code = conn.getResponseCode();
|
int code = conn.getResponseCode();
|
||||||
if (code >= 300 && code <= 307 && code != 306 && code != 304) {
|
if (code >= 300 && code <= 308 && code != 306 && code != 304) {
|
||||||
String newURL = conn.getHeaderField("Location");
|
String newURL = conn.getHeaderField("Location");
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
|
|
||||||
@@ -178,19 +186,15 @@ public final class NetworkUtils {
|
|||||||
return conn;
|
return conn;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String doGet(URL url) throws IOException {
|
public static String doGet(URI uri) throws IOException {
|
||||||
HttpURLConnection con = createHttpConnection(url);
|
return readFullyAsString(resolveConnection(createHttpConnection(uri)));
|
||||||
con = resolveConnection(con);
|
|
||||||
return IOUtils.readFullyAsString(con.getInputStream());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String doGet(List<URL> urls) throws IOException {
|
public static String doGet(List<URI> uris) throws IOException {
|
||||||
List<IOException> exceptions = null;
|
List<IOException> exceptions = null;
|
||||||
for (URL url : urls) {
|
for (URI uri : uris) {
|
||||||
try {
|
try {
|
||||||
HttpURLConnection con = createHttpConnection(url);
|
return doGet(uri);
|
||||||
con = resolveConnection(con);
|
|
||||||
return IOUtils.readFullyAsString(con.getInputStream());
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (exceptions == null) {
|
if (exceptions == null) {
|
||||||
exceptions = new ArrayList<>(1);
|
exceptions = new ArrayList<>(1);
|
||||||
@@ -212,7 +216,11 @@ public final class NetworkUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String doPost(URL u, Map<String, String> params) throws IOException {
|
public static String doPost(URI uri, String post) throws IOException {
|
||||||
|
return doPost(uri, post, "application/x-www-form-urlencoded");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String doPost(URI u, Map<String, String> params) throws IOException {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
if (params != null) {
|
if (params != null) {
|
||||||
for (Map.Entry<String, String> e : params.entrySet())
|
for (Map.Entry<String, String> e : params.entrySet())
|
||||||
@@ -222,50 +230,72 @@ public final class NetworkUtils {
|
|||||||
return doPost(u, sb.toString());
|
return doPost(u, sb.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String doPost(URL u, String post) throws IOException {
|
public static String doPost(URI uri, String post, String contentType) throws IOException {
|
||||||
return doPost(u, post, "application/x-www-form-urlencoded");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String doPost(URL url, String post, String contentType) throws IOException {
|
|
||||||
byte[] bytes = post.getBytes(UTF_8);
|
byte[] bytes = post.getBytes(UTF_8);
|
||||||
|
|
||||||
HttpURLConnection con = createHttpConnection(url);
|
HttpURLConnection con = createHttpConnection(uri);
|
||||||
con.setRequestMethod("POST");
|
con.setRequestMethod("POST");
|
||||||
con.setDoOutput(true);
|
con.setDoOutput(true);
|
||||||
con.setRequestProperty("Content-Type", contentType + "; charset=utf-8");
|
con.setRequestProperty("Content-Type", contentType + "; charset=utf-8");
|
||||||
con.setRequestProperty("Content-Length", "" + bytes.length);
|
con.setRequestProperty("Content-Length", String.valueOf(bytes.length));
|
||||||
try (OutputStream os = con.getOutputStream()) {
|
try (OutputStream os = con.getOutputStream()) {
|
||||||
os.write(bytes);
|
os.write(bytes);
|
||||||
}
|
}
|
||||||
return readData(con);
|
return readFullyAsString(con);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String readData(HttpURLConnection con) throws IOException {
|
static final Pattern CHARSET_REGEX = Pattern.compile("\\s*(charset)\\s*=\\s*['|\"]?(?<charset>[^\"^';,]+)['|\"]?");
|
||||||
try {
|
|
||||||
try (InputStream stdout = con.getInputStream()) {
|
static Charset getCharsetFromContentType(String contentType) {
|
||||||
return IOUtils.readFullyAsString("gzip".equals(con.getContentEncoding()) ? IOUtils.wrapFromGZip(stdout) : stdout);
|
if (contentType == null || contentType.isBlank())
|
||||||
|
return UTF_8;
|
||||||
|
|
||||||
|
Matcher matcher = CHARSET_REGEX.matcher(contentType);
|
||||||
|
if (matcher.find()) {
|
||||||
|
String charsetName = matcher.group("charset");
|
||||||
|
try {
|
||||||
|
return Charset.forName(charsetName);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// Ignore invalid charset
|
||||||
|
LOG.warning("Bad charset name: " + charsetName + ", using UTF-8 instead", e);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
}
|
||||||
try (InputStream stderr = con.getErrorStream()) {
|
return UTF_8;
|
||||||
if (stderr == null)
|
}
|
||||||
|
|
||||||
|
public static String readFullyAsString(URLConnection con) throws IOException {
|
||||||
|
try {
|
||||||
|
var contentEncoding = ContentEncoding.fromConnection(con);
|
||||||
|
Charset charset = getCharsetFromContentType(con.getHeaderField("Content-Type"));
|
||||||
|
|
||||||
|
try (InputStream stdout = con.getInputStream()) {
|
||||||
|
return IOUtils.readFullyAsString(contentEncoding.wrap(stdout), charset);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (con instanceof HttpURLConnection) {
|
||||||
|
try (InputStream stderr = ((HttpURLConnection) con).getErrorStream()) {
|
||||||
|
if (stderr == null)
|
||||||
|
throw e;
|
||||||
|
return IOUtils.readFullyAsString(contentEncoding.wrap(stderr), charset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
throw e;
|
throw e;
|
||||||
return IOUtils.readFullyAsString("gzip".equals(con.getContentEncoding()) ? IOUtils.wrapFromGZip(stderr) : stderr);
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (con instanceof HttpURLConnection) {
|
||||||
|
((HttpURLConnection) con).disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String detectFileName(URL url) throws IOException {
|
public static String detectFileName(URI uri) throws IOException {
|
||||||
HttpURLConnection conn = resolveConnection(createHttpConnection(url));
|
HttpURLConnection conn = resolveConnection(createHttpConnection(uri));
|
||||||
int code = conn.getResponseCode();
|
int code = conn.getResponseCode();
|
||||||
if (code / 100 == 4)
|
if (code / 100 == 4)
|
||||||
throw new FileNotFoundException();
|
throw new FileNotFoundException();
|
||||||
if (code / 100 != 2)
|
if (code / 100 != 2)
|
||||||
throw new IOException(url + ": response code " + conn.getResponseCode());
|
throw new ResponseCodeException(uri, conn.getResponseCode());
|
||||||
|
|
||||||
return detectFileName(conn);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String detectFileName(HttpURLConnection conn) {
|
|
||||||
String disposition = conn.getHeaderField("Content-Disposition");
|
String disposition = conn.getHeaderField("Content-Disposition");
|
||||||
if (disposition == null || !disposition.contains("filename=")) {
|
if (disposition == null || !disposition.contains("filename=")) {
|
||||||
String u = conn.getURL().toString();
|
String u = conn.getURL().toString();
|
||||||
@@ -275,46 +305,22 @@ public final class NetworkUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static URL toURL(String str) {
|
public static URI toURI(URL url) {
|
||||||
try {
|
try {
|
||||||
return new URL(str);
|
return url.toURI();
|
||||||
} catch (MalformedURLException e) {
|
} catch (URISyntaxException e) {
|
||||||
throw new IllegalArgumentException(e);
|
throw new IllegalArgumentException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isURL(String str) {
|
|
||||||
try {
|
|
||||||
new URL(str);
|
|
||||||
return true;
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean urlExists(URL url) throws IOException {
|
|
||||||
HttpURLConnection con = createHttpConnection(url);
|
|
||||||
con = resolveConnection(con);
|
|
||||||
int responseCode = con.getResponseCode();
|
|
||||||
con.disconnect();
|
|
||||||
return responseCode / 100 == 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==== Shortcut methods for encoding/decoding URLs in UTF-8 ====
|
// ==== Shortcut methods for encoding/decoding URLs in UTF-8 ====
|
||||||
public static String encodeURL(String toEncode) {
|
public static String encodeURL(String toEncode) {
|
||||||
try {
|
return URLEncoder.encode(toEncode, UTF_8);
|
||||||
return URLEncoder.encode(toEncode, "UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String decodeURL(String toDecode) {
|
public static String decodeURL(String toDecode) {
|
||||||
try {
|
return URLDecoder.decode(toDecode, UTF_8);
|
||||||
return URLDecoder.decode(toDecode, "UTF-8");
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// ====
|
// ====
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,37 +18,37 @@
|
|||||||
package org.jackhuang.hmcl.util.io;
|
package org.jackhuang.hmcl.util.io;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URI;
|
||||||
|
|
||||||
public final class ResponseCodeException extends IOException {
|
public final class ResponseCodeException extends IOException {
|
||||||
|
|
||||||
private final URL url;
|
private final URI uri;
|
||||||
private final int responseCode;
|
private final int responseCode;
|
||||||
private final String data;
|
private final String data;
|
||||||
|
|
||||||
public ResponseCodeException(URL url, int responseCode) {
|
public ResponseCodeException(URI uri, int responseCode) {
|
||||||
super("Unable to request url " + url + ", response code: " + responseCode);
|
super("Unable to request url " + uri + ", response code: " + responseCode);
|
||||||
this.url = url;
|
this.uri = uri;
|
||||||
this.responseCode = responseCode;
|
this.responseCode = responseCode;
|
||||||
this.data = null;
|
this.data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResponseCodeException(URL url, int responseCode, Throwable cause) {
|
public ResponseCodeException(URI uri, int responseCode, Throwable cause) {
|
||||||
super("Unable to request url " + url + ", response code: " + responseCode, cause);
|
super("Unable to request url " + uri + ", response code: " + responseCode, cause);
|
||||||
this.url = url;
|
this.uri = uri;
|
||||||
this.responseCode = responseCode;
|
this.responseCode = responseCode;
|
||||||
this.data = null;
|
this.data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResponseCodeException(URL url, int responseCode, String data) {
|
public ResponseCodeException(URI uri, int responseCode, String data) {
|
||||||
super("Unable to request url " + url + ", response code: " + responseCode + ", data: " + data);
|
super("Unable to request url " + uri + ", response code: " + responseCode + ", data: " + data);
|
||||||
this.url = url;
|
this.uri = uri;
|
||||||
this.responseCode = responseCode;
|
this.responseCode = responseCode;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public URL getUrl() {
|
public URI getUri() {
|
||||||
return url;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getResponseCode() {
|
public int getResponseCode() {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.util.io;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Glavo
|
||||||
|
*/
|
||||||
|
public class NetworkUtilsTest {
|
||||||
|
@Test
|
||||||
|
public void testGetEncodingFromUrl() {
|
||||||
|
assertEquals(UTF_8, NetworkUtils.getCharsetFromContentType(null));
|
||||||
|
assertEquals(UTF_8, NetworkUtils.getCharsetFromContentType(""));
|
||||||
|
assertEquals(UTF_8, NetworkUtils.getCharsetFromContentType("text/html"));
|
||||||
|
assertEquals(UTF_8, NetworkUtils.getCharsetFromContentType("text/html; charset=utf-8"));
|
||||||
|
assertEquals(US_ASCII, NetworkUtils.getCharsetFromContentType("text/html; charset=ascii"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user