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 if proxy is None:
553 proxy_headers = None
554 else:
555 proxy_headers = self._prepare_headers(proxy_headers)
556 try:
557 proxy = URL(proxy)
558 except ValueError as e:
559 raise InvalidURL(proxy) from e
561 if timeout is sentinel or timeout is None:
562 real_timeout: ClientTimeout = self._timeout
563 else:
564 real_timeout = timeout
565 # timeout is cumulative for all request operations
566 # (request, redirects, responses, data consuming)
567 tm = TimeoutHandle(
568 self._loop, real_timeout.total, ceil_threshold=real_timeout.ceil_threshold
569 )
570 handle = tm.start()
572 if read_bufsize is None:
573 read_bufsize = self._read_bufsize
575 if auto_decompress is None:
576 auto_decompress = self._auto_decompress
578 if max_line_size is None:
579 max_line_size = self._max_line_size
581 if max_field_size is None:
582 max_field_size = self._max_field_size
584 if max_headers is None:
585 max_headers = self._max_headers
587 traces = [
588 Trace(
589 self,
590 trace_config,
591 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx),
592 )
593 for trace_config in self._trace_configs
594 ]
596 for trace in traces:
597 await trace.send_request_start(method, url.update_query(params), headers)
599 timer = tm.timer()
600 try:
601 with timer:
602 # https://www.rfc-editor.org/rfc/rfc9112.html#name-retrying-requests
603 retry_persistent_connection = (
604 self._retry_connection and method in IDEMPOTENT_METHODS
605 )
606 while True:
607 url, auth_from_url = strip_auth_from_url(url)
608 if not url.raw_host:
609 # NOTE: Bail early, otherwise, causes `InvalidURL` through
610 # NOTE: `self._request_class()` below.
611 err_exc_cls = (
612 InvalidUrlRedirectClientError
613 if redirects
614 else InvalidUrlClientError
615 )
616 raise err_exc_cls(url)
618 if auth_from_url is not None:
619 # URL-embedded credentials override any Authorization
620 # header already present (e.g. carried from a previous
621 # redirect). On the initial request, refuse to silently
622 # shadow an explicit Authorization header.
623 if not history and hdrs.AUTHORIZATION in headers:
624 raise ValueError(
625 "Cannot combine AUTHORIZATION header with "
626 "credentials encoded in URL"
627 )
628 headers[hdrs.AUTHORIZATION] = auth_from_url
629 elif (
630 self._trust_env
631 and url.host is not None
632 and hdrs.AUTHORIZATION not in headers
633 ):
634 # Fall back to ~/.netrc credentials when trust_env is set.
635 netrc_auth = await self._loop.run_in_executor(
636 None, self._get_netrc_auth, url.host
637 )
638 if netrc_auth is not None:
639 headers[hdrs.AUTHORIZATION] = netrc_auth
641 all_cookies = self._cookie_jar.filter_cookies(url)
643 if cookies is not None:
644 tmp_cookie_jar = CookieJar(
645 unsafe=self._cookie_jar.unsafe,
646 quote_cookie=self._cookie_jar.quote_cookie,
647 )
648 tmp_cookie_jar.update_cookies(cookies)
649 req_cookies = tmp_cookie_jar.filter_cookies(url)
650 if req_cookies:
651 all_cookies.load(req_cookies)
653 proxy_: URL | None = None
654 if proxy is not None:
655 proxy_ = URL(proxy)
656 elif self._trust_env:
657 with suppress(LookupError):
658 proxy_, env_proxy_auth = await asyncio.to_thread(
659 get_env_proxy_for_url, url
660 )
661 if env_proxy_auth is not None and (
662 proxy_headers is None
663 or hdrs.PROXY_AUTHORIZATION not in proxy_headers
664 ):
665 proxy_headers = proxy_headers or CIMultiDict()
666 proxy_headers[hdrs.PROXY_AUTHORIZATION] = env_proxy_auth
668 req = self._request_class(
669 method,
670 url,
671 params=params,
672 headers=headers,
673 skip_auto_headers=skip_headers,
674 data=data,
675 cookies=all_cookies,
676 version=version,
677 compress=compress,
678 chunked=chunked,
679 expect100=expect100,
680 loop=self._loop,
681 response_class=self._response_class,
682 proxy=proxy_,
683 timer=timer,
684 session=self,
685 ssl=ssl,
686 server_hostname=server_hostname,
687 proxy_headers=proxy_headers,
688 traces=traces,
689 trust_env=self.trust_env,
690 )
692 async def _connect_and_send_request(
693 req: ClientRequest,
694 ) -> ClientResponse:
695 # connection timeout
696 assert self._connector is not None
697 try:
698 conn = await self._connector.connect(
699 req, traces=traces, timeout=real_timeout
700 )
701 except asyncio.TimeoutError as exc:
702 raise ConnectionTimeoutError(
703 f"Connection timeout to host {req.url}"
704 ) from exc
706 assert conn.protocol is not None
707 conn.protocol.set_response_params(
708 timer=timer,
709 skip_payload=req.method in EMPTY_BODY_METHODS,
710 read_until_eof=read_until_eof,
711 auto_decompress=auto_decompress,
712 read_timeout=real_timeout.sock_read,
713 read_bufsize=read_bufsize,
714 timeout_ceil_threshold=self._connector._timeout_ceil_threshold,
715 max_line_size=max_line_size,
716 max_field_size=max_field_size,
717 max_headers=max_headers,
718 )
719 try:
720 resp = await req._send(conn)
721 try:
722 await resp.start(conn)
723 except BaseException:
724 resp.close()
725 raise
726 except BaseException:
727 conn.close()
728 raise
729 return resp
731 # Apply middleware (if any) - per-request middleware overrides session middleware
732 effective_middlewares = (
733 self._middlewares if middlewares is None else middlewares
734 )
736 if effective_middlewares:
737 handler = build_client_middlewares(
738 _connect_and_send_request, effective_middlewares
739 )
740 else:
741 handler = _connect_and_send_request
743 try:
744 resp = await handler(req)
745 # Client connector errors should not be retried
746 except (
747 ConnectionTimeoutError,
748 ClientConnectorError,
749 ClientConnectorCertificateError,
750 ClientConnectorSSLError,
751 ):
752 raise
753 except (ClientOSError, ServerDisconnectedError):
754 if retry_persistent_connection:
755 retry_persistent_connection = False
756 continue
757 raise
758 except ClientError:
759 raise
760 except OSError as exc:
761 if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
762 raise
763 raise ClientOSError(*exc.args) from exc
765 # Update cookies from raw headers to preserve duplicates
766 if resp._raw_cookie_headers:
767 self._cookie_jar.update_cookies_from_headers(
768 resp._raw_cookie_headers, resp.url
769 )
771 # redirects
772 if resp.status in (301, 302, 303, 307, 308) and allow_redirects:
773 for trace in traces:
774 await trace.send_request_redirect(
775 method, url.update_query(params), headers, resp
776 )
778 redirects += 1
779 history.append(resp)
780 if max_redirects and redirects >= max_redirects:
781 if req._body is not None:
782 await req._body.close()
783 resp.close()
784 raise TooManyRedirects(
785 history[0].request_info, tuple(history)
786 )
788 # For 301 and 302, mimic IE, now changed in RFC
789 # https://github.com/kennethreitz/requests/pull/269
790 if (resp.status == 303 and resp.method != hdrs.METH_HEAD) or (
791 resp.status in (301, 302) and resp.method == hdrs.METH_POST
792 ):
793 method = hdrs.METH_GET
794 data = None
795 if headers.get(hdrs.CONTENT_LENGTH):
796 headers.pop(hdrs.CONTENT_LENGTH)
797 else:
798 # For 307/308, always preserve the request body
799 # For 301/302 with non-POST methods, preserve the request body
800 # https://www.rfc-editor.org/rfc/rfc9110#section-15.4.3-3.1
801 # Use the existing payload to avoid recreating it from
802 # a potentially consumed file.
803 #
804 # If the payload is already consumed and cannot be replayed,
805 # fail fast instead of silently sending an empty body.
806 if req._body.consumed:
807 resp.close()
808 raise ClientPayloadError(
809 "Cannot follow redirect with a consumed request "
810 "body. Use bytes, a seekable file-like object, "
811 "or set allow_redirects=False."
812 )
813 data = req._body
815 r_url = resp.headers.get(hdrs.LOCATION) or resp.headers.get(
816 hdrs.URI
817 )
818 if r_url is None:
819 # see github.com/aio-libs/aiohttp/issues/2022
820 break
821 else:
822 # reading from correct redirection
823 # response is forbidden
824 resp.release()
826 try:
827 parsed_redirect_url = URL(
828 r_url, encoded=not self._requote_redirect_url
829 )
830 except ValueError as e:
831 if req._body is not None:
832 await req._body.close()
833 resp.close()
834 raise InvalidUrlRedirectClientError(
835 r_url,
836 "Server attempted redirecting to a location that does not look like a URL",
837 ) from e
839 scheme = parsed_redirect_url.scheme
840 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET:
841 if req._body is not None:
842 await req._body.close()
843 resp.close()
844 raise NonHttpUrlRedirectClientError(r_url)
845 elif not scheme:
846 parsed_redirect_url = url.join(parsed_redirect_url)
848 try:
849 redirect_origin = parsed_redirect_url.origin()
850 except ValueError as origin_val_err:
851 if req._body is not None:
852 await req._body.close()
853 resp.close()
854 raise InvalidUrlRedirectClientError(
855 parsed_redirect_url,
856 "Invalid redirect URL origin",
857 ) from origin_val_err
859 if url.origin() != redirect_origin:
860 cookies = None
861 headers.pop(hdrs.AUTHORIZATION, None)
862 headers.pop(hdrs.COOKIE, None)
863 headers.pop(hdrs.PROXY_AUTHORIZATION, None)
865 url = parsed_redirect_url
866 params = {}
867 resp.release()
868 continue
870 break
872 if req._body is not None:
873 await req._body.close()
874 # check response status
875 if raise_for_status is None:
876 raise_for_status = self._raise_for_status
878 if callable(raise_for_status):
879 await raise_for_status(resp)
880 elif raise_for_status:
881 resp.raise_for_status()
883 # register connection
884 if handle is not None:
885 if resp.connection is not None:
886 resp.connection.add_callback(handle.cancel)
887 else:
888 handle.cancel()
890 resp._history = tuple(history)
892 for trace in traces:
893 await trace.send_request_end(
894 method, url.update_query(params), headers, resp
895 )
896 return resp
898 except BaseException as e:
899 # cleanup timer
900 tm.close()
901 if handle:
902 handle.cancel()
903 handle = None
905 for trace in traces:
906 await trace.send_request_exception(
907 method, url.update_query(params), headers, e
908 )
909 raise
911 if sys.version_info >= (3, 11) and TYPE_CHECKING:
913 @overload
914 def ws_connect(
915 self,
916 url: StrOrURL,
917 *,
918 decode_text: Literal[True] = ...,
919 **kwargs: Unpack[_WSConnectOptions],
920 ) -> "_BaseRequestContextManager[ClientWebSocketResponse[Literal[True]]]": ...
922 @overload
923 def ws_connect(
924 self,
925 url: StrOrURL,
926 *,
927 decode_text: Literal[False],
928 **kwargs: Unpack[_WSConnectOptions],
929 ) -> "_BaseRequestContextManager[ClientWebSocketResponse[Literal[False]]]": ...
931 @overload
932 def ws_connect(
933 self,
934 url: StrOrURL,
935 *,
936 decode_text: bool = ...,
937 **kwargs: Unpack[_WSConnectOptions],
938 ) -> "_BaseRequestContextManager[ClientWebSocketResponse[bool]]": ...
940 def ws_connect(
941 self,
942 url: StrOrURL,
943 *,
944 method: str = hdrs.METH_GET,
945 protocols: Collection[str] = (),
946 timeout: ClientWSTimeout | _SENTINEL = sentinel,
947 receive_timeout: float | None = None,
948 autoclose: bool = True,
949 autoping: bool = True,
950 heartbeat: float | None = None,
951 origin: str | None = None,
952 params: Query = None,
953 headers: LooseHeaders | None = None,
954 proxy: StrOrURL | None = None,
955 ssl: SSLContext | bool | Fingerprint = True,
956 server_hostname: str | None = None,
957 proxy_headers: LooseHeaders | None = None,
958 compress: int = 0,
959 max_msg_size: int = 4 * 1024 * 1024,
960 decode_text: bool = True,
961 ) -> "_BaseRequestContextManager[ClientWebSocketResponse[bool]]":
962 """Initiate websocket connection."""
963 return _WSRequestContextManager(
964 self._ws_connect(
965 url,
966 method=method,
967 protocols=protocols,
968 timeout=timeout,
969 receive_timeout=receive_timeout,
970 autoclose=autoclose,
971 autoping=autoping,
972 heartbeat=heartbeat,
973 origin=origin,
974 params=params,
975 headers=headers,
976 proxy=proxy,
977 ssl=ssl,
978 server_hostname=server_hostname,
979 proxy_headers=proxy_headers,
980 compress=compress,
981 max_msg_size=max_msg_size,
982 decode_text=decode_text,
983 )
984 )
986 if sys.version_info >= (3, 11) and TYPE_CHECKING:
988 @overload
989 async def _ws_connect(
990 self,
991 url: StrOrURL,
992 *,
993 decode_text: Literal[True] = ...,
994 **kwargs: Unpack[_WSConnectOptions],
995 ) -> "ClientWebSocketResponse[Literal[True]]": ...
997 @overload
998 async def _ws_connect(
999 self,
1000 url: StrOrURL,
1001 *,
1002 decode_text: Literal[False],
1003 **kwargs: Unpack[_WSConnectOptions],
1004 ) -> "ClientWebSocketResponse[Literal[False]]": ...
1006 @overload
1007 async def _ws_connect(
1008 self,
1009 url: StrOrURL,
1010 *,
1011 decode_text: bool = ...,
1012 **kwargs: Unpack[_WSConnectOptions],
1013 ) -> "ClientWebSocketResponse[bool]": ...
1015 async def _ws_connect(
1016 self,
1017 url: StrOrURL,
1018 *,
1019 method: str = hdrs.METH_GET,
1020 protocols: Collection[str] = (),
1021 timeout: ClientWSTimeout | _SENTINEL = sentinel,
1022 receive_timeout: float | None = None,
1023 autoclose: bool = True,
1024 autoping: bool = True,
1025 heartbeat: float | None = None,
1026 origin: str | None = None,
1027 params: Query = None,
1028 headers: LooseHeaders | None = None,
1029 proxy: StrOrURL | None = None,
1030 ssl: SSLContext | bool | Fingerprint = True,
1031 server_hostname: str | None = None,
1032 proxy_headers: LooseHeaders | None = None,
1033 compress: int = 0,
1034 max_msg_size: int = 4 * 1024 * 1024,
1035 decode_text: bool = True,
1036 ) -> "ClientWebSocketResponse[bool]":
1037 if timeout is not sentinel:
1038 if isinstance(timeout, ClientWSTimeout):
1039 ws_timeout = timeout
1040 else:
1041 warnings.warn( # type: ignore[unreachable]
1042 "parameter 'timeout' of type 'float' "
1043 "is deprecated, please use "
1044 "'timeout=ClientWSTimeout(ws_close=...)'",
1045 DeprecationWarning,
1046 stacklevel=2,
1047 )
1048 ws_timeout = ClientWSTimeout(ws_close=timeout)
1049 else:
1050 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT
1051 if receive_timeout is not None:
1052 warnings.warn(
1053 "float parameter 'receive_timeout' "
1054 "is deprecated, please use parameter "
1055 "'timeout=ClientWSTimeout(ws_receive=...)'",
1056 DeprecationWarning,
1057 stacklevel=2,
1058 )
1059 ws_timeout = dataclasses.replace(ws_timeout, ws_receive=receive_timeout)
1061 if headers is None:
1062 real_headers: CIMultiDict[str] = CIMultiDict()
1063 else:
1064 real_headers = CIMultiDict(headers)
1066 default_headers = {
1067 hdrs.UPGRADE: "websocket",
1068 hdrs.CONNECTION: "Upgrade",
1069 hdrs.SEC_WEBSOCKET_VERSION: "13",
1070 }
1072 for key, value in default_headers.items():
1073 real_headers.setdefault(key, value)
1075 sec_key = base64.b64encode(os.urandom(16))
1076 real_headers[hdrs.SEC_WEBSOCKET_KEY] = sec_key.decode()
1078 if protocols:
1079 real_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ",".join(protocols)
1080 if origin is not None:
1081 real_headers[hdrs.ORIGIN] = origin
1082 if compress:
1083 extstr = ws_ext_gen(compress=compress)
1084 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr
1086 if not isinstance(ssl, SSL_ALLOWED_TYPES):
1087 raise TypeError(
1088 "ssl should be SSLContext, Fingerprint, or bool, "
1089 f"got {ssl!r} instead."
1090 )
1092 # send request
1093 resp = await self.request(
1094 method,
1095 url,
1096 params=params,
1097 headers=real_headers,
1098 read_until_eof=False,
1099 proxy=proxy,
1100 ssl=ssl,
1101 server_hostname=server_hostname,
1102 proxy_headers=proxy_headers,
1103 )
1105 try:
1106 # check handshake
1107 if resp.status != 101:
1108 raise WSServerHandshakeError(
1109 resp.request_info,
1110 resp.history,
1111 message="Invalid response status",
1112 status=resp.status,
1113 headers=resp.headers,
1114 )
1116 if resp.headers.get(hdrs.UPGRADE, "").lower() != "websocket":
1117 raise WSServerHandshakeError(
1118 resp.request_info,
1119 resp.history,
1120 message="Invalid upgrade header",
1121 status=resp.status,
1122 headers=resp.headers,
1123 )
1125 if resp.headers.get(hdrs.CONNECTION, "").lower() != "upgrade":
1126 raise WSServerHandshakeError(
1127 resp.request_info,
1128 resp.history,
1129 message="Invalid connection header",
1130 status=resp.status,
1131 headers=resp.headers,
1132 )
1134 # key calculation
1135 r_key = resp.headers.get(hdrs.SEC_WEBSOCKET_ACCEPT, "")
1136 match = base64.b64encode(hashlib.sha1(sec_key + WS_KEY).digest()).decode()
1137 if r_key != match:
1138 raise WSServerHandshakeError(
1139 resp.request_info,
1140 resp.history,
1141 message="Invalid challenge response",
1142 status=resp.status,
1143 headers=resp.headers,
1144 )
1146 # websocket protocol
1147 protocol = None
1148 if protocols and hdrs.SEC_WEBSOCKET_PROTOCOL in resp.headers:
1149 resp_protocols = [
1150 proto.strip()
1151 for proto in resp.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",")
1152 ]
1154 for proto in resp_protocols:
1155 if proto in protocols:
1156 protocol = proto
1157 break
1159 # websocket compress
1160 notakeover = False
1161 if compress:
1162 compress_hdrs = resp.headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)
1163 if compress_hdrs:
1164 try:
1165 compress, notakeover = ws_ext_parse(compress_hdrs)
1166 except WSHandshakeError as exc:
1167 raise WSServerHandshakeError(
1168 resp.request_info,
1169 resp.history,
1170 message=exc.args[0],
1171 status=resp.status,
1172 headers=resp.headers,
1173 ) from exc
1174 else:
1175 compress = 0
1176 notakeover = False
1178 conn = resp.connection
1179 assert conn is not None
1180 conn_proto = conn.protocol
1181 assert conn_proto is not None
1183 # For WS connection the read_timeout must be either ws_timeout.ws_receive or greater
1184 # None == no timeout, i.e. infinite timeout, so None is the max timeout possible
1185 if ws_timeout.ws_receive is None:
1186 # Reset regardless
1187 conn_proto.read_timeout = None
1188 elif conn_proto.read_timeout is not None:
1189 conn_proto.read_timeout = max(
1190 ws_timeout.ws_receive, conn_proto.read_timeout
1191 )
1193 transport = conn.transport
1194 assert transport is not None
1195 reader = WebSocketDataQueue(conn_proto, DEFAULT_CHUNK_SIZE, loop=self._loop)
1196 writer = WebSocketWriter(
1197 conn_proto,
1198 transport,
1199 use_mask=True,
1200 compress=compress,
1201 notakeover=notakeover,
1202 )
1203 except BaseException:
1204 resp.close()
1205 raise
1206 else:
1207 ws_resp = self._ws_response_class(
1208 reader,
1209 writer,
1210 protocol,
1211 resp,
1212 ws_timeout,
1213 autoclose,
1214 autoping,
1215 self._loop,
1216 heartbeat=heartbeat,
1217 compress=compress,
1218 client_notakeover=notakeover,
1219 )
1220 parser = WebSocketReader(reader, max_msg_size, decode_text=decode_text)
1221 cb = None if heartbeat is None else ws_resp._on_data_received
1222 conn_proto.set_parser(parser, reader, data_received_cb=cb)
1223 return ws_resp
1225 def _prepare_headers(self, headers: LooseHeaders | None) -> "CIMultiDict[str]":
1226 """Add default headers and transform it to CIMultiDict"""
1227 # Convert headers to MultiDict
1228 result = CIMultiDict(self._default_headers)
1229 if headers:
1230 if not isinstance(headers, (MultiDictProxy, MultiDict)):
1231 headers = CIMultiDict(headers)
1232 added_names: set[str] = set()
1233 for key, value in headers.items():
1234 if key in added_names:
1235 result.add(key, value)
1236 else:
1237 result[key] = value
1238 added_names.add(key)
1239 return result
1241 def _get_netrc_auth(self, host: str) -> str | None:
1242 """Return an ``Authorization`` header value for ``host`` from netrc.
1244 Designed to be called in an executor to avoid blocking I/O on the
1245 event loop.
1246 """
1247 netrc_obj = netrc_from_env()
1248 try:
1249 return _auth_header_from_netrc(netrc_obj, host)
1250 except LookupError:
1251 return None
1253 if sys.version_info >= (3, 11) and TYPE_CHECKING:
1255 def get(
1256 self,
1257 url: StrOrURL,
1258 **kwargs: Unpack[_RequestOptions],
1259 ) -> "_RequestContextManager": ...
1261 def options(
1262 self,
1263 url: StrOrURL,
1264 **kwargs: Unpack[_RequestOptions],
1265 ) -> "_RequestContextManager": ...
1267 def head(
1268 self,
1269 url: StrOrURL,
1270 **kwargs: Unpack[_RequestOptions],
1271 ) -> "_RequestContextManager": ...
1273 def post(
1274 self,
1275 url: StrOrURL,
1276 **kwargs: Unpack[_RequestOptions],
1277 ) -> "_RequestContextManager": ...
1279 def put(
1280 self,
1281 url: StrOrURL,
1282 **kwargs: Unpack[_RequestOptions],
1283 ) -> "_RequestContextManager": ...
1285 def patch(
1286 self,
1287 url: StrOrURL,
1288 **kwargs: Unpack[_RequestOptions],
1289 ) -> "_RequestContextManager": ...
1291 def delete(
1292 self,
1293 url: StrOrURL,
1294 **kwargs: Unpack[_RequestOptions],
1295 ) -> "_RequestContextManager": ...
1297 else:
1299 def get(
1300 self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any
1301 ) -> "_RequestContextManager":
1302 """Perform HTTP GET request."""
1303 return _RequestContextManager(
1304 self._request(
1305 hdrs.METH_GET, url, allow_redirects=allow_redirects, **kwargs
1306 )
1307 )
1309 def options(
1310 self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any
1311 ) -> "_RequestContextManager":
1312 """Perform HTTP OPTIONS request."""
1313 return _RequestContextManager(
1314 self._request(
1315 hdrs.METH_OPTIONS, url, allow_redirects=allow_redirects, **kwargs
1316 )
1317 )
1319 def head(
1320 self, url: StrOrURL, *, allow_redirects: bool = False, **kwargs: Any
1321 ) -> "_RequestContextManager":
1322 """Perform HTTP HEAD request."""
1323 return _RequestContextManager(
1324 self._request(
1325 hdrs.METH_HEAD, url, allow_redirects=allow_redirects, **kwargs
1326 )
1327 )
1329 def post(
1330 self, url: StrOrURL, *, data: Any = None, **kwargs: Any
1331 ) -> "_RequestContextManager":
1332 """Perform HTTP POST request."""
1333 return _RequestContextManager(
1334 self._request(hdrs.METH_POST, url, data=data, **kwargs)
1335 )
1337 def put(
1338 self, url: StrOrURL, *, data: Any = None, **kwargs: Any
1339 ) -> "_RequestContextManager":
1340 """Perform HTTP PUT request."""
1341 return _RequestContextManager(
1342 self._request(hdrs.METH_PUT, url, data=data, **kwargs)
1343 )
1345 def patch(
1346 self, url: StrOrURL, *, data: Any = None, **kwargs: Any
1347 ) -> "_RequestContextManager":
1348 """Perform HTTP PATCH request."""
1349 return _RequestContextManager(
1350 self._request(hdrs.METH_PATCH, url, data=data, **kwargs)
1351 )
1353 def delete(self, url: StrOrURL, **kwargs: Any) -> "_RequestContextManager":
1354 """Perform HTTP DELETE request."""
1355 return _RequestContextManager(
1356 self._request(hdrs.METH_DELETE, url, **kwargs)
1357 )
1359 async def close(self) -> None:
1360 """Close underlying connector.
1362 Release all acquired resources.
1363 """
1364 if not self.closed:
1365 if self._connector is not None and self._connector_owner:
1366 await self._connector.close()
1367 self._connector = None
1369 @property
1370 def closed(self) -> bool:
1371 """Is client session closed.
1373 A readonly property.
1374 """
1375 return self._connector is None or self._connector.closed
1377 @property
1378 def connector(self) -> BaseConnector | None:
1379 """Connector instance used for the session."""
1380 return self._connector
1382 @property
1383 def cookie_jar(self) -> AbstractCookieJar:
1384 """The session cookies."""
1385 return self._cookie_jar
1387 @property
1388 def version(self) -> tuple[int, int]:
1389 """The session HTTP protocol version."""
1390 return self._version
1392 @property
1393 def requote_redirect_url(self) -> bool:
1394 """Do URL requoting on redirection handling."""
1395 return self._requote_redirect_url
1397 @property
1398 def timeout(self) -> ClientTimeout:
1399 """Timeout for the session."""
1400 return self._timeout
1402 @property
1403 def headers(self) -> "CIMultiDict[str]":
1404 """The default headers of the client session."""
1405 return self._default_headers
1407 @property
1408 def skip_auto_headers(self) -> frozenset[istr]:
1409 """Headers for which autogeneration should be skipped"""
1410 return self._skip_auto_headers
1412 @property
1413 def json_serialize(self) -> JSONEncoder:
1414 """Json serializer callable"""
1415 return self._json_serialize
1417 @property
1418 def connector_owner(self) -> bool:
1419 """Should connector be closed on session closing"""
1420 return self._connector_owner
1422 @property
1423 def raise_for_status(
1424 self,
1425 ) -> bool | Callable[[ClientResponse], Awaitable[None]]:
1426 """Should `ClientResponse.raise_for_status()` be called for each response."""
1427 return self._raise_for_status
1429 @property
1430 def auto_decompress(self) -> bool:
1431 """Should the body response be automatically decompressed."""
1432 return self._auto_decompress
1434 @property
1435 def trust_env(self) -> bool:
1436 """
1437 Should proxies information from environment or netrc be trusted.
1439 Information is from HTTP_PROXY / HTTPS_PROXY environment variables
1440 or ~/.netrc file if present.
1441 """
1442 return self._trust_env
1444 @property
1445 def trace_configs(self) -> list[TraceConfig[Any]]:
1446 """A list of TraceConfig instances used for client tracing"""
1447 return self._trace_configs
1449 def detach(self) -> None:
1450 """Detach connector from session without closing the former.
1452 Session is switched to closed state anyway.
1453 """
1454 self._connector = None
1456 async def __aenter__(self) -> "ClientSession":
1457 return self
1459 async def __aexit__(
1460 self,
1461 exc_type: type[BaseException] | None,
1462 exc_val: BaseException | None,
1463 exc_tb: TracebackType | None,
1464 ) -> None:
1465 await self.close()
1468class _BaseRequestContextManager(
1469 Coroutine[Any, Any, _RetType_co], Generic[_RetType_co]
1470):
1471 __slots__ = ("_coro", "_resp")
1473 def __init__(self, coro: Coroutine[asyncio.Future[Any], None, _RetType_co]) -> None:
1474 self._coro: Coroutine[asyncio.Future[Any], None, _RetType_co] = coro
1476 def send(self, arg: None) -> asyncio.Future[Any]:
1477 return self._coro.send(arg)
1479 def throw(self, *args: Any, **kwargs: Any) -> asyncio.Future[Any]:
1480 return self._coro.throw(*args, **kwargs)
1482 def close(self) -> None:
1483 return self._coro.close()
1485 def __await__(self) -> Generator[Any, None, _RetType_co]:
1486 ret = self._coro.__await__()
1487 return ret
1489 def __iter__(self) -> Generator[Any, None, _RetType_co]:
1490 return self.__await__()
1492 async def __aenter__(self) -> _RetType_co:
1493 self._resp: _RetType_co = await self._coro
1494 return await self._resp.__aenter__() # type: ignore[return-value]
1496 async def __aexit__(
1497 self,
1498 exc_type: type[BaseException] | None,
1499 exc: BaseException | None,
1500 tb: TracebackType | None,
1501 ) -> None:
1502 await self._resp.__aexit__(exc_type, exc, tb)
1505_RequestContextManager = _BaseRequestContextManager[ClientResponse]
1506_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse[bool]]
1509class _SessionRequestContextManager:
1510 __slots__ = ("_coro", "_resp", "_session")
1512 def __init__(
1513 self,
1514 coro: Coroutine[asyncio.Future[Any], None, ClientResponse],
1515 session: ClientSession,
1516 ) -> None:
1517 self._coro = coro
1518 self._resp: ClientResponse | None = None
1519 self._session = session
1521 async def __aenter__(self) -> ClientResponse:
1522 try:
1523 self._resp = await self._coro
1524 except BaseException:
1525 await self._session.close()
1526 raise
1527 else:
1528 return self._resp
1530 async def __aexit__(
1531 self,
1532 exc_type: type[BaseException] | None,
1533 exc: BaseException | None,
1534 tb: TracebackType | None,
1535 ) -> None:
1536 assert self._resp is not None
1537 self._resp.close()
1538 await self._session.close()
1541if sys.version_info >= (3, 11) and TYPE_CHECKING:
1543 def request(
1544 method: str,
1545 url: StrOrURL,
1546 *,
1547 version: HttpVersion = http.HttpVersion11,
1548 connector: BaseConnector | None = None,
1549 **kwargs: Unpack[_RequestOptions],
1550 ) -> _SessionRequestContextManager: ...
1552else:
1554 def request(
1555 method: str,
1556 url: StrOrURL,
1557 *,
1558 version: HttpVersion = http.HttpVersion11,
1559 connector: BaseConnector | None = None,
1560 **kwargs: Any,
1561 ) -> _SessionRequestContextManager:
1562 """Constructs and sends a request.
1564 Returns response object.
1565 method - HTTP method
1566 url - request url
1567 params - (optional) Dictionary or bytes to be sent in the query
1568 string of the new request
1569 data - (optional) Dictionary, bytes, or file-like object to
1570 send in the body of the request
1571 json - (optional) Any json compatible python object
1572 headers - (optional) Dictionary of HTTP Headers to send with
1573 the request
1574 cookies - (optional) Dict object to send with the request
1575 allow_redirects - (optional) If set to False, do not follow
1576 redirects
1577 version - Request HTTP version.
1578 compress - Set to True if request has to be compressed
1579 with deflate encoding.
1580 chunked - Set to chunk size for chunked transfer encoding.
1581 expect100 - Expect 100-continue response from server.
1582 connector - BaseConnector sub-class instance to support
1583 connection pooling.
1584 read_until_eof - Read response until eof if response
1585 does not have Content-Length header.
1586 loop - Optional event loop.
1587 timeout - Optional ClientTimeout settings structure, 5min
1588 total timeout by default.
1589 Usage::
1590 >>> import aiohttp
1591 >>> async with aiohttp.request('GET', 'http://python.org/') as resp:
1592 ... print(resp)
1593 ... data = await resp.read()
1594 <ClientResponse(https://www.python.org/) [200 OK]>
1595 """
1596 connector_owner = False
1597 if connector is None:
1598 connector_owner = True
1599 connector = TCPConnector(force_close=True)
1601 session = ClientSession(
1602 cookies=kwargs.pop("cookies", None),
1603 version=version,
1604 timeout=kwargs.pop("timeout", sentinel),
1605 connector=connector,
1606 connector_owner=connector_owner,
1607 )
1609 return _SessionRequestContextManager(
1610 session._request(method, url, **kwargs),
1611 session,
1612 )