]> git.ipfire.org Git - thirdparty/httpx.git/commitdiff
Fix some cases of merging with base_url (#1532)
authorTom Christie <tom@tomchristie.com>
Wed, 24 Mar 2021 10:51:33 +0000 (10:51 +0000)
committerGitHub <noreply@github.com>
Wed, 24 Mar 2021 10:51:33 +0000 (10:51 +0000)
* Fix some cases of merging with base_url

* Fix for joining relative URLs

* Improve code comment in _merge_url implementation

httpx/_client.py
httpx/_models.py
tests/client/test_client.py
tests/models/test_url.py

index 3465a10b758830201f6b7406ffef0026a3b79758..da38a14346bcc079daccb171702b295fd1593166 100644 (file)
@@ -325,10 +325,19 @@ class BaseClient:
         """
         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(
index 2d11888254a03491d646c9ce07cec60502f1f59d..83deb9a243530f0a78038805b8143a249cd45bbb 100644 (file)
@@ -394,7 +394,14 @@ class 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.
index 13bb7f03ad049ca653c148ff9325412b96b8a7ba..7e32bcf6f3a53e42960e5f9ec921323ceee3d59f 100644 (file)
@@ -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")
index 9d67618b5b8f8757bf256f2583e80b3c4e067784..2d14afd71c24f3d892371b447d79d07305008969 100644 (file)
@@ -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.