Skip to content

Commit 97fb2f7

Browse files
committed
remove _invalid_iri_to_uri workaround
tell Python to handle itms-services scheme correctly
1 parent 249527f commit 97fb2f7

File tree

5 files changed

+17
-21
lines changed

5 files changed

+17
-21
lines changed

CHANGES.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ Unreleased
77

88
- Make reloader more robust when ``""`` is in ``sys.path``. :pr:`2823`
99
- Better TLS cert format with ``adhoc`` dev certs. :pr:`2891`
10+
- Inform Python < 3.12 how to handle ``itms-services`` URIs correctly, rather
11+
than using an overly-broad workaround in Werkzeug that caused some redirect
12+
URIs to be passed on without encoding. :issue:`2828`
1013

1114

1215
Version 3.0.2

src/werkzeug/urls.py

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import codecs
44
import re
55
import typing as t
6+
import urllib.parse
67
from urllib.parse import quote
78
from urllib.parse import unquote
89
from urllib.parse import urlencode
@@ -164,25 +165,11 @@ def iri_to_uri(iri: str) -> str:
164165
return urlunsplit((parts.scheme, netloc, path, query, fragment))
165166

166167

167-
def _invalid_iri_to_uri(iri: str) -> str:
168-
"""The URL scheme ``itms-services://`` must contain the ``//`` even though it does
169-
not have a host component. There may be other invalid schemes as well. Currently,
170-
responses will always call ``iri_to_uri`` on the redirect ``Location`` header, which
171-
removes the ``//``. For now, if the IRI only contains ASCII and does not contain
172-
spaces, pass it on as-is. In Werkzeug 3.0, this should become a
173-
``response.process_location`` flag.
174-
175-
:meta private:
176-
"""
177-
try:
178-
iri.encode("ascii")
179-
except UnicodeError:
180-
pass
181-
else:
182-
if len(iri.split(None, 1)) == 1:
183-
return iri
184-
185-
return iri_to_uri(iri)
168+
# Python < 3.12
169+
# itms-services was worked around in previous iri_to_uri implementations, but
170+
# we can tell Python directly that it needs to preserve the //.
171+
if "itms-services" not in urllib.parse.uses_netloc:
172+
urllib.parse.uses_netloc.append("itms-services")
186173

187174

188175
def _decode_idna(domain: str) -> str:

src/werkzeug/wrappers/response.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from ..http import parse_range_header
1515
from ..http import remove_entity_headers
1616
from ..sansio.response import Response as _SansIOResponse
17-
from ..urls import _invalid_iri_to_uri
1817
from ..urls import iri_to_uri
1918
from ..utils import cached_property
2019
from ..wsgi import _RangeWrapper
@@ -479,7 +478,7 @@ def get_wsgi_headers(self, environ: WSGIEnvironment) -> Headers:
479478
content_length = value
480479

481480
if location is not None:
482-
location = _invalid_iri_to_uri(location)
481+
location = iri_to_uri(location)
483482

484483
if self.autocorrect_location_header:
485484
# Make the location header an absolute URL.

tests/test_urls.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,9 @@ def test_iri_to_uri_dont_quote_valid_code_points():
9898
# [] are not valid URL code points according to WhatWG URL Standard
9999
# https://url.spec.whatwg.org/#url-code-points
100100
assert urls.iri_to_uri("/path[bracket]?(paren)") == "/path%5Bbracket%5D?(paren)"
101+
102+
103+
# Python < 3.12
104+
def test_itms_services() -> None:
105+
url = "itms-services://?action=download-manifest&url=https://test.example/path"
106+
assert urls.iri_to_uri(url) == url

tests/test_wrappers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,7 @@ class MyResponse(wrappers.Response):
11541154
("auto", "location", "expect"),
11551155
(
11561156
(False, "/test", "/test"),
1157+
(False, "/\\\\test.example?q", "/%5C%5Ctest.example?q"),
11571158
(True, "/test", "http://localhost/test"),
11581159
(True, "test", "http://localhost/a/b/test"),
11591160
(True, "./test", "http://localhost/a/b/test"),

0 commit comments

Comments
 (0)