Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/requests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,16 @@ def prepare_url(self, url, params):
query = enc_params

url = requote_uri(urlunparse([scheme, netloc, path, None, query, fragment]))

# Preserve the IPv6 zone identifier separator ('%') inside a bracketed
# host. ``requote_uri`` percent-encodes a literal '%' to '%25', but
# ``urllib3`` forwards the (already-parsed) host to ``getaddrinfo()``
# verbatim, so an encoded zone identifier like ``%25eth0`` cannot be
# resolved by the OS. See https://github.com/psf/requests/issues/6735.
if host and "%" in host:
encoded_host = host.replace("%", "%25", 1)
url = url.replace(encoded_host, host, 1)

self.url = url

def prepare_headers(self, headers):
Expand Down
33 changes: 33 additions & 0 deletions tests/test_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,39 @@ def test_path_is_not_double_encoded(self):

assert request.path_url == "/get/test%20case"

@pytest.mark.parametrize(
"url, expected",
(
(
# Literal '%' zone-id separator must be preserved, otherwise
# urllib3 will forward '%25ens192' to getaddrinfo() which the
# OS cannot resolve. Regression test for #6735.
"https://[fe80::1%ens192]/redfish/v1",
"https://[fe80::1%ens192]/redfish/v1",
),
(
# RFC 6874 form ('%25') must be normalized back to the literal
# form expected by getaddrinfo() / urllib3.
"https://[fe80::1%25ens192]/redfish/v1",
"https://[fe80::1%ens192]/redfish/v1",
),
(
# Zone-id preserved alongside port/query/fragment.
"https://[fe80::5eed:8cff:fe00:0da4%ens192]:8443/api?x=1#f",
"https://[fe80::5eed:8cff:fe00:0da4%ens192]:8443/api?x=1#f",
),
(
# Plain IPv6 (no zone id) must be unaffected.
"https://[::1]/",
"https://[::1]/",
),
),
)
def test_ipv6_zone_id_is_preserved(self, url, expected):
"""See: https://github.com/psf/requests/issues/6735"""
request = requests.Request("GET", url).prepare()
assert request.url == expected

@pytest.mark.parametrize(
"url, expected",
(
Expand Down