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