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,
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
# 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:
verify: VerifyTypes = None,
timeout: TimeoutTypes = None,
) -> AsyncResponse:
+ url = self.base_url.join(url)
+ cookies = self.merge_cookies(cookies)
request = AsyncRequest(
method,
url,
json=json,
params=params,
headers=headers,
- cookies=self.merge_cookies(cookies),
+ cookies=cookies,
)
response = await self.send(
request,
verify: VerifyTypes = None,
timeout: TimeoutTypes = None,
) -> Response:
+ url = self.base_url.join(url)
+ cookies = self.merge_cookies(cookies)
request = AsyncRequest(
method,
url,
json=json,
params=params,
headers=headers,
- cookies=self.merge_cookies(cookies),
+ cookies=cookies,
)
concurrency_backend = self.concurrency_backend
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))
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