diff --git a/starlette/datastructures.py b/starlette/datastructures.py index ebef2ebdfa..fe9ff6e45e 100644 --- a/starlette/datastructures.py +++ b/starlette/datastructures.py @@ -114,11 +114,18 @@ def replace(self, **kwargs: typing.Any) -> "URL": or "hostname" in kwargs or "port" in kwargs ): - hostname = kwargs.pop("hostname", self.hostname) + hostname = kwargs.pop("hostname", None) port = kwargs.pop("port", self.port) username = kwargs.pop("username", self.username) password = kwargs.pop("password", self.password) + if not hostname: + netloc = self.netloc + _, _, hostname = netloc.rpartition("@") + + if hostname[-1] != "]": + hostname = hostname.rsplit(":", 1)[0] + netloc = hostname if port is not None: netloc += f":{port}" diff --git a/tests/test_datastructures.py b/tests/test_datastructures.py index b87b26e22f..5c4cb34cf0 100644 --- a/tests/test_datastructures.py +++ b/tests/test_datastructures.py @@ -38,6 +38,24 @@ def test_url(): assert new == "https://example.com:123/path/to/somewhere?abc=123#anchor" assert new.hostname == "example.com" + ipv6_url = URL("https://[fe::2]:12345") + new = ipv6_url.replace(port=8080) + assert new == "https://[fe::2]:8080" + + new = ipv6_url.replace(username="username", password="password") + assert new == "https://username:password@[fe::2]:12345" + assert new.netloc == "username:password@[fe::2]:12345" + + ipv6_url = URL("https://[fe::2]") + new = ipv6_url.replace(port=123) + assert new == "https://[fe::2]:123" + + url = URL("http://u:p@host/") + assert url.replace(hostname="bar") == URL("http://u:p@bar/") + + url = URL("http://u:p@host:80") + assert url.replace(port=88) == URL("http://u:p@host:88") + def test_url_query_params(): u = URL("https://example.org/path/?page=3")