From 7179f0838fc557bea901b1bd039bdf8eb76875e0 Mon Sep 17 00:00:00 2001 From: Yuriy Tumanov <57435526+Eljees@users.noreply.github.com> Date: Wed, 28 Oct 2026 03:12:18 +0100 Subject: [PATCH] Use the default port for Hysteria2 links without a port (#6358) --- .../java/com/v2ray/ang/fmt/Hysteria2Fmt.kt | 2 +- .../com/v2ray/ang/fmt/Hysteria2FmtTest.kt | 89 +++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 V2rayNG/app/src/test/java/com/v2ray/ang/fmt/Hysteria2FmtTest.kt diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt index 68f5be35b..410613cf0 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt @@ -23,7 +23,7 @@ object Hysteria2Fmt : FmtBase() { val uri = URI(Utils.fixIllegalUrl(str)) config.remarks = Utils.decodeURIComponent(uri.fragment.orEmpty()).let { it.ifEmpty { "none" } } config.server = uri.idnHost - config.serverPort = uri.port.toString() + config.serverPort = (if (uri.port == -1) AppConfig.DEFAULT_PORT else uri.port).toString() config.password = uri.userInfo config.security = AppConfig.TLS config.network = NetworkType.HYSTERIA.type diff --git a/V2rayNG/app/src/test/java/com/v2ray/ang/fmt/Hysteria2FmtTest.kt b/V2rayNG/app/src/test/java/com/v2ray/ang/fmt/Hysteria2FmtTest.kt new file mode 100644 index 000000000..5c7e89bf1 --- /dev/null +++ b/V2rayNG/app/src/test/java/com/v2ray/ang/fmt/Hysteria2FmtTest.kt @@ -0,0 +1,89 @@ +package com.v2ray.ang.fmt + +import com.v2ray.ang.AppConfig +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import java.net.URI + +class Hysteria2FmtTest { + + @Test + fun hysteria2WithoutPortUses443() { + val profile = Hysteria2Fmt.parse("hysteria2://test@example.com/") + + assertEquals("example.com", profile.server) + assertEquals("443", profile.serverPort) + } + + @Test + fun hy2WithoutPortUses443() { + val profile = Hysteria2Fmt.parse("hy2://test@example.com/") + + assertEquals("example.com", profile.server) + assertEquals("443", profile.serverPort) + } + + @Test + fun uriWithoutCredentialsUses443() { + val profile = Hysteria2Fmt.parse("hysteria2://example.com/") + + assertEquals("example.com", profile.server) + assertEquals("443", profile.serverPort) + } + + @Test + fun omittedPortPreservesTlsParameters() { + val profile = Hysteria2Fmt.parse( + "hy2://test@example.com/?sni=example.org&insecure=0&pinSHA256=test-pin#example" + ) + + assertEquals("test", profile.password) + assertEquals("example", profile.remarks) + assertEquals(AppConfig.TLS, profile.security) + assertEquals("example.org", profile.sni) + assertEquals(false, profile.insecure) + assertEquals("test-pin", profile.pinnedCA256) + assertEquals("443", profile.serverPort) + } + + @Test + fun ipv6WithoutPortUses443() { + val profile = Hysteria2Fmt.parse("hysteria2://test@[2001:db8::1]/") + + assertEquals("2001:db8::1", profile.server) + assertEquals("443", profile.serverPort) + } + + @Test + fun explicitPortsArePreserved() { + for (scheme in listOf("hysteria2", "hy2")) { + // Port range validation is separate from choosing a missing-port default. + for (port in listOf(0, 443, 8443, 65535, 65536)) { + val profile = Hysteria2Fmt.parse("$scheme://test@example.com:$port/") + + assertEquals("example.com", profile.server) + assertEquals(port.toString(), profile.serverPort) + } + } + } + + @Test + fun nonnumericPortDoesNotAcquireAHost() { + val profile = Hysteria2Fmt.parse("hysteria2://test@example.com:invalid/") + + assertEquals("", profile.server) + } + + @Test + fun defaultPortSurvivesUriRoundTrip() { + val profile = Hysteria2Fmt.parse("hy2://test@example.com/?sni=example.org") + val exported = "hysteria2://${Hysteria2Fmt.toUri(profile)}" + val reparsed = Hysteria2Fmt.parse(exported) + + assertEquals(443, URI(exported).port) + assertEquals("443", reparsed.serverPort) + assertEquals(profile.server, reparsed.server) + assertEquals(profile.password, reparsed.password) + assertEquals(profile.sni, reparsed.sni) + } +}