Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix some cases of merging with base_url #1532

Merged
merged 3 commits into from Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 13 additions & 4 deletions httpx/_client.py
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
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
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
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