# For 'path' we need to drop ? and # from the GEN_DELIMS set.
parsed_path: str = quote(path, safe=SUB_DELIMS + ":/[]@")
# For 'query' we need to drop '#' from the GEN_DELIMS set.
+ # We also exclude '/' because it is more robust to replace it with a percent
+ # encoding despite it not being a requirement of the spec.
parsed_query: typing.Optional[str] = (
- None if query is None else quote(query, safe=SUB_DELIMS + ":/?[]@")
+ None if query is None else quote(query, safe=SUB_DELIMS + ":?[]@")
)
# For 'fragment' we can include all of the GEN_DELIMS set.
parsed_fragment: typing.Optional[str] = (
#
# https://github.com/python/cpython/blob/b2f7b2ef0b5421e01efb8c7bee2ef95d3bab77eb/Lib/urllib/parse.py#L926
#
- # Note that we use '%20' encoding for spaces, and treat '/' as a safe
- # character. This means our query params have the same escaping as other
- # characters in the URL path. This is slightly different to `requests`,
- # but is the behaviour that browsers use.
+ # Note that we use '%20' encoding for spaces. and '%2F for '/'.
+ # This is slightly different than `requests`, but is the behaviour that browsers use.
#
- # See https://github.com/encode/httpx/issues/2536 and
- # https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlencode
- return "&".join([quote(k) + "=" + quote(v) for k, v in items])
+ # See
+ # - https://github.com/encode/httpx/issues/2536
+ # - https://github.com/encode/httpx/issues/2721
+ # - https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlencode
+ return "&".join([quote(k, safe="") + "=" + quote(v, safe="") for k, v in items])
and https://github.com/encode/httpx/discussions/2460
"""
url = httpx.URL("https://www.example.com/?a=b c&d=e/f")
- assert url.raw_path == b"/?a=b%20c&d=e/f"
+ assert url.raw_path == b"/?a=b%20c&d=e%2Ff"
url = httpx.URL("https://www.example.com/", params={"a": "b c", "d": "e/f"})
- assert url.raw_path == b"/?a=b%20c&d=e/f"
+ assert url.raw_path == b"/?a=b%20c&d=e%2Ff"
def test_url_with_url_encoded_path():
def test_param_with_existing_escape_requires_encoding():
url = httpx.URL("http://webservice", params={"u": "http://example.com?q=foo%2Fa"})
- assert str(url) == "http://webservice?u=http%3A//example.com%3Fq%3Dfoo%252Fa"
+ assert str(url) == "http://webservice?u=http%3A%2F%2Fexample.com%3Fq%3Dfoo%252Fa"
# Tests for invalid URLs
def test_query_percent_encoding():
# Test percent encoding for SUB_DELIMS ALPHA NUM and allowable GEN_DELIMS
url = httpx.URL("https://example.com/?!$&'()*+,;= abc ABC 123 :/[]@" + "?")
- assert url.raw_path == b"/?!$&'()*+,;=%20abc%20ABC%20123%20:/[]@?"
+ assert url.raw_path == b"/?!$&'()*+,;=%20abc%20ABC%20123%20:%2F[]@?"
assert url.path == "/"
- assert url.query == b"!$&'()*+,;=%20abc%20ABC%20123%20:/[]@?"
+ assert url.query == b"!$&'()*+,;=%20abc%20ABC%20123%20:%2F[]@?"
assert url.fragment == ""