| | """HTTP Client for asyncio."""
|
| |
|
| | import asyncio
|
| | import base64
|
| | import hashlib
|
| | import json
|
| | import os
|
| | import sys
|
| | import traceback
|
| | import warnings
|
| | from contextlib import suppress
|
| | from types import TracebackType
|
| | from typing import (
|
| | TYPE_CHECKING,
|
| | Any,
|
| | Awaitable,
|
| | Callable,
|
| | Coroutine,
|
| | Final,
|
| | FrozenSet,
|
| | Generator,
|
| | Generic,
|
| | Iterable,
|
| | List,
|
| | Mapping,
|
| | Optional,
|
| | Set,
|
| | Tuple,
|
| | Type,
|
| | TypedDict,
|
| | TypeVar,
|
| | Union,
|
| | )
|
| |
|
| | import attr
|
| | from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr
|
| | from yarl import URL
|
| |
|
| | from . import hdrs, http, payload
|
| | from .abc import AbstractCookieJar
|
| | from .client_exceptions import (
|
| | ClientConnectionError,
|
| | ClientConnectionResetError,
|
| | ClientConnectorCertificateError,
|
| | ClientConnectorDNSError,
|
| | ClientConnectorError,
|
| | ClientConnectorSSLError,
|
| | ClientError,
|
| | ClientHttpProxyError,
|
| | ClientOSError,
|
| | ClientPayloadError,
|
| | ClientProxyConnectionError,
|
| | ClientResponseError,
|
| | ClientSSLError,
|
| | ConnectionTimeoutError,
|
| | ContentTypeError,
|
| | InvalidURL,
|
| | InvalidUrlClientError,
|
| | InvalidUrlRedirectClientError,
|
| | NonHttpUrlClientError,
|
| | NonHttpUrlRedirectClientError,
|
| | RedirectClientError,
|
| | ServerConnectionError,
|
| | ServerDisconnectedError,
|
| | ServerFingerprintMismatch,
|
| | ServerTimeoutError,
|
| | SocketTimeoutError,
|
| | TooManyRedirects,
|
| | WSServerHandshakeError,
|
| | )
|
| | from .client_reqrep import (
|
| | ClientRequest as ClientRequest,
|
| | ClientResponse as ClientResponse,
|
| | Fingerprint as Fingerprint,
|
| | RequestInfo as RequestInfo,
|
| | _merge_ssl_params,
|
| | )
|
| | from .client_ws import ClientWebSocketResponse as ClientWebSocketResponse
|
| | from .connector import (
|
| | HTTP_AND_EMPTY_SCHEMA_SET,
|
| | BaseConnector as BaseConnector,
|
| | NamedPipeConnector as NamedPipeConnector,
|
| | TCPConnector as TCPConnector,
|
| | UnixConnector as UnixConnector,
|
| | )
|
| | from .cookiejar import CookieJar
|
| | from .helpers import (
|
| | _SENTINEL,
|
| | DEBUG,
|
| | BasicAuth,
|
| | TimeoutHandle,
|
| | ceil_timeout,
|
| | get_env_proxy_for_url,
|
| | method_must_be_empty_body,
|
| | sentinel,
|
| | strip_auth_from_url,
|
| | )
|
| | from .http import WS_KEY, HttpVersion, WebSocketReader, WebSocketWriter
|
| | from .http_websocket import WSHandshakeError, WSMessage, ws_ext_gen, ws_ext_parse
|
| | from .streams import FlowControlDataQueue
|
| | from .tracing import Trace, TraceConfig
|
| | from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, Query, StrOrURL
|
| |
|
| | __all__ = (
|
| |
|
| | "ClientConnectionError",
|
| | "ClientConnectionResetError",
|
| | "ClientConnectorCertificateError",
|
| | "ClientConnectorDNSError",
|
| | "ClientConnectorError",
|
| | "ClientConnectorSSLError",
|
| | "ClientError",
|
| | "ClientHttpProxyError",
|
| | "ClientOSError",
|
| | "ClientPayloadError",
|
| | "ClientProxyConnectionError",
|
| | "ClientResponseError",
|
| | "ClientSSLError",
|
| | "ConnectionTimeoutError",
|
| | "ContentTypeError",
|
| | "InvalidURL",
|
| | "InvalidUrlClientError",
|
| | "RedirectClientError",
|
| | "NonHttpUrlClientError",
|
| | "InvalidUrlRedirectClientError",
|
| | "NonHttpUrlRedirectClientError",
|
| | "ServerConnectionError",
|
| | "ServerDisconnectedError",
|
| | "ServerFingerprintMismatch",
|
| | "ServerTimeoutError",
|
| | "SocketTimeoutError",
|
| | "TooManyRedirects",
|
| | "WSServerHandshakeError",
|
| |
|
| | "ClientRequest",
|
| | "ClientResponse",
|
| | "Fingerprint",
|
| | "RequestInfo",
|
| |
|
| | "BaseConnector",
|
| | "TCPConnector",
|
| | "UnixConnector",
|
| | "NamedPipeConnector",
|
| |
|
| | "ClientWebSocketResponse",
|
| |
|
| | "ClientSession",
|
| | "ClientTimeout",
|
| | "request",
|
| | )
|
| |
|
| |
|
| | if TYPE_CHECKING:
|
| | from ssl import SSLContext
|
| | else:
|
| | SSLContext = None
|
| |
|
| | if sys.version_info >= (3, 11) and TYPE_CHECKING:
|
| | from typing import Unpack
|
| |
|
| |
|
| | class _RequestOptions(TypedDict, total=False):
|
| | params: Query
|
| | data: Any
|
| | json: Any
|
| | cookies: Union[LooseCookies, None]
|
| | headers: Union[LooseHeaders, None]
|
| | skip_auto_headers: Union[Iterable[str], None]
|
| | auth: Union[BasicAuth, None]
|
| | allow_redirects: bool
|
| | max_redirects: int
|
| | compress: Union[str, bool, None]
|
| | chunked: Union[bool, None]
|
| | expect100: bool
|
| | raise_for_status: Union[None, bool, Callable[[ClientResponse], Awaitable[None]]]
|
| | read_until_eof: bool
|
| | proxy: Union[StrOrURL, None]
|
| | proxy_auth: Union[BasicAuth, None]
|
| | timeout: "Union[ClientTimeout, _SENTINEL, None]"
|
| | ssl: Union[SSLContext, bool, Fingerprint]
|
| | server_hostname: Union[str, None]
|
| | proxy_headers: Union[LooseHeaders, None]
|
| | trace_request_ctx: Union[Mapping[str, Any], None]
|
| | read_bufsize: Union[int, None]
|
| | auto_decompress: Union[bool, None]
|
| | max_line_size: Union[int, None]
|
| | max_field_size: Union[int, None]
|
| |
|
| |
|
| | @attr.s(auto_attribs=True, frozen=True, slots=True)
|
| | class ClientTimeout:
|
| | total: Optional[float] = None
|
| | connect: Optional[float] = None
|
| | sock_read: Optional[float] = None
|
| | sock_connect: Optional[float] = None
|
| | ceil_threshold: float = 5
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | DEFAULT_TIMEOUT: Final[ClientTimeout] = ClientTimeout(total=5 * 60, sock_connect=30)
|
| |
|
| |
|
| | IDEMPOTENT_METHODS = frozenset({"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"})
|
| |
|
| | _RetType = TypeVar("_RetType", ClientResponse, ClientWebSocketResponse)
|
| | _CharsetResolver = Callable[[ClientResponse, bytes], str]
|
| |
|
| |
|
| | class ClientSession:
|
| | """First-class interface for making HTTP requests."""
|
| |
|
| | ATTRS = frozenset(
|
| | [
|
| | "_base_url",
|
| | "_source_traceback",
|
| | "_connector",
|
| | "requote_redirect_url",
|
| | "_loop",
|
| | "_cookie_jar",
|
| | "_connector_owner",
|
| | "_default_auth",
|
| | "_version",
|
| | "_json_serialize",
|
| | "_requote_redirect_url",
|
| | "_timeout",
|
| | "_raise_for_status",
|
| | "_auto_decompress",
|
| | "_trust_env",
|
| | "_default_headers",
|
| | "_skip_auto_headers",
|
| | "_request_class",
|
| | "_response_class",
|
| | "_ws_response_class",
|
| | "_trace_configs",
|
| | "_read_bufsize",
|
| | "_max_line_size",
|
| | "_max_field_size",
|
| | "_resolve_charset",
|
| | ]
|
| | )
|
| |
|
| | _source_traceback: Optional[traceback.StackSummary] = None
|
| | _connector: Optional[BaseConnector] = None
|
| |
|
| | def __init__(
|
| | self,
|
| | base_url: Optional[StrOrURL] = None,
|
| | *,
|
| | connector: Optional[BaseConnector] = None,
|
| | loop: Optional[asyncio.AbstractEventLoop] = None,
|
| | cookies: Optional[LooseCookies] = None,
|
| | headers: Optional[LooseHeaders] = None,
|
| | skip_auto_headers: Optional[Iterable[str]] = None,
|
| | auth: Optional[BasicAuth] = None,
|
| | json_serialize: JSONEncoder = json.dumps,
|
| | request_class: Type[ClientRequest] = ClientRequest,
|
| | response_class: Type[ClientResponse] = ClientResponse,
|
| | ws_response_class: Type[ClientWebSocketResponse] = ClientWebSocketResponse,
|
| | version: HttpVersion = http.HttpVersion11,
|
| | cookie_jar: Optional[AbstractCookieJar] = None,
|
| | connector_owner: bool = True,
|
| | raise_for_status: Union[
|
| | bool, Callable[[ClientResponse], Awaitable[None]]
|
| | ] = False,
|
| | read_timeout: Union[float, _SENTINEL] = sentinel,
|
| | conn_timeout: Optional[float] = None,
|
| | timeout: Union[object, ClientTimeout] = sentinel,
|
| | auto_decompress: bool = True,
|
| | trust_env: bool = False,
|
| | requote_redirect_url: bool = True,
|
| | trace_configs: Optional[List[TraceConfig]] = None,
|
| | read_bufsize: int = 2**16,
|
| | max_line_size: int = 8190,
|
| | max_field_size: int = 8190,
|
| | fallback_charset_resolver: _CharsetResolver = lambda r, b: "utf-8",
|
| | ) -> None:
|
| |
|
| |
|
| | self._connector: Optional[BaseConnector] = None
|
| |
|
| | if loop is None:
|
| | if connector is not None:
|
| | loop = connector._loop
|
| |
|
| | loop = loop or asyncio.get_running_loop()
|
| |
|
| | if base_url is None or isinstance(base_url, URL):
|
| | self._base_url: Optional[URL] = base_url
|
| | else:
|
| | self._base_url = URL(base_url)
|
| | assert (
|
| | self._base_url.origin() == self._base_url
|
| | ), "Only absolute URLs without path part are supported"
|
| |
|
| | if timeout is sentinel or timeout is None:
|
| | self._timeout = DEFAULT_TIMEOUT
|
| | if read_timeout is not sentinel:
|
| | warnings.warn(
|
| | "read_timeout is deprecated, " "use timeout argument instead",
|
| | DeprecationWarning,
|
| | stacklevel=2,
|
| | )
|
| | self._timeout = attr.evolve(self._timeout, total=read_timeout)
|
| | if conn_timeout is not None:
|
| | self._timeout = attr.evolve(self._timeout, connect=conn_timeout)
|
| | warnings.warn(
|
| | "conn_timeout is deprecated, " "use timeout argument instead",
|
| | DeprecationWarning,
|
| | stacklevel=2,
|
| | )
|
| | else:
|
| | if not isinstance(timeout, ClientTimeout):
|
| | raise ValueError(
|
| | f"timeout parameter cannot be of {type(timeout)} type, "
|
| | "please use 'timeout=ClientTimeout(...)'",
|
| | )
|
| | self._timeout = timeout
|
| | if read_timeout is not sentinel:
|
| | raise ValueError(
|
| | "read_timeout and timeout parameters "
|
| | "conflict, please setup "
|
| | "timeout.read"
|
| | )
|
| | if conn_timeout is not None:
|
| | raise ValueError(
|
| | "conn_timeout and timeout parameters "
|
| | "conflict, please setup "
|
| | "timeout.connect"
|
| | )
|
| |
|
| | if connector is None:
|
| | connector = TCPConnector(loop=loop)
|
| |
|
| | if connector._loop is not loop:
|
| | raise RuntimeError("Session and connector has to use same event loop")
|
| |
|
| | self._loop = loop
|
| |
|
| | if loop.get_debug():
|
| | self._source_traceback = traceback.extract_stack(sys._getframe(1))
|
| |
|
| | if cookie_jar is None:
|
| | cookie_jar = CookieJar(loop=loop)
|
| | self._cookie_jar = cookie_jar
|
| |
|
| | if cookies is not None:
|
| | self._cookie_jar.update_cookies(cookies)
|
| |
|
| | self._connector = connector
|
| | self._connector_owner = connector_owner
|
| | self._default_auth = auth
|
| | self._version = version
|
| | self._json_serialize = json_serialize
|
| | self._raise_for_status = raise_for_status
|
| | self._auto_decompress = auto_decompress
|
| | self._trust_env = trust_env
|
| | self._requote_redirect_url = requote_redirect_url
|
| | self._read_bufsize = read_bufsize
|
| | self._max_line_size = max_line_size
|
| | self._max_field_size = max_field_size
|
| |
|
| |
|
| | if headers:
|
| | real_headers: CIMultiDict[str] = CIMultiDict(headers)
|
| | else:
|
| | real_headers = CIMultiDict()
|
| | self._default_headers: CIMultiDict[str] = real_headers
|
| | if skip_auto_headers is not None:
|
| | self._skip_auto_headers = frozenset(istr(i) for i in skip_auto_headers)
|
| | else:
|
| | self._skip_auto_headers = frozenset()
|
| |
|
| | self._request_class = request_class
|
| | self._response_class = response_class
|
| | self._ws_response_class = ws_response_class
|
| |
|
| | self._trace_configs = trace_configs or []
|
| | for trace_config in self._trace_configs:
|
| | trace_config.freeze()
|
| |
|
| | self._resolve_charset = fallback_charset_resolver
|
| |
|
| | def __init_subclass__(cls: Type["ClientSession"]) -> None:
|
| | warnings.warn(
|
| | "Inheritance class {} from ClientSession "
|
| | "is discouraged".format(cls.__name__),
|
| | DeprecationWarning,
|
| | stacklevel=2,
|
| | )
|
| |
|
| | if DEBUG:
|
| |
|
| | def __setattr__(self, name: str, val: Any) -> None:
|
| | if name not in self.ATTRS:
|
| | warnings.warn(
|
| | "Setting custom ClientSession.{} attribute "
|
| | "is discouraged".format(name),
|
| | DeprecationWarning,
|
| | stacklevel=2,
|
| | )
|
| | super().__setattr__(name, val)
|
| |
|
| | def __del__(self, _warnings: Any = warnings) -> None:
|
| | if not self.closed:
|
| | kwargs = {"source": self}
|
| | _warnings.warn(
|
| | f"Unclosed client session {self!r}", ResourceWarning, **kwargs
|
| | )
|
| | context = {"client_session": self, "message": "Unclosed client session"}
|
| | if self._source_traceback is not None:
|
| | context["source_traceback"] = self._source_traceback
|
| | self._loop.call_exception_handler(context)
|
| |
|
| | if sys.version_info >= (3, 11) and TYPE_CHECKING:
|
| |
|
| | def request(
|
| | self,
|
| | method: str,
|
| | url: StrOrURL,
|
| | **kwargs: Unpack[_RequestOptions],
|
| | ) -> "_RequestContextManager": ...
|
| |
|
| | else:
|
| |
|
| | def request(
|
| | self, method: str, url: StrOrURL, **kwargs: Any
|
| | ) -> "_RequestContextManager":
|
| | """Perform HTTP request."""
|
| | return _RequestContextManager(self._request(method, url, **kwargs))
|
| |
|
| | def _build_url(self, str_or_url: StrOrURL) -> URL:
|
| | url = URL(str_or_url)
|
| | if self._base_url is None:
|
| | return url
|
| | else:
|
| | assert not url.absolute and url.path.startswith("/")
|
| | return self._base_url.join(url)
|
| |
|
| | async def _request(
|
| | self,
|
| | method: str,
|
| | str_or_url: StrOrURL,
|
| | *,
|
| | params: Query = None,
|
| | data: Any = None,
|
| | json: Any = None,
|
| | cookies: Optional[LooseCookies] = None,
|
| | headers: Optional[LooseHeaders] = None,
|
| | skip_auto_headers: Optional[Iterable[str]] = None,
|
| | auth: Optional[BasicAuth] = None,
|
| | allow_redirects: bool = True,
|
| | max_redirects: int = 10,
|
| | compress: Union[str, bool, None] = None,
|
| | chunked: Optional[bool] = None,
|
| | expect100: bool = False,
|
| | raise_for_status: Union[
|
| | None, bool, Callable[[ClientResponse], Awaitable[None]]
|
| | ] = None,
|
| | read_until_eof: bool = True,
|
| | proxy: Optional[StrOrURL] = None,
|
| | proxy_auth: Optional[BasicAuth] = None,
|
| | timeout: Union[ClientTimeout, _SENTINEL] = sentinel,
|
| | verify_ssl: Optional[bool] = None,
|
| | fingerprint: Optional[bytes] = None,
|
| | ssl_context: Optional[SSLContext] = None,
|
| | ssl: Union[SSLContext, bool, Fingerprint] = True,
|
| | server_hostname: Optional[str] = None,
|
| | proxy_headers: Optional[LooseHeaders] = None,
|
| | trace_request_ctx: Optional[Mapping[str, Any]] = None,
|
| | read_bufsize: Optional[int] = None,
|
| | auto_decompress: Optional[bool] = None,
|
| | max_line_size: Optional[int] = None,
|
| | max_field_size: Optional[int] = None,
|
| | ) -> ClientResponse:
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | if self.closed:
|
| | raise RuntimeError("Session is closed")
|
| |
|
| | ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint)
|
| |
|
| | if data is not None and json is not None:
|
| | raise ValueError(
|
| | "data and json parameters can not be used at the same time"
|
| | )
|
| | elif json is not None:
|
| | data = payload.JsonPayload(json, dumps=self._json_serialize)
|
| |
|
| | if not isinstance(chunked, bool) and chunked is not None:
|
| | warnings.warn("Chunk size is deprecated #1615", DeprecationWarning)
|
| |
|
| | redirects = 0
|
| | history = []
|
| | version = self._version
|
| | params = params or {}
|
| |
|
| |
|
| | headers = self._prepare_headers(headers)
|
| |
|
| | try:
|
| | url = self._build_url(str_or_url)
|
| | except ValueError as e:
|
| | raise InvalidUrlClientError(str_or_url) from e
|
| |
|
| | assert self._connector is not None
|
| | if url.scheme not in self._connector.allowed_protocol_schema_set:
|
| | raise NonHttpUrlClientError(url)
|
| |
|
| | skip_headers = set(self._skip_auto_headers)
|
| | if skip_auto_headers is not None:
|
| | for i in skip_auto_headers:
|
| | skip_headers.add(istr(i))
|
| |
|
| | if proxy is None:
|
| | proxy_headers = None
|
| | else:
|
| | proxy_headers = self._prepare_headers(proxy_headers)
|
| | try:
|
| | proxy = URL(proxy)
|
| | except ValueError as e:
|
| | raise InvalidURL(proxy) from e
|
| |
|
| | if timeout is sentinel:
|
| | real_timeout: ClientTimeout = self._timeout
|
| | else:
|
| | if not isinstance(timeout, ClientTimeout):
|
| | real_timeout = ClientTimeout(total=timeout)
|
| | else:
|
| | real_timeout = timeout
|
| |
|
| |
|
| | tm = TimeoutHandle(
|
| | self._loop, real_timeout.total, ceil_threshold=real_timeout.ceil_threshold
|
| | )
|
| | handle = tm.start()
|
| |
|
| | if read_bufsize is None:
|
| | read_bufsize = self._read_bufsize
|
| |
|
| | if auto_decompress is None:
|
| | auto_decompress = self._auto_decompress
|
| |
|
| | if max_line_size is None:
|
| | max_line_size = self._max_line_size
|
| |
|
| | if max_field_size is None:
|
| | max_field_size = self._max_field_size
|
| |
|
| | traces = [
|
| | Trace(
|
| | self,
|
| | trace_config,
|
| | trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx),
|
| | )
|
| | for trace_config in self._trace_configs
|
| | ]
|
| |
|
| | for trace in traces:
|
| | await trace.send_request_start(method, url.update_query(params), headers)
|
| |
|
| | timer = tm.timer()
|
| | try:
|
| | with timer:
|
| |
|
| | retry_persistent_connection = method in IDEMPOTENT_METHODS
|
| | while True:
|
| | url, auth_from_url = strip_auth_from_url(url)
|
| | if not url.raw_host:
|
| |
|
| |
|
| | err_exc_cls = (
|
| | InvalidUrlRedirectClientError
|
| | if redirects
|
| | else InvalidUrlClientError
|
| | )
|
| | raise err_exc_cls(url)
|
| | if auth and auth_from_url:
|
| | raise ValueError(
|
| | "Cannot combine AUTH argument with "
|
| | "credentials encoded in URL"
|
| | )
|
| |
|
| | if auth is None:
|
| | auth = auth_from_url
|
| | if auth is None:
|
| | auth = self._default_auth
|
| |
|
| |
|
| | if (
|
| | headers is not None
|
| | and auth is not None
|
| | and hdrs.AUTHORIZATION in headers
|
| | ):
|
| | raise ValueError(
|
| | "Cannot combine AUTHORIZATION header "
|
| | "with AUTH argument or credentials "
|
| | "encoded in URL"
|
| | )
|
| |
|
| | all_cookies = self._cookie_jar.filter_cookies(url)
|
| |
|
| | if cookies is not None:
|
| | tmp_cookie_jar = CookieJar()
|
| | tmp_cookie_jar.update_cookies(cookies)
|
| | req_cookies = tmp_cookie_jar.filter_cookies(url)
|
| | if req_cookies:
|
| | all_cookies.load(req_cookies)
|
| |
|
| | if proxy is not None:
|
| | proxy = URL(proxy)
|
| | elif self._trust_env:
|
| | with suppress(LookupError):
|
| | proxy, proxy_auth = get_env_proxy_for_url(url)
|
| |
|
| | req = self._request_class(
|
| | method,
|
| | url,
|
| | params=params,
|
| | headers=headers,
|
| | skip_auto_headers=skip_headers if skip_headers else None,
|
| | data=data,
|
| | cookies=all_cookies,
|
| | auth=auth,
|
| | version=version,
|
| | compress=compress,
|
| | chunked=chunked,
|
| | expect100=expect100,
|
| | loop=self._loop,
|
| | response_class=self._response_class,
|
| | proxy=proxy,
|
| | proxy_auth=proxy_auth,
|
| | timer=timer,
|
| | session=self,
|
| | ssl=ssl if ssl is not None else True,
|
| | server_hostname=server_hostname,
|
| | proxy_headers=proxy_headers,
|
| | traces=traces,
|
| | trust_env=self.trust_env,
|
| | )
|
| |
|
| |
|
| | try:
|
| | async with ceil_timeout(
|
| | real_timeout.connect,
|
| | ceil_threshold=real_timeout.ceil_threshold,
|
| | ):
|
| | conn = await self._connector.connect(
|
| | req, traces=traces, timeout=real_timeout
|
| | )
|
| | except asyncio.TimeoutError as exc:
|
| | raise ConnectionTimeoutError(
|
| | f"Connection timeout to host {url}"
|
| | ) from exc
|
| |
|
| | assert conn.transport is not None
|
| |
|
| | assert conn.protocol is not None
|
| | conn.protocol.set_response_params(
|
| | timer=timer,
|
| | skip_payload=method_must_be_empty_body(method),
|
| | read_until_eof=read_until_eof,
|
| | auto_decompress=auto_decompress,
|
| | read_timeout=real_timeout.sock_read,
|
| | read_bufsize=read_bufsize,
|
| | timeout_ceil_threshold=self._connector._timeout_ceil_threshold,
|
| | max_line_size=max_line_size,
|
| | max_field_size=max_field_size,
|
| | )
|
| |
|
| | try:
|
| | try:
|
| | resp = await req.send(conn)
|
| | try:
|
| | await resp.start(conn)
|
| | except BaseException:
|
| | resp.close()
|
| | raise
|
| | except BaseException:
|
| | conn.close()
|
| | raise
|
| | except (ClientOSError, ServerDisconnectedError):
|
| | if retry_persistent_connection:
|
| | retry_persistent_connection = False
|
| | continue
|
| | raise
|
| | except ClientError:
|
| | raise
|
| | except OSError as exc:
|
| | if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
|
| | raise
|
| | raise ClientOSError(*exc.args) from exc
|
| |
|
| | self._cookie_jar.update_cookies(resp.cookies, resp.url)
|
| |
|
| |
|
| | if resp.status in (301, 302, 303, 307, 308) and allow_redirects:
|
| |
|
| | for trace in traces:
|
| | await trace.send_request_redirect(
|
| | method, url.update_query(params), headers, resp
|
| | )
|
| |
|
| | redirects += 1
|
| | history.append(resp)
|
| | if max_redirects and redirects >= max_redirects:
|
| | resp.close()
|
| | raise TooManyRedirects(
|
| | history[0].request_info, tuple(history)
|
| | )
|
| |
|
| |
|
| |
|
| | if (resp.status == 303 and resp.method != hdrs.METH_HEAD) or (
|
| | resp.status in (301, 302) and resp.method == hdrs.METH_POST
|
| | ):
|
| | method = hdrs.METH_GET
|
| | data = None
|
| | if headers.get(hdrs.CONTENT_LENGTH):
|
| | headers.pop(hdrs.CONTENT_LENGTH)
|
| |
|
| | r_url = resp.headers.get(hdrs.LOCATION) or resp.headers.get(
|
| | hdrs.URI
|
| | )
|
| | if r_url is None:
|
| |
|
| | break
|
| | else:
|
| |
|
| |
|
| | resp.release()
|
| |
|
| | try:
|
| | parsed_redirect_url = URL(
|
| | r_url, encoded=not self._requote_redirect_url
|
| | )
|
| | except ValueError as e:
|
| | raise InvalidUrlRedirectClientError(
|
| | r_url,
|
| | "Server attempted redirecting to a location that does not look like a URL",
|
| | ) from e
|
| |
|
| | scheme = parsed_redirect_url.scheme
|
| | if scheme not in HTTP_AND_EMPTY_SCHEMA_SET:
|
| | resp.close()
|
| | raise NonHttpUrlRedirectClientError(r_url)
|
| | elif not scheme:
|
| | parsed_redirect_url = url.join(parsed_redirect_url)
|
| |
|
| | try:
|
| | redirect_origin = parsed_redirect_url.origin()
|
| | except ValueError as origin_val_err:
|
| | raise InvalidUrlRedirectClientError(
|
| | parsed_redirect_url,
|
| | "Invalid redirect URL origin",
|
| | ) from origin_val_err
|
| |
|
| | if url.origin() != redirect_origin:
|
| | auth = None
|
| | headers.pop(hdrs.AUTHORIZATION, None)
|
| |
|
| | url = parsed_redirect_url
|
| | params = {}
|
| | resp.release()
|
| | continue
|
| |
|
| | break
|
| |
|
| |
|
| | if raise_for_status is None:
|
| | raise_for_status = self._raise_for_status
|
| |
|
| | if raise_for_status is None:
|
| | pass
|
| | elif callable(raise_for_status):
|
| | await raise_for_status(resp)
|
| | elif raise_for_status:
|
| | resp.raise_for_status()
|
| |
|
| |
|
| | if handle is not None:
|
| | if resp.connection is not None:
|
| | resp.connection.add_callback(handle.cancel)
|
| | else:
|
| | handle.cancel()
|
| |
|
| | resp._history = tuple(history)
|
| |
|
| | for trace in traces:
|
| | await trace.send_request_end(
|
| | method, url.update_query(params), headers, resp
|
| | )
|
| | return resp
|
| |
|
| | except BaseException as e:
|
| |
|
| | tm.close()
|
| | if handle:
|
| | handle.cancel()
|
| | handle = None
|
| |
|
| | for trace in traces:
|
| | await trace.send_request_exception(
|
| | method, url.update_query(params), headers, e
|
| | )
|
| | raise
|
| |
|
| | def ws_connect(
|
| | self,
|
| | url: StrOrURL,
|
| | *,
|
| | method: str = hdrs.METH_GET,
|
| | protocols: Iterable[str] = (),
|
| | timeout: float = 10.0,
|
| | receive_timeout: Optional[float] = None,
|
| | autoclose: bool = True,
|
| | autoping: bool = True,
|
| | heartbeat: Optional[float] = None,
|
| | auth: Optional[BasicAuth] = None,
|
| | origin: Optional[str] = None,
|
| | params: Query = None,
|
| | headers: Optional[LooseHeaders] = None,
|
| | proxy: Optional[StrOrURL] = None,
|
| | proxy_auth: Optional[BasicAuth] = None,
|
| | ssl: Union[SSLContext, bool, Fingerprint] = True,
|
| | verify_ssl: Optional[bool] = None,
|
| | fingerprint: Optional[bytes] = None,
|
| | ssl_context: Optional[SSLContext] = None,
|
| | proxy_headers: Optional[LooseHeaders] = None,
|
| | compress: int = 0,
|
| | max_msg_size: int = 4 * 1024 * 1024,
|
| | ) -> "_WSRequestContextManager":
|
| | """Initiate websocket connection."""
|
| | return _WSRequestContextManager(
|
| | self._ws_connect(
|
| | url,
|
| | method=method,
|
| | protocols=protocols,
|
| | timeout=timeout,
|
| | receive_timeout=receive_timeout,
|
| | autoclose=autoclose,
|
| | autoping=autoping,
|
| | heartbeat=heartbeat,
|
| | auth=auth,
|
| | origin=origin,
|
| | params=params,
|
| | headers=headers,
|
| | proxy=proxy,
|
| | proxy_auth=proxy_auth,
|
| | ssl=ssl,
|
| | verify_ssl=verify_ssl,
|
| | fingerprint=fingerprint,
|
| | ssl_context=ssl_context,
|
| | proxy_headers=proxy_headers,
|
| | compress=compress,
|
| | max_msg_size=max_msg_size,
|
| | )
|
| | )
|
| |
|
| | async def _ws_connect(
|
| | self,
|
| | url: StrOrURL,
|
| | *,
|
| | method: str = hdrs.METH_GET,
|
| | protocols: Iterable[str] = (),
|
| | timeout: float = 10.0,
|
| | receive_timeout: Optional[float] = None,
|
| | autoclose: bool = True,
|
| | autoping: bool = True,
|
| | heartbeat: Optional[float] = None,
|
| | auth: Optional[BasicAuth] = None,
|
| | origin: Optional[str] = None,
|
| | params: Query = None,
|
| | headers: Optional[LooseHeaders] = None,
|
| | proxy: Optional[StrOrURL] = None,
|
| | proxy_auth: Optional[BasicAuth] = None,
|
| | ssl: Union[SSLContext, bool, Fingerprint] = True,
|
| | verify_ssl: Optional[bool] = None,
|
| | fingerprint: Optional[bytes] = None,
|
| | ssl_context: Optional[SSLContext] = None,
|
| | proxy_headers: Optional[LooseHeaders] = None,
|
| | compress: int = 0,
|
| | max_msg_size: int = 4 * 1024 * 1024,
|
| | ) -> ClientWebSocketResponse:
|
| |
|
| | if headers is None:
|
| | real_headers: CIMultiDict[str] = CIMultiDict()
|
| | else:
|
| | real_headers = CIMultiDict(headers)
|
| |
|
| | default_headers = {
|
| | hdrs.UPGRADE: "websocket",
|
| | hdrs.CONNECTION: "Upgrade",
|
| | hdrs.SEC_WEBSOCKET_VERSION: "13",
|
| | }
|
| |
|
| | for key, value in default_headers.items():
|
| | real_headers.setdefault(key, value)
|
| |
|
| | sec_key = base64.b64encode(os.urandom(16))
|
| | real_headers[hdrs.SEC_WEBSOCKET_KEY] = sec_key.decode()
|
| |
|
| | if protocols:
|
| | real_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ",".join(protocols)
|
| | if origin is not None:
|
| | real_headers[hdrs.ORIGIN] = origin
|
| | if compress:
|
| | extstr = ws_ext_gen(compress=compress)
|
| | real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr
|
| |
|
| |
|
| | if ssl is None:
|
| | warnings.warn(
|
| | "ssl=None is deprecated, please use ssl=True",
|
| | DeprecationWarning,
|
| | stacklevel=2,
|
| | )
|
| | ssl = True
|
| | ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint)
|
| |
|
| |
|
| | resp = await self.request(
|
| | method,
|
| | url,
|
| | params=params,
|
| | headers=real_headers,
|
| | read_until_eof=False,
|
| | auth=auth,
|
| | proxy=proxy,
|
| | proxy_auth=proxy_auth,
|
| | ssl=ssl,
|
| | proxy_headers=proxy_headers,
|
| | )
|
| |
|
| | try:
|
| |
|
| | if resp.status != 101:
|
| | raise WSServerHandshakeError(
|
| | resp.request_info,
|
| | resp.history,
|
| | message="Invalid response status",
|
| | status=resp.status,
|
| | headers=resp.headers,
|
| | )
|
| |
|
| | if resp.headers.get(hdrs.UPGRADE, "").lower() != "websocket":
|
| | raise WSServerHandshakeError(
|
| | resp.request_info,
|
| | resp.history,
|
| | message="Invalid upgrade header",
|
| | status=resp.status,
|
| | headers=resp.headers,
|
| | )
|
| |
|
| | if resp.headers.get(hdrs.CONNECTION, "").lower() != "upgrade":
|
| | raise WSServerHandshakeError(
|
| | resp.request_info,
|
| | resp.history,
|
| | message="Invalid connection header",
|
| | status=resp.status,
|
| | headers=resp.headers,
|
| | )
|
| |
|
| |
|
| | r_key = resp.headers.get(hdrs.SEC_WEBSOCKET_ACCEPT, "")
|
| | match = base64.b64encode(hashlib.sha1(sec_key + WS_KEY).digest()).decode()
|
| | if r_key != match:
|
| | raise WSServerHandshakeError(
|
| | resp.request_info,
|
| | resp.history,
|
| | message="Invalid challenge response",
|
| | status=resp.status,
|
| | headers=resp.headers,
|
| | )
|
| |
|
| |
|
| | protocol = None
|
| | if protocols and hdrs.SEC_WEBSOCKET_PROTOCOL in resp.headers:
|
| | resp_protocols = [
|
| | proto.strip()
|
| | for proto in resp.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",")
|
| | ]
|
| |
|
| | for proto in resp_protocols:
|
| | if proto in protocols:
|
| | protocol = proto
|
| | break
|
| |
|
| |
|
| | notakeover = False
|
| | if compress:
|
| | compress_hdrs = resp.headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)
|
| | if compress_hdrs:
|
| | try:
|
| | compress, notakeover = ws_ext_parse(compress_hdrs)
|
| | except WSHandshakeError as exc:
|
| | raise WSServerHandshakeError(
|
| | resp.request_info,
|
| | resp.history,
|
| | message=exc.args[0],
|
| | status=resp.status,
|
| | headers=resp.headers,
|
| | ) from exc
|
| | else:
|
| | compress = 0
|
| | notakeover = False
|
| |
|
| | conn = resp.connection
|
| | assert conn is not None
|
| | conn_proto = conn.protocol
|
| | assert conn_proto is not None
|
| |
|
| |
|
| |
|
| | if receive_timeout is None:
|
| |
|
| | conn_proto.read_timeout = receive_timeout
|
| | elif conn_proto.read_timeout is not None:
|
| |
|
| | conn_proto.read_timeout = max(receive_timeout, conn_proto.read_timeout)
|
| |
|
| | transport = conn.transport
|
| | assert transport is not None
|
| | reader: FlowControlDataQueue[WSMessage] = FlowControlDataQueue(
|
| | conn_proto, 2**16, loop=self._loop
|
| | )
|
| | conn_proto.set_parser(WebSocketReader(reader, max_msg_size), reader)
|
| | writer = WebSocketWriter(
|
| | conn_proto,
|
| | transport,
|
| | use_mask=True,
|
| | compress=compress,
|
| | notakeover=notakeover,
|
| | )
|
| | except BaseException:
|
| | resp.close()
|
| | raise
|
| | else:
|
| | return self._ws_response_class(
|
| | reader,
|
| | writer,
|
| | protocol,
|
| | resp,
|
| | timeout,
|
| | autoclose,
|
| | autoping,
|
| | self._loop,
|
| | receive_timeout=receive_timeout,
|
| | heartbeat=heartbeat,
|
| | compress=compress,
|
| | client_notakeover=notakeover,
|
| | )
|
| |
|
| | def _prepare_headers(self, headers: Optional[LooseHeaders]) -> "CIMultiDict[str]":
|
| | """Add default headers and transform it to CIMultiDict"""
|
| |
|
| | result = CIMultiDict(self._default_headers)
|
| | if headers:
|
| | if not isinstance(headers, (MultiDictProxy, MultiDict)):
|
| | headers = CIMultiDict(headers)
|
| | added_names: Set[str] = set()
|
| | for key, value in headers.items():
|
| | if key in added_names:
|
| | result.add(key, value)
|
| | else:
|
| | result[key] = value
|
| | added_names.add(key)
|
| | return result
|
| |
|
| | if sys.version_info >= (3, 11) and TYPE_CHECKING:
|
| |
|
| | def get(
|
| | self,
|
| | url: StrOrURL,
|
| | **kwargs: Unpack[_RequestOptions],
|
| | ) -> "_RequestContextManager": ...
|
| |
|
| | def options(
|
| | self,
|
| | url: StrOrURL,
|
| | **kwargs: Unpack[_RequestOptions],
|
| | ) -> "_RequestContextManager": ...
|
| |
|
| | def head(
|
| | self,
|
| | url: StrOrURL,
|
| | **kwargs: Unpack[_RequestOptions],
|
| | ) -> "_RequestContextManager": ...
|
| |
|
| | def post(
|
| | self,
|
| | url: StrOrURL,
|
| | **kwargs: Unpack[_RequestOptions],
|
| | ) -> "_RequestContextManager": ...
|
| |
|
| | def put(
|
| | self,
|
| | url: StrOrURL,
|
| | **kwargs: Unpack[_RequestOptions],
|
| | ) -> "_RequestContextManager": ...
|
| |
|
| | def patch(
|
| | self,
|
| | url: StrOrURL,
|
| | **kwargs: Unpack[_RequestOptions],
|
| | ) -> "_RequestContextManager": ...
|
| |
|
| | def delete(
|
| | self,
|
| | url: StrOrURL,
|
| | **kwargs: Unpack[_RequestOptions],
|
| | ) -> "_RequestContextManager": ...
|
| |
|
| | else:
|
| |
|
| | def get(
|
| | self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any
|
| | ) -> "_RequestContextManager":
|
| | """Perform HTTP GET request."""
|
| | return _RequestContextManager(
|
| | self._request(
|
| | hdrs.METH_GET, url, allow_redirects=allow_redirects, **kwargs
|
| | )
|
| | )
|
| |
|
| | def options(
|
| | self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any
|
| | ) -> "_RequestContextManager":
|
| | """Perform HTTP OPTIONS request."""
|
| | return _RequestContextManager(
|
| | self._request(
|
| | hdrs.METH_OPTIONS, url, allow_redirects=allow_redirects, **kwargs
|
| | )
|
| | )
|
| |
|
| | def head(
|
| | self, url: StrOrURL, *, allow_redirects: bool = False, **kwargs: Any
|
| | ) -> "_RequestContextManager":
|
| | """Perform HTTP HEAD request."""
|
| | return _RequestContextManager(
|
| | self._request(
|
| | hdrs.METH_HEAD, url, allow_redirects=allow_redirects, **kwargs
|
| | )
|
| | )
|
| |
|
| | def post(
|
| | self, url: StrOrURL, *, data: Any = None, **kwargs: Any
|
| | ) -> "_RequestContextManager":
|
| | """Perform HTTP POST request."""
|
| | return _RequestContextManager(
|
| | self._request(hdrs.METH_POST, url, data=data, **kwargs)
|
| | )
|
| |
|
| | def put(
|
| | self, url: StrOrURL, *, data: Any = None, **kwargs: Any
|
| | ) -> "_RequestContextManager":
|
| | """Perform HTTP PUT request."""
|
| | return _RequestContextManager(
|
| | self._request(hdrs.METH_PUT, url, data=data, **kwargs)
|
| | )
|
| |
|
| | def patch(
|
| | self, url: StrOrURL, *, data: Any = None, **kwargs: Any
|
| | ) -> "_RequestContextManager":
|
| | """Perform HTTP PATCH request."""
|
| | return _RequestContextManager(
|
| | self._request(hdrs.METH_PATCH, url, data=data, **kwargs)
|
| | )
|
| |
|
| | def delete(self, url: StrOrURL, **kwargs: Any) -> "_RequestContextManager":
|
| | """Perform HTTP DELETE request."""
|
| | return _RequestContextManager(
|
| | self._request(hdrs.METH_DELETE, url, **kwargs)
|
| | )
|
| |
|
| | async def close(self) -> None:
|
| | """Close underlying connector.
|
| |
|
| | Release all acquired resources.
|
| | """
|
| | if not self.closed:
|
| | if self._connector is not None and self._connector_owner:
|
| | await self._connector.close()
|
| | self._connector = None
|
| |
|
| | @property
|
| | def closed(self) -> bool:
|
| | """Is client session closed.
|
| |
|
| | A readonly property.
|
| | """
|
| | return self._connector is None or self._connector.closed
|
| |
|
| | @property
|
| | def connector(self) -> Optional[BaseConnector]:
|
| | """Connector instance used for the session."""
|
| | return self._connector
|
| |
|
| | @property
|
| | def cookie_jar(self) -> AbstractCookieJar:
|
| | """The session cookies."""
|
| | return self._cookie_jar
|
| |
|
| | @property
|
| | def version(self) -> Tuple[int, int]:
|
| | """The session HTTP protocol version."""
|
| | return self._version
|
| |
|
| | @property
|
| | def requote_redirect_url(self) -> bool:
|
| | """Do URL requoting on redirection handling."""
|
| | return self._requote_redirect_url
|
| |
|
| | @requote_redirect_url.setter
|
| | def requote_redirect_url(self, val: bool) -> None:
|
| | """Do URL requoting on redirection handling."""
|
| | warnings.warn(
|
| | "session.requote_redirect_url modification " "is deprecated #2778",
|
| | DeprecationWarning,
|
| | stacklevel=2,
|
| | )
|
| | self._requote_redirect_url = val
|
| |
|
| | @property
|
| | def loop(self) -> asyncio.AbstractEventLoop:
|
| | """Session's loop."""
|
| | warnings.warn(
|
| | "client.loop property is deprecated", DeprecationWarning, stacklevel=2
|
| | )
|
| | return self._loop
|
| |
|
| | @property
|
| | def timeout(self) -> ClientTimeout:
|
| | """Timeout for the session."""
|
| | return self._timeout
|
| |
|
| | @property
|
| | def headers(self) -> "CIMultiDict[str]":
|
| | """The default headers of the client session."""
|
| | return self._default_headers
|
| |
|
| | @property
|
| | def skip_auto_headers(self) -> FrozenSet[istr]:
|
| | """Headers for which autogeneration should be skipped"""
|
| | return self._skip_auto_headers
|
| |
|
| | @property
|
| | def auth(self) -> Optional[BasicAuth]:
|
| | """An object that represents HTTP Basic Authorization"""
|
| | return self._default_auth
|
| |
|
| | @property
|
| | def json_serialize(self) -> JSONEncoder:
|
| | """Json serializer callable"""
|
| | return self._json_serialize
|
| |
|
| | @property
|
| | def connector_owner(self) -> bool:
|
| | """Should connector be closed on session closing"""
|
| | return self._connector_owner
|
| |
|
| | @property
|
| | def raise_for_status(
|
| | self,
|
| | ) -> Union[bool, Callable[[ClientResponse], Awaitable[None]]]:
|
| | """Should `ClientResponse.raise_for_status()` be called for each response."""
|
| | return self._raise_for_status
|
| |
|
| | @property
|
| | def auto_decompress(self) -> bool:
|
| | """Should the body response be automatically decompressed."""
|
| | return self._auto_decompress
|
| |
|
| | @property
|
| | def trust_env(self) -> bool:
|
| | """
|
| | Should proxies information from environment or netrc be trusted.
|
| |
|
| | Information is from HTTP_PROXY / HTTPS_PROXY environment variables
|
| | or ~/.netrc file if present.
|
| | """
|
| | return self._trust_env
|
| |
|
| | @property
|
| | def trace_configs(self) -> List[TraceConfig]:
|
| | """A list of TraceConfig instances used for client tracing"""
|
| | return self._trace_configs
|
| |
|
| | def detach(self) -> None:
|
| | """Detach connector from session without closing the former.
|
| |
|
| | Session is switched to closed state anyway.
|
| | """
|
| | self._connector = None
|
| |
|
| | def __enter__(self) -> None:
|
| | raise TypeError("Use async with instead")
|
| |
|
| | def __exit__(
|
| | self,
|
| | exc_type: Optional[Type[BaseException]],
|
| | exc_val: Optional[BaseException],
|
| | exc_tb: Optional[TracebackType],
|
| | ) -> None:
|
| |
|
| | pass
|
| |
|
| | async def __aenter__(self) -> "ClientSession":
|
| | return self
|
| |
|
| | async def __aexit__(
|
| | self,
|
| | exc_type: Optional[Type[BaseException]],
|
| | exc_val: Optional[BaseException],
|
| | exc_tb: Optional[TracebackType],
|
| | ) -> None:
|
| | await self.close()
|
| |
|
| |
|
| | class _BaseRequestContextManager(Coroutine[Any, Any, _RetType], Generic[_RetType]):
|
| |
|
| | __slots__ = ("_coro", "_resp")
|
| |
|
| | def __init__(self, coro: Coroutine["asyncio.Future[Any]", None, _RetType]) -> None:
|
| | self._coro: Coroutine["asyncio.Future[Any]", None, _RetType] = coro
|
| |
|
| | def send(self, arg: None) -> "asyncio.Future[Any]":
|
| | return self._coro.send(arg)
|
| |
|
| | def throw(self, *args: Any, **kwargs: Any) -> "asyncio.Future[Any]":
|
| | return self._coro.throw(*args, **kwargs)
|
| |
|
| | def close(self) -> None:
|
| | return self._coro.close()
|
| |
|
| | def __await__(self) -> Generator[Any, None, _RetType]:
|
| | ret = self._coro.__await__()
|
| | return ret
|
| |
|
| | def __iter__(self) -> Generator[Any, None, _RetType]:
|
| | return self.__await__()
|
| |
|
| | async def __aenter__(self) -> _RetType:
|
| | self._resp: _RetType = await self._coro
|
| | return await self._resp.__aenter__()
|
| |
|
| | async def __aexit__(
|
| | self,
|
| | exc_type: Optional[Type[BaseException]],
|
| | exc: Optional[BaseException],
|
| | tb: Optional[TracebackType],
|
| | ) -> None:
|
| | await self._resp.__aexit__(exc_type, exc, tb)
|
| |
|
| |
|
| | _RequestContextManager = _BaseRequestContextManager[ClientResponse]
|
| | _WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse]
|
| |
|
| |
|
| | class _SessionRequestContextManager:
|
| |
|
| | __slots__ = ("_coro", "_resp", "_session")
|
| |
|
| | def __init__(
|
| | self,
|
| | coro: Coroutine["asyncio.Future[Any]", None, ClientResponse],
|
| | session: ClientSession,
|
| | ) -> None:
|
| | self._coro = coro
|
| | self._resp: Optional[ClientResponse] = None
|
| | self._session = session
|
| |
|
| | async def __aenter__(self) -> ClientResponse:
|
| | try:
|
| | self._resp = await self._coro
|
| | except BaseException:
|
| | await self._session.close()
|
| | raise
|
| | else:
|
| | return self._resp
|
| |
|
| | async def __aexit__(
|
| | self,
|
| | exc_type: Optional[Type[BaseException]],
|
| | exc: Optional[BaseException],
|
| | tb: Optional[TracebackType],
|
| | ) -> None:
|
| | assert self._resp is not None
|
| | self._resp.close()
|
| | await self._session.close()
|
| |
|
| |
|
| | def request(
|
| | method: str,
|
| | url: StrOrURL,
|
| | *,
|
| | params: Query = None,
|
| | data: Any = None,
|
| | json: Any = None,
|
| | headers: Optional[LooseHeaders] = None,
|
| | skip_auto_headers: Optional[Iterable[str]] = None,
|
| | auth: Optional[BasicAuth] = None,
|
| | allow_redirects: bool = True,
|
| | max_redirects: int = 10,
|
| | compress: Optional[str] = None,
|
| | chunked: Optional[bool] = None,
|
| | expect100: bool = False,
|
| | raise_for_status: Optional[bool] = None,
|
| | read_until_eof: bool = True,
|
| | proxy: Optional[StrOrURL] = None,
|
| | proxy_auth: Optional[BasicAuth] = None,
|
| | timeout: Union[ClientTimeout, object] = sentinel,
|
| | cookies: Optional[LooseCookies] = None,
|
| | version: HttpVersion = http.HttpVersion11,
|
| | connector: Optional[BaseConnector] = None,
|
| | read_bufsize: Optional[int] = None,
|
| | loop: Optional[asyncio.AbstractEventLoop] = None,
|
| | max_line_size: int = 8190,
|
| | max_field_size: int = 8190,
|
| | ) -> _SessionRequestContextManager:
|
| | """Constructs and sends a request.
|
| |
|
| | Returns response object.
|
| | method - HTTP method
|
| | url - request url
|
| | params - (optional) Dictionary or bytes to be sent in the query
|
| | string of the new request
|
| | data - (optional) Dictionary, bytes, or file-like object to
|
| | send in the body of the request
|
| | json - (optional) Any json compatible python object
|
| | headers - (optional) Dictionary of HTTP Headers to send with
|
| | the request
|
| | cookies - (optional) Dict object to send with the request
|
| | auth - (optional) BasicAuth named tuple represent HTTP Basic Auth
|
| | auth - aiohttp.helpers.BasicAuth
|
| | allow_redirects - (optional) If set to False, do not follow
|
| | redirects
|
| | version - Request HTTP version.
|
| | compress - Set to True if request has to be compressed
|
| | with deflate encoding.
|
| | chunked - Set to chunk size for chunked transfer encoding.
|
| | expect100 - Expect 100-continue response from server.
|
| | connector - BaseConnector sub-class instance to support
|
| | connection pooling.
|
| | read_until_eof - Read response until eof if response
|
| | does not have Content-Length header.
|
| | loop - Optional event loop.
|
| | timeout - Optional ClientTimeout settings structure, 5min
|
| | total timeout by default.
|
| | Usage::
|
| | >>> import aiohttp
|
| | >>> resp = await aiohttp.request('GET', 'http://python.org/')
|
| | >>> resp
|
| | <ClientResponse(python.org/) [200]>
|
| | >>> data = await resp.read()
|
| | """
|
| | connector_owner = False
|
| | if connector is None:
|
| | connector_owner = True
|
| | connector = TCPConnector(loop=loop, force_close=True)
|
| |
|
| | session = ClientSession(
|
| | loop=loop,
|
| | cookies=cookies,
|
| | version=version,
|
| | timeout=timeout,
|
| | connector=connector,
|
| | connector_owner=connector_owner,
|
| | )
|
| |
|
| | return _SessionRequestContextManager(
|
| | session._request(
|
| | method,
|
| | url,
|
| | params=params,
|
| | data=data,
|
| | json=json,
|
| | headers=headers,
|
| | skip_auto_headers=skip_auto_headers,
|
| | auth=auth,
|
| | allow_redirects=allow_redirects,
|
| | max_redirects=max_redirects,
|
| | compress=compress,
|
| | chunked=chunked,
|
| | expect100=expect100,
|
| | raise_for_status=raise_for_status,
|
| | read_until_eof=read_until_eof,
|
| | proxy=proxy,
|
| | proxy_auth=proxy_auth,
|
| | read_bufsize=read_bufsize,
|
| | max_line_size=max_line_size,
|
| | max_field_size=max_field_size,
|
| | ),
|
| | session,
|
| | )
|
| |
|