Skip to content

Commit 7dc81b6

Browse files
committed
fix: skip IDNA encoding for IP hosts in URL.to_uri
IPv6 literals contain colons and are already ASCII. Passing them through idna_encode raised InvalidCodepoint for hosts like ::1. Skip IDNA when parse_host reports an IP family.
1 parent 978f2e6 commit 7dc81b6

2 files changed

Lines changed: 19 additions & 5 deletions

File tree

src/hyperlink/_url.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1667,11 +1667,16 @@ def to_uri(self):
16671667
new_path = _encode_path_parts(
16681668
self.path, has_scheme=bool(self.scheme), rooted=False, maximal=True
16691669
)
1670-
new_host = (
1671-
self.host
1672-
if not self.host
1673-
else idna_encode(self.host, uts46=True).decode("ascii")
1674-
)
1670+
# IP literals must not be IDNA-encoded (colons fail idna).
1671+
if not self.host:
1672+
new_host = self.host
1673+
else:
1674+
family, _ = parse_host(self.host)
1675+
new_host = (
1676+
self.host
1677+
if family is not None
1678+
else idna_encode(self.host, uts46=True).decode("ascii")
1679+
)
16751680
return self.replace(
16761681
userinfo=new_userinfo,
16771682
host=new_host,

src/hyperlink/test/test_url.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,6 +1132,15 @@ def test_idna(self):
11321132
self.assertEqual(u2.to_text(), "https://xn--bcher-kva.ch")
11331133
self.assertEqual(u2.to_iri().to_text(), "https://bücher.ch")
11341134

1135+
def test_to_uri_ip_literal(self):
1136+
# type: () -> None
1137+
ipv6 = URL.from_text("http://[::1]/")
1138+
self.assertEqual(ipv6.to_uri().to_text(), "http://[::1]/")
1139+
self.assertEqual(ipv6.to_uri().host, "::1")
1140+
ipv4 = URL.from_text("http://127.0.0.1/")
1141+
self.assertEqual(ipv4.to_uri().to_text(), "http://127.0.0.1/")
1142+
self.assertEqual(ipv4.to_uri().host, "127.0.0.1")
1143+
11351144
def test_netloc_slashes(self):
11361145
# type: () -> None
11371146

0 commit comments

Comments
 (0)