mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-13 22:07:01 -04:00
Merge pull request #468 from yushijinhun/authlib-injector-ali
支持 authlib-injector API 地址指示
This commit is contained in:
commit
f0d1c9b361
@ -31,6 +31,7 @@ import javafx.scene.layout.BorderPane;
|
|||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
import org.jackhuang.hmcl.auth.*;
|
import org.jackhuang.hmcl.auth.*;
|
||||||
|
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorDownloadException;
|
||||||
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer;
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
|
import org.jackhuang.hmcl.auth.yggdrasil.GameProfile;
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.RemoteAuthenticationException;
|
import org.jackhuang.hmcl.auth.yggdrasil.RemoteAuthenticationException;
|
||||||
@ -278,6 +279,8 @@ public class AddAccountPane extends StackPane {
|
|||||||
return i18n("account.failed.invalid_password");
|
return i18n("account.failed.invalid_password");
|
||||||
}
|
}
|
||||||
return exception.getMessage();
|
return exception.getMessage();
|
||||||
|
} else if (exception instanceof AuthlibInjectorDownloadException) {
|
||||||
|
return i18n("account.failed.injector_download_failure");
|
||||||
} else {
|
} else {
|
||||||
return exception.getClass().getName() + ": " + exception.getLocalizedMessage();
|
return exception.getClass().getName() + ": " + exception.getLocalizedMessage();
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import com.jfoenix.controls.JFXButton;
|
|||||||
import com.jfoenix.controls.JFXDialogLayout;
|
import com.jfoenix.controls.JFXDialogLayout;
|
||||||
import com.jfoenix.controls.JFXTextField;
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
|
||||||
import javafx.beans.binding.Bindings;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.layout.StackPane;
|
import javafx.scene.layout.StackPane;
|
||||||
@ -39,7 +38,6 @@ import java.io.IOException;
|
|||||||
|
|
||||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||||
import static org.jackhuang.hmcl.ui.FXUtils.loadFXML;
|
import static org.jackhuang.hmcl.ui.FXUtils.loadFXML;
|
||||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
public class AddAuthlibInjectorServerPane extends StackPane implements DialogAware {
|
public class AddAuthlibInjectorServerPane extends StackPane implements DialogAware {
|
||||||
@ -70,24 +68,13 @@ public class AddAuthlibInjectorServerPane extends StackPane implements DialogAwa
|
|||||||
transitionHandler = new TransitionHandler(addServerContainer);
|
transitionHandler = new TransitionHandler(addServerContainer);
|
||||||
transitionHandler.setContent(addServerPane, ContainerAnimations.NONE.getAnimationProducer());
|
transitionHandler.setContent(addServerPane, ContainerAnimations.NONE.getAnimationProducer());
|
||||||
|
|
||||||
btnAddNext.disableProperty().bind(
|
btnAddNext.disableProperty().bind(txtServerUrl.textProperty().isEmpty());
|
||||||
Bindings.createBooleanBinding(txtServerUrl::validate, txtServerUrl.textProperty()).not());
|
|
||||||
nextPane.hideSpinner();
|
nextPane.hideSpinner();
|
||||||
|
|
||||||
txtServerUrl.setText("https://");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDialogShown() {
|
public void onDialogShown() {
|
||||||
txtServerUrl.requestFocus();
|
txtServerUrl.requestFocus();
|
||||||
txtServerUrl.selectEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String fixInputUrl(String url) {
|
|
||||||
if (!url.endsWith("/")) {
|
|
||||||
url += "/";
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolveFetchExceptionMessage(Throwable exception) {
|
private String resolveFetchExceptionMessage(Throwable exception) {
|
||||||
@ -110,13 +97,13 @@ public class AddAuthlibInjectorServerPane extends StackPane implements DialogAwa
|
|||||||
|
|
||||||
lblCreationWarning.setText("");
|
lblCreationWarning.setText("");
|
||||||
|
|
||||||
String url = fixInputUrl(txtServerUrl.getText());
|
String url = txtServerUrl.getText();
|
||||||
|
|
||||||
nextPane.showSpinner();
|
nextPane.showSpinner();
|
||||||
addServerPane.setDisable(true);
|
addServerPane.setDisable(true);
|
||||||
|
|
||||||
Task.of(() -> {
|
Task.of(() -> {
|
||||||
serverBeingAdded = AuthlibInjectorServer.fetchServerInfo(url);
|
serverBeingAdded = AuthlibInjectorServer.locateServer(url);
|
||||||
}).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
|
}).finalized(Schedulers.javafx(), (variables, isDependentsSucceeded) -> {
|
||||||
addServerPane.setDisable(false);
|
addServerPane.setDisable(false);
|
||||||
nextPane.hideSpinner();
|
nextPane.hideSpinner();
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
package org.jackhuang.hmcl.ui.construct;
|
|
||||||
|
|
||||||
import com.jfoenix.validation.base.ValidatorBase;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.scene.control.TextInputControl;
|
|
||||||
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
|
|
||||||
public class URLValidator extends ValidatorBase {
|
|
||||||
|
|
||||||
private final ObservableList<String> protocols = FXCollections.observableArrayList();
|
|
||||||
|
|
||||||
public URLValidator() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObservableList<String> getProtocols() {
|
|
||||||
return protocols;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void eval() {
|
|
||||||
if (srcControl.get() instanceof TextInputControl) {
|
|
||||||
try {
|
|
||||||
URL url = new URL(((TextInputControl) srcControl.get()).getText());
|
|
||||||
if (protocols.isEmpty())
|
|
||||||
hasErrors.set(false);
|
|
||||||
else
|
|
||||||
hasErrors.set(!protocols.contains(url.getProtocol()));
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
hasErrors.set(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,7 +4,6 @@
|
|||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import com.jfoenix.controls.*?>
|
<?import com.jfoenix.controls.*?>
|
||||||
<?import org.jackhuang.hmcl.ui.construct.URLValidator?>
|
|
||||||
<?import org.jackhuang.hmcl.ui.construct.SpinnerPane?>
|
<?import org.jackhuang.hmcl.ui.construct.SpinnerPane?>
|
||||||
|
|
||||||
<fx:root xmlns="http://javafx.com/javafx"
|
<fx:root xmlns="http://javafx.com/javafx"
|
||||||
@ -16,16 +15,7 @@
|
|||||||
<Label text="%account.injector.add" />
|
<Label text="%account.injector.add" />
|
||||||
</heading>
|
</heading>
|
||||||
<body>
|
<body>
|
||||||
<JFXTextField fx:id="txtServerUrl" promptText="%account.injector.server_url" onAction="#onAddNext">
|
<JFXTextField fx:id="txtServerUrl" promptText="%account.injector.server_url" onAction="#onAddNext" />
|
||||||
<validators>
|
|
||||||
<URLValidator message="%input.url">
|
|
||||||
<protocols>
|
|
||||||
<String fx:value="http" />
|
|
||||||
<String fx:value="https" />
|
|
||||||
</protocols>
|
|
||||||
</URLValidator>
|
|
||||||
</validators>
|
|
||||||
</JFXTextField>
|
|
||||||
</body>
|
</body>
|
||||||
<actions>
|
<actions>
|
||||||
<Label fx:id="lblCreationWarning" />
|
<Label fx:id="lblCreationWarning" />
|
||||||
|
@ -36,6 +36,7 @@ account.create=Create a new account
|
|||||||
account.email=Email
|
account.email=Email
|
||||||
account.failed.connect_authentication_server=Cannot connect to the authentication server. Check your network.
|
account.failed.connect_authentication_server=Cannot connect to the authentication server. Check your network.
|
||||||
account.failed.connect_injector_server=Cannot connect to the authentication server. Check your network and ensure the URL is correct.
|
account.failed.connect_injector_server=Cannot connect to the authentication server. Check your network and ensure the URL is correct.
|
||||||
|
account.failed.injector_download_failure=Failed to download authlib-injector. Check your network and try switching to another download source.
|
||||||
account.failed.invalid_credentials=Incorrect password, or you are forbidden to login temporarily.
|
account.failed.invalid_credentials=Incorrect password, or you are forbidden to login temporarily.
|
||||||
account.failed.invalid_password=Invalid password
|
account.failed.invalid_password=Invalid password
|
||||||
account.failed.invalid_token=Please log out and re-input your password to login.
|
account.failed.invalid_token=Please log out and re-input your password to login.
|
||||||
|
@ -36,6 +36,7 @@ account.create=建立帳戶
|
|||||||
account.email=電子信箱
|
account.email=電子信箱
|
||||||
account.failed.connect_authentication_server=無法連接認證伺服器,可能是網路問題
|
account.failed.connect_authentication_server=無法連接認證伺服器,可能是網路問題
|
||||||
account.failed.connect_injector_server=無法連接認證伺服器,可能是網路故障或 URL 輸入錯誤
|
account.failed.connect_injector_server=無法連接認證伺服器,可能是網路故障或 URL 輸入錯誤
|
||||||
|
account.failed.injector_download_failure=無法下載 authlib-injector,請檢查網路或嘗試切換下載源
|
||||||
account.failed.invalid_credentials=您的使用者名稱或密碼錯誤,或者登入次數過多被暫時禁止登入,請稍後再試
|
account.failed.invalid_credentials=您的使用者名稱或密碼錯誤,或者登入次數過多被暫時禁止登入,請稍後再試
|
||||||
account.failed.invalid_password=無效的密碼
|
account.failed.invalid_password=無效的密碼
|
||||||
account.failed.invalid_token=請嘗試登出並重新輸入密碼登入
|
account.failed.invalid_token=請嘗試登出並重新輸入密碼登入
|
||||||
|
@ -36,6 +36,7 @@ account.create=新建账户
|
|||||||
account.email=邮箱
|
account.email=邮箱
|
||||||
account.failed.connect_authentication_server=无法连接认证服务器,可能是网络问题
|
account.failed.connect_authentication_server=无法连接认证服务器,可能是网络问题
|
||||||
account.failed.connect_injector_server=无法连接认证服务器,可能是网络故障或 URL 输入错误
|
account.failed.connect_injector_server=无法连接认证服务器,可能是网络故障或 URL 输入错误
|
||||||
|
account.failed.injector_download_failure=无法下载 authlib-injector,请检查网络或尝试切换下载源
|
||||||
account.failed.invalid_credentials=您的用户名或密码错误,或者登录次数过多被暂时禁止登录,请稍后再试
|
account.failed.invalid_credentials=您的用户名或密码错误,或者登录次数过多被暂时禁止登录,请稍后再试
|
||||||
account.failed.invalid_password=无效的密码
|
account.failed.invalid_password=无效的密码
|
||||||
account.failed.invalid_token=请尝试登出并重新输入密码登录
|
account.failed.invalid_token=请尝试登出并重新输入密码登录
|
||||||
|
@ -20,21 +20,20 @@ package org.jackhuang.hmcl.auth.authlibinjector;
|
|||||||
import org.jackhuang.hmcl.auth.AuthInfo;
|
import org.jackhuang.hmcl.auth.AuthInfo;
|
||||||
import org.jackhuang.hmcl.auth.AuthenticationException;
|
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||||
import org.jackhuang.hmcl.auth.CharacterSelector;
|
import org.jackhuang.hmcl.auth.CharacterSelector;
|
||||||
|
import org.jackhuang.hmcl.auth.ServerDisconnectException;
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilAccount;
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilService;
|
||||||
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilSession;
|
import org.jackhuang.hmcl.auth.yggdrasil.YggdrasilSession;
|
||||||
import org.jackhuang.hmcl.game.Arguments;
|
import org.jackhuang.hmcl.game.Arguments;
|
||||||
import org.jackhuang.hmcl.task.GetTask;
|
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
|
||||||
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
|
import org.jackhuang.hmcl.util.function.ExceptionalSupplier;
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Level;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import java.util.concurrent.ExecutionException;
|
||||||
import static org.jackhuang.hmcl.util.Logging.LOG;
|
import static org.jackhuang.hmcl.util.io.IOUtils.readFullyWithoutClosing;
|
||||||
|
|
||||||
public class AuthlibInjectorAccount extends YggdrasilAccount {
|
public class AuthlibInjectorAccount extends YggdrasilAccount {
|
||||||
private AuthlibInjectorServer server;
|
private AuthlibInjectorServer server;
|
||||||
@ -58,41 +57,44 @@ public class AuthlibInjectorAccount extends YggdrasilAccount {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private AuthInfo inject(ExceptionalSupplier<AuthInfo, AuthenticationException> loginAction) throws AuthenticationException {
|
private AuthInfo inject(ExceptionalSupplier<AuthInfo, AuthenticationException> loginAction) throws AuthenticationException {
|
||||||
// Pre-fetch metadata
|
CompletableFuture<byte[]> prefetchedMetaTask = CompletableFuture.supplyAsync(() -> {
|
||||||
GetTask metadataFetchTask = new GetTask(NetworkUtils.toURL(server.getUrl()));
|
try (InputStream in = new URL(server.getUrl()).openStream()) {
|
||||||
Thread metadataFetchThread = Lang.thread(() -> {
|
return readFullyWithoutClosing(in);
|
||||||
try {
|
} catch (IOException e) {
|
||||||
metadataFetchTask.run();
|
throw new CompletionException(new ServerDisconnectException(e));
|
||||||
} catch (Exception e) {
|
|
||||||
LOG.log(Level.WARNING, "Failed to pre-fetch Yggdrasil metadata", e);
|
|
||||||
}
|
}
|
||||||
}, "Yggdrasil metadata fetch thread");
|
});
|
||||||
|
|
||||||
// Update authlib-injector
|
CompletableFuture<AuthlibInjectorArtifactInfo> artifactTask = CompletableFuture.supplyAsync(() -> {
|
||||||
|
try {
|
||||||
|
return authlibInjectorDownloader.get();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CompletionException(new AuthlibInjectorDownloadException(e));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
AuthInfo auth = loginAction.get();
|
||||||
|
byte[] prefetchedMeta;
|
||||||
AuthlibInjectorArtifactInfo artifact;
|
AuthlibInjectorArtifactInfo artifact;
|
||||||
try {
|
|
||||||
artifact = authlibInjectorDownloader.get();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new AuthenticationException("Failed to download authlib-injector", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform authentication
|
|
||||||
AuthInfo info = loginAction.get();
|
|
||||||
Arguments arguments = new Arguments().addJVMArguments("-javaagent:" + artifact.getLocation().toString() + "=" + server.getUrl());
|
|
||||||
|
|
||||||
// Wait for metadata to be fetched
|
|
||||||
try {
|
try {
|
||||||
metadataFetchThread.join();
|
prefetchedMeta = prefetchedMetaTask.get();
|
||||||
|
artifact = artifactTask.get();
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
}
|
throw new AuthenticationException(e);
|
||||||
Optional<String> metadata = Optional.ofNullable(metadataFetchTask.getResult());
|
} catch (ExecutionException e) {
|
||||||
if (metadata.isPresent()) {
|
if (e.getCause() instanceof AuthenticationException) {
|
||||||
arguments = arguments.addJVMArguments(
|
throw (AuthenticationException) e.getCause();
|
||||||
"-Dorg.to2mbn.authlibinjector.config.prefetched=" + Base64.getEncoder().encodeToString(metadata.get().getBytes(UTF_8)));
|
} else {
|
||||||
|
throw new AuthenticationException(e.getCause());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return info.withArguments(arguments);
|
return auth.withArguments(new Arguments().addJVMArguments(
|
||||||
|
"-javaagent:" + artifact.getLocation().toString() + "=" + server.getUrl(),
|
||||||
|
"-Dauthlibinjector.side=client",
|
||||||
|
"-Dorg.to2mbn.authlibinjector.config.prefetched=" + Base64.getEncoder().encodeToString(prefetchedMeta)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher.
|
||||||
|
* Copyright (C) 2018 huangyuhui <huanghongxun2008@126.com>
|
||||||
|
*
|
||||||
|
* 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 {http://www.gnu.org/licenses/}.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.auth.authlibinjector;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.auth.AuthenticationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author yushijinhun
|
||||||
|
*/
|
||||||
|
public class AuthlibInjectorDownloadException extends AuthenticationException {
|
||||||
|
|
||||||
|
public AuthlibInjectorDownloadException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthlibInjectorDownloadException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthlibInjectorDownloadException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthlibInjectorDownloadException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
@ -45,6 +45,11 @@ public class AuthlibInjectorDownloader {
|
|||||||
private Path artifactLocation;
|
private Path artifactLocation;
|
||||||
private Supplier<DownloadProvider> downloadProvider;
|
private Supplier<DownloadProvider> downloadProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The flag will be reset after application restart.
|
||||||
|
*/
|
||||||
|
private boolean updateChecked = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param artifactsDirectory where to save authlib-injector artifacts
|
* @param artifactsDirectory where to save authlib-injector artifacts
|
||||||
*/
|
*/
|
||||||
@ -57,14 +62,17 @@ public class AuthlibInjectorDownloader {
|
|||||||
synchronized (artifactLocation) {
|
synchronized (artifactLocation) {
|
||||||
Optional<AuthlibInjectorArtifactInfo> local = getLocalArtifact();
|
Optional<AuthlibInjectorArtifactInfo> local = getLocalArtifact();
|
||||||
|
|
||||||
try {
|
if (!local.isPresent() || !updateChecked) {
|
||||||
update(local);
|
try {
|
||||||
} catch (IOException e) {
|
update(local);
|
||||||
LOG.log(Level.WARNING, "Failed to download authlib-injector", e);
|
updateChecked = true;
|
||||||
if (!local.isPresent()) {
|
} catch (IOException e) {
|
||||||
throw e;
|
LOG.log(Level.WARNING, "Failed to download authlib-injector", e);
|
||||||
|
if (!local.isPresent()) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
LOG.warning("Fallback to use cached artifact: " + local.get());
|
||||||
}
|
}
|
||||||
LOG.warning("Fallback to use cached artifact: " + local.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return getLocalArtifact().orElseThrow(() -> new IOException("The updated authlib-inejector cannot be recognized"));
|
return getLocalArtifact().orElseThrow(() -> new IOException("The updated authlib-inejector cannot be recognized"));
|
||||||
@ -72,6 +80,7 @@ public class AuthlibInjectorDownloader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void update(Optional<AuthlibInjectorArtifactInfo> local) throws IOException {
|
private void update(Optional<AuthlibInjectorArtifactInfo> local) throws IOException {
|
||||||
|
LOG.info("Checking update of authlib-injector");
|
||||||
AuthlibInjectorVersionInfo latest = getLatestArtifactInfo();
|
AuthlibInjectorVersionInfo latest = getLatestArtifactInfo();
|
||||||
|
|
||||||
if (local.isPresent() && local.get().getBuildNumber() >= latest.buildNumber) {
|
if (local.isPresent() && local.get().getBuildNumber() >= latest.buildNumber) {
|
||||||
|
@ -17,23 +17,104 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.auth.authlibinjector;
|
package org.jackhuang.hmcl.auth.authlibinjector;
|
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
|
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
||||||
|
import static org.jackhuang.hmcl.util.Logging.LOG;
|
||||||
|
import static org.jackhuang.hmcl.util.io.IOUtils.readFullyWithoutClosing;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
import com.google.gson.JsonPrimitive;
|
import com.google.gson.JsonPrimitive;
|
||||||
|
|
||||||
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
|
||||||
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static org.jackhuang.hmcl.util.Lang.tryCast;
|
|
||||||
|
|
||||||
public class AuthlibInjectorServer {
|
public class AuthlibInjectorServer {
|
||||||
|
|
||||||
public static AuthlibInjectorServer fetchServerInfo(String url) throws IOException {
|
private static final int MAX_REDIRECTS = 5;
|
||||||
|
|
||||||
|
public static AuthlibInjectorServer locateServer(String url) throws IOException {
|
||||||
|
url = parseInputUrl(url);
|
||||||
|
HttpURLConnection conn;
|
||||||
|
int redirectCount = 0;
|
||||||
|
for (;;) {
|
||||||
|
conn = (HttpURLConnection) new URL(url).openConnection();
|
||||||
|
Optional<String> ali = getApiLocationIndication(conn);
|
||||||
|
if (ali.isPresent()) {
|
||||||
|
conn.disconnect();
|
||||||
|
url = ali.get();
|
||||||
|
if (redirectCount >= MAX_REDIRECTS) {
|
||||||
|
throw new IOException("Exceeded maximum number of redirects (" + MAX_REDIRECTS + ")");
|
||||||
|
}
|
||||||
|
redirectCount++;
|
||||||
|
LOG.info("Redirect API root to: " + url);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JsonObject response = JsonUtils.fromNonNullJson(NetworkUtils.doGet(NetworkUtils.toURL(url)), JsonObject.class);
|
return parseResponse(url, readFullyWithoutClosing(conn.getInputStream()));
|
||||||
|
} finally {
|
||||||
|
conn.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Optional<String> getApiLocationIndication(URLConnection conn) {
|
||||||
|
return Optional.ofNullable(conn.getHeaderFields().get("X-Authlib-Injector-API-Location"))
|
||||||
|
.flatMap(list -> list.isEmpty() ? Optional.empty() : Optional.of(list.get(0)))
|
||||||
|
.flatMap(indication -> {
|
||||||
|
String currentUrl = appendSuffixSlash(conn.getURL().toString());
|
||||||
|
String newUrl;
|
||||||
|
try {
|
||||||
|
newUrl = appendSuffixSlash(new URL(conn.getURL(), indication).toString());
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
LOG.warning("Failed to resolve absolute ALI, the header is [" + indication + "]. Ignore it.");
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newUrl.equals(currentUrl)) {
|
||||||
|
return Optional.empty();
|
||||||
|
} else {
|
||||||
|
return Optional.of(newUrl);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String parseInputUrl(String url) {
|
||||||
|
String lowercased = url.toLowerCase();
|
||||||
|
if (!lowercased.startsWith("http://") && !lowercased.startsWith("https://")) {
|
||||||
|
url = "https://" + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
url = appendSuffixSlash(url);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String appendSuffixSlash(String url) {
|
||||||
|
if (!url.endsWith("/")) {
|
||||||
|
return url + "/";
|
||||||
|
} else {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AuthlibInjectorServer fetchServerInfo(String url) throws IOException {
|
||||||
|
try (InputStream in = new URL(url).openStream()) {
|
||||||
|
return parseResponse(url, readFullyWithoutClosing(in));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AuthlibInjectorServer parseResponse(String url, byte[] rawResponse) throws IOException {
|
||||||
|
try {
|
||||||
|
JsonObject response = JsonUtils.fromNonNullJson(new String(rawResponse, UTF_8), JsonObject.class);
|
||||||
String name = extractServerName(response).orElse(url);
|
String name = extractServerName(response).orElse(url);
|
||||||
return new AuthlibInjectorServer(url, name);
|
return new AuthlibInjectorServer(url, name);
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user