From: Tom Christie Date: Fri, 21 Jun 2019 14:03:01 +0000 (+0100) Subject: Support Client(base_url=...) X-Git-Tag: 0.6.1~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9d5aaff5bc57698c111dcf990a4918fe961d1584;p=thirdparty%2Fhttpx.git Support Client(base_url=...) --- diff --git a/http3/client.py b/http3/client.py index fd92ef32..6c0557b8 100644 --- a/http3/client.py +++ b/http3/client.py @@ -51,6 +51,7 @@ class BaseClient: timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG, pool_limits: PoolLimits = DEFAULT_POOL_LIMITS, max_redirects: int = DEFAULT_MAX_REDIRECTS, + base_url: URLTypes = None, dispatch: typing.Union[AsyncDispatcher, Dispatcher] = None, app: typing.Callable = None, backend: ConcurrencyBackend = None, @@ -79,6 +80,11 @@ class BaseClient: else: async_dispatch = dispatch + if base_url is None: + self.base_url = URL('', allow_relative=True) + else: + self.base_url = URL(base_url) + self.auth = auth self.cookies = Cookies(cookies) self.max_redirects = max_redirects @@ -238,7 +244,7 @@ class BaseClient: # Facilitate relative 'Location' headers, as allowed by RFC 7231. # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') if url.is_relative_url: - url = url.resolve_with(request.url) + url = request.url.join(url) # Attach previous fragment if needed (RFC 7231 7.1.2) if request.url.fragment and not url.fragment: @@ -506,6 +512,8 @@ class AsyncClient(BaseClient): verify: VerifyTypes = None, timeout: TimeoutTypes = None, ) -> AsyncResponse: + url = self.base_url.join(url) + cookies = self.merge_cookies(cookies) request = AsyncRequest( method, url, @@ -514,7 +522,7 @@ class AsyncClient(BaseClient): json=json, params=params, headers=headers, - cookies=self.merge_cookies(cookies), + cookies=cookies, ) response = await self.send( request, @@ -585,6 +593,8 @@ class Client(BaseClient): verify: VerifyTypes = None, timeout: TimeoutTypes = None, ) -> Response: + url = self.base_url.join(url) + cookies = self.merge_cookies(cookies) request = AsyncRequest( method, url, @@ -593,7 +603,7 @@ class Client(BaseClient): json=json, params=params, headers=headers, - cookies=self.merge_cookies(cookies), + cookies=cookies, ) concurrency_backend = self.concurrency_backend diff --git a/http3/models.py b/http3/models.py index 94720a51..bd3333a1 100644 --- a/http3/models.py +++ b/http3/models.py @@ -183,14 +183,18 @@ class URL: def copy_with(self, **kwargs: typing.Any) -> "URL": return URL(self.components.copy_with(**kwargs)) - def resolve_with(self, base_url: URLTypes) -> "URL": + def join(self, relative_url: URLTypes) -> "URL": """ - Return an absolute URL, using base_url as the base. + Return an absolute URL, using given this URL as the base. """ + if self.is_relative_url: + return URL(relative_url) + # We drop any fragment portion, because RFC 3986 strictly # treats URLs with a fragment portion as not being absolute URLs. - base_url = URL(base_url).copy_with(fragment=None) - return URL(self.components.resolve_with(base_url.components)) + base_components = self.components.copy_with(fragment=None) + relative_url = URL(relative_url, allow_relative=True) + return URL(relative_url.components.resolve_with(base_components)) def __hash__(self) -> int: return hash(str(self)) diff --git a/tests/client/test_client.py b/tests/client/test_client.py index 48924cb5..4e52c6f8 100644 --- a/tests/client/test_client.py +++ b/tests/client/test_client.py @@ -141,3 +141,12 @@ def test_delete(server): response = http.delete("http://127.0.0.1:8000/") assert response.status_code == 200 assert response.reason_phrase == "OK" + + +@threadpool +def test_base_url(server): + base_url = "http://127.0.0.1:8000/" + with http3.Client(base_url=base_url) as http: + response = http.get('/') + assert response.status_code == 200 + assert str(response.url) == base_url