101 lines
4.5 KiB
Python
101 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from vpn_egressctl.errors import ValidationError
|
|
from vpn_egressctl.uri import parse_hysteria2_uri
|
|
|
|
|
|
class UriParserTests(unittest.TestCase):
|
|
def test_minimal_uri(self) -> None:
|
|
endpoint = parse_hysteria2_uri("hysteria2://secret@example.com/")
|
|
self.assertEqual(endpoint.server, "example.com")
|
|
self.assertEqual(endpoint.server_port, 443)
|
|
self.assertEqual(endpoint.password, "secret")
|
|
self.assertEqual(endpoint.sni, "example.com")
|
|
|
|
def test_short_scheme_and_percent_encoding(self) -> None:
|
|
endpoint = parse_hysteria2_uri("hy2://user%3Apass@EXAMPLE.com:8443?sni=t%C3%A9st.example#Moscow")
|
|
self.assertEqual(endpoint.password, "user:pass")
|
|
self.assertEqual(endpoint.server, "example.com")
|
|
self.assertEqual(endpoint.server_port, 8443)
|
|
self.assertEqual(endpoint.sni, "xn--tst-bma.example")
|
|
self.assertEqual(endpoint.display_name, "Moscow")
|
|
|
|
def test_salamander(self) -> None:
|
|
endpoint = parse_hysteria2_uri(
|
|
"hysteria2://secret@example.com:443/?insecure=1&obfs=salamander&obfs-password=o%40p"
|
|
)
|
|
self.assertTrue(endpoint.insecure)
|
|
self.assertEqual(endpoint.obfs_type, "salamander")
|
|
self.assertEqual(endpoint.obfs_password, "o@p")
|
|
|
|
def test_ipv6(self) -> None:
|
|
endpoint = parse_hysteria2_uri("hysteria2://secret@[2001:db8::1]:444/")
|
|
self.assertEqual(endpoint.server, "2001:db8::1")
|
|
self.assertEqual(endpoint.server_port, 444)
|
|
self.assertEqual(endpoint.endpoint_label(), "[2001:db8::1]:444")
|
|
|
|
def test_multi_port(self) -> None:
|
|
endpoint = parse_hysteria2_uri("hysteria2://secret@example.com:443,5000-6000/")
|
|
self.assertIsNone(endpoint.server_port)
|
|
self.assertEqual(endpoint.server_ports, ("443", "5000:6000"))
|
|
|
|
def test_range_only_uses_server_ports(self) -> None:
|
|
endpoint = parse_hysteria2_uri("hysteria2://secret@example.com:5000-6000/")
|
|
self.assertIsNone(endpoint.server_port)
|
|
self.assertEqual(endpoint.server_ports, ("5000:6000",))
|
|
|
|
def test_userpass_is_preserved(self) -> None:
|
|
endpoint = parse_hysteria2_uri("hysteria2://alice%3Acorrect%20horse@example.com")
|
|
self.assertEqual(endpoint.password, "alice:correct horse")
|
|
|
|
def test_unicode_host_is_idna(self) -> None:
|
|
endpoint = parse_hysteria2_uri("hysteria2://secret@пример.рф")
|
|
self.assertEqual(endpoint.server, "xn--e1afmkfd.xn--p1ai")
|
|
|
|
def test_repr_hides_secrets(self) -> None:
|
|
endpoint = parse_hysteria2_uri(
|
|
"hysteria2://TOPSECRET@example.com?obfs=salamander&obfs-password=OBFSSECRET"
|
|
)
|
|
text = repr(endpoint)
|
|
self.assertNotIn("TOPSECRET", text)
|
|
self.assertNotIn("OBFSSECRET", text)
|
|
|
|
def assert_invalid(self, uri: str, marker: str | None = None) -> None:
|
|
with self.assertRaises(ValidationError) as caught:
|
|
parse_hysteria2_uri(uri)
|
|
if marker:
|
|
self.assertIn(marker, str(caught.exception))
|
|
|
|
def test_rejections(self) -> None:
|
|
cases = [
|
|
("http://secret@example.com", "scheme"),
|
|
("hysteria2://example.com", "authentication"),
|
|
("hysteria2://@example.com", "authentication"),
|
|
("hysteria2://secret@", "server"),
|
|
("hysteria2://secret@example.com:0", "1..65535"),
|
|
("hysteria2://secret@example.com:65536", "1..65535"),
|
|
("hysteria2://secret@example.com:100-99", "range"),
|
|
("hysteria2://secret@example.com:100,100", "Overlapping"),
|
|
("hysteria2://secret@2001:db8::1", "brackets"),
|
|
("hysteria2://secret@example.com/path", "paths"),
|
|
("hysteria2://secret@example.com?unknown=x", "Unsupported"),
|
|
("hysteria2://secret@example.com?sni=a&sni=b", "Duplicate"),
|
|
("hysteria2://secret@example.com?insecure=true", "exactly"),
|
|
("hysteria2://secret@example.com?obfs=gecko", "1.13.19"),
|
|
("hysteria2://secret@example.com?obfs=salamander", "obfs-password"),
|
|
("hysteria2://secret@example.com?obfs-password=x", "requires"),
|
|
("hysteria2://secret@example.com?pinSHA256=x", "safely"),
|
|
("hysteria2://secret@example.com?ech=x", "safely"),
|
|
("hysteria2://sec%ZZret@example.com", "percent"),
|
|
("hysteria2://secret@example.com?sni=%FF", "UTF-8"),
|
|
]
|
|
for uri, marker in cases:
|
|
with self.subTest(uri=uri):
|
|
self.assert_invalid(uri, marker)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|