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