Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/aiohttp/client.py: 55%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""HTTP Client for asyncio."""
3import asyncio
4import base64
5import hashlib
6import json
7import os
8import sys
9import traceback
10import warnings
11from contextlib import suppress
12from types import TracebackType
13from typing import (
14 TYPE_CHECKING,
15 Any,
16 Awaitable,
17 Callable,
18 Coroutine,
19 Final,
20 FrozenSet,
21 Generator,
22 Generic,
23 Iterable,
24 List,
25 Mapping,
26 Optional,
27 Sequence,
28 Set,
29 Tuple,
30 Type,
31 TypedDict,
32 TypeVar,
33 Union,
34)
36import attr
37from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr
38from yarl import URL
40from . import hdrs, http, payload
41from ._websocket.reader import WebSocketDataQueue
42from .abc import AbstractCookieJar
43from .client_exceptions import (
44 ClientConnectionError,
45 ClientConnectionResetError,
46 ClientConnectorCertificateError,
47 ClientConnectorDNSError,
48 ClientConnectorError,
49 ClientConnectorSSLError,
50 ClientError,
51 ClientHttpProxyError,
52 ClientOSError,
53 ClientPayloadError,
54 ClientProxyConnectionError,
55 ClientResponseError,
56 ClientSSLError,
57 ConnectionTimeoutError,
58 ContentTypeError,
59 InvalidURL,
60 InvalidUrlClientError,
61 InvalidUrlRedirectClientError,
62 NonHttpUrlClientError,
63 NonHttpUrlRedirectClientError,
64 RedirectClientError,
65 ServerConnectionError,
66 ServerDisconnectedError,
67 ServerFingerprintMismatch,
68 ServerTimeoutError,
69 SocketTimeoutError,
70 TooManyRedirects,
71 WSMessageTypeError,
72 WSServerHandshakeError,
73)
74from .client_middlewares import ClientMiddlewareType, build_client_middlewares
75from .client_reqrep import (
76 ClientRequest as ClientRequest,
77 ClientResponse as ClientResponse,
78 Fingerprint as Fingerprint,
79 RequestInfo as RequestInfo,
80 _merge_ssl_params,
81)
82from .client_ws import (
83 DEFAULT_WS_CLIENT_TIMEOUT,
84 ClientWebSocketResponse as ClientWebSocketResponse,
85 ClientWSTimeout as ClientWSTimeout,
86)
87from .connector import (
88 HTTP_AND_EMPTY_SCHEMA_SET,
89 BaseConnector as BaseConnector,
90 NamedPipeConnector as NamedPipeConnector,
91 TCPConnector as TCPConnector,
92 UnixConnector as UnixConnector,
93)
94from .cookiejar import CookieJar
95from .helpers import (
96 _SENTINEL,
97 DEBUG,
98 EMPTY_BODY_METHODS,
99 BasicAuth,
100 TimeoutHandle,
101 basicauth_from_netrc,
102 get_env_proxy_for_url,
103 netrc_from_env,
104 sentinel,
105 strip_auth_from_url,
106)
107from .http import WS_KEY, HttpVersion, WebSocketReader, WebSocketWriter
108from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse
109from .tracing import Trace, TraceConfig
110from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, Query, StrOrURL
112__all__ = (
113 # client_exceptions
114 "ClientConnectionError",
115 "ClientConnectionResetError",
116 "ClientConnectorCertificateError",
117 "ClientConnectorDNSError",
118 "ClientConnectorError",
119 "ClientConnectorSSLError",
120 "ClientError",
121 "ClientHttpProxyError",
122 "ClientOSError",
123 "ClientPayloadError",
124 "ClientProxyConnectionError",
125 "ClientResponseError",
126 "ClientSSLError",
127 "ConnectionTimeoutError",
128 "ContentTypeError",
129 "InvalidURL",
130 "InvalidUrlClientError",
131 "RedirectClientError",
132 "NonHttpUrlClientError",
133 "InvalidUrlRedirectClientError",
134 "NonHttpUrlRedirectClientError",
135 "ServerConnectionError",
136 "ServerDisconnectedError",
137 "ServerFingerprintMismatch",
138 "ServerTimeoutError",
139 "SocketTimeoutError",
140 "TooManyRedirects",
141 "WSServerHandshakeError",
142 # client_reqrep
143 "ClientRequest",
144 "ClientResponse",
145 "Fingerprint",
146 "RequestInfo",
147 # connector
148 "BaseConnector",
149 "TCPConnector",
150 "UnixConnector",
151 "NamedPipeConnector",
152 # client_ws
153 "ClientWebSocketResponse",
154 # client
155 "ClientSession",
156 "ClientTimeout",
157 "ClientWSTimeout",
158 "request",
159 "WSMessageTypeError",
160)
163if TYPE_CHECKING:
164 from ssl import SSLContext
165else:
166 SSLContext = None
168if sys.version_info >= (3, 11) and TYPE_CHECKING:
169 from typing import Unpack
172class _RequestOptions(TypedDict, total=False):
173 params: Query
174 data: Any
175 json: Any
176 cookies: Union[LooseCookies, None]
177 headers: Union[LooseHeaders, None]
178 skip_auto_headers: Union[Iterable[str], None]
179 auth: Union[BasicAuth, None]
180 allow_redirects: bool
181 max_redirects: int
182 compress: Union[str, bool, None]
183 chunked: Union[bool, None]
184 expect100: bool
185 raise_for_status: Union[None, bool, Callable[[ClientResponse], Awaitable[None]]]
186 read_until_eof: bool
187 proxy: Union[StrOrURL, None]
188 proxy_auth: Union[BasicAuth, None]
189 timeout: "Union[ClientTimeout, _SENTINEL, None]"
190 ssl: Union[SSLContext, bool, Fingerprint]
191 server_hostname: Union[str, None]
192 proxy_headers: Union[LooseHeaders, None]
193 trace_request_ctx: Union[Mapping[str, Any], None]
194 read_bufsize: Union[int, None]
195 auto_decompress: Union[bool, None]
196 max_line_size: Union[int, None]
197 max_field_size: Union[int, None]
198 middlewares: Optional[Sequence[ClientMiddlewareType]]
201@attr.s(auto_attribs=True, frozen=True, slots=True)
202class ClientTimeout:
203 total: Optional[float] = None
204 connect: Optional[float] = None
205 sock_read: Optional[float] = None
206 sock_connect: Optional[float] = None
207 ceil_threshold: float = 5
209 # pool_queue_timeout: Optional[float] = None
210 # dns_resolution_timeout: Optional[float] = None
211 # socket_connect_timeout: Optional[float] = None
212 # connection_acquiring_timeout: Optional[float] = None
213 # new_connection_timeout: Optional[float] = None
214 # http_header_timeout: Optional[float] = None
215 # response_body_timeout: Optional[float] = None
217 # to create a timeout specific for a single request, either
218 # - create a completely new one to overwrite the default
219 # - or use http://www.attrs.org/en/stable/api.html#attr.evolve
220 # to overwrite the defaults
223# 5 Minute default read timeout
224DEFAULT_TIMEOUT: Final[ClientTimeout] = ClientTimeout(total=5 * 60, sock_connect=30)
226# https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2
227IDEMPOTENT_METHODS = frozenset({"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"})
229_RetType = TypeVar("_RetType", ClientResponse, ClientWebSocketResponse)
230_CharsetResolver = Callable[[ClientResponse, bytes], str]
233class ClientSession:
234 """First-class interface for making HTTP requests."""
236 ATTRS = frozenset(
237 [
238 "_base_url",
239 "_base_url_origin",
240 "_source_traceback",
241 "_connector",
242 "_loop",
243 "_cookie_jar",
244 "_connector_owner",
245 "_default_auth",
246 "_version",
247 "_json_serialize",
248 "_requote_redirect_url",
249 "_timeout",
250 "_raise_for_status",
251 "_auto_decompress",
252 "_trust_env",
253 "_default_headers",
254 "_skip_auto_headers",
255 "_request_class",
256 "_response_class",
257 "_ws_response_class",
258 "_trace_configs",
259 "_read_bufsize",
260 "_max_line_size",
261 "_max_field_size",
262 "_resolve_charset",
263 "_default_proxy",
264 "_default_proxy_auth",
265 "_retry_connection",
266 "_middlewares",
267 "requote_redirect_url",
268 ]
269 )
271 _source_traceback: Optional[traceback.StackSummary] = None
272 _connector: Optional[BaseConnector] = None
274 def __init__(
275 self,
276 base_url: Optional[StrOrURL] = None,
277 *,
278 connector: Optional[BaseConnector] = None,
279 loop: Optional[asyncio.AbstractEventLoop] = None,
280 cookies: Optional[LooseCookies] = None,
281 headers: Optional[LooseHeaders] = None,
282 proxy: Optional[StrOrURL] = None,
283 proxy_auth: Optional[BasicAuth] = None,
284 skip_auto_headers: Optional[Iterable[str]] = None,
285 auth: Optional[BasicAuth] = None,
286 json_serialize: JSONEncoder = json.dumps,
287 request_class: Type[ClientRequest] = ClientRequest,
288 response_class: Type[ClientResponse] = ClientResponse,
289 ws_response_class: Type[ClientWebSocketResponse] = ClientWebSocketResponse,
290 version: HttpVersion = http.HttpVersion11,
291 cookie_jar: Optional[AbstractCookieJar] = None,
292 connector_owner: bool = True,
293 raise_for_status: Union[
294 bool, Callable[[ClientResponse], Awaitable[None]]
295 ] = False,
296 read_timeout: Union[float, _SENTINEL] = sentinel,
297 conn_timeout: Optional[float] = None,
298 timeout: Union[object, ClientTimeout] = sentinel,
299 auto_decompress: bool = True,
300 trust_env: bool = False,
301 requote_redirect_url: bool = True,
302 trace_configs: Optional[List[TraceConfig]] = None,
303 read_bufsize: int = 2**16,
304 max_line_size: int = 8190,
305 max_field_size: int = 8190,
306 fallback_charset_resolver: _CharsetResolver = lambda r, b: "utf-8",
307 middlewares: Sequence[ClientMiddlewareType] = (),
308 ssl_shutdown_timeout: Union[_SENTINEL, None, float] = sentinel,
309 ) -> None:
310 # We initialise _connector to None immediately, as it's referenced in __del__()
311 # and could cause issues if an exception occurs during initialisation.
312 self._connector: Optional[BaseConnector] = None
314 if loop is None:
315 if connector is not None:
316 loop = connector._loop
318 loop = loop or asyncio.get_running_loop()
320 if base_url is None or isinstance(base_url, URL):
321 self._base_url: Optional[URL] = base_url
322 self._base_url_origin = None if base_url is None else base_url.origin()
323 else:
324 self._base_url = URL(base_url)
325 self._base_url_origin = self._base_url.origin()
326 assert self._base_url.absolute, "Only absolute URLs are supported"
327 if self._base_url is not None and not self._base_url.path.endswith("/"):
328 raise ValueError("base_url must have a trailing '/'")
330 if timeout is sentinel or timeout is None:
331 self._timeout = DEFAULT_TIMEOUT
332 if read_timeout is not sentinel:
333 warnings.warn(
334 "read_timeout is deprecated, use timeout argument instead",
335 DeprecationWarning,
336 stacklevel=2,
337 )
338 self._timeout = attr.evolve(self._timeout, total=read_timeout)
339 if conn_timeout is not None:
340 self._timeout = attr.evolve(self._timeout, connect=conn_timeout)
341 warnings.warn(
342 "conn_timeout is deprecated, use timeout argument instead",
343 DeprecationWarning,
344 stacklevel=2,
345 )
346 else:
347 if not isinstance(timeout, ClientTimeout):
348 raise ValueError(
349 f"timeout parameter cannot be of {type(timeout)} type, "
350 "please use 'timeout=ClientTimeout(...)'",
351 )
352 self._timeout = timeout
353 if read_timeout is not sentinel:
354 raise ValueError(
355 "read_timeout and timeout parameters "
356 "conflict, please setup "
357 "timeout.read"
358 )
359 if conn_timeout is not None:
360 raise ValueError(
361 "conn_timeout and timeout parameters "
362 "conflict, please setup "
363 "timeout.connect"
364 )
366 if ssl_shutdown_timeout is not sentinel:
367 warnings.warn(
368 "The ssl_shutdown_timeout parameter is deprecated and will be removed in aiohttp 4.0",
369 DeprecationWarning,
370 stacklevel=2,
371 )
373 if connector is None:
374 connector = TCPConnector(
375 loop=loop, ssl_shutdown_timeout=ssl_shutdown_timeout
376 )
378 if connector._loop is not loop:
379 raise RuntimeError("Session and connector has to use same event loop")
381 self._loop = loop
383 if loop.get_debug():
384 self._source_traceback = traceback.extract_stack(sys._getframe(1))
386 if cookie_jar is None:
387 cookie_jar = CookieJar(loop=loop)
388 self._cookie_jar = cookie_jar
390 if cookies:
391 self._cookie_jar.update_cookies(cookies)
393 self._connector = connector
394 self._connector_owner = connector_owner
395 self._default_auth = auth
396 self._version = version
397 self._json_serialize = json_serialize
398 self._raise_for_status = raise_for_status
399 self._auto_decompress = auto_decompress
400 self._trust_env = trust_env
401 self._requote_redirect_url = requote_redirect_url
402 self._read_bufsize = read_bufsize
403 self._max_line_size = max_line_size
404 self._max_field_size = max_field_size
406 # Convert to list of tuples
407 if headers:
408 real_headers: CIMultiDict[str] = CIMultiDict(headers)
409 else:
410 real_headers = CIMultiDict()
411 self._default_headers: CIMultiDict[str] = real_headers
412 if skip_auto_headers is not None:
413 self._skip_auto_headers = frozenset(istr(i) for i in skip_auto_headers)
414 else:
415 self._skip_auto_headers = frozenset()
417 self._request_class = request_class
418 self._response_class = response_class
419 self._ws_response_class = ws_response_class
421 self._trace_configs = trace_configs or []
422 for trace_config in self._trace_configs:
423 trace_config.freeze()
425 self._resolve_charset = fallback_charset_resolver
427 self._default_proxy = proxy
428 self._default_proxy_auth = proxy_auth
429 self._retry_connection: bool = True
430 self._middlewares = middlewares
432 def __init_subclass__(cls: Type["ClientSession"]) -> None:
433 warnings.warn(
434 "Inheritance class {} from ClientSession "
435 "is discouraged".format(cls.__name__),
436 DeprecationWarning,
437 stacklevel=2,
438 )
440 if DEBUG:
442 def __setattr__(self, name: str, val: Any) -> None:
443 if name not in self.ATTRS:
444 warnings.warn(
445 "Setting custom ClientSession.{} attribute "
446 "is discouraged".format(name),
447 DeprecationWarning,
448 stacklevel=2,
449 )
450 super().__setattr__(name, val)
452 def __del__(self, _warnings: Any = warnings) -> None:
453 if not self.closed:
454 kwargs = {"source": self}
455 _warnings.warn(
456 f"Unclosed client session {self!r}", ResourceWarning, **kwargs
457 )
458 context = {"client_session": self, "message": "Unclosed client session"}
459 if self._source_traceback is not None:
460 context["source_traceback"] = self._source_traceback
461 self._loop.call_exception_handler(context)
463 if sys.version_info >= (3, 11) and TYPE_CHECKING:
465 def request(
466 self,
467 method: str,
468 url: StrOrURL,
469 **kwargs: Unpack[_RequestOptions],
470 ) -> "_RequestContextManager": ...
472 else:
474 def request(
475 self, method: str, url: StrOrURL, **kwargs: Any
476 ) -> "_RequestContextManager":
477 """Perform HTTP request."""
478 return _RequestContextManager(self._request(method, url, **kwargs))
480 def _build_url(self, str_or_url: StrOrURL) -> URL:
481 url = URL(str_or_url)
482 if self._base_url and not url.absolute:
483 return self._base_url.join(url)
484 return url
486 async def _request(
487 self,
488 method: str,
489 str_or_url: StrOrURL,
490 *,
491 params: Query = None,
492 data: Any = None,
493 json: Any = None,
494 cookies: Optional[LooseCookies] = None,
495 headers: Optional[LooseHeaders] = None,
496 skip_auto_headers: Optional[Iterable[str]] = None,
497 auth: Optional[BasicAuth] = None,
498 allow_redirects: bool = True,
499 max_redirects: int = 10,
500 compress: Union[str, bool, None] = None,
501 chunked: Optional[bool] = None,
502 expect100: bool = False,
503 raise_for_status: Union[
504 None, bool, Callable[[ClientResponse], Awaitable[None]]
505 ] = None,
506 read_until_eof: bool = True,
507 proxy: Optional[StrOrURL] = None,
508 proxy_auth: Optional[BasicAuth] = None,
509 timeout: Union[ClientTimeout, _SENTINEL] = sentinel,
510 verify_ssl: Optional[bool] = None,
511 fingerprint: Optional[bytes] = None,
512 ssl_context: Optional[SSLContext] = None,
513 ssl: Union[SSLContext, bool, Fingerprint] = True,
514 server_hostname: Optional[str] = None,
515 proxy_headers: Optional[LooseHeaders] = None,
516 trace_request_ctx: Optional[Mapping[str, Any]] = None,
517 read_bufsize: Optional[int] = None,
518 auto_decompress: Optional[bool] = None,
519 max_line_size: Optional[int] = None,
520 max_field_size: Optional[int] = None,
521 middlewares: Optional[Sequence[ClientMiddlewareType]] = None,
522 ) -> ClientResponse:
524 # NOTE: timeout clamps existing connect and read timeouts. We cannot
525 # set the default to None because we need to detect if the user wants
526 # to use the existing timeouts by setting timeout to None.
528 if self.closed:
529 raise RuntimeError("Session is closed")
531 ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint)
533 if data is not None and json is not None:
534 raise ValueError(
535 "data and json parameters can not be used at the same time"
536 )
537 elif json is not None:
538 data = payload.JsonPayload(json, dumps=self._json_serialize)
540 if not isinstance(chunked, bool) and chunked is not None:
541 warnings.warn("Chunk size is deprecated #1615", DeprecationWarning)
543 redirects = 0
544 history: List[ClientResponse] = []
545 version = self._version
546 params = params or {}
548 # Merge with default headers and transform to CIMultiDict
549 headers = self._prepare_headers(headers)
551 try:
552 url = self._build_url(str_or_url)
553 except ValueError as e:
554 raise InvalidUrlClientError(str_or_url) from e
556 assert self._connector is not None
557 if url.scheme not in self._connector.allowed_protocol_schema_set:
558 raise NonHttpUrlClientError(url)
560 skip_headers: Optional[Iterable[istr]]
561 if skip_auto_headers is not None:
562 skip_headers = {
563 istr(i) for i in skip_auto_headers
564 } | self._skip_auto_headers
565 elif self._skip_auto_headers:
566 skip_headers = self._skip_auto_headers
567 else:
568 skip_headers = None
570 if proxy is None:
571 proxy = self._default_proxy
572 if proxy_auth is None:
573 proxy_auth = self._default_proxy_auth
575 if proxy is None:
576 proxy_headers = None
577 else:
578 proxy_headers = self._prepare_headers(proxy_headers)
579 try:
580 proxy = URL(proxy)
581 except ValueError as e:
582 raise InvalidURL(proxy) from e
584 if timeout is sentinel:
585 real_timeout: ClientTimeout = self._timeout
586 else:
587 if not isinstance(timeout, ClientTimeout):
588 real_timeout = ClientTimeout(total=timeout)
589 else:
590 real_timeout = timeout
591 # timeout is cumulative for all request operations
592 # (request, redirects, responses, data consuming)
593 tm = TimeoutHandle(
594 self._loop, real_timeout.total, ceil_threshold=real_timeout.ceil_threshold
595 )
596 handle = tm.start()
598 if read_bufsize is None:
599 read_bufsize = self._read_bufsize
601 if auto_decompress is None:
602 auto_decompress = self._auto_decompress
604 if max_line_size is None:
605 max_line_size = self._max_line_size
607 if max_field_size is None:
608 max_field_size = self._max_field_size
610 traces = [
611 Trace(
612 self,
613 trace_config,
614 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx),
615 )
616 for trace_config in self._trace_configs
617 ]
619 for trace in traces:
620 await trace.send_request_start(method, url.update_query(params), headers)
622 timer = tm.timer()
623 try:
624 with timer:
625 # https://www.rfc-editor.org/rfc/rfc9112.html#name-retrying-requests
626 retry_persistent_connection = (
627 self._retry_connection and method in IDEMPOTENT_METHODS
628 )
629 while True:
630 url, auth_from_url = strip_auth_from_url(url)
631 if not url.raw_host:
632 # NOTE: Bail early, otherwise, causes `InvalidURL` through
633 # NOTE: `self._request_class()` below.
634 err_exc_cls = (
635 InvalidUrlRedirectClientError
636 if redirects
637 else InvalidUrlClientError
638 )
639 raise err_exc_cls(url)
640 # If `auth` was passed for an already authenticated URL,
641 # disallow only if this is the initial URL; this is to avoid issues
642 # with sketchy redirects that are not the caller's responsibility
643 if not history and (auth and auth_from_url):
644 raise ValueError(
645 "Cannot combine AUTH argument with "
646 "credentials encoded in URL"
647 )
649 # Override the auth with the one from the URL only if we
650 # have no auth, or if we got an auth from a redirect URL
651 if auth is None or (history and auth_from_url is not None):
652 auth = auth_from_url
654 if (
655 auth is None
656 and self._default_auth
657 and (
658 not self._base_url or self._base_url_origin == url.origin()
659 )
660 ):
661 auth = self._default_auth
663 # Try netrc if auth is still None and trust_env is enabled.
664 if auth is None and self._trust_env and url.host is not None:
665 auth = await self._loop.run_in_executor(
666 None, self._get_netrc_auth, url.host
667 )
669 # It would be confusing if we support explicit
670 # Authorization header with auth argument
671 if (
672 headers is not None
673 and auth is not None
674 and hdrs.AUTHORIZATION in headers
675 ):
676 raise ValueError(
677 "Cannot combine AUTHORIZATION header "
678 "with AUTH argument or credentials "
679 "encoded in URL"
680 )
682 all_cookies = self._cookie_jar.filter_cookies(url)
684 if cookies is not None:
685 tmp_cookie_jar = CookieJar(
686 quote_cookie=self._cookie_jar.quote_cookie
687 )
688 tmp_cookie_jar.update_cookies(cookies)
689 req_cookies = tmp_cookie_jar.filter_cookies(url)
690 if req_cookies:
691 all_cookies.load(req_cookies)
693 proxy_: Optional[URL] = None
694 if proxy is not None:
695 proxy_ = URL(proxy)
696 elif self._trust_env:
697 with suppress(LookupError):
698 proxy_, proxy_auth = await asyncio.to_thread(
699 get_env_proxy_for_url, url
700 )
702 req = self._request_class(
703 method,
704 url,
705 params=params,
706 headers=headers,
707 skip_auto_headers=skip_headers,
708 data=data,
709 cookies=all_cookies,
710 auth=auth,
711 version=version,
712 compress=compress,
713 chunked=chunked,
714 expect100=expect100,
715 loop=self._loop,
716 response_class=self._response_class,
717 proxy=proxy_,
718 proxy_auth=proxy_auth,
719 timer=timer,
720 session=self,
721 ssl=ssl if ssl is not None else True,
722 server_hostname=server_hostname,
723 proxy_headers=proxy_headers,
724 traces=traces,
725 trust_env=self.trust_env,
726 )
728 async def _connect_and_send_request(
729 req: ClientRequest,
730 ) -> ClientResponse:
731 # connection timeout
732 assert self._connector is not None
733 try:
734 conn = await self._connector.connect(
735 req, traces=traces, timeout=real_timeout
736 )
737 except asyncio.TimeoutError as exc:
738 raise ConnectionTimeoutError(
739 f"Connection timeout to host {req.url}"
740 ) from exc
742 assert conn.protocol is not None
743 conn.protocol.set_response_params(
744 timer=timer,
745 skip_payload=req.method in EMPTY_BODY_METHODS,
746 read_until_eof=read_until_eof,
747 auto_decompress=auto_decompress,
748 read_timeout=real_timeout.sock_read,
749 read_bufsize=read_bufsize,
750 timeout_ceil_threshold=self._connector._timeout_ceil_threshold,
751 max_line_size=max_line_size,
752 max_field_size=max_field_size,
753 )
754 try:
755 resp = await req.send(conn)
756 try:
757 await resp.start(conn)
758 except BaseException:
759 resp.close()
760 raise
761 except BaseException:
762 conn.close()
763 raise
764 return resp
766 # Apply middleware (if any) - per-request middleware overrides session middleware
767 effective_middlewares = (
768 self._middlewares if middlewares is None else middlewares
769 )
771 if effective_middlewares:
772 handler = build_client_middlewares(
773 _connect_and_send_request, effective_middlewares
774 )
775 else:
776 handler = _connect_and_send_request
778 try:
779 resp = await handler(req)
780 # Client connector errors should not be retried
781 except (
782 ConnectionTimeoutError,
783 ClientConnectorError,
784 ClientConnectorCertificateError,
785 ClientConnectorSSLError,
786 ):
787 raise
788 except (ClientOSError, ServerDisconnectedError):
789 if retry_persistent_connection:
790 retry_persistent_connection = False
791 continue
792 raise
793 except ClientError:
794 raise
795 except OSError as exc:
796 if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
797 raise
798 raise ClientOSError(*exc.args) from exc
800 # Update cookies from raw headers to preserve duplicates
801 if resp._raw_cookie_headers:
802 self._cookie_jar.update_cookies_from_headers(
803 resp._raw_cookie_headers, resp.url
804 )
806 # redirects
807 if resp.status in (301, 302, 303, 307, 308) and allow_redirects:
809 for trace in traces:
810 await trace.send_request_redirect(
811 method, url.update_query(params), headers, resp
812 )
814 redirects += 1
815 history.append(resp)
816 if max_redirects and redirects >= max_redirects:
817 if req._body is not None:
818 await req._body.close()
819 resp.close()
820 raise TooManyRedirects(
821 history[0].request_info, tuple(history)
822 )
824 # For 301 and 302, mimic IE, now changed in RFC
825 # https://github.com/kennethreitz/requests/pull/269
826 if (resp.status == 303 and resp.method != hdrs.METH_HEAD) or (
827 resp.status in (301, 302) and resp.method == hdrs.METH_POST
828 ):
829 method = hdrs.METH_GET
830 data = None
831 if headers.get(hdrs.CONTENT_LENGTH):
832 headers.pop(hdrs.CONTENT_LENGTH)
833 else:
834 # For 307/308, always preserve the request body
835 # For 301/302 with non-POST methods, preserve the request body
836 # https://www.rfc-editor.org/rfc/rfc9110#section-15.4.3-3.1
837 # Use the existing payload to avoid recreating it from a potentially consumed file
838 data = req._body
840 r_url = resp.headers.get(hdrs.LOCATION) or resp.headers.get(
841 hdrs.URI
842 )
843 if r_url is None:
844 # see github.com/aio-libs/aiohttp/issues/2022
845 break
846 else:
847 # reading from correct redirection
848 # response is forbidden
849 resp.release()
851 try:
852 parsed_redirect_url = URL(
853 r_url, encoded=not self._requote_redirect_url
854 )
855 except ValueError as e:
856 if req._body is not None:
857 await req._body.close()
858 resp.close()
859 raise InvalidUrlRedirectClientError(
860 r_url,
861 "Server attempted redirecting to a location that does not look like a URL",
862 ) from e
864 scheme = parsed_redirect_url.scheme
865 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET:
866 if req._body is not None:
867 await req._body.close()
868 resp.close()
869 raise NonHttpUrlRedirectClientError(r_url)
870 elif not scheme:
871 parsed_redirect_url = url.join(parsed_redirect_url)
873 try:
874 redirect_origin = parsed_redirect_url.origin()
875 except ValueError as origin_val_err:
876 if req._body is not None:
877 await req._body.close()
878 resp.close()
879 raise InvalidUrlRedirectClientError(
880 parsed_redirect_url,
881 "Invalid redirect URL origin",
882 ) from origin_val_err
884 if url.origin() != redirect_origin:
885 auth = None
886 headers.pop(hdrs.AUTHORIZATION, None)
888 url = parsed_redirect_url
889 params = {}
890 resp.release()
891 continue
893 break
895 if req._body is not None:
896 await req._body.close()
897 # check response status
898 if raise_for_status is None:
899 raise_for_status = self._raise_for_status
901 if raise_for_status is None:
902 pass
903 elif callable(raise_for_status):
904 await raise_for_status(resp)
905 elif raise_for_status:
906 resp.raise_for_status()
908 # register connection
909 if handle is not None:
910 if resp.connection is not None:
911 resp.connection.add_callback(handle.cancel)
912 else:
913 handle.cancel()
915 resp._history = tuple(history)
917 for trace in traces:
918 await trace.send_request_end(
919 method, url.update_query(params), headers, resp
920 )
921 return resp
923 except BaseException as e:
924 # cleanup timer
925 tm.close()
926 if handle:
927 handle.cancel()
928 handle = None
930 for trace in traces:
931 await trace.send_request_exception(
932 method, url.update_query(params), headers, e
933 )
934 raise
936 def ws_connect(
937 self,
938 url: StrOrURL,
939 *,
940 method: str = hdrs.METH_GET,
941 protocols: Iterable[str] = (),
942 timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel,
943 receive_timeout: Optional[float] = None,
944 autoclose: bool = True,
945 autoping: bool = True,
946 heartbeat: Optional[float] = None,
947 auth: Optional[BasicAuth] = None,
948 origin: Optional[str] = None,
949 params: Query = None,
950 headers: Optional[LooseHeaders] = None,
951 proxy: Optional[StrOrURL] = None,
952 proxy_auth: Optional[BasicAuth] = None,
953 ssl: Union[SSLContext, bool, Fingerprint] = True,
954 verify_ssl: Optional[bool] = None,
955 fingerprint: Optional[bytes] = None,
956 ssl_context: Optional[SSLContext] = None,
957 server_hostname: Optional[str] = None,
958 proxy_headers: Optional[LooseHeaders] = None,
959 compress: int = 0,
960 max_msg_size: int = 4 * 1024 * 1024,
961 ) -> "_WSRequestContextManager":
962 """Initiate websocket connection."""
963 return _WSRequestContextManager(
964 self._ws_connect(
965 url,
966 method=method,
967 protocols=protocols,
968 timeout=timeout,
969 receive_timeout=receive_timeout,
970 autoclose=autoclose,
971 autoping=autoping,
972 heartbeat=heartbeat,
973 auth=auth,
974 origin=origin,
975 params=params,
976 headers=headers,
977 proxy=proxy,
978 proxy_auth=proxy_auth,
979 ssl=ssl,
980 verify_ssl=verify_ssl,
981 fingerprint=fingerprint,
982 ssl_context=ssl_context,
983 server_hostname=server_hostname,
984 proxy_headers=proxy_headers,
985 compress=compress,
986 max_msg_size=max_msg_size,
987 )
988 )
990 async def _ws_connect(
991 self,
992 url: StrOrURL,
993 *,
994 method: str = hdrs.METH_GET,
995 protocols: Iterable[str] = (),
996 timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel,
997 receive_timeout: Optional[float] = None,
998 autoclose: bool = True,
999 autoping: bool = True,
1000 heartbeat: Optional[float] = None,
1001 auth: Optional[BasicAuth] = None,
1002 origin: Optional[str] = None,
1003 params: Query = None,
1004 headers: Optional[LooseHeaders] = None,
1005 proxy: Optional[StrOrURL] = None,
1006 proxy_auth: Optional[BasicAuth] = None,
1007 ssl: Union[SSLContext, bool, Fingerprint] = True,
1008 verify_ssl: Optional[bool] = None,
1009 fingerprint: Optional[bytes] = None,
1010 ssl_context: Optional[SSLContext] = None,
1011 server_hostname: Optional[str] = None,
1012 proxy_headers: Optional[LooseHeaders] = None,
1013 compress: int = 0,
1014 max_msg_size: int = 4 * 1024 * 1024,
1015 ) -> ClientWebSocketResponse:
1016 if timeout is not sentinel:
1017 if isinstance(timeout, ClientWSTimeout):
1018 ws_timeout = timeout
1019 else:
1020 warnings.warn(
1021 "parameter 'timeout' of type 'float' "
1022 "is deprecated, please use "
1023 "'timeout=ClientWSTimeout(ws_close=...)'",
1024 DeprecationWarning,
1025 stacklevel=2,
1026 )
1027 ws_timeout = ClientWSTimeout(ws_close=timeout)
1028 else:
1029 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT
1030 if receive_timeout is not None:
1031 warnings.warn(
1032 "float parameter 'receive_timeout' "
1033 "is deprecated, please use parameter "
1034 "'timeout=ClientWSTimeout(ws_receive=...)'",
1035 DeprecationWarning,
1036 stacklevel=2,
1037 )
1038 ws_timeout = attr.evolve(ws_timeout, ws_receive=receive_timeout)
1040 if headers is None:
1041 real_headers: CIMultiDict[str] = CIMultiDict()
1042 else:
1043 real_headers = CIMultiDict(headers)
1045 default_headers = {
1046 hdrs.UPGRADE: "websocket",
1047 hdrs.CONNECTION: "Upgrade",
1048 hdrs.SEC_WEBSOCKET_VERSION: "13",
1049 }
1051 for key, value in default_headers.items():
1052 real_headers.setdefault(key, value)
1054 sec_key = base64.b64encode(os.urandom(16))
1055 real_headers[hdrs.SEC_WEBSOCKET_KEY] = sec_key.decode()
1057 if protocols:
1058 real_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ",".join(protocols)
1059 if origin is not None:
1060 real_headers[hdrs.ORIGIN] = origin
1061 if compress:
1062 extstr = ws_ext_gen(compress=compress)
1063 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr
1065 # For the sake of backward compatibility, if user passes in None, convert it to True
1066 if ssl is None:
1067 warnings.warn(
1068 "ssl=None is deprecated, please use ssl=True",
1069 DeprecationWarning,
1070 stacklevel=2,
1071 )
1072 ssl = True
1073 ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint)
1075 # send request
1076 resp = await self.request(
1077 method,
1078 url,
1079 params=params,
1080 headers=real_headers,
1081 read_until_eof=False,
1082 auth=auth,
1083 proxy=proxy,
1084 proxy_auth=proxy_auth,
1085 ssl=ssl,
1086 server_hostname=server_hostname,
1087 proxy_headers=proxy_headers,
1088 )
1090 try:
1091 # check handshake
1092 if resp.status != 101:
1093 raise WSServerHandshakeError(
1094 resp.request_info,
1095 resp.history,
1096 message="Invalid response status",
1097 status=resp.status,
1098 headers=resp.headers,
1099 )
1101 if resp.headers.get(hdrs.UPGRADE, "").lower() != "websocket":
1102 raise WSServerHandshakeError(
1103 resp.request_info,
1104 resp.history,
1105 message="Invalid upgrade header",
1106 status=resp.status,
1107 headers=resp.headers,
1108 )
1110 if resp.headers.get(hdrs.CONNECTION, "").lower() != "upgrade":
1111 raise WSServerHandshakeError(
1112 resp.request_info,
1113 resp.history,
1114 message="Invalid connection header",
1115 status=resp.status,
1116 headers=resp.headers,
1117 )
1119 # key calculation
1120 r_key = resp.headers.get(hdrs.SEC_WEBSOCKET_ACCEPT, "")
1121 match = base64.b64encode(hashlib.sha1(sec_key + WS_KEY).digest()).decode()
1122 if r_key != match:
1123 raise WSServerHandshakeError(
1124 resp.request_info,
1125 resp.history,
1126 message="Invalid challenge response",
1127 status=resp.status,
1128 headers=resp.headers,
1129 )
1131 # websocket protocol
1132 protocol = None
1133 if protocols and hdrs.SEC_WEBSOCKET_PROTOCOL in resp.headers:
1134 resp_protocols = [
1135 proto.strip()
1136 for proto in resp.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",")
1137 ]
1139 for proto in resp_protocols:
1140 if proto in protocols:
1141 protocol = proto
1142 break
1144 # websocket compress
1145 notakeover = False
1146 if compress:
1147 compress_hdrs = resp.headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)
1148 if compress_hdrs:
1149 try:
1150 compress, notakeover = ws_ext_parse(compress_hdrs)
1151 except WSHandshakeError as exc:
1152 raise WSServerHandshakeError(
1153 resp.request_info,
1154 resp.history,
1155 message=exc.args[0],
1156 status=resp.status,
1157 headers=resp.headers,
1158 ) from exc
1159 else:
1160 compress = 0
1161 notakeover = False
1163 conn = resp.connection
1164 assert conn is not None
1165 conn_proto = conn.protocol
1166 assert conn_proto is not None
1168 # For WS connection the read_timeout must be either receive_timeout or greater
1169 # None == no timeout, i.e. infinite timeout, so None is the max timeout possible
1170 if ws_timeout.ws_receive is None:
1171 # Reset regardless
1172 conn_proto.read_timeout = None
1173 elif conn_proto.read_timeout is not None:
1174 conn_proto.read_timeout = max(
1175 ws_timeout.ws_receive, conn_proto.read_timeout
1176 )
1178 transport = conn.transport
1179 assert transport is not None
1180 reader = WebSocketDataQueue(conn_proto, 2**16, loop=self._loop)
1181 conn_proto.set_parser(WebSocketReader(reader, max_msg_size), reader)
1182 writer = WebSocketWriter(
1183 conn_proto,
1184 transport,
1185 use_mask=True,
1186 compress=compress,
1187 notakeover=notakeover,
1188 )
1189 except BaseException:
1190 resp.close()
1191 raise
1192 else:
1193 return self._ws_response_class(
1194 reader,
1195 writer,
1196 protocol,
1197 resp,
1198 ws_timeout,
1199 autoclose,
1200 autoping,
1201 self._loop,
1202 heartbeat=heartbeat,
1203 compress=compress,
1204 client_notakeover=notakeover,
1205 )
1207 def _prepare_headers(self, headers: Optional[LooseHeaders]) -> "CIMultiDict[str]":
1208 """Add default headers and transform it to CIMultiDict"""
1209 # Convert headers to MultiDict
1210 result = CIMultiDict(self._default_headers)
1211 if headers:
1212 if not isinstance(headers, (MultiDictProxy, MultiDict)):
1213 headers = CIMultiDict(headers)
1214 added_names: Set[str] = set()
1215 for key, value in headers.items():
1216 if key in added_names:
1217 result.add(key, value)
1218 else:
1219 result[key] = value
1220 added_names.add(key)
1221 return result
1223 def _get_netrc_auth(self, host: str) -> Optional[BasicAuth]:
1224 """
1225 Get auth from netrc for the given host.
1227 This method is designed to be called in an executor to avoid
1228 blocking I/O in the event loop.
1229 """
1230 netrc_obj = netrc_from_env()
1231 try:
1232 return basicauth_from_netrc(netrc_obj, host)
1233 except LookupError:
1234 return None
1236 if sys.version_info >= (3, 11) and TYPE_CHECKING:
1238 def get(
1239 self,
1240 url: StrOrURL,
1241 **kwargs: Unpack[_RequestOptions],
1242 ) -> "_RequestContextManager": ...
1244 def options(
1245 self,
1246 url: StrOrURL,
1247 **kwargs: Unpack[_RequestOptions],
1248 ) -> "_RequestContextManager": ...
1250 def head(
1251 self,
1252 url: StrOrURL,
1253 **kwargs: Unpack[_RequestOptions],
1254 ) -> "_RequestContextManager": ...
1256 def post(
1257 self,
1258 url: StrOrURL,
1259 **kwargs: Unpack[_RequestOptions],
1260 ) -> "_RequestContextManager": ...
1262 def put(
1263 self,
1264 url: StrOrURL,
1265 **kwargs: Unpack[_RequestOptions],
1266 ) -> "_RequestContextManager": ...
1268 def patch(
1269 self,
1270 url: StrOrURL,
1271 **kwargs: Unpack[_RequestOptions],
1272 ) -> "_RequestContextManager": ...
1274 def delete(
1275 self,
1276 url: StrOrURL,
1277 **kwargs: Unpack[_RequestOptions],
1278 ) -> "_RequestContextManager": ...
1280 else:
1282 def get(
1283 self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any
1284 ) -> "_RequestContextManager":
1285 """Perform HTTP GET request."""
1286 return _RequestContextManager(
1287 self._request(
1288 hdrs.METH_GET, url, allow_redirects=allow_redirects, **kwargs
1289 )
1290 )
1292 def options(
1293 self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any
1294 ) -> "_RequestContextManager":
1295 """Perform HTTP OPTIONS request."""
1296 return _RequestContextManager(
1297 self._request(
1298 hdrs.METH_OPTIONS, url, allow_redirects=allow_redirects, **kwargs
1299 )
1300 )
1302 def head(
1303 self, url: StrOrURL, *, allow_redirects: bool = False, **kwargs: Any
1304 ) -> "_RequestContextManager":
1305 """Perform HTTP HEAD request."""
1306 return _RequestContextManager(
1307 self._request(
1308 hdrs.METH_HEAD, url, allow_redirects=allow_redirects, **kwargs
1309 )
1310 )
1312 def post(
1313 self, url: StrOrURL, *, data: Any = None, **kwargs: Any
1314 ) -> "_RequestContextManager":
1315 """Perform HTTP POST request."""
1316 return _RequestContextManager(
1317 self._request(hdrs.METH_POST, url, data=data, **kwargs)
1318 )
1320 def put(
1321 self, url: StrOrURL, *, data: Any = None, **kwargs: Any
1322 ) -> "_RequestContextManager":
1323 """Perform HTTP PUT request."""
1324 return _RequestContextManager(
1325 self._request(hdrs.METH_PUT, url, data=data, **kwargs)
1326 )
1328 def patch(
1329 self, url: StrOrURL, *, data: Any = None, **kwargs: Any
1330 ) -> "_RequestContextManager":
1331 """Perform HTTP PATCH request."""
1332 return _RequestContextManager(
1333 self._request(hdrs.METH_PATCH, url, data=data, **kwargs)
1334 )
1336 def delete(self, url: StrOrURL, **kwargs: Any) -> "_RequestContextManager":
1337 """Perform HTTP DELETE request."""
1338 return _RequestContextManager(
1339 self._request(hdrs.METH_DELETE, url, **kwargs)
1340 )
1342 async def close(self) -> None:
1343 """Close underlying connector.
1345 Release all acquired resources.
1346 """
1347 if not self.closed:
1348 if self._connector is not None and self._connector_owner:
1349 await self._connector.close()
1350 self._connector = None
1352 @property
1353 def closed(self) -> bool:
1354 """Is client session closed.
1356 A readonly property.
1357 """
1358 return self._connector is None or self._connector.closed
1360 @property
1361 def connector(self) -> Optional[BaseConnector]:
1362 """Connector instance used for the session."""
1363 return self._connector
1365 @property
1366 def cookie_jar(self) -> AbstractCookieJar:
1367 """The session cookies."""
1368 return self._cookie_jar
1370 @property
1371 def version(self) -> Tuple[int, int]:
1372 """The session HTTP protocol version."""
1373 return self._version
1375 @property
1376 def requote_redirect_url(self) -> bool:
1377 """Do URL requoting on redirection handling."""
1378 return self._requote_redirect_url
1380 @requote_redirect_url.setter
1381 def requote_redirect_url(self, val: bool) -> None:
1382 """Do URL requoting on redirection handling."""
1383 warnings.warn(
1384 "session.requote_redirect_url modification is deprecated #2778",
1385 DeprecationWarning,
1386 stacklevel=2,
1387 )
1388 self._requote_redirect_url = val
1390 @property
1391 def loop(self) -> asyncio.AbstractEventLoop:
1392 """Session's loop."""
1393 warnings.warn(
1394 "client.loop property is deprecated", DeprecationWarning, stacklevel=2
1395 )
1396 return self._loop
1398 @property
1399 def timeout(self) -> ClientTimeout:
1400 """Timeout for the session."""
1401 return self._timeout
1403 @property
1404 def headers(self) -> "CIMultiDict[str]":
1405 """The default headers of the client session."""
1406 return self._default_headers
1408 @property
1409 def skip_auto_headers(self) -> FrozenSet[istr]:
1410 """Headers for which autogeneration should be skipped"""
1411 return self._skip_auto_headers
1413 @property
1414 def auth(self) -> Optional[BasicAuth]:
1415 """An object that represents HTTP Basic Authorization"""
1416 return self._default_auth
1418 @property
1419 def json_serialize(self) -> JSONEncoder:
1420 """Json serializer callable"""
1421 return self._json_serialize
1423 @property
1424 def connector_owner(self) -> bool:
1425 """Should connector be closed on session closing"""
1426 return self._connector_owner
1428 @property
1429 def raise_for_status(
1430 self,
1431 ) -> Union[bool, Callable[[ClientResponse], Awaitable[None]]]:
1432 """Should `ClientResponse.raise_for_status()` be called for each response."""
1433 return self._raise_for_status
1435 @property
1436 def auto_decompress(self) -> bool:
1437 """Should the body response be automatically decompressed."""
1438 return self._auto_decompress
1440 @property
1441 def trust_env(self) -> bool:
1442 """
1443 Should proxies information from environment or netrc be trusted.
1445 Information is from HTTP_PROXY / HTTPS_PROXY environment variables
1446 or ~/.netrc file if present.
1447 """
1448 return self._trust_env
1450 @property
1451 def trace_configs(self) -> List[TraceConfig]:
1452 """A list of TraceConfig instances used for client tracing"""
1453 return self._trace_configs
1455 def detach(self) -> None:
1456 """Detach connector from session without closing the former.
1458 Session is switched to closed state anyway.
1459 """
1460 self._connector = None
1462 def __enter__(self) -> None:
1463 raise TypeError("Use async with instead")
1465 def __exit__(
1466 self,
1467 exc_type: Optional[Type[BaseException]],
1468 exc_val: Optional[BaseException],
1469 exc_tb: Optional[TracebackType],
1470 ) -> None:
1471 # __exit__ should exist in pair with __enter__ but never executed
1472 pass # pragma: no cover
1474 async def __aenter__(self) -> "ClientSession":
1475 return self
1477 async def __aexit__(
1478 self,
1479 exc_type: Optional[Type[BaseException]],
1480 exc_val: Optional[BaseException],
1481 exc_tb: Optional[TracebackType],
1482 ) -> None:
1483 await self.close()
1486class _BaseRequestContextManager(Coroutine[Any, Any, _RetType], Generic[_RetType]):
1488 __slots__ = ("_coro", "_resp")
1490 def __init__(self, coro: Coroutine["asyncio.Future[Any]", None, _RetType]) -> None:
1491 self._coro: Coroutine["asyncio.Future[Any]", None, _RetType] = coro
1493 def send(self, arg: None) -> "asyncio.Future[Any]":
1494 return self._coro.send(arg)
1496 def throw(self, *args: Any, **kwargs: Any) -> "asyncio.Future[Any]":
1497 return self._coro.throw(*args, **kwargs)
1499 def close(self) -> None:
1500 return self._coro.close()
1502 def __await__(self) -> Generator[Any, None, _RetType]:
1503 ret = self._coro.__await__()
1504 return ret
1506 def __iter__(self) -> Generator[Any, None, _RetType]:
1507 return self.__await__()
1509 async def __aenter__(self) -> _RetType:
1510 self._resp: _RetType = await self._coro
1511 return await self._resp.__aenter__()
1513 async def __aexit__(
1514 self,
1515 exc_type: Optional[Type[BaseException]],
1516 exc: Optional[BaseException],
1517 tb: Optional[TracebackType],
1518 ) -> None:
1519 await self._resp.__aexit__(exc_type, exc, tb)
1522_RequestContextManager = _BaseRequestContextManager[ClientResponse]
1523_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse]
1526class _SessionRequestContextManager:
1528 __slots__ = ("_coro", "_resp", "_session")
1530 def __init__(
1531 self,
1532 coro: Coroutine["asyncio.Future[Any]", None, ClientResponse],
1533 session: ClientSession,
1534 ) -> None:
1535 self._coro = coro
1536 self._resp: Optional[ClientResponse] = None
1537 self._session = session
1539 async def __aenter__(self) -> ClientResponse:
1540 try:
1541 self._resp = await self._coro
1542 except BaseException:
1543 await self._session.close()
1544 raise
1545 else:
1546 return self._resp
1548 async def __aexit__(
1549 self,
1550 exc_type: Optional[Type[BaseException]],
1551 exc: Optional[BaseException],
1552 tb: Optional[TracebackType],
1553 ) -> None:
1554 assert self._resp is not None
1555 self._resp.close()
1556 await self._session.close()
1559if sys.version_info >= (3, 11) and TYPE_CHECKING:
1561 def request(
1562 method: str,
1563 url: StrOrURL,
1564 *,
1565 version: HttpVersion = http.HttpVersion11,
1566 connector: Optional[BaseConnector] = None,
1567 loop: Optional[asyncio.AbstractEventLoop] = None,
1568 **kwargs: Unpack[_RequestOptions],
1569 ) -> _SessionRequestContextManager: ...
1571else:
1573 def request(
1574 method: str,
1575 url: StrOrURL,
1576 *,
1577 version: HttpVersion = http.HttpVersion11,
1578 connector: Optional[BaseConnector] = None,
1579 loop: Optional[asyncio.AbstractEventLoop] = None,
1580 **kwargs: Any,
1581 ) -> _SessionRequestContextManager:
1582 """Constructs and sends a request.
1584 Returns response object.
1585 method - HTTP method
1586 url - request url
1587 params - (optional) Dictionary or bytes to be sent in the query
1588 string of the new request
1589 data - (optional) Dictionary, bytes, or file-like object to
1590 send in the body of the request
1591 json - (optional) Any json compatible python object
1592 headers - (optional) Dictionary of HTTP Headers to send with
1593 the request
1594 cookies - (optional) Dict object to send with the request
1595 auth - (optional) BasicAuth named tuple represent HTTP Basic Auth
1596 auth - aiohttp.helpers.BasicAuth
1597 allow_redirects - (optional) If set to False, do not follow
1598 redirects
1599 version - Request HTTP version.
1600 compress - Set to True if request has to be compressed
1601 with deflate encoding.
1602 chunked - Set to chunk size for chunked transfer encoding.
1603 expect100 - Expect 100-continue response from server.
1604 connector - BaseConnector sub-class instance to support
1605 connection pooling.
1606 read_until_eof - Read response until eof if response
1607 does not have Content-Length header.
1608 loop - Optional event loop.
1609 timeout - Optional ClientTimeout settings structure, 5min
1610 total timeout by default.
1611 Usage::
1612 >>> import aiohttp
1613 >>> async with aiohttp.request('GET', 'http://python.org/') as resp:
1614 ... print(resp)
1615 ... data = await resp.read()
1616 <ClientResponse(https://www.python.org/) [200 OK]>
1617 """
1618 connector_owner = False
1619 if connector is None:
1620 connector_owner = True
1621 connector = TCPConnector(loop=loop, force_close=True)
1623 session = ClientSession(
1624 loop=loop,
1625 cookies=kwargs.pop("cookies", None),
1626 version=version,
1627 timeout=kwargs.pop("timeout", sentinel),
1628 connector=connector,
1629 connector_owner=connector_owner,
1630 )
1632 return _SessionRequestContextManager(
1633 session._request(method, url, **kwargs),
1634 session,
1635 )