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