Skip to content

Commit

Permalink
Fix some cases of merging with base_url (#1532)
Browse files Browse the repository at this point in the history
* Fix some cases of merging with base_url

* Fix for joining relative URLs

* Improve code comment in _merge_url implementation
  • Loading branch information
tomchristie authored Mar 24, 2021
1 parent ea5ffce commit 6cb1672
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 5 deletions.
17 changes: 13 additions & 4 deletions httpx/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,19 @@ def _merge_url(self, url: URLTypes) -> URL:
"""
merge_url = URL(url)
if merge_url.is_relative_url:
# We always ensure the base_url paths include the trailing '/',
# and always strip any leading '/' from the merge URL.
merge_url = merge_url.copy_with(raw_path=merge_url.raw_path.lstrip(b"/"))
return self.base_url.join(merge_url)
# To merge URLs we always append to the base URL. To get this
# behaviour correct we always ensure the base URL ends in a '/'
# seperator, and strip any leading '/' from the merge URL.
#
# So, eg...
#
# >>> client = Client(base_url="https://www.example.com/subpath")
# >>> client.base_url
# URL('https://www.example.com/subpath/')
# >>> client.build_request("GET", "/path").url
# URL('https://www.example.com/subpath/path')
merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/")
return self.base_url.copy_with(raw_path=merge_raw_path)
return merge_url

def _merge_cookies(
Expand Down
9 changes: 8 additions & 1 deletion httpx/_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,14 @@ def join(self, url: URLTypes) -> "URL":
assert url == "https://www.example.com/test/new/path"
"""
if self.is_relative_url:
return URL(url)
# Workaround to handle relative URLs, which otherwise raise
# rfc3986.exceptions.ResolutionError when used as an argument
# in `.resolve_with`.
return (
self.copy_with(scheme="http", host="example.com")
.join(url)
.copy_with(scheme=None, host=None)
)

# We drop any fragment portion, because RFC 3986 strictly
# treats URLs with a fragment portion as not being absolute URLs.
Expand Down
6 changes: 6 additions & 0 deletions tests/client/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@ def test_merge_relative_url_with_dotted_path():
assert request.url == "https://www.example.com/some/testing/123"


def test_merge_relative_url_with_path_including_colon():
client = httpx.Client(base_url="https://www.example.com/some/path")
request = client.build_request("GET", "/testing:123")
assert request.url == "https://www.example.com/some/path/testing:123"


def test_merge_relative_url_with_encoded_slashes():
client = httpx.Client(base_url="https://www.example.com/")
request = client.build_request("GET", "/testing%2F123")
Expand Down
8 changes: 8 additions & 0 deletions tests/models/test_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ def test_url_join():
assert url.join("../../somewhere-else") == "https://example.org:123/somewhere-else"


def test_relative_url_join():
url = httpx.URL("/path/to/somewhere")
assert url.join("/somewhere-else") == "/somewhere-else"
assert url.join("somewhere-else") == "/path/to/somewhere-else"
assert url.join("../somewhere-else") == "/path/somewhere-else"
assert url.join("../../somewhere-else") == "/somewhere-else"


def test_url_join_rfc3986():
"""
URL joining tests, as-per reference examples in RFC 3986.
Expand Down

0 comments on commit 6cb1672

Please sign in to comment.