Coverage for /pythoncovmergedfiles/medio/medio/src/aiohttp/aiohttp/client.py: 28%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""HTTP Client for asyncio."""
3import asyncio
4import base64
5import dataclasses
6import hashlib
7import json
8import os
9import sys
10import traceback
11import warnings
12from collections.abc import (
13 Awaitable,
14 Callable,
15 Collection,
16 Coroutine,
17 Generator,
18 Iterable,
19 Sequence,
20)
21from contextlib import suppress
22from types import TracebackType
23from typing import (
24 TYPE_CHECKING,
25 Any,
26 Generic,
27 Literal,
28 TypedDict,
29 TypeVar,
30 Unpack,
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 UploadAbortedError,
70 WSMessageTypeError,
71 WSServerHandshakeError,
72)
73from .client_middlewares import ClientMiddlewareType, _cached_build_client_middlewares
74from .client_reqrep import (
75 SSL_ALLOWED_TYPES,
76 ClientRequest,
77 ClientResponse,
78 ClientTimeout,
79 Fingerprint,
80 RequestInfo,
81 ResponseParams,
82 UploadTracker,
83)
84from .client_ws import (
85 DEFAULT_WS_CLIENT_TIMEOUT,
86 ClientWebSocketResponse,
87 ClientWSTimeout,
88)
89from .connector import BaseConnector, NamedPipeConnector, TCPConnector, UnixConnector
90from .cookiejar import CookieJar, _UnlimitedCookieJar
91from .helpers import (
92 _SENTINEL,
93 DEFAULT_CHUNK_SIZE,
94 EMPTY_BODY_METHODS,
95 HTTP_AND_EMPTY_SCHEMA_SET,
96 TimeoutHandle,
97 _auth_header_from_netrc,
98 get_env_proxy_for_url,
99 netrc_from_env,
100 sentinel,
101 strip_auth_from_url,
102)
103from .http import WS_KEY, HttpVersion, WebSocketReader, WebSocketWriter
104from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse
105from .tracing import Trace, TraceConfig
106from .typedefs import (
107 JSONBytesEncoder,
108 JSONEncoder,
109 LooseCookies,
110 LooseHeaders,
111 StrOrURL,
112)
114__all__ = (
115 # client_exceptions
116 "ClientConnectionError",
117 "ClientConnectionResetError",
118 "ClientConnectorCertificateError",
119 "ClientConnectorDNSError",
120 "ClientConnectorError",
121 "ClientConnectorSSLError",
122 "ClientError",
123 "ClientHttpProxyError",
124 "ClientOSError",
125 "ClientPayloadError",
126 "ClientProxyConnectionError",
127 "ClientResponseError",
128 "ClientSSLError",
129 "ConnectionTimeoutError",
130 "ContentTypeError",
131 "InvalidURL",
132 "InvalidUrlClientError",
133 "RedirectClientError",
134 "NonHttpUrlClientError",
135 "InvalidUrlRedirectClientError",
136 "NonHttpUrlRedirectClientError",
137 "ServerConnectionError",
138 "ServerDisconnectedError",
139 "ServerFingerprintMismatch",
140 "ServerTimeoutError",
141 "SocketTimeoutError",
142 "TooManyRedirects",
143 "UploadAbortedError",
144 "WSServerHandshakeError",
145 # client_reqrep
146 "ClientRequest",
147 "ClientResponse",
148 "Fingerprint",
149 "RequestInfo",
150 "UploadTracker",
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
172# URL parsers strip leading and trailing C0 control characters and spaces and
173# drop tabs and newlines before splitting a URL.
174_C0_CONTROL_OR_SPACE = "".join(map(chr, range(0x21)))
175_REMOVE_TAB_OR_NEWLINE = str.maketrans("", "", "\t\n\r")
178def _has_no_authority(location: str, scheme: str) -> bool:
179 """Tell if a URL with a scheme has no "//" after the scheme.
181 Browsers read a backslash like a slash there for http and https.
182 """
183 location = location.strip(_C0_CONTROL_OR_SPACE).translate(_REMOVE_TAB_OR_NEWLINE)
184 rest = location[len(scheme) + 1 : len(scheme) + 3]
185 return rest[:1] not in ("/", "\\") or rest[1:] not in ("/", "\\")
188class _RequestOptions(TypedDict, total=False):
189 params: Query
190 data: Any
191 json: Any
192 cookies: LooseCookies | None
193 headers: LooseHeaders | None
194 skip_auto_headers: Iterable[str] | None
195 allow_redirects: bool
196 max_redirects: int
197 compress: Literal["deflate", "gzip"] | bool
198 chunked: bool | None
199 expect100: bool
200 raise_for_status: None | bool | Callable[[ClientResponse], Awaitable[None]]
201 read_until_eof: bool
202 proxy: StrOrURL | None
203 timeout: "ClientTimeout | _SENTINEL | None"
204 ssl: SSLContext | bool | Fingerprint | _SENTINEL
205 server_hostname: str | None
206 proxy_headers: LooseHeaders | None
207 trace_request_ctx: object
208 read_bufsize: int | None
209 auto_decompress: bool | None
210 max_line_size: int | None
211 max_field_size: int | None
212 max_headers: int | None
213 middlewares: Sequence[ClientMiddlewareType] | None
214 upload_tracker: UploadTracker | None
217class _WSConnectOptions(TypedDict, total=False):
218 method: str
219 protocols: Collection[str]
220 timeout: "ClientWSTimeout | _SENTINEL"
221 receive_timeout: float | None
222 autoclose: bool
223 autoping: bool
224 heartbeat: float | None
225 origin: str | None
226 params: Query
227 headers: LooseHeaders | None
228 proxy: StrOrURL | None
229 ssl: SSLContext | bool | Fingerprint | _SENTINEL
230 server_hostname: str | None
231 proxy_headers: LooseHeaders | None
232 compress: int
233 max_msg_size: int
236# https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2
237# https://www.rfc-editor.org/info/rfc10008/#section-1-12
238IDEMPOTENT_METHODS = frozenset(
239 {"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE", "QUERY"}
240)
242_RetType_co = TypeVar(
243 "_RetType_co",
244 bound="ClientResponse | ClientWebSocketResponse[bool]",
245 covariant=True,
246)
247_CharsetResolver = Callable[[ClientResponse, bytes], str]
250class _SessionOptions(TypedDict, total=False):
251 base_url: StrOrURL | None
252 connector: BaseConnector | None
253 cookies: LooseCookies | None
254 headers: LooseHeaders | None
255 proxy: StrOrURL | None
256 ssl: SSLContext | bool | Fingerprint
257 skip_auto_headers: Iterable[str] | None
258 json_serialize: JSONEncoder
259 json_serialize_bytes: JSONBytesEncoder | None
260 request_class: type[ClientRequest]
261 response_class: type[ClientResponse]
262 ws_response_class: type[ClientWebSocketResponse]
263 version: HttpVersion
264 connector_owner: bool
265 raise_for_status: bool | Callable[[ClientResponse], Awaitable[None]]
266 timeout: "_SENTINEL | ClientTimeout | None"
267 auto_decompress: bool
268 trust_env: bool
269 requote_redirect_url: bool
270 trace_configs: list[TraceConfig[object]] | None
271 read_bufsize: int
272 max_line_size: int
273 max_field_size: int
274 max_headers: int
275 fallback_charset_resolver: _CharsetResolver
276 middlewares: Sequence[ClientMiddlewareType]
277 ssl_shutdown_timeout: _SENTINEL | None | float
280# Module-level (not a closure) so it has a stable identity for the
281# ``_cached_build_client_middlewares`` cache key.
282async def _connect_and_send_request(req: ClientRequest) -> ClientResponse:
283 connector = req._session._connector
284 assert connector is not None
285 if (tracker := req._upload_tracker) is not None:
286 req._upload_gen = tracker._attempt_started()
287 try:
288 conn = await connector.connect(req, traces=req._traces, timeout=req._timeout)
289 except TimeoutError as exc:
290 raise ConnectionTimeoutError(f"Connection timeout to host {req.url}") from exc
292 assert conn.protocol is not None
293 conn.protocol.set_response_params(**req._response_params)
294 try:
295 resp = await req._send(conn)
296 try:
297 await resp.start(conn)
298 except BaseException:
299 resp.close()
300 raise
301 except BaseException:
302 conn.close()
303 raise
304 return resp
307@final
308class ClientSession:
309 """First-class interface for making HTTP requests."""
311 __slots__ = (
312 "_base_url",
313 "_base_url_origin",
314 "_source_traceback",
315 "_connector",
316 "_loop",
317 "_cookie_jar",
318 "_connector_owner",
319 "_version",
320 "_json_serialize",
321 "_json_serialize_bytes",
322 "_requote_redirect_url",
323 "_timeout",
324 "_raise_for_status",
325 "_auto_decompress",
326 "_trust_env",
327 "_default_headers",
328 "_skip_auto_headers",
329 "_request_class",
330 "_response_class",
331 "_ws_response_class",
332 "_trace_configs",
333 "_read_bufsize",
334 "_max_line_size",
335 "_max_field_size",
336 "_max_headers",
337 "_resolve_charset",
338 "_default_proxy",
339 "_default_ssl",
340 "_retry_connection",
341 "_middlewares",
342 )
344 def __init__(
345 self,
346 base_url: StrOrURL | None = None,
347 *,
348 connector: BaseConnector | None = None,
349 cookies: LooseCookies | None = None,
350 headers: LooseHeaders | None = None,
351 proxy: StrOrURL | None = None,
352 ssl: SSLContext | bool | Fingerprint = True,
353 skip_auto_headers: Iterable[str] | None = None,
354 json_serialize: JSONEncoder = json.dumps,
355 json_serialize_bytes: JSONBytesEncoder | None = None,
356 request_class: type[ClientRequest] = ClientRequest,
357 response_class: type[ClientResponse] = ClientResponse,
358 ws_response_class: type[ClientWebSocketResponse] = ClientWebSocketResponse,
359 version: HttpVersion = http.HttpVersion11,
360 cookie_jar: AbstractCookieJar | None = None,
361 connector_owner: bool = True,
362 raise_for_status: bool | Callable[[ClientResponse], Awaitable[None]] = False,
363 timeout: _SENTINEL | ClientTimeout | None = sentinel,
364 auto_decompress: bool = True,
365 trust_env: bool = False,
366 requote_redirect_url: bool = True,
367 trace_configs: list[TraceConfig[object]] | None = None,
368 read_bufsize: int = DEFAULT_CHUNK_SIZE,
369 max_line_size: int = 8190,
370 max_field_size: int = 8190,
371 max_headers: int = 128,
372 fallback_charset_resolver: _CharsetResolver = lambda r, b: "utf-8",
373 middlewares: Sequence[ClientMiddlewareType] = (),
374 ssl_shutdown_timeout: _SENTINEL | None | float = sentinel,
375 ) -> None:
376 # We initialise _connector to None immediately, as it's referenced in __del__()
377 # and could cause issues if an exception occurs during initialisation.
378 self._connector: BaseConnector | None = None
379 if base_url is None or isinstance(base_url, URL):
380 self._base_url: URL | None = base_url
381 self._base_url_origin = None if base_url is None else base_url.origin()
382 else:
383 self._base_url = URL(base_url)
384 self._base_url_origin = self._base_url.origin()
385 assert self._base_url.absolute, "Only absolute URLs are supported"
386 if self._base_url is not None and not self._base_url.path.endswith("/"):
387 raise ValueError("base_url must have a trailing '/'")
389 if not isinstance(ssl, SSL_ALLOWED_TYPES):
390 raise TypeError(
391 "ssl should be SSLContext, Fingerprint, or bool, "
392 f"got {ssl!r} instead."
393 )
395 loop = asyncio.get_running_loop()
397 if timeout is sentinel or timeout is None:
398 timeout = ClientTimeout()
399 if not isinstance(timeout, ClientTimeout):
400 raise ValueError(
401 f"timeout parameter cannot be of {type(timeout)} type, "
402 "please use 'timeout=ClientTimeout(...)'",
403 )
404 self._timeout = timeout
406 if ssl_shutdown_timeout is not sentinel:
407 warnings.warn(
408 "The ssl_shutdown_timeout parameter is deprecated and will be removed in aiohttp 4.0",
409 DeprecationWarning,
410 stacklevel=2,
411 )
413 if connector is None:
414 connector = TCPConnector(ssl_shutdown_timeout=ssl_shutdown_timeout)
415 # Initialize these three attrs before raising any exception,
416 # they are used in __del__
417 self._connector = connector
418 self._loop = loop
419 if loop.get_debug():
420 self._source_traceback: traceback.StackSummary | None = (
421 traceback.extract_stack(sys._getframe(1))
422 )
423 else:
424 self._source_traceback = None
426 if connector._loop is not loop:
427 raise RuntimeError("Session and connector have to use same event loop")
429 if cookie_jar is None:
430 cookie_jar = CookieJar()
431 self._cookie_jar = cookie_jar
433 if cookies:
434 self._cookie_jar.update_cookies(cookies)
436 self._connector_owner = connector_owner
437 self._version = version
438 self._json_serialize = json_serialize
439 self._json_serialize_bytes = json_serialize_bytes
440 self._raise_for_status = raise_for_status
441 self._auto_decompress = auto_decompress
442 self._trust_env = trust_env
443 self._requote_redirect_url = requote_redirect_url
444 self._read_bufsize = read_bufsize
445 self._max_line_size = max_line_size
446 self._max_field_size = max_field_size
447 self._max_headers = max_headers
449 # Convert to list of tuples
450 if headers:
451 real_headers: CIMultiDict[str] = CIMultiDict(headers)
452 else:
453 real_headers = CIMultiDict()
454 self._default_headers: CIMultiDict[str] = real_headers
455 if skip_auto_headers is not None:
456 self._skip_auto_headers = frozenset(istr(i) for i in skip_auto_headers)
457 else:
458 self._skip_auto_headers = frozenset()
460 self._request_class = request_class
461 self._response_class = response_class
462 self._ws_response_class = ws_response_class
464 self._trace_configs = trace_configs or []
465 for trace_config in self._trace_configs:
466 trace_config.freeze()
468 self._resolve_charset = fallback_charset_resolver
470 self._default_proxy = proxy
471 self._default_ssl = ssl
472 self._retry_connection: bool = True
473 self._middlewares = tuple(middlewares)
475 def __init_subclass__(cls: type["ClientSession"]) -> None:
476 raise TypeError(
477 f"Inheritance class {cls.__name__} from ClientSession is forbidden"
478 )
480 def __del__(self, _warnings: Any = warnings) -> None:
481 if not self.closed:
482 _warnings.warn(
483 f"Unclosed client session {self!r}",
484 ResourceWarning,
485 source=self,
486 )
487 context = {"client_session": self, "message": "Unclosed client session"}
488 if self._source_traceback is not None:
489 context["source_traceback"] = self._source_traceback
490 self._loop.call_exception_handler(context)
492 if TYPE_CHECKING:
494 def request(
495 self,
496 method: str,
497 url: StrOrURL,
498 **kwargs: Unpack[_RequestOptions],
499 ) -> "_RequestContextManager": ...
501 else:
503 def request(
504 self, method: str, url: StrOrURL, **kwargs: Any
505 ) -> "_RequestContextManager":
506 """Perform HTTP request."""
507 return _RequestContextManager(self._request(method, url, **kwargs))
509 def _build_url(self, str_or_url: StrOrURL) -> URL:
510 url = URL(str_or_url)
511 if self._base_url and not url.absolute:
512 return self._base_url.join(url)
513 return url
515 async def _request(
516 self,
517 method: str,
518 str_or_url: StrOrURL,
519 *,
520 params: Query = None,
521 data: Any = None,
522 json: Any = None,
523 cookies: LooseCookies | None = None,
524 headers: LooseHeaders | None = None,
525 skip_auto_headers: Iterable[str] | None = None,
526 allow_redirects: bool = True,
527 max_redirects: int = 10,
528 compress: Literal["deflate", "gzip"] | bool = False,
529 chunked: bool | None = None,
530 expect100: bool = False,
531 raise_for_status: (
532 None | bool | Callable[[ClientResponse], Awaitable[None]]
533 ) = None,
534 read_until_eof: bool = True,
535 proxy: StrOrURL | None = None,
536 timeout: ClientTimeout | _SENTINEL | None = sentinel,
537 ssl: SSLContext | bool | Fingerprint | _SENTINEL = sentinel,
538 server_hostname: str | None = None,
539 proxy_headers: LooseHeaders | None = None,
540 trace_request_ctx: object = None,
541 read_bufsize: int | None = None,
542 auto_decompress: bool | None = None,
543 max_line_size: int | None = None,
544 max_field_size: int | None = None,
545 max_headers: int | None = None,
546 middlewares: Sequence[ClientMiddlewareType] | None = None,
547 upload_tracker: UploadTracker | None = None,
548 ) -> ClientResponse:
549 # NOTE: timeout clamps existing connect and read timeouts. We cannot
550 # set the default to None because we need to detect if the user wants
551 # to use the existing timeouts by setting timeout to None.
553 # Bound outside the settle-guaranteeing try block: a rebind error
554 # must not settle a tracker owned by another request.
555 if upload_tracker is not None:
556 upload_tracker._bind()
558 tm: TimeoutHandle | None = None
559 handle: asyncio.TimerHandle | None = None
560 # Only traces that saw send_request_start; they must also see a terminal event.
561 traces: list[Trace] = []
562 req: ClientRequest | None = None
563 resp: ClientResponse | None = None
564 try:
565 if self.closed:
566 raise RuntimeError("Session is closed")
568 method = method.upper()
570 if ssl is sentinel:
571 ssl = self._default_ssl
572 if not isinstance(ssl, SSL_ALLOWED_TYPES):
573 raise TypeError(
574 "ssl should be SSLContext, Fingerprint, or bool, "
575 f"got {ssl!r} instead."
576 )
578 if data is not None and json is not None:
579 raise ValueError(
580 "data and json parameters can not be used at the same time"
581 )
582 elif json is not None:
583 if self._json_serialize_bytes is not None:
584 data = payload.JsonBytesPayload(
585 json, dumps=self._json_serialize_bytes
586 )
587 else:
588 data = payload.JsonPayload(json, dumps=self._json_serialize)
590 redirects = 0
591 history: list[ClientResponse] = []
592 version = self._version
593 params = params or {}
595 # Merge with default headers and transform to CIMultiDict
596 headers = self._prepare_headers(headers)
598 try:
599 url = self._build_url(str_or_url)
600 except ValueError as e:
601 raise InvalidUrlClientError(str_or_url) from e
603 assert self._connector is not None
604 if url.scheme not in self._connector.allowed_protocol_schema_set:
605 raise NonHttpUrlClientError(url)
607 skip_headers: Iterable[istr] | None
608 if skip_auto_headers is not None:
609 skip_headers = {
610 istr(i) for i in skip_auto_headers
611 } | self._skip_auto_headers
612 elif self._skip_auto_headers:
613 skip_headers = self._skip_auto_headers
614 else:
615 skip_headers = None
617 if proxy is None:
618 proxy = self._default_proxy
620 resolved_proxy_headers: CIMultiDict[str] | None
621 if proxy is None:
622 resolved_proxy_headers = None
623 else:
624 resolved_proxy_headers = self._prepare_headers(proxy_headers)
625 try:
626 proxy = URL(proxy)
627 except ValueError as e:
628 raise InvalidURL(proxy) from e
630 real_timeout = (
631 self._timeout if timeout is sentinel or timeout is None else timeout
632 )
633 # timeout is cumulative for all request operations
634 # (request, redirects, responses, data consuming)
635 tm = TimeoutHandle(
636 self._loop,
637 real_timeout.total,
638 ceil_threshold=real_timeout.ceil_threshold,
639 )
640 handle = tm.start()
642 if read_bufsize is None:
643 read_bufsize = self._read_bufsize
645 if auto_decompress is None:
646 auto_decompress = self._auto_decompress
648 if max_line_size is None:
649 max_line_size = self._max_line_size
651 if max_field_size is None:
652 max_field_size = self._max_field_size
654 if max_headers is None:
655 max_headers = self._max_headers
657 for trace_config in self._trace_configs:
658 trace = Trace(
659 self,
660 trace_config,
661 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx),
662 )
663 await trace.send_request_start(
664 method, url.update_query(params), headers
665 )
666 traces.append(trace)
668 timer = tm.timer()
669 with timer:
670 # https://www.rfc-editor.org/rfc/rfc9112.html#name-retrying-requests
671 retry_persistent_connection = (
672 self._retry_connection and method in IDEMPOTENT_METHODS
673 )
674 while True:
675 url, auth_from_url = strip_auth_from_url(url)
676 if not url.raw_host:
677 # NOTE: Bail early, otherwise, causes `InvalidURL` through
678 # NOTE: `self._request_class()` below.
679 err_exc_cls = (
680 InvalidUrlRedirectClientError
681 if redirects
682 else InvalidUrlClientError
683 )
684 raise err_exc_cls(url)
686 if auth_from_url is not None:
687 # URL-embedded credentials override any Authorization
688 # header already present (e.g. carried from a previous
689 # redirect). On the initial request, refuse to silently
690 # shadow an explicit Authorization header.
691 if not history and hdrs.AUTHORIZATION in headers:
692 raise ValueError(
693 "Cannot combine AUTHORIZATION header with "
694 "credentials encoded in URL"
695 )
696 headers[hdrs.AUTHORIZATION] = auth_from_url
697 elif (
698 self._trust_env
699 and url.host is not None
700 and hdrs.AUTHORIZATION not in headers
701 ):
702 # Fall back to ~/.netrc credentials when trust_env is set.
703 netrc_auth = await self._loop.run_in_executor(
704 None, self._get_netrc_auth, url.host
705 )
706 if netrc_auth is not None:
707 headers[hdrs.AUTHORIZATION] = netrc_auth
709 all_cookies = self._cookie_jar.filter_cookies(url)
711 if cookies is not None:
712 tmp_cookie_jar = _UnlimitedCookieJar(
713 unsafe=self._cookie_jar.unsafe,
714 quote_cookie=self._cookie_jar.quote_cookie,
715 treat_as_secure_origin=self._cookie_jar.treat_as_secure_origin,
716 )
717 tmp_cookie_jar.update_cookies(cookies)
718 req_cookies = tmp_cookie_jar.filter_cookies(url)
719 if req_cookies:
720 all_cookies.load(req_cookies)
722 proxy_: URL | None = None
723 if proxy is not None:
724 proxy_ = URL(proxy)
725 elif self._trust_env:
726 # Re-resolve per iteration; drop stale env-proxy auth so
727 # a redirect that switches proxies can't leak credentials.
728 resolved_proxy_headers = None
729 with suppress(LookupError):
730 proxy_, env_proxy_auth = await asyncio.to_thread(
731 get_env_proxy_for_url, url
732 )
733 if env_proxy_auth is not None:
734 resolved_proxy_headers = CIMultiDict(
735 {hdrs.PROXY_AUTHORIZATION: env_proxy_auth}
736 )
738 response_params: ResponseParams = {
739 "timer": timer,
740 "skip_payload": method in EMPTY_BODY_METHODS,
741 "read_until_eof": read_until_eof,
742 "auto_decompress": auto_decompress,
743 "read_timeout": real_timeout.sock_read,
744 "read_bufsize": read_bufsize,
745 "timeout_ceil_threshold": self._connector._timeout_ceil_threshold,
746 "max_line_size": max_line_size,
747 "max_field_size": max_field_size,
748 "max_headers": max_headers,
749 }
751 req = self._request_class(
752 method,
753 url,
754 params=params,
755 headers=headers,
756 skip_auto_headers=skip_headers,
757 data=data,
758 cookies=all_cookies,
759 version=version,
760 compress=compress,
761 chunked=chunked,
762 expect100=expect100,
763 loop=self._loop,
764 response_class=self._response_class,
765 proxy=proxy_,
766 response_params=response_params,
767 timer=timer,
768 timeout=real_timeout,
769 session=self,
770 ssl=ssl,
771 server_hostname=server_hostname,
772 proxy_headers=resolved_proxy_headers,
773 traces=traces,
774 trust_env=self.trust_env,
775 )
776 req._upload_tracker = upload_tracker
778 # Apply middleware (if any) - per-request middleware overrides session middleware
779 effective_middlewares = (
780 self._middlewares if middlewares is None else tuple(middlewares)
781 )
783 if effective_middlewares:
784 handler = _cached_build_client_middlewares(
785 _connect_and_send_request, effective_middlewares
786 )
787 else:
788 handler = _connect_and_send_request
790 try:
791 resp = await handler(req)
792 # Client connector errors should not be retried
793 except (
794 ConnectionTimeoutError,
795 ClientConnectorError,
796 ClientConnectorCertificateError,
797 ClientConnectorSSLError,
798 ):
799 raise
800 except (ClientOSError, ServerDisconnectedError):
801 if not retry_persistent_connection:
802 raise
803 retry_persistent_connection = False
804 if data is not None:
805 # Rebuilding from `data` would resend only the unread
806 # remainder of a file object; reuse the payload, which
807 # rewinds itself once the cancelled writer has settled.
808 await req._close()
809 if req._body.consumed:
810 raise
811 data = req._body
812 continue
813 except ClientError:
814 raise
815 except OSError as exc:
816 if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
817 raise
818 raise ClientOSError(*exc.args) from exc
820 # Update cookies from raw headers to preserve duplicates
821 if resp._raw_cookie_headers:
822 self._cookie_jar.update_cookies_from_headers(
823 resp._raw_cookie_headers, resp.url
824 )
826 # redirects
827 if resp.status in (301, 302, 303, 307, 308) and allow_redirects:
828 for trace in traces:
829 await trace.send_request_redirect(
830 method, url.update_query(params), headers, resp
831 )
833 redirects += 1
834 history.append(resp)
835 if max_redirects and redirects >= max_redirects:
836 if req._body is not None:
837 await req._body.close()
838 resp.close()
839 raise TooManyRedirects(
840 history[0].request_info, tuple(history)
841 )
843 # For 301 and 302, mimic IE, now changed in RFC
844 # https://github.com/kennethreitz/requests/pull/269
845 if (resp.status == 303 and resp.method != hdrs.METH_HEAD) or (
846 resp.status in (301, 302) and resp.method == hdrs.METH_POST
847 ):
848 method = hdrs.METH_GET
849 data = None
850 if headers.get(hdrs.CONTENT_LENGTH):
851 headers.pop(hdrs.CONTENT_LENGTH)
852 else:
853 # For 307/308, always preserve the request body
854 # For 301/302 with non-POST methods, preserve the request body
855 # https://www.rfc-editor.org/rfc/rfc9110#section-15.4.3-3.1
856 # Use the existing payload to avoid recreating it from
857 # a potentially consumed file.
858 #
859 # If the payload is already consumed and cannot be replayed,
860 # fail fast instead of silently sending an empty body.
861 if req._body.consumed:
862 resp.close()
863 raise ClientPayloadError(
864 "Cannot follow redirect with a consumed request "
865 "body. Use bytes, a seekable file-like object, "
866 "or set allow_redirects=False."
867 )
868 data = req._body
870 r_url = resp.headers.get(hdrs.LOCATION) or resp.headers.get(
871 hdrs.URI
872 )
873 if r_url is None:
874 # see github.com/aio-libs/aiohttp/issues/2022
875 break
876 else:
877 # reading from correct redirection
878 # response is forbidden
879 resp.release()
881 try:
882 parsed_redirect_url = URL(
883 r_url, encoded=not self._requote_redirect_url
884 )
885 except ValueError as e:
886 if req._body is not None:
887 await req._body.close()
888 resp.close()
889 raise InvalidUrlRedirectClientError(
890 r_url,
891 "Server attempted redirecting to a location that does not look like a URL",
892 ) from e
894 scheme = parsed_redirect_url.scheme
895 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET:
896 if req._body is not None:
897 await req._body.close()
898 resp.close()
899 raise NonHttpUrlRedirectClientError(r_url)
900 elif not scheme or (
901 scheme == url.scheme and _has_no_authority(r_url, scheme)
902 ):
903 # "http:/path" or "http:path" is a reference to
904 # the current URL, as browsers resolve it.
905 parsed_redirect_url = url.join(parsed_redirect_url)
907 try:
908 redirect_origin = parsed_redirect_url.origin()
909 except ValueError as origin_val_err:
910 if req._body is not None:
911 await req._body.close()
912 resp.close()
913 raise InvalidUrlRedirectClientError(
914 parsed_redirect_url,
915 "Invalid redirect URL origin",
916 ) from origin_val_err
918 if url.origin() != redirect_origin:
919 cookies = None
920 headers.popall(hdrs.AUTHORIZATION, None)
921 headers.popall(hdrs.COOKIE, None)
922 headers.popall(hdrs.PROXY_AUTHORIZATION, None)
924 url = parsed_redirect_url
925 params = {}
926 resp.release()
927 continue
929 break
931 if req._body is not None:
932 await req._body.close()
933 # check response status
934 if raise_for_status is None:
935 raise_for_status = self._raise_for_status
937 if callable(raise_for_status):
938 await raise_for_status(resp)
939 elif raise_for_status:
940 resp.raise_for_status()
942 # register connection
943 if handle is not None:
944 if resp.connection is not None:
945 resp.connection.add_callback(handle.cancel)
946 else:
947 handle.cancel()
949 resp._history = tuple(history)
951 for trace in traces:
952 await trace.send_request_end(
953 method, url.update_query(params), headers, resp
954 )
955 if upload_tracker is not None:
956 upload_tracker._finalize()
957 return resp
959 except BaseException as e:
960 # cleanup timer
961 if tm is not None:
962 tm.close()
963 if handle:
964 handle.cancel()
966 if upload_tracker is not None:
967 upload_tracker._finalize()
969 if resp is not None:
970 # A failure occurred after the response was received.
971 resp.close()
973 if req is not None and req._body is not None:
974 await req._body.close()
976 for trace in traces:
977 # url and headers are bound whenever traces is non-empty.
978 await trace.send_request_exception(
979 method, url.update_query(params), headers, e # type: ignore[possibly-undefined, arg-type]
980 )
981 raise
983 if TYPE_CHECKING:
985 @overload
986 def ws_connect(
987 self,
988 url: StrOrURL,
989 *,
990 decode_text: Literal[True] = ...,
991 **kwargs: Unpack[_WSConnectOptions],
992 ) -> "_BaseRequestContextManager[ClientWebSocketResponse[Literal[True]]]": ...
994 @overload
995 def ws_connect(
996 self,
997 url: StrOrURL,
998 *,
999 decode_text: Literal[False],
1000 **kwargs: Unpack[_WSConnectOptions],
1001 ) -> "_BaseRequestContextManager[ClientWebSocketResponse[Literal[False]]]": ...
1003 @overload
1004 def ws_connect(
1005 self,
1006 url: StrOrURL,
1007 *,
1008 decode_text: bool = ...,
1009 **kwargs: Unpack[_WSConnectOptions],
1010 ) -> "_BaseRequestContextManager[ClientWebSocketResponse[bool]]": ...
1012 def ws_connect(
1013 self,
1014 url: StrOrURL,
1015 *,
1016 method: str = hdrs.METH_GET,
1017 protocols: Collection[str] = (),
1018 timeout: ClientWSTimeout | _SENTINEL = sentinel,
1019 receive_timeout: float | None = None,
1020 autoclose: bool = True,
1021 autoping: bool = True,
1022 heartbeat: float | None = None,
1023 origin: str | None = None,
1024 params: Query = None,
1025 headers: LooseHeaders | None = None,
1026 proxy: StrOrURL | None = None,
1027 ssl: SSLContext | bool | Fingerprint | _SENTINEL = sentinel,
1028 server_hostname: str | None = None,
1029 proxy_headers: LooseHeaders | None = None,
1030 compress: int = 0,
1031 max_msg_size: int = 4 * 1024 * 1024,
1032 decode_text: bool = True,
1033 ) -> "_BaseRequestContextManager[ClientWebSocketResponse[bool]]":
1034 """Initiate websocket connection."""
1035 return _WSRequestContextManager(
1036 self._ws_connect(
1037 url,
1038 method=method,
1039 protocols=protocols,
1040 timeout=timeout,
1041 receive_timeout=receive_timeout,
1042 autoclose=autoclose,
1043 autoping=autoping,
1044 heartbeat=heartbeat,
1045 origin=origin,
1046 params=params,
1047 headers=headers,
1048 proxy=proxy,
1049 ssl=ssl,
1050 server_hostname=server_hostname,
1051 proxy_headers=proxy_headers,
1052 compress=compress,
1053 max_msg_size=max_msg_size,
1054 decode_text=decode_text,
1055 )
1056 )
1058 if TYPE_CHECKING:
1060 @overload
1061 async def _ws_connect(
1062 self,
1063 url: StrOrURL,
1064 *,
1065 decode_text: Literal[True] = ...,
1066 **kwargs: Unpack[_WSConnectOptions],
1067 ) -> "ClientWebSocketResponse[Literal[True]]": ...
1069 @overload
1070 async def _ws_connect(
1071 self,
1072 url: StrOrURL,
1073 *,
1074 decode_text: Literal[False],
1075 **kwargs: Unpack[_WSConnectOptions],
1076 ) -> "ClientWebSocketResponse[Literal[False]]": ...
1078 @overload
1079 async def _ws_connect(
1080 self,
1081 url: StrOrURL,
1082 *,
1083 decode_text: bool = ...,
1084 **kwargs: Unpack[_WSConnectOptions],
1085 ) -> "ClientWebSocketResponse[bool]": ...
1087 async def _ws_connect(
1088 self,
1089 url: StrOrURL,
1090 *,
1091 method: str = hdrs.METH_GET,
1092 protocols: Collection[str] = (),
1093 timeout: ClientWSTimeout | _SENTINEL = sentinel,
1094 receive_timeout: float | None = None,
1095 autoclose: bool = True,
1096 autoping: bool = True,
1097 heartbeat: float | None = None,
1098 origin: str | None = None,
1099 params: Query = None,
1100 headers: LooseHeaders | None = None,
1101 proxy: StrOrURL | None = None,
1102 ssl: SSLContext | bool | Fingerprint | _SENTINEL = sentinel,
1103 server_hostname: str | None = None,
1104 proxy_headers: LooseHeaders | None = None,
1105 compress: int = 0,
1106 max_msg_size: int = 4 * 1024 * 1024,
1107 decode_text: bool = True,
1108 ) -> "ClientWebSocketResponse[bool]":
1109 if timeout is not sentinel:
1110 if isinstance(timeout, ClientWSTimeout):
1111 ws_timeout = timeout
1112 else:
1113 warnings.warn( # type: ignore[unreachable]
1114 "parameter 'timeout' of type 'float' "
1115 "is deprecated, please use "
1116 "'timeout=ClientWSTimeout(ws_close=...)'",
1117 DeprecationWarning,
1118 stacklevel=2,
1119 )
1120 ws_timeout = ClientWSTimeout(ws_close=timeout)
1121 else:
1122 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT
1123 if receive_timeout is not None:
1124 warnings.warn(
1125 "float parameter 'receive_timeout' "
1126 "is deprecated, please use parameter "
1127 "'timeout=ClientWSTimeout(ws_receive=...)'",
1128 DeprecationWarning,
1129 stacklevel=2,
1130 )
1131 ws_timeout = dataclasses.replace(ws_timeout, ws_receive=receive_timeout)
1133 if headers is None:
1134 real_headers: CIMultiDict[str] = CIMultiDict()
1135 else:
1136 real_headers = CIMultiDict(headers)
1138 default_headers = {
1139 hdrs.UPGRADE: "websocket",
1140 hdrs.CONNECTION: "Upgrade",
1141 hdrs.SEC_WEBSOCKET_VERSION: "13",
1142 }
1144 for key, value in default_headers.items():
1145 real_headers.setdefault(key, value)
1147 sec_key = base64.b64encode(os.urandom(16))
1148 real_headers[hdrs.SEC_WEBSOCKET_KEY] = sec_key.decode()
1150 if protocols:
1151 real_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ",".join(protocols)
1152 if origin is not None:
1153 real_headers[hdrs.ORIGIN] = origin
1154 if compress:
1155 extstr = ws_ext_gen(compress=compress)
1156 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr
1158 if ssl is not sentinel and not isinstance(ssl, SSL_ALLOWED_TYPES):
1159 raise TypeError(
1160 "ssl should be SSLContext, Fingerprint, or bool, "
1161 f"got {ssl!r} instead."
1162 )
1164 # send request
1165 resp = await self.request(
1166 method,
1167 url,
1168 params=params,
1169 headers=real_headers,
1170 read_until_eof=False,
1171 proxy=proxy,
1172 ssl=ssl,
1173 server_hostname=server_hostname,
1174 proxy_headers=proxy_headers,
1175 )
1177 try:
1178 # check handshake
1179 if resp.status != 101:
1180 raise WSServerHandshakeError(
1181 resp.request_info,
1182 resp.history,
1183 message="Invalid response status",
1184 status=resp.status,
1185 headers=resp.headers,
1186 )
1188 if resp.headers.get(hdrs.UPGRADE, "").lower() != "websocket":
1189 raise WSServerHandshakeError(
1190 resp.request_info,
1191 resp.history,
1192 message="Invalid upgrade header",
1193 status=resp.status,
1194 headers=resp.headers,
1195 )
1197 if not resp._upgraded:
1198 raise WSServerHandshakeError(
1199 resp.request_info,
1200 resp.history,
1201 message="Invalid connection header",
1202 status=resp.status,
1203 headers=resp.headers,
1204 )
1206 # key calculation
1207 r_key = resp.headers.get(hdrs.SEC_WEBSOCKET_ACCEPT, "")
1208 match = base64.b64encode(hashlib.sha1(sec_key + WS_KEY).digest()).decode()
1209 if r_key != match:
1210 raise WSServerHandshakeError(
1211 resp.request_info,
1212 resp.history,
1213 message="Invalid challenge response",
1214 status=resp.status,
1215 headers=resp.headers,
1216 )
1218 # websocket protocol
1219 protocol = None
1220 if protocols and hdrs.SEC_WEBSOCKET_PROTOCOL in resp.headers:
1221 resp_protocols = [
1222 proto.strip()
1223 for proto in resp.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",")
1224 ]
1226 for proto in resp_protocols:
1227 if proto in protocols:
1228 protocol = proto
1229 break
1231 # websocket compress
1232 notakeover = False
1233 if compress:
1234 compress_hdrs = resp.headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)
1235 if compress_hdrs:
1236 try:
1237 compress, notakeover = ws_ext_parse(compress_hdrs)
1238 except WSHandshakeError as exc:
1239 raise WSServerHandshakeError(
1240 resp.request_info,
1241 resp.history,
1242 message=exc.args[0],
1243 status=resp.status,
1244 headers=resp.headers,
1245 ) from exc
1246 else:
1247 compress = 0
1248 notakeover = False
1250 conn = resp.connection
1251 assert conn is not None
1252 conn_proto = conn.protocol
1253 assert conn_proto is not None
1255 # For WS connection the read_timeout must be either ws_timeout.ws_receive or greater
1256 # None == no timeout, i.e. infinite timeout, so None is the max timeout possible
1257 if ws_timeout.ws_receive is None:
1258 # Reset regardless
1259 conn_proto.read_timeout = None
1260 elif conn_proto.read_timeout is not None:
1261 conn_proto.read_timeout = max(
1262 ws_timeout.ws_receive, conn_proto.read_timeout
1263 )
1265 transport = conn.transport
1266 assert transport is not None
1267 reader = WebSocketDataQueue(conn_proto, DEFAULT_CHUNK_SIZE, loop=self._loop)
1268 writer = WebSocketWriter(
1269 conn_proto,
1270 transport,
1271 use_mask=True,
1272 compress=compress,
1273 notakeover=notakeover,
1274 )
1275 except BaseException:
1276 resp.close()
1277 raise
1278 else:
1279 ws_resp = self._ws_response_class(
1280 reader,
1281 writer,
1282 protocol,
1283 resp,
1284 ws_timeout,
1285 autoclose,
1286 autoping,
1287 self._loop,
1288 heartbeat=heartbeat,
1289 compress=compress,
1290 client_notakeover=notakeover,
1291 )
1292 ws_resp._parser = WebSocketReader(
1293 reader,
1294 max_msg_size,
1295 compress=bool(compress),
1296 decode_text=decode_text,
1297 )
1298 cb = None if heartbeat is None else ws_resp._on_data_received
1299 conn_proto.set_parser(ws_resp._parser, reader, data_received_cb=cb)
1300 return ws_resp
1302 def _prepare_headers(self, headers: LooseHeaders | None) -> "CIMultiDict[str]":
1303 """Add default headers and transform it to CIMultiDict"""
1304 # Convert headers to MultiDict
1305 result = CIMultiDict(self._default_headers)
1306 if headers:
1307 if not isinstance(headers, (MultiDictProxy, MultiDict)):
1308 headers = CIMultiDict(headers)
1309 added_names: set[str] = set()
1310 for key, value in headers.items():
1311 if key in added_names:
1312 result.add(key, value)
1313 else:
1314 result[key] = value
1315 added_names.add(key)
1316 return result
1318 def _get_netrc_auth(self, host: str) -> str | None:
1319 """Return an ``Authorization`` header value for ``host`` from netrc.
1321 Designed to be called in an executor to avoid blocking I/O on the
1322 event loop.
1323 """
1324 netrc_obj = netrc_from_env()
1325 try:
1326 return _auth_header_from_netrc(netrc_obj, host)
1327 except LookupError:
1328 return None
1330 if TYPE_CHECKING:
1332 def get(
1333 self,
1334 url: StrOrURL,
1335 **kwargs: Unpack[_RequestOptions],
1336 ) -> "_RequestContextManager": ...
1338 def options(
1339 self,
1340 url: StrOrURL,
1341 **kwargs: Unpack[_RequestOptions],
1342 ) -> "_RequestContextManager": ...
1344 def head(
1345 self,
1346 url: StrOrURL,
1347 **kwargs: Unpack[_RequestOptions],
1348 ) -> "_RequestContextManager": ...
1350 def post(
1351 self,
1352 url: StrOrURL,
1353 **kwargs: Unpack[_RequestOptions],
1354 ) -> "_RequestContextManager": ...
1356 def put(
1357 self,
1358 url: StrOrURL,
1359 **kwargs: Unpack[_RequestOptions],
1360 ) -> "_RequestContextManager": ...
1362 def patch(
1363 self,
1364 url: StrOrURL,
1365 **kwargs: Unpack[_RequestOptions],
1366 ) -> "_RequestContextManager": ...
1368 def delete(
1369 self,
1370 url: StrOrURL,
1371 **kwargs: Unpack[_RequestOptions],
1372 ) -> "_RequestContextManager": ...
1374 else:
1376 def get(
1377 self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any
1378 ) -> "_RequestContextManager":
1379 """Perform HTTP GET request."""
1380 return _RequestContextManager(
1381 self._request(
1382 hdrs.METH_GET, url, allow_redirects=allow_redirects, **kwargs
1383 )
1384 )
1386 def options(
1387 self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any
1388 ) -> "_RequestContextManager":
1389 """Perform HTTP OPTIONS request."""
1390 return _RequestContextManager(
1391 self._request(
1392 hdrs.METH_OPTIONS, url, allow_redirects=allow_redirects, **kwargs
1393 )
1394 )
1396 def head(
1397 self, url: StrOrURL, *, allow_redirects: bool = False, **kwargs: Any
1398 ) -> "_RequestContextManager":
1399 """Perform HTTP HEAD request."""
1400 return _RequestContextManager(
1401 self._request(
1402 hdrs.METH_HEAD, url, allow_redirects=allow_redirects, **kwargs
1403 )
1404 )
1406 def post(
1407 self, url: StrOrURL, *, data: Any = None, **kwargs: Any
1408 ) -> "_RequestContextManager":
1409 """Perform HTTP POST request."""
1410 return _RequestContextManager(
1411 self._request(hdrs.METH_POST, url, data=data, **kwargs)
1412 )
1414 def put(
1415 self, url: StrOrURL, *, data: Any = None, **kwargs: Any
1416 ) -> "_RequestContextManager":
1417 """Perform HTTP PUT request."""
1418 return _RequestContextManager(
1419 self._request(hdrs.METH_PUT, url, data=data, **kwargs)
1420 )
1422 def patch(
1423 self, url: StrOrURL, *, data: Any = None, **kwargs: Any
1424 ) -> "_RequestContextManager":
1425 """Perform HTTP PATCH request."""
1426 return _RequestContextManager(
1427 self._request(hdrs.METH_PATCH, url, data=data, **kwargs)
1428 )
1430 def delete(self, url: StrOrURL, **kwargs: Any) -> "_RequestContextManager":
1431 """Perform HTTP DELETE request."""
1432 return _RequestContextManager(
1433 self._request(hdrs.METH_DELETE, url, **kwargs)
1434 )
1436 async def close(self) -> None:
1437 """Close underlying connector.
1439 Release all acquired resources.
1440 """
1441 if not self.closed:
1442 if self._connector is not None and self._connector_owner:
1443 await self._connector.close()
1444 self._connector = None
1446 @property
1447 def closed(self) -> bool:
1448 """Is client session closed.
1450 A readonly property.
1451 """
1452 return self._connector is None or self._connector.closed
1454 @property
1455 def connector(self) -> BaseConnector | None:
1456 """Connector instance used for the session."""
1457 return self._connector
1459 @property
1460 def cookie_jar(self) -> AbstractCookieJar:
1461 """The session cookies."""
1462 return self._cookie_jar
1464 @property
1465 def version(self) -> tuple[int, int]:
1466 """The session HTTP protocol version."""
1467 return self._version
1469 @property
1470 def requote_redirect_url(self) -> bool:
1471 """Do URL requoting on redirection handling."""
1472 return self._requote_redirect_url
1474 @property
1475 def timeout(self) -> ClientTimeout:
1476 """Timeout for the session."""
1477 return self._timeout
1479 @property
1480 def headers(self) -> "CIMultiDict[str]":
1481 """The default headers of the client session."""
1482 return self._default_headers
1484 @property
1485 def skip_auto_headers(self) -> frozenset[istr]:
1486 """Headers for which autogeneration should be skipped"""
1487 return self._skip_auto_headers
1489 @property
1490 def json_serialize(self) -> JSONEncoder:
1491 """Json serializer callable"""
1492 return self._json_serialize
1494 @property
1495 def connector_owner(self) -> bool:
1496 """Should connector be closed on session closing"""
1497 return self._connector_owner
1499 @property
1500 def raise_for_status(
1501 self,
1502 ) -> bool | Callable[[ClientResponse], Awaitable[None]]:
1503 """Should `ClientResponse.raise_for_status()` be called for each response."""
1504 return self._raise_for_status
1506 @property
1507 def auto_decompress(self) -> bool:
1508 """Should the body response be automatically decompressed."""
1509 return self._auto_decompress
1511 @property
1512 def trust_env(self) -> bool:
1513 """
1514 Should proxies information from environment or netrc be trusted.
1516 Information is from HTTP_PROXY / HTTPS_PROXY environment variables
1517 or ~/.netrc file if present.
1518 """
1519 return self._trust_env
1521 @property
1522 def trace_configs(self) -> list[TraceConfig[Any]]:
1523 """A list of TraceConfig instances used for client tracing"""
1524 return self._trace_configs
1526 def detach(self) -> None:
1527 """Detach connector from session without closing the former.
1529 Session is switched to closed state anyway.
1530 """
1531 self._connector = None
1533 async def __aenter__(self) -> "ClientSession":
1534 return self
1536 async def __aexit__(
1537 self,
1538 exc_type: type[BaseException] | None,
1539 exc_val: BaseException | None,
1540 exc_tb: TracebackType | None,
1541 ) -> None:
1542 await self.close()
1545class _BaseRequestContextManager(
1546 Coroutine[Any, Any, _RetType_co], Generic[_RetType_co]
1547):
1548 __slots__ = ("_coro", "_resp")
1550 def __init__(self, coro: Coroutine[asyncio.Future[Any], None, _RetType_co]) -> None:
1551 self._coro: Coroutine[asyncio.Future[Any], None, _RetType_co] = coro
1553 def send(self, arg: None) -> asyncio.Future[Any]:
1554 return self._coro.send(arg)
1556 def throw(self, *args: Any, **kwargs: Any) -> asyncio.Future[Any]:
1557 return self._coro.throw(*args, **kwargs)
1559 def close(self) -> None:
1560 return self._coro.close()
1562 def __await__(self) -> Generator[Any, None, _RetType_co]:
1563 ret = self._coro.__await__()
1564 return ret
1566 def __iter__(self) -> Generator[Any, None, _RetType_co]:
1567 return self.__await__()
1569 async def __aenter__(self) -> _RetType_co:
1570 self._resp: _RetType_co = await self._coro
1571 return await self._resp.__aenter__() # type: ignore[return-value]
1573 async def __aexit__(
1574 self,
1575 exc_type: type[BaseException] | None,
1576 exc: BaseException | None,
1577 tb: TracebackType | None,
1578 ) -> None:
1579 await self._resp.__aexit__(exc_type, exc, tb)
1582_RequestContextManager = _BaseRequestContextManager[ClientResponse]
1583_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse[bool]]
1586class _SessionRequestContextManager:
1587 __slots__ = ("_coro", "_resp", "_session")
1589 def __init__(
1590 self,
1591 coro: Coroutine[asyncio.Future[Any], None, ClientResponse],
1592 session: ClientSession,
1593 ) -> None:
1594 self._coro = coro
1595 self._resp: ClientResponse | None = None
1596 self._session = session
1598 async def __aenter__(self) -> ClientResponse:
1599 try:
1600 self._resp = await self._coro
1601 except BaseException:
1602 await self._session.close()
1603 raise
1604 else:
1605 return self._resp
1607 async def __aexit__(
1608 self,
1609 exc_type: type[BaseException] | None,
1610 exc: BaseException | None,
1611 tb: TracebackType | None,
1612 ) -> None:
1613 assert self._resp is not None
1614 self._resp.close()
1615 await self._session.close()
1618if TYPE_CHECKING:
1620 def request(
1621 method: str,
1622 url: StrOrURL,
1623 *,
1624 version: HttpVersion = http.HttpVersion11,
1625 connector: BaseConnector | None = None,
1626 **kwargs: Unpack[_RequestOptions],
1627 ) -> _SessionRequestContextManager: ...
1629else:
1631 def request(
1632 method: str,
1633 url: StrOrURL,
1634 *,
1635 version: HttpVersion = http.HttpVersion11,
1636 connector: BaseConnector | None = None,
1637 **kwargs: Any,
1638 ) -> _SessionRequestContextManager:
1639 """Constructs and sends a request.
1641 Returns response object.
1642 method - HTTP method
1643 url - request url
1644 params - (optional) Dictionary or bytes to be sent in the query
1645 string of the new request
1646 data - (optional) Dictionary, bytes, or file-like object to
1647 send in the body of the request
1648 json - (optional) Any json compatible python object
1649 headers - (optional) Dictionary of HTTP Headers to send with
1650 the request
1651 cookies - (optional) Dict object to send with the request
1652 allow_redirects - (optional) If set to False, do not follow
1653 redirects
1654 version - Request HTTP version.
1655 compress - Set to True if request has to be compressed
1656 with deflate encoding.
1657 chunked - Set to chunk size for chunked transfer encoding.
1658 expect100 - Expect 100-continue response from server.
1659 connector - BaseConnector sub-class instance to support
1660 connection pooling.
1661 read_until_eof - Read response until eof if response
1662 does not have Content-Length header.
1663 loop - Optional event loop.
1664 timeout - Optional ClientTimeout settings structure, 5min
1665 total timeout by default.
1666 Usage::
1667 >>> import aiohttp
1668 >>> async with aiohttp.request('GET', 'http://python.org/') as resp:
1669 ... print(resp)
1670 ... data = await resp.read()
1671 <ClientResponse(https://www.python.org/) [200 OK]>
1672 """
1673 connector_owner = False
1674 if connector is None:
1675 connector_owner = True
1676 connector = TCPConnector(force_close=True)
1678 session = ClientSession(
1679 cookies=kwargs.pop("cookies", None),
1680 version=version,
1681 timeout=kwargs.pop("timeout", sentinel),
1682 connector=connector,
1683 connector_owner=connector_owner,
1684 )
1686 return _SessionRequestContextManager(
1687 session._request(method, url, **kwargs),
1688 session,
1689 )