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