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