Skip to content

Commit ee432c0

Browse files
Fix for gen-delims escaping behaviour in path/query/fragment (#2701)
1 parent df5dbc0 commit ee432c0

File tree

2 files changed

+40
-3
lines changed

2 files changed

+40
-3
lines changed

httpx/_urlparse.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,12 +253,19 @@ def urlparse(url: str = "", **kwargs: typing.Optional[str]) -> ParseResult:
253253
if has_authority:
254254
path = normalize_path(path)
255255

256-
parsed_path: str = quote(path, safe=SUB_DELIMS + ":@/")
256+
# The GEN_DELIMS set is... : / ? # [ ] @
257+
# These do not need to be percent-quoted unless they serve as delimiters for the
258+
# specific component.
259+
260+
# For 'path' we need to drop ? and # from the GEN_DELIMS set.
261+
parsed_path: str = quote(path, safe=SUB_DELIMS + ":/[]@")
262+
# For 'query' we need to drop '#' from the GEN_DELIMS set.
257263
parsed_query: typing.Optional[str] = (
258-
None if query is None else quote(query, safe=SUB_DELIMS + "/?")
264+
None if query is None else quote(query, safe=SUB_DELIMS + ":/?[]@")
259265
)
266+
# For 'fragment' we can include all of the GEN_DELIMS set.
260267
parsed_fragment: typing.Optional[str] = (
261-
None if fragment is None else quote(fragment, safe=SUB_DELIMS + "/?")
268+
None if fragment is None else quote(fragment, safe=SUB_DELIMS + ":/?#[]@")
262269
)
263270

264271
# The parsed ASCII bytestrings are our canonical form.

tests/test_urlparse.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,33 @@ def test_copy_with():
247247

248248
url = url.copy_with(path="/abc")
249249
assert str(url) == "http://example.com/abc"
250+
251+
252+
# Tests for percent encoding across path, query, and fragement...
253+
254+
255+
def test_path_percent_encoding():
256+
# Test percent encoding for SUB_DELIMS ALPHA NUM and allowable GEN_DELIMS
257+
url = httpx.URL("https://example.com/!$&'()*+,;= abc ABC 123 :/[]@")
258+
assert url.raw_path == b"/!$&'()*+,;=%20abc%20ABC%20123%20:/[]@"
259+
assert url.path == "/!$&'()*+,;= abc ABC 123 :/[]@"
260+
assert url.query == b""
261+
assert url.fragment == ""
262+
263+
264+
def test_query_percent_encoding():
265+
# Test percent encoding for SUB_DELIMS ALPHA NUM and allowable GEN_DELIMS
266+
url = httpx.URL("https://example.com/?!$&'()*+,;= abc ABC 123 :/[]@" + "?")
267+
assert url.raw_path == b"/?!$&'()*+,;=%20abc%20ABC%20123%20:/[]@?"
268+
assert url.path == "/"
269+
assert url.query == b"!$&'()*+,;=%20abc%20ABC%20123%20:/[]@?"
270+
assert url.fragment == ""
271+
272+
273+
def test_fragment_percent_encoding():
274+
# Test percent encoding for SUB_DELIMS ALPHA NUM and allowable GEN_DELIMS
275+
url = httpx.URL("https://example.com/#!$&'()*+,;= abc ABC 123 :/[]@" + "?#")
276+
assert url.raw_path == b"/"
277+
assert url.path == "/"
278+
assert url.query == b""
279+
assert url.fragment == "!$&'()*+,;= abc ABC 123 :/[]@?#"

0 commit comments

Comments
 (0)