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