diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/ProxyManager.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/ProxyManager.java index 79da3a93b..3370ff38a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/ProxyManager.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/ProxyManager.java @@ -19,22 +19,28 @@ package org.jackhuang.hmcl.setting; import javafx.beans.InvalidationListener; import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.net.*; -import java.util.Collections; import java.util.List; -import java.util.Objects; import static org.jackhuang.hmcl.setting.ConfigHolder.config; import static org.jackhuang.hmcl.util.logging.Logger.LOG; public final class ProxyManager { - private static final ProxySelector NO_PROXY = new SimpleProxySelector(Proxy.NO_PROXY); - private static final ProxySelector SYSTEM_DEFAULT = Objects.requireNonNullElse(ProxySelector.getDefault(), NO_PROXY); + private static final SimpleProxySelector NO_PROXY = new SimpleProxySelector(Proxy.NO_PROXY); + private static final ProxySelector SYSTEM_DEFAULT; + + static { + ProxySelector systemProxySelector = ProxySelector.getDefault(); + SYSTEM_DEFAULT = systemProxySelector != null + ? new ProxySelectorWrapper(systemProxySelector) + : NO_PROXY; + } private static volatile @NotNull ProxySelector defaultProxySelector = SYSTEM_DEFAULT; private static volatile @Nullable SimpleAuthenticator defaultAuthenticator = null; @@ -51,7 +57,7 @@ public final class ProxyManager { LOG.warning("Illegal proxy port: " + port); return NO_PROXY; } else { - return new SimpleProxySelector(new Proxy(proxyType, new InetSocketAddress(host, port))); + return new ProxySelectorWrapper(new SimpleProxySelector(new Proxy(proxyType, new InetSocketAddress(host, port)))); } } else { return ProxyManager.SYSTEM_DEFAULT; @@ -106,15 +112,41 @@ public final class ProxyManager { config().proxyPassProperty().addListener(updateAuthenticator); } - private static final class SimpleProxySelector extends ProxySelector { + private static abstract class AbstractProxySelector extends ProxySelector { + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + if (uri == null || sa == null || ioe == null) { + throw new IllegalArgumentException("Arguments can't be null."); + } + } + } + + private static final class SimpleProxySelector extends AbstractProxySelector { private final List proxies; SimpleProxySelector(Proxy proxy) { - this(Collections.singletonList(proxy)); + this.proxies = List.of(proxy); } - SimpleProxySelector(List proxies) { - this.proxies = proxies; + @Override + public List select(URI uri) { + if (uri == null) + throw new IllegalArgumentException("URI can't be null."); + return proxies; + } + + @Override + public String toString() { + return "SimpleProxySelector" + proxies; + } + } + + /// Wraps another ProxySelector to avoid using proxy for loopback addresses. + private static final class ProxySelectorWrapper extends AbstractProxySelector { + private final ProxySelector source; + + ProxySelectorWrapper(ProxySelector source) { + this.source = source; } @Override @@ -122,19 +154,10 @@ public final class ProxyManager { if (uri == null) throw new IllegalArgumentException("URI can't be null."); - return proxies; - } + if (NetworkUtils.isLoopbackAddress(uri)) + return NO_PROXY.proxies; - @Override - public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { - if (uri == null || sa == null || ioe == null) { - throw new IllegalArgumentException("Arguments can't be null."); - } - } - - @Override - public String toString() { - return "SimpleProxySelector" + proxies; + return source.select(uri); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/NetworkUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/NetworkUtils.java index 554ccf466..2cb67bb86 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/NetworkUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/NetworkUtils.java @@ -18,6 +18,7 @@ package org.jackhuang.hmcl.util.io; import org.jackhuang.hmcl.util.Pair; +import org.jackhuang.hmcl.util.StringUtils; import org.jetbrains.annotations.NotNull; import java.io.*; @@ -45,6 +46,19 @@ public final class NetworkUtils { private NetworkUtils() { } + public static boolean isLoopbackAddress(URI uri) { + String host = uri.getHost(); + if (StringUtils.isBlank(host)) + return false; + + try { + InetAddress addr = InetAddress.getByName(host); + return addr.isLoopbackAddress(); + } catch (UnknownHostException e) { + return false; + } + } + public static boolean isHttpUri(URI uri) { return "http".equals(uri.getScheme()) || "https".equals(uri.getScheme()); } diff --git a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/io/NetworkUtilsTest.java b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/io/NetworkUtilsTest.java index b3abc6396..5d87584bb 100644 --- a/HMCLCore/src/test/java/org/jackhuang/hmcl/util/io/NetworkUtilsTest.java +++ b/HMCLCore/src/test/java/org/jackhuang/hmcl/util/io/NetworkUtilsTest.java @@ -19,17 +19,31 @@ package org.jackhuang.hmcl.util.io; import org.junit.jupiter.api.Test; +import java.net.URI; + import static java.nio.charset.StandardCharsets.US_ASCII; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.jackhuang.hmcl.util.io.NetworkUtils.encodeLocation; -import static org.jackhuang.hmcl.util.io.NetworkUtils.getCharsetFromContentType; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.jackhuang.hmcl.util.io.NetworkUtils.*; +import static org.junit.jupiter.api.Assertions.*; /** * @author Glavo */ public class NetworkUtilsTest { + @Test + public void testIsLoopbackAddress() { + assertTrue(isLoopbackAddress(URI.create("https://127.0.0.1/test"))); + assertTrue(isLoopbackAddress(URI.create("https://127.0.0.1:8080/test"))); + assertTrue(isLoopbackAddress(URI.create("https://localhost/test"))); + assertTrue(isLoopbackAddress(URI.create("https://localhost:8080/test"))); + assertTrue(isLoopbackAddress(URI.create("https://[::1]/test"))); + assertTrue(isLoopbackAddress(URI.create("https://[::1]:8080/test"))); + + assertFalse(isLoopbackAddress(URI.create("https://www.example.com/test"))); + assertFalse(isLoopbackAddress(URI.create("https://www.example.com:8080/test"))); + } + @Test public void testEncodeLocation() { assertEquals("https://github.com", encodeLocation("https://github.com"));