Coverage for /pythoncovmergedfiles/medio/medio/src/aiohttp/aiohttp/client.py: 27%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

616 statements  

1"""HTTP Client for asyncio.""" 

2 

3import asyncio 

4import base64 

5import dataclasses 

6import hashlib 

7import json 

8import os 

9import sys 

10import traceback 

11import warnings 

12from collections.abc import ( 

13 Awaitable, 

14 Callable, 

15 Collection, 

16 Coroutine, 

17 Generator, 

18 Iterable, 

19 Sequence, 

20) 

21from contextlib import suppress 

22from types import TracebackType 

23from typing import ( 

24 TYPE_CHECKING, 

25 Any, 

26 Final, 

27 Generic, 

28 Literal, 

29 TypedDict, 

30 TypeVar, 

31 final, 

32 overload, 

33) 

34 

35from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr 

36from yarl import URL, Query 

37 

38from . import hdrs, http, payload 

39from ._websocket.reader import WebSocketDataQueue 

40from .abc import AbstractCookieJar 

41from .client_exceptions import ( 

42 ClientConnectionError, 

43 ClientConnectionResetError, 

44 ClientConnectorCertificateError, 

45 ClientConnectorDNSError, 

46 ClientConnectorError, 

47 ClientConnectorSSLError, 

48 ClientError, 

49 ClientHttpProxyError, 

50 ClientOSError, 

51 ClientPayloadError, 

52 ClientProxyConnectionError, 

53 ClientResponseError, 

54 ClientSSLError, 

55 ConnectionTimeoutError, 

56 ContentTypeError, 

57 InvalidURL, 

58 InvalidUrlClientError, 

59 InvalidUrlRedirectClientError, 

60 NonHttpUrlClientError, 

61 NonHttpUrlRedirectClientError, 

62 RedirectClientError, 

63 ServerConnectionError, 

64 ServerDisconnectedError, 

65 ServerFingerprintMismatch, 

66 ServerTimeoutError, 

67 SocketTimeoutError, 

68 TooManyRedirects, 

69 WSMessageTypeError, 

70 WSServerHandshakeError, 

71) 

72from .client_middlewares import ClientMiddlewareType, build_client_middlewares 

73from .client_reqrep import ( 

74 SSL_ALLOWED_TYPES, 

75 ClientRequest, 

76 ClientResponse, 

77 Fingerprint, 

78 RequestInfo, 

79) 

80from .client_ws import ( 

81 DEFAULT_WS_CLIENT_TIMEOUT, 

82 ClientWebSocketResponse, 

83 ClientWSTimeout, 

84) 

85from .connector import ( 

86 HTTP_AND_EMPTY_SCHEMA_SET, 

87 BaseConnector, 

88 NamedPipeConnector, 

89 TCPConnector, 

90 UnixConnector, 

91) 

92from .cookiejar import CookieJar 

93from .helpers import ( 

94 _SENTINEL, 

95 DEFAULT_CHUNK_SIZE, 

96 EMPTY_BODY_METHODS, 

97 TimeoutHandle, 

98 _auth_header_from_netrc, 

99 frozen_dataclass_decorator, 

100 get_env_proxy_for_url, 

101 netrc_from_env, 

102 sentinel, 

103 strip_auth_from_url, 

104) 

105from .http import WS_KEY, HttpVersion, WebSocketReader, WebSocketWriter 

106from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse 

107from .tracing import Trace, TraceConfig 

108from .typedefs import ( 

109 JSONBytesEncoder, 

110 JSONEncoder, 

111 LooseCookies, 

112 LooseHeaders, 

113 StrOrURL, 

114) 

115 

116__all__ = ( 

117 # client_exceptions 

118 "ClientConnectionError", 

119 "ClientConnectionResetError", 

120 "ClientConnectorCertificateError", 

121 "ClientConnectorDNSError", 

122 "ClientConnectorError", 

123 "ClientConnectorSSLError", 

124 "ClientError", 

125 "ClientHttpProxyError", 

126 "ClientOSError", 

127 "ClientPayloadError", 

128 "ClientProxyConnectionError", 

129 "ClientResponseError", 

130 "ClientSSLError", 

131 "ConnectionTimeoutError", 

132 "ContentTypeError", 

133 "InvalidURL", 

134 "InvalidUrlClientError", 

135 "RedirectClientError", 

136 "NonHttpUrlClientError", 

137 "InvalidUrlRedirectClientError", 

138 "NonHttpUrlRedirectClientError", 

139 "ServerConnectionError", 

140 "ServerDisconnectedError", 

141 "ServerFingerprintMismatch", 

142 "ServerTimeoutError", 

143 "SocketTimeoutError", 

144 "TooManyRedirects", 

145 "WSServerHandshakeError", 

146 # client_reqrep 

147 "ClientRequest", 

148 "ClientResponse", 

149 "Fingerprint", 

150 "RequestInfo", 

151 # connector 

152 "BaseConnector", 

153 "TCPConnector", 

154 "UnixConnector", 

155 "NamedPipeConnector", 

156 # client_ws 

157 "ClientWebSocketResponse", 

158 # client 

159 "ClientSession", 

160 "ClientTimeout", 

161 "ClientWSTimeout", 

162 "request", 

163 "WSMessageTypeError", 

164) 

165 

166 

167if TYPE_CHECKING: 

168 from ssl import SSLContext 

169else: 

170 SSLContext = None 

171 

172if sys.version_info >= (3, 11) and TYPE_CHECKING: 

173 from typing import Unpack 

174 

175 

176class _RequestOptions(TypedDict, total=False): 

177 params: Query 

178 data: Any 

179 json: Any 

180 cookies: LooseCookies | None 

181 headers: LooseHeaders | None 

182 skip_auto_headers: Iterable[str] | None 

183 allow_redirects: bool 

184 max_redirects: int 

185 compress: Literal["deflate", "gzip"] | bool 

186 chunked: bool | None 

187 expect100: bool 

188 raise_for_status: None | bool | Callable[[ClientResponse], Awaitable[None]] 

189 read_until_eof: bool 

190 proxy: StrOrURL | None 

191 timeout: "ClientTimeout | _SENTINEL | None" 

192 ssl: SSLContext | bool | Fingerprint 

193 server_hostname: str | None 

194 proxy_headers: LooseHeaders | None 

195 trace_request_ctx: object 

196 read_bufsize: int | None 

197 auto_decompress: bool | None 

198 max_line_size: int | None 

199 max_field_size: int | None 

200 max_headers: int | None 

201 middlewares: Sequence[ClientMiddlewareType] | None 

202 

203 

204class _WSConnectOptions(TypedDict, total=False): 

205 method: str 

206 protocols: Collection[str] 

207 timeout: "ClientWSTimeout | _SENTINEL" 

208 receive_timeout: float | None 

209 autoclose: bool 

210 autoping: bool 

211 heartbeat: float | None 

212 origin: str | None 

213 params: Query 

214 headers: LooseHeaders | None 

215 proxy: StrOrURL | None 

216 ssl: SSLContext | bool | Fingerprint 

217 server_hostname: str | None 

218 proxy_headers: LooseHeaders | None 

219 compress: int 

220 max_msg_size: int 

221 

222 

223@frozen_dataclass_decorator 

224class ClientTimeout: 

225 total: float | None = None 

226 connect: float | None = None 

227 sock_read: float | None = None 

228 sock_connect: float | None = None 

229 ceil_threshold: float = 5 

230 

231 # pool_queue_timeout: Optional[float] = None 

232 # dns_resolution_timeout: Optional[float] = None 

233 # socket_connect_timeout: Optional[float] = None 

234 # connection_acquiring_timeout: Optional[float] = None 

235 # new_connection_timeout: Optional[float] = None 

236 # http_header_timeout: Optional[float] = None 

237 # response_body_timeout: Optional[float] = None 

238 

239 # to create a timeout specific for a single request, either 

240 # - create a completely new one to overwrite the default 

241 # - or use https://docs.python.org/3/library/dataclasses.html#dataclasses.replace 

242 # to overwrite the defaults 

243 

244 def __post_init__(self) -> None: 

245 if self.total is not None and self.total == 0: 

246 raise ValueError( 

247 "total timeout must be a positive number or None to disable, " 

248 "got 0. Using 0 to disable timeouts is no longer supported, " 

249 "use None instead." 

250 ) 

251 

252 

253# 5 Minute default read timeout 

254DEFAULT_TIMEOUT: Final[ClientTimeout] = ClientTimeout(total=5 * 60, sock_connect=30) 

255 

256# https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2 

257IDEMPOTENT_METHODS = frozenset({"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"}) 

258 

259_RetType_co = TypeVar( 

260 "_RetType_co", 

261 bound="ClientResponse | ClientWebSocketResponse[bool]", 

262 covariant=True, 

263) 

264_CharsetResolver = Callable[[ClientResponse, bytes], str] 

265 

266 

267@final 

268class ClientSession: 

269 """First-class interface for making HTTP requests.""" 

270 

271 __slots__ = ( 

272 "_base_url", 

273 "_base_url_origin", 

274 "_source_traceback", 

275 "_connector", 

276 "_loop", 

277 "_cookie_jar", 

278 "_connector_owner", 

279 "_version", 

280 "_json_serialize", 

281 "_json_serialize_bytes", 

282 "_requote_redirect_url", 

283 "_timeout", 

284 "_raise_for_status", 

285 "_auto_decompress", 

286 "_trust_env", 

287 "_default_headers", 

288 "_skip_auto_headers", 

289 "_request_class", 

290 "_response_class", 

291 "_ws_response_class", 

292 "_trace_configs", 

293 "_read_bufsize", 

294 "_max_line_size", 

295 "_max_field_size", 

296 "_max_headers", 

297 "_resolve_charset", 

298 "_default_proxy", 

299 "_retry_connection", 

300 "_middlewares", 

301 ) 

302 

303 def __init__( 

304 self, 

305 base_url: StrOrURL | None = None, 

306 *, 

307 connector: BaseConnector | None = None, 

308 cookies: LooseCookies | None = None, 

309 headers: LooseHeaders | None = None, 

310 proxy: StrOrURL | None = None, 

311 skip_auto_headers: Iterable[str] | None = None, 

312 json_serialize: JSONEncoder = json.dumps, 

313 json_serialize_bytes: JSONBytesEncoder | None = None, 

314 request_class: type[ClientRequest] = ClientRequest, 

315 response_class: type[ClientResponse] = ClientResponse, 

316 ws_response_class: type[ClientWebSocketResponse] = ClientWebSocketResponse, 

317 version: HttpVersion = http.HttpVersion11, 

318 cookie_jar: AbstractCookieJar | None = None, 

319 connector_owner: bool = True, 

320 raise_for_status: bool | Callable[[ClientResponse], Awaitable[None]] = False, 

321 timeout: _SENTINEL | ClientTimeout | None = sentinel, 

322 auto_decompress: bool = True, 

323 trust_env: bool = False, 

324 requote_redirect_url: bool = True, 

325 trace_configs: list[TraceConfig[object]] | None = None, 

326 read_bufsize: int = DEFAULT_CHUNK_SIZE, 

327 max_line_size: int = 8190, 

328 max_field_size: int = 8190, 

329 max_headers: int = 128, 

330 fallback_charset_resolver: _CharsetResolver = lambda r, b: "utf-8", 

331 middlewares: Sequence[ClientMiddlewareType] = (), 

332 ssl_shutdown_timeout: _SENTINEL | None | float = sentinel, 

333 ) -> None: 

334 # We initialise _connector to None immediately, as it's referenced in __del__() 

335 # and could cause issues if an exception occurs during initialisation. 

336 self._connector: BaseConnector | None = None 

337 if base_url is None or isinstance(base_url, URL): 

338 self._base_url: URL | None = base_url 

339 self._base_url_origin = None if base_url is None else base_url.origin() 

340 else: 

341 self._base_url = URL(base_url) 

342 self._base_url_origin = self._base_url.origin() 

343 assert self._base_url.absolute, "Only absolute URLs are supported" 

344 if self._base_url is not None and not self._base_url.path.endswith("/"): 

345 raise ValueError("base_url must have a trailing '/'") 

346 

347 loop = asyncio.get_running_loop() 

348 

349 if timeout is sentinel or timeout is None: 

350 timeout = DEFAULT_TIMEOUT 

351 if not isinstance(timeout, ClientTimeout): 

352 raise ValueError( 

353 f"timeout parameter cannot be of {type(timeout)} type, " 

354 "please use 'timeout=ClientTimeout(...)'", 

355 ) 

356 self._timeout = timeout 

357 

358 if ssl_shutdown_timeout is not sentinel: 

359 warnings.warn( 

360 "The ssl_shutdown_timeout parameter is deprecated and will be removed in aiohttp 4.0", 

361 DeprecationWarning, 

362 stacklevel=2, 

363 ) 

364 

365 if connector is None: 

366 connector = TCPConnector(ssl_shutdown_timeout=ssl_shutdown_timeout) 

367 # Initialize these three attrs before raising any exception, 

368 # they are used in __del__ 

369 self._connector = connector 

370 self._loop = loop 

371 if loop.get_debug(): 

372 self._source_traceback: traceback.StackSummary | None = ( 

373 traceback.extract_stack(sys._getframe(1)) 

374 ) 

375 else: 

376 self._source_traceback = None 

377 

378 if connector._loop is not loop: 

379 raise RuntimeError("Session and connector have to use same event loop") 

380 

381 if cookie_jar is None: 

382 cookie_jar = CookieJar() 

383 self._cookie_jar = cookie_jar 

384 

385 if cookies: 

386 self._cookie_jar.update_cookies(cookies) 

387 

388 self._connector_owner = connector_owner 

389 self._version = version 

390 self._json_serialize = json_serialize 

391 self._json_serialize_bytes = json_serialize_bytes 

392 self._raise_for_status = raise_for_status 

393 self._auto_decompress = auto_decompress 

394 self._trust_env = trust_env 

395 self._requote_redirect_url = requote_redirect_url 

396 self._read_bufsize = read_bufsize 

397 self._max_line_size = max_line_size 

398 self._max_field_size = max_field_size 

399 self._max_headers = max_headers 

400 

401 # Convert to list of tuples 

402 if headers: 

403 real_headers: CIMultiDict[str] = CIMultiDict(headers) 

404 else: 

405 real_headers = CIMultiDict() 

406 self._default_headers: CIMultiDict[str] = real_headers 

407 if skip_auto_headers is not None: 

408 self._skip_auto_headers = frozenset(istr(i) for i in skip_auto_headers) 

409 else: 

410 self._skip_auto_headers = frozenset() 

411 

412 self._request_class = request_class 

413 self._response_class = response_class 

414 self._ws_response_class = ws_response_class 

415 

416 self._trace_configs = trace_configs or [] 

417 for trace_config in self._trace_configs: 

418 trace_config.freeze() 

419 

420 self._resolve_charset = fallback_charset_resolver 

421 

422 self._default_proxy = proxy 

423 self._retry_connection: bool = True 

424 self._middlewares = middlewares 

425 

426 def __init_subclass__(cls: type["ClientSession"]) -> None: 

427 raise TypeError( 

428 f"Inheritance class {cls.__name__} from ClientSession is forbidden" 

429 ) 

430 

431 def __del__(self, _warnings: Any = warnings) -> None: 

432 if not self.closed: 

433 _warnings.warn( 

434 f"Unclosed client session {self!r}", 

435 ResourceWarning, 

436 source=self, 

437 ) 

438 context = {"client_session": self, "message": "Unclosed client session"} 

439 if self._source_traceback is not None: 

440 context["source_traceback"] = self._source_traceback 

441 self._loop.call_exception_handler(context) 

442 

443 if sys.version_info >= (3, 11) and TYPE_CHECKING: 

444 

445 def request( 

446 self, 

447 method: str, 

448 url: StrOrURL, 

449 **kwargs: Unpack[_RequestOptions], 

450 ) -> "_RequestContextManager": ... 

451 

452 else: 

453 

454 def request( 

455 self, method: str, url: StrOrURL, **kwargs: Any 

456 ) -> "_RequestContextManager": 

457 """Perform HTTP request.""" 

458 return _RequestContextManager(self._request(method, url, **kwargs)) 

459 

460 def _build_url(self, str_or_url: StrOrURL) -> URL: 

461 url = URL(str_or_url) 

462 if self._base_url and not url.absolute: 

463 return self._base_url.join(url) 

464 return url 

465 

466 async def _request( 

467 self, 

468 method: str, 

469 str_or_url: StrOrURL, 

470 *, 

471 params: Query = None, 

472 data: Any = None, 

473 json: Any = None, 

474 cookies: LooseCookies | None = None, 

475 headers: LooseHeaders | None = None, 

476 skip_auto_headers: Iterable[str] | None = None, 

477 allow_redirects: bool = True, 

478 max_redirects: int = 10, 

479 compress: Literal["deflate", "gzip"] | bool = False, 

480 chunked: bool | None = None, 

481 expect100: bool = False, 

482 raise_for_status: ( 

483 None | bool | Callable[[ClientResponse], Awaitable[None]] 

484 ) = None, 

485 read_until_eof: bool = True, 

486 proxy: StrOrURL | None = None, 

487 timeout: ClientTimeout | _SENTINEL | None = sentinel, 

488 ssl: SSLContext | bool | Fingerprint = True, 

489 server_hostname: str | None = None, 

490 proxy_headers: LooseHeaders | None = None, 

491 trace_request_ctx: object = None, 

492 read_bufsize: int | None = None, 

493 auto_decompress: bool | None = None, 

494 max_line_size: int | None = None, 

495 max_field_size: int | None = None, 

496 max_headers: int | None = None, 

497 middlewares: Sequence[ClientMiddlewareType] | None = None, 

498 ) -> ClientResponse: 

499 # NOTE: timeout clamps existing connect and read timeouts. We cannot 

500 # set the default to None because we need to detect if the user wants 

501 # to use the existing timeouts by setting timeout to None. 

502 

503 if self.closed: 

504 raise RuntimeError("Session is closed") 

505 

506 if not isinstance(ssl, SSL_ALLOWED_TYPES): 

507 raise TypeError( 

508 "ssl should be SSLContext, Fingerprint, or bool, " 

509 f"got {ssl!r} instead." 

510 ) 

511 

512 if data is not None and json is not None: 

513 raise ValueError( 

514 "data and json parameters can not be used at the same time" 

515 ) 

516 elif json is not None: 

517 if self._json_serialize_bytes is not None: 

518 data = payload.JsonBytesPayload(json, dumps=self._json_serialize_bytes) 

519 else: 

520 data = payload.JsonPayload(json, dumps=self._json_serialize) 

521 

522 redirects = 0 

523 history: list[ClientResponse] = [] 

524 version = self._version 

525 params = params or {} 

526 

527 # Merge with default headers and transform to CIMultiDict 

528 headers = self._prepare_headers(headers) 

529 

530 try: 

531 url = self._build_url(str_or_url) 

532 except ValueError as e: 

533 raise InvalidUrlClientError(str_or_url) from e 

534 

535 assert self._connector is not None 

536 if url.scheme not in self._connector.allowed_protocol_schema_set: 

537 raise NonHttpUrlClientError(url) 

538 

539 skip_headers: Iterable[istr] | None 

540 if skip_auto_headers is not None: 

541 skip_headers = { 

542 istr(i) for i in skip_auto_headers 

543 } | self._skip_auto_headers 

544 elif self._skip_auto_headers: 

545 skip_headers = self._skip_auto_headers 

546 else: 

547 skip_headers = None 

548 

549 if proxy is None: 

550 proxy = self._default_proxy 

551 

552 resolved_proxy_headers: CIMultiDict[str] | None 

553 if proxy is None: 

554 resolved_proxy_headers = None 

555 else: 

556 resolved_proxy_headers = self._prepare_headers(proxy_headers) 

557 try: 

558 proxy = URL(proxy) 

559 except ValueError as e: 

560 raise InvalidURL(proxy) from e 

561 

562 if timeout is sentinel or timeout is None: 

563 real_timeout: ClientTimeout = self._timeout 

564 else: 

565 real_timeout = timeout 

566 # timeout is cumulative for all request operations 

567 # (request, redirects, responses, data consuming) 

568 tm = TimeoutHandle( 

569 self._loop, real_timeout.total, ceil_threshold=real_timeout.ceil_threshold 

570 ) 

571 handle = tm.start() 

572 

573 if read_bufsize is None: 

574 read_bufsize = self._read_bufsize 

575 

576 if auto_decompress is None: 

577 auto_decompress = self._auto_decompress 

578 

579 if max_line_size is None: 

580 max_line_size = self._max_line_size 

581 

582 if max_field_size is None: 

583 max_field_size = self._max_field_size 

584 

585 if max_headers is None: 

586 max_headers = self._max_headers 

587 

588 traces = [ 

589 Trace( 

590 self, 

591 trace_config, 

592 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), 

593 ) 

594 for trace_config in self._trace_configs 

595 ] 

596 

597 for trace in traces: 

598 await trace.send_request_start(method, url.update_query(params), headers) 

599 

600 timer = tm.timer() 

601 try: 

602 with timer: 

603 # https://www.rfc-editor.org/rfc/rfc9112.html#name-retrying-requests 

604 retry_persistent_connection = ( 

605 self._retry_connection and method in IDEMPOTENT_METHODS 

606 ) 

607 while True: 

608 url, auth_from_url = strip_auth_from_url(url) 

609 if not url.raw_host: 

610 # NOTE: Bail early, otherwise, causes `InvalidURL` through 

611 # NOTE: `self._request_class()` below. 

612 err_exc_cls = ( 

613 InvalidUrlRedirectClientError 

614 if redirects 

615 else InvalidUrlClientError 

616 ) 

617 raise err_exc_cls(url) 

618 

619 if auth_from_url is not None: 

620 # URL-embedded credentials override any Authorization 

621 # header already present (e.g. carried from a previous 

622 # redirect). On the initial request, refuse to silently 

623 # shadow an explicit Authorization header. 

624 if not history and hdrs.AUTHORIZATION in headers: 

625 raise ValueError( 

626 "Cannot combine AUTHORIZATION header with " 

627 "credentials encoded in URL" 

628 ) 

629 headers[hdrs.AUTHORIZATION] = auth_from_url 

630 elif ( 

631 self._trust_env 

632 and url.host is not None 

633 and hdrs.AUTHORIZATION not in headers 

634 ): 

635 # Fall back to ~/.netrc credentials when trust_env is set. 

636 netrc_auth = await self._loop.run_in_executor( 

637 None, self._get_netrc_auth, url.host 

638 ) 

639 if netrc_auth is not None: 

640 headers[hdrs.AUTHORIZATION] = netrc_auth 

641 

642 all_cookies = self._cookie_jar.filter_cookies(url) 

643 

644 if cookies is not None: 

645 tmp_cookie_jar = CookieJar( 

646 unsafe=self._cookie_jar.unsafe, 

647 quote_cookie=self._cookie_jar.quote_cookie, 

648 ) 

649 tmp_cookie_jar.update_cookies(cookies) 

650 req_cookies = tmp_cookie_jar.filter_cookies(url) 

651 if req_cookies: 

652 all_cookies.load(req_cookies) 

653 

654 proxy_: URL | None = None 

655 if proxy is not None: 

656 proxy_ = URL(proxy) 

657 elif self._trust_env: 

658 # Re-resolve per iteration; drop stale env-proxy auth so 

659 # a redirect that switches proxies can't leak credentials. 

660 resolved_proxy_headers = None 

661 with suppress(LookupError): 

662 proxy_, env_proxy_auth = await asyncio.to_thread( 

663 get_env_proxy_for_url, url 

664 ) 

665 if env_proxy_auth is not None: 

666 resolved_proxy_headers = CIMultiDict( 

667 {hdrs.PROXY_AUTHORIZATION: env_proxy_auth} 

668 ) 

669 

670 req = self._request_class( 

671 method, 

672 url, 

673 params=params, 

674 headers=headers, 

675 skip_auto_headers=skip_headers, 

676 data=data, 

677 cookies=all_cookies, 

678 version=version, 

679 compress=compress, 

680 chunked=chunked, 

681 expect100=expect100, 

682 loop=self._loop, 

683 response_class=self._response_class, 

684 proxy=proxy_, 

685 timer=timer, 

686 session=self, 

687 ssl=ssl, 

688 server_hostname=server_hostname, 

689 proxy_headers=resolved_proxy_headers, 

690 traces=traces, 

691 trust_env=self.trust_env, 

692 ) 

693 

694 async def _connect_and_send_request( 

695 req: ClientRequest, 

696 ) -> ClientResponse: 

697 # connection timeout 

698 assert self._connector is not None 

699 try: 

700 conn = await self._connector.connect( 

701 req, traces=traces, timeout=real_timeout 

702 ) 

703 except asyncio.TimeoutError as exc: 

704 raise ConnectionTimeoutError( 

705 f"Connection timeout to host {req.url}" 

706 ) from exc 

707 

708 assert conn.protocol is not None 

709 conn.protocol.set_response_params( 

710 timer=timer, 

711 skip_payload=req.method in EMPTY_BODY_METHODS, 

712 read_until_eof=read_until_eof, 

713 auto_decompress=auto_decompress, 

714 read_timeout=real_timeout.sock_read, 

715 read_bufsize=read_bufsize, 

716 timeout_ceil_threshold=self._connector._timeout_ceil_threshold, 

717 max_line_size=max_line_size, 

718 max_field_size=max_field_size, 

719 max_headers=max_headers, 

720 ) 

721 try: 

722 resp = await req._send(conn) 

723 try: 

724 await resp.start(conn) 

725 except BaseException: 

726 resp.close() 

727 raise 

728 except BaseException: 

729 conn.close() 

730 raise 

731 return resp 

732 

733 # Apply middleware (if any) - per-request middleware overrides session middleware 

734 effective_middlewares = ( 

735 self._middlewares if middlewares is None else middlewares 

736 ) 

737 

738 if effective_middlewares: 

739 handler = build_client_middlewares( 

740 _connect_and_send_request, effective_middlewares 

741 ) 

742 else: 

743 handler = _connect_and_send_request 

744 

745 try: 

746 resp = await handler(req) 

747 # Client connector errors should not be retried 

748 except ( 

749 ConnectionTimeoutError, 

750 ClientConnectorError, 

751 ClientConnectorCertificateError, 

752 ClientConnectorSSLError, 

753 ): 

754 raise 

755 except (ClientOSError, ServerDisconnectedError): 

756 if retry_persistent_connection: 

757 retry_persistent_connection = False 

758 continue 

759 raise 

760 except ClientError: 

761 raise 

762 except OSError as exc: 

763 if exc.errno is None and isinstance(exc, asyncio.TimeoutError): 

764 raise 

765 raise ClientOSError(*exc.args) from exc 

766 

767 # Update cookies from raw headers to preserve duplicates 

768 if resp._raw_cookie_headers: 

769 self._cookie_jar.update_cookies_from_headers( 

770 resp._raw_cookie_headers, resp.url 

771 ) 

772 

773 # redirects 

774 if resp.status in (301, 302, 303, 307, 308) and allow_redirects: 

775 for trace in traces: 

776 await trace.send_request_redirect( 

777 method, url.update_query(params), headers, resp 

778 ) 

779 

780 redirects += 1 

781 history.append(resp) 

782 if max_redirects and redirects >= max_redirects: 

783 if req._body is not None: 

784 await req._body.close() 

785 resp.close() 

786 raise TooManyRedirects( 

787 history[0].request_info, tuple(history) 

788 ) 

789 

790 # For 301 and 302, mimic IE, now changed in RFC 

791 # https://github.com/kennethreitz/requests/pull/269 

792 if (resp.status == 303 and resp.method != hdrs.METH_HEAD) or ( 

793 resp.status in (301, 302) and resp.method == hdrs.METH_POST 

794 ): 

795 method = hdrs.METH_GET 

796 data = None 

797 if headers.get(hdrs.CONTENT_LENGTH): 

798 headers.pop(hdrs.CONTENT_LENGTH) 

799 else: 

800 # For 307/308, always preserve the request body 

801 # For 301/302 with non-POST methods, preserve the request body 

802 # https://www.rfc-editor.org/rfc/rfc9110#section-15.4.3-3.1 

803 # Use the existing payload to avoid recreating it from 

804 # a potentially consumed file. 

805 # 

806 # If the payload is already consumed and cannot be replayed, 

807 # fail fast instead of silently sending an empty body. 

808 if req._body.consumed: 

809 resp.close() 

810 raise ClientPayloadError( 

811 "Cannot follow redirect with a consumed request " 

812 "body. Use bytes, a seekable file-like object, " 

813 "or set allow_redirects=False." 

814 ) 

815 data = req._body 

816 

817 r_url = resp.headers.get(hdrs.LOCATION) or resp.headers.get( 

818 hdrs.URI 

819 ) 

820 if r_url is None: 

821 # see github.com/aio-libs/aiohttp/issues/2022 

822 break 

823 else: 

824 # reading from correct redirection 

825 # response is forbidden 

826 resp.release() 

827 

828 try: 

829 parsed_redirect_url = URL( 

830 r_url, encoded=not self._requote_redirect_url 

831 ) 

832 except ValueError as e: 

833 if req._body is not None: 

834 await req._body.close() 

835 resp.close() 

836 raise InvalidUrlRedirectClientError( 

837 r_url, 

838 "Server attempted redirecting to a location that does not look like a URL", 

839 ) from e 

840 

841 scheme = parsed_redirect_url.scheme 

842 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: 

843 if req._body is not None: 

844 await req._body.close() 

845 resp.close() 

846 raise NonHttpUrlRedirectClientError(r_url) 

847 elif not scheme: 

848 parsed_redirect_url = url.join(parsed_redirect_url) 

849 

850 try: 

851 redirect_origin = parsed_redirect_url.origin() 

852 except ValueError as origin_val_err: 

853 if req._body is not None: 

854 await req._body.close() 

855 resp.close() 

856 raise InvalidUrlRedirectClientError( 

857 parsed_redirect_url, 

858 "Invalid redirect URL origin", 

859 ) from origin_val_err 

860 

861 if url.origin() != redirect_origin: 

862 cookies = None 

863 headers.pop(hdrs.AUTHORIZATION, None) 

864 headers.pop(hdrs.COOKIE, None) 

865 headers.pop(hdrs.PROXY_AUTHORIZATION, None) 

866 

867 url = parsed_redirect_url 

868 params = {} 

869 resp.release() 

870 continue 

871 

872 break 

873 

874 if req._body is not None: 

875 await req._body.close() 

876 # check response status 

877 if raise_for_status is None: 

878 raise_for_status = self._raise_for_status 

879 

880 if callable(raise_for_status): 

881 await raise_for_status(resp) 

882 elif raise_for_status: 

883 resp.raise_for_status() 

884 

885 # register connection 

886 if handle is not None: 

887 if resp.connection is not None: 

888 resp.connection.add_callback(handle.cancel) 

889 else: 

890 handle.cancel() 

891 

892 resp._history = tuple(history) 

893 

894 for trace in traces: 

895 await trace.send_request_end( 

896 method, url.update_query(params), headers, resp 

897 ) 

898 return resp 

899 

900 except BaseException as e: 

901 # cleanup timer 

902 tm.close() 

903 if handle: 

904 handle.cancel() 

905 handle = None 

906 

907 for trace in traces: 

908 await trace.send_request_exception( 

909 method, url.update_query(params), headers, e 

910 ) 

911 raise 

912 

913 if sys.version_info >= (3, 11) and TYPE_CHECKING: 

914 

915 @overload 

916 def ws_connect( 

917 self, 

918 url: StrOrURL, 

919 *, 

920 decode_text: Literal[True] = ..., 

921 **kwargs: Unpack[_WSConnectOptions], 

922 ) -> "_BaseRequestContextManager[ClientWebSocketResponse[Literal[True]]]": ... 

923 

924 @overload 

925 def ws_connect( 

926 self, 

927 url: StrOrURL, 

928 *, 

929 decode_text: Literal[False], 

930 **kwargs: Unpack[_WSConnectOptions], 

931 ) -> "_BaseRequestContextManager[ClientWebSocketResponse[Literal[False]]]": ... 

932 

933 @overload 

934 def ws_connect( 

935 self, 

936 url: StrOrURL, 

937 *, 

938 decode_text: bool = ..., 

939 **kwargs: Unpack[_WSConnectOptions], 

940 ) -> "_BaseRequestContextManager[ClientWebSocketResponse[bool]]": ... 

941 

942 def ws_connect( 

943 self, 

944 url: StrOrURL, 

945 *, 

946 method: str = hdrs.METH_GET, 

947 protocols: Collection[str] = (), 

948 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

949 receive_timeout: float | None = None, 

950 autoclose: bool = True, 

951 autoping: bool = True, 

952 heartbeat: float | None = None, 

953 origin: str | None = None, 

954 params: Query = None, 

955 headers: LooseHeaders | None = None, 

956 proxy: StrOrURL | None = None, 

957 ssl: SSLContext | bool | Fingerprint = True, 

958 server_hostname: str | None = None, 

959 proxy_headers: LooseHeaders | None = None, 

960 compress: int = 0, 

961 max_msg_size: int = 4 * 1024 * 1024, 

962 decode_text: bool = True, 

963 ) -> "_BaseRequestContextManager[ClientWebSocketResponse[bool]]": 

964 """Initiate websocket connection.""" 

965 return _WSRequestContextManager( 

966 self._ws_connect( 

967 url, 

968 method=method, 

969 protocols=protocols, 

970 timeout=timeout, 

971 receive_timeout=receive_timeout, 

972 autoclose=autoclose, 

973 autoping=autoping, 

974 heartbeat=heartbeat, 

975 origin=origin, 

976 params=params, 

977 headers=headers, 

978 proxy=proxy, 

979 ssl=ssl, 

980 server_hostname=server_hostname, 

981 proxy_headers=proxy_headers, 

982 compress=compress, 

983 max_msg_size=max_msg_size, 

984 decode_text=decode_text, 

985 ) 

986 ) 

987 

988 if sys.version_info >= (3, 11) and TYPE_CHECKING: 

989 

990 @overload 

991 async def _ws_connect( 

992 self, 

993 url: StrOrURL, 

994 *, 

995 decode_text: Literal[True] = ..., 

996 **kwargs: Unpack[_WSConnectOptions], 

997 ) -> "ClientWebSocketResponse[Literal[True]]": ... 

998 

999 @overload 

1000 async def _ws_connect( 

1001 self, 

1002 url: StrOrURL, 

1003 *, 

1004 decode_text: Literal[False], 

1005 **kwargs: Unpack[_WSConnectOptions], 

1006 ) -> "ClientWebSocketResponse[Literal[False]]": ... 

1007 

1008 @overload 

1009 async def _ws_connect( 

1010 self, 

1011 url: StrOrURL, 

1012 *, 

1013 decode_text: bool = ..., 

1014 **kwargs: Unpack[_WSConnectOptions], 

1015 ) -> "ClientWebSocketResponse[bool]": ... 

1016 

1017 async def _ws_connect( 

1018 self, 

1019 url: StrOrURL, 

1020 *, 

1021 method: str = hdrs.METH_GET, 

1022 protocols: Collection[str] = (), 

1023 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

1024 receive_timeout: float | None = None, 

1025 autoclose: bool = True, 

1026 autoping: bool = True, 

1027 heartbeat: float | None = None, 

1028 origin: str | None = None, 

1029 params: Query = None, 

1030 headers: LooseHeaders | None = None, 

1031 proxy: StrOrURL | None = None, 

1032 ssl: SSLContext | bool | Fingerprint = True, 

1033 server_hostname: str | None = None, 

1034 proxy_headers: LooseHeaders | None = None, 

1035 compress: int = 0, 

1036 max_msg_size: int = 4 * 1024 * 1024, 

1037 decode_text: bool = True, 

1038 ) -> "ClientWebSocketResponse[bool]": 

1039 if timeout is not sentinel: 

1040 if isinstance(timeout, ClientWSTimeout): 

1041 ws_timeout = timeout 

1042 else: 

1043 warnings.warn( # type: ignore[unreachable] 

1044 "parameter 'timeout' of type 'float' " 

1045 "is deprecated, please use " 

1046 "'timeout=ClientWSTimeout(ws_close=...)'", 

1047 DeprecationWarning, 

1048 stacklevel=2, 

1049 ) 

1050 ws_timeout = ClientWSTimeout(ws_close=timeout) 

1051 else: 

1052 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT 

1053 if receive_timeout is not None: 

1054 warnings.warn( 

1055 "float parameter 'receive_timeout' " 

1056 "is deprecated, please use parameter " 

1057 "'timeout=ClientWSTimeout(ws_receive=...)'", 

1058 DeprecationWarning, 

1059 stacklevel=2, 

1060 ) 

1061 ws_timeout = dataclasses.replace(ws_timeout, ws_receive=receive_timeout) 

1062 

1063 if headers is None: 

1064 real_headers: CIMultiDict[str] = CIMultiDict() 

1065 else: 

1066 real_headers = CIMultiDict(headers) 

1067 

1068 default_headers = { 

1069 hdrs.UPGRADE: "websocket", 

1070 hdrs.CONNECTION: "Upgrade", 

1071 hdrs.SEC_WEBSOCKET_VERSION: "13", 

1072 } 

1073 

1074 for key, value in default_headers.items(): 

1075 real_headers.setdefault(key, value) 

1076 

1077 sec_key = base64.b64encode(os.urandom(16)) 

1078 real_headers[hdrs.SEC_WEBSOCKET_KEY] = sec_key.decode() 

1079 

1080 if protocols: 

1081 real_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ",".join(protocols) 

1082 if origin is not None: 

1083 real_headers[hdrs.ORIGIN] = origin 

1084 if compress: 

1085 extstr = ws_ext_gen(compress=compress) 

1086 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

1087 

1088 if not isinstance(ssl, SSL_ALLOWED_TYPES): 

1089 raise TypeError( 

1090 "ssl should be SSLContext, Fingerprint, or bool, " 

1091 f"got {ssl!r} instead." 

1092 ) 

1093 

1094 # send request 

1095 resp = await self.request( 

1096 method, 

1097 url, 

1098 params=params, 

1099 headers=real_headers, 

1100 read_until_eof=False, 

1101 proxy=proxy, 

1102 ssl=ssl, 

1103 server_hostname=server_hostname, 

1104 proxy_headers=proxy_headers, 

1105 ) 

1106 

1107 try: 

1108 # check handshake 

1109 if resp.status != 101: 

1110 raise WSServerHandshakeError( 

1111 resp.request_info, 

1112 resp.history, 

1113 message="Invalid response status", 

1114 status=resp.status, 

1115 headers=resp.headers, 

1116 ) 

1117 

1118 if resp.headers.get(hdrs.UPGRADE, "").lower() != "websocket": 

1119 raise WSServerHandshakeError( 

1120 resp.request_info, 

1121 resp.history, 

1122 message="Invalid upgrade header", 

1123 status=resp.status, 

1124 headers=resp.headers, 

1125 ) 

1126 

1127 if resp.headers.get(hdrs.CONNECTION, "").lower() != "upgrade": 

1128 raise WSServerHandshakeError( 

1129 resp.request_info, 

1130 resp.history, 

1131 message="Invalid connection header", 

1132 status=resp.status, 

1133 headers=resp.headers, 

1134 ) 

1135 

1136 # key calculation 

1137 r_key = resp.headers.get(hdrs.SEC_WEBSOCKET_ACCEPT, "") 

1138 match = base64.b64encode(hashlib.sha1(sec_key + WS_KEY).digest()).decode() 

1139 if r_key != match: 

1140 raise WSServerHandshakeError( 

1141 resp.request_info, 

1142 resp.history, 

1143 message="Invalid challenge response", 

1144 status=resp.status, 

1145 headers=resp.headers, 

1146 ) 

1147 

1148 # websocket protocol 

1149 protocol = None 

1150 if protocols and hdrs.SEC_WEBSOCKET_PROTOCOL in resp.headers: 

1151 resp_protocols = [ 

1152 proto.strip() 

1153 for proto in resp.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",") 

1154 ] 

1155 

1156 for proto in resp_protocols: 

1157 if proto in protocols: 

1158 protocol = proto 

1159 break 

1160 

1161 # websocket compress 

1162 notakeover = False 

1163 if compress: 

1164 compress_hdrs = resp.headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS) 

1165 if compress_hdrs: 

1166 try: 

1167 compress, notakeover = ws_ext_parse(compress_hdrs) 

1168 except WSHandshakeError as exc: 

1169 raise WSServerHandshakeError( 

1170 resp.request_info, 

1171 resp.history, 

1172 message=exc.args[0], 

1173 status=resp.status, 

1174 headers=resp.headers, 

1175 ) from exc 

1176 else: 

1177 compress = 0 

1178 notakeover = False 

1179 

1180 conn = resp.connection 

1181 assert conn is not None 

1182 conn_proto = conn.protocol 

1183 assert conn_proto is not None 

1184 

1185 # For WS connection the read_timeout must be either ws_timeout.ws_receive or greater 

1186 # None == no timeout, i.e. infinite timeout, so None is the max timeout possible 

1187 if ws_timeout.ws_receive is None: 

1188 # Reset regardless 

1189 conn_proto.read_timeout = None 

1190 elif conn_proto.read_timeout is not None: 

1191 conn_proto.read_timeout = max( 

1192 ws_timeout.ws_receive, conn_proto.read_timeout 

1193 ) 

1194 

1195 transport = conn.transport 

1196 assert transport is not None 

1197 reader = WebSocketDataQueue(conn_proto, DEFAULT_CHUNK_SIZE, loop=self._loop) 

1198 writer = WebSocketWriter( 

1199 conn_proto, 

1200 transport, 

1201 use_mask=True, 

1202 compress=compress, 

1203 notakeover=notakeover, 

1204 ) 

1205 except BaseException: 

1206 resp.close() 

1207 raise 

1208 else: 

1209 ws_resp = self._ws_response_class( 

1210 reader, 

1211 writer, 

1212 protocol, 

1213 resp, 

1214 ws_timeout, 

1215 autoclose, 

1216 autoping, 

1217 self._loop, 

1218 heartbeat=heartbeat, 

1219 compress=compress, 

1220 client_notakeover=notakeover, 

1221 ) 

1222 parser = WebSocketReader(reader, max_msg_size, decode_text=decode_text) 

1223 cb = None if heartbeat is None else ws_resp._on_data_received 

1224 conn_proto.set_parser(parser, reader, data_received_cb=cb) 

1225 return ws_resp 

1226 

1227 def _prepare_headers(self, headers: LooseHeaders | None) -> "CIMultiDict[str]": 

1228 """Add default headers and transform it to CIMultiDict""" 

1229 # Convert headers to MultiDict 

1230 result = CIMultiDict(self._default_headers) 

1231 if headers: 

1232 if not isinstance(headers, (MultiDictProxy, MultiDict)): 

1233 headers = CIMultiDict(headers) 

1234 added_names: set[str] = set() 

1235 for key, value in headers.items(): 

1236 if key in added_names: 

1237 result.add(key, value) 

1238 else: 

1239 result[key] = value 

1240 added_names.add(key) 

1241 return result 

1242 

1243 def _get_netrc_auth(self, host: str) -> str | None: 

1244 """Return an ``Authorization`` header value for ``host`` from netrc. 

1245 

1246 Designed to be called in an executor to avoid blocking I/O on the 

1247 event loop. 

1248 """ 

1249 netrc_obj = netrc_from_env() 

1250 try: 

1251 return _auth_header_from_netrc(netrc_obj, host) 

1252 except LookupError: 

1253 return None 

1254 

1255 if sys.version_info >= (3, 11) and TYPE_CHECKING: 

1256 

1257 def get( 

1258 self, 

1259 url: StrOrURL, 

1260 **kwargs: Unpack[_RequestOptions], 

1261 ) -> "_RequestContextManager": ... 

1262 

1263 def options( 

1264 self, 

1265 url: StrOrURL, 

1266 **kwargs: Unpack[_RequestOptions], 

1267 ) -> "_RequestContextManager": ... 

1268 

1269 def head( 

1270 self, 

1271 url: StrOrURL, 

1272 **kwargs: Unpack[_RequestOptions], 

1273 ) -> "_RequestContextManager": ... 

1274 

1275 def post( 

1276 self, 

1277 url: StrOrURL, 

1278 **kwargs: Unpack[_RequestOptions], 

1279 ) -> "_RequestContextManager": ... 

1280 

1281 def put( 

1282 self, 

1283 url: StrOrURL, 

1284 **kwargs: Unpack[_RequestOptions], 

1285 ) -> "_RequestContextManager": ... 

1286 

1287 def patch( 

1288 self, 

1289 url: StrOrURL, 

1290 **kwargs: Unpack[_RequestOptions], 

1291 ) -> "_RequestContextManager": ... 

1292 

1293 def delete( 

1294 self, 

1295 url: StrOrURL, 

1296 **kwargs: Unpack[_RequestOptions], 

1297 ) -> "_RequestContextManager": ... 

1298 

1299 else: 

1300 

1301 def get( 

1302 self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any 

1303 ) -> "_RequestContextManager": 

1304 """Perform HTTP GET request.""" 

1305 return _RequestContextManager( 

1306 self._request( 

1307 hdrs.METH_GET, url, allow_redirects=allow_redirects, **kwargs 

1308 ) 

1309 ) 

1310 

1311 def options( 

1312 self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any 

1313 ) -> "_RequestContextManager": 

1314 """Perform HTTP OPTIONS request.""" 

1315 return _RequestContextManager( 

1316 self._request( 

1317 hdrs.METH_OPTIONS, url, allow_redirects=allow_redirects, **kwargs 

1318 ) 

1319 ) 

1320 

1321 def head( 

1322 self, url: StrOrURL, *, allow_redirects: bool = False, **kwargs: Any 

1323 ) -> "_RequestContextManager": 

1324 """Perform HTTP HEAD request.""" 

1325 return _RequestContextManager( 

1326 self._request( 

1327 hdrs.METH_HEAD, url, allow_redirects=allow_redirects, **kwargs 

1328 ) 

1329 ) 

1330 

1331 def post( 

1332 self, url: StrOrURL, *, data: Any = None, **kwargs: Any 

1333 ) -> "_RequestContextManager": 

1334 """Perform HTTP POST request.""" 

1335 return _RequestContextManager( 

1336 self._request(hdrs.METH_POST, url, data=data, **kwargs) 

1337 ) 

1338 

1339 def put( 

1340 self, url: StrOrURL, *, data: Any = None, **kwargs: Any 

1341 ) -> "_RequestContextManager": 

1342 """Perform HTTP PUT request.""" 

1343 return _RequestContextManager( 

1344 self._request(hdrs.METH_PUT, url, data=data, **kwargs) 

1345 ) 

1346 

1347 def patch( 

1348 self, url: StrOrURL, *, data: Any = None, **kwargs: Any 

1349 ) -> "_RequestContextManager": 

1350 """Perform HTTP PATCH request.""" 

1351 return _RequestContextManager( 

1352 self._request(hdrs.METH_PATCH, url, data=data, **kwargs) 

1353 ) 

1354 

1355 def delete(self, url: StrOrURL, **kwargs: Any) -> "_RequestContextManager": 

1356 """Perform HTTP DELETE request.""" 

1357 return _RequestContextManager( 

1358 self._request(hdrs.METH_DELETE, url, **kwargs) 

1359 ) 

1360 

1361 async def close(self) -> None: 

1362 """Close underlying connector. 

1363 

1364 Release all acquired resources. 

1365 """ 

1366 if not self.closed: 

1367 if self._connector is not None and self._connector_owner: 

1368 await self._connector.close() 

1369 self._connector = None 

1370 

1371 @property 

1372 def closed(self) -> bool: 

1373 """Is client session closed. 

1374 

1375 A readonly property. 

1376 """ 

1377 return self._connector is None or self._connector.closed 

1378 

1379 @property 

1380 def connector(self) -> BaseConnector | None: 

1381 """Connector instance used for the session.""" 

1382 return self._connector 

1383 

1384 @property 

1385 def cookie_jar(self) -> AbstractCookieJar: 

1386 """The session cookies.""" 

1387 return self._cookie_jar 

1388 

1389 @property 

1390 def version(self) -> tuple[int, int]: 

1391 """The session HTTP protocol version.""" 

1392 return self._version 

1393 

1394 @property 

1395 def requote_redirect_url(self) -> bool: 

1396 """Do URL requoting on redirection handling.""" 

1397 return self._requote_redirect_url 

1398 

1399 @property 

1400 def timeout(self) -> ClientTimeout: 

1401 """Timeout for the session.""" 

1402 return self._timeout 

1403 

1404 @property 

1405 def headers(self) -> "CIMultiDict[str]": 

1406 """The default headers of the client session.""" 

1407 return self._default_headers 

1408 

1409 @property 

1410 def skip_auto_headers(self) -> frozenset[istr]: 

1411 """Headers for which autogeneration should be skipped""" 

1412 return self._skip_auto_headers 

1413 

1414 @property 

1415 def json_serialize(self) -> JSONEncoder: 

1416 """Json serializer callable""" 

1417 return self._json_serialize 

1418 

1419 @property 

1420 def connector_owner(self) -> bool: 

1421 """Should connector be closed on session closing""" 

1422 return self._connector_owner 

1423 

1424 @property 

1425 def raise_for_status( 

1426 self, 

1427 ) -> bool | Callable[[ClientResponse], Awaitable[None]]: 

1428 """Should `ClientResponse.raise_for_status()` be called for each response.""" 

1429 return self._raise_for_status 

1430 

1431 @property 

1432 def auto_decompress(self) -> bool: 

1433 """Should the body response be automatically decompressed.""" 

1434 return self._auto_decompress 

1435 

1436 @property 

1437 def trust_env(self) -> bool: 

1438 """ 

1439 Should proxies information from environment or netrc be trusted. 

1440 

1441 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1442 or ~/.netrc file if present. 

1443 """ 

1444 return self._trust_env 

1445 

1446 @property 

1447 def trace_configs(self) -> list[TraceConfig[Any]]: 

1448 """A list of TraceConfig instances used for client tracing""" 

1449 return self._trace_configs 

1450 

1451 def detach(self) -> None: 

1452 """Detach connector from session without closing the former. 

1453 

1454 Session is switched to closed state anyway. 

1455 """ 

1456 self._connector = None 

1457 

1458 async def __aenter__(self) -> "ClientSession": 

1459 return self 

1460 

1461 async def __aexit__( 

1462 self, 

1463 exc_type: type[BaseException] | None, 

1464 exc_val: BaseException | None, 

1465 exc_tb: TracebackType | None, 

1466 ) -> None: 

1467 await self.close() 

1468 

1469 

1470class _BaseRequestContextManager( 

1471 Coroutine[Any, Any, _RetType_co], Generic[_RetType_co] 

1472): 

1473 __slots__ = ("_coro", "_resp") 

1474 

1475 def __init__(self, coro: Coroutine[asyncio.Future[Any], None, _RetType_co]) -> None: 

1476 self._coro: Coroutine[asyncio.Future[Any], None, _RetType_co] = coro 

1477 

1478 def send(self, arg: None) -> asyncio.Future[Any]: 

1479 return self._coro.send(arg) 

1480 

1481 def throw(self, *args: Any, **kwargs: Any) -> asyncio.Future[Any]: 

1482 return self._coro.throw(*args, **kwargs) 

1483 

1484 def close(self) -> None: 

1485 return self._coro.close() 

1486 

1487 def __await__(self) -> Generator[Any, None, _RetType_co]: 

1488 ret = self._coro.__await__() 

1489 return ret 

1490 

1491 def __iter__(self) -> Generator[Any, None, _RetType_co]: 

1492 return self.__await__() 

1493 

1494 async def __aenter__(self) -> _RetType_co: 

1495 self._resp: _RetType_co = await self._coro 

1496 return await self._resp.__aenter__() # type: ignore[return-value] 

1497 

1498 async def __aexit__( 

1499 self, 

1500 exc_type: type[BaseException] | None, 

1501 exc: BaseException | None, 

1502 tb: TracebackType | None, 

1503 ) -> None: 

1504 await self._resp.__aexit__(exc_type, exc, tb) 

1505 

1506 

1507_RequestContextManager = _BaseRequestContextManager[ClientResponse] 

1508_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse[bool]] 

1509 

1510 

1511class _SessionRequestContextManager: 

1512 __slots__ = ("_coro", "_resp", "_session") 

1513 

1514 def __init__( 

1515 self, 

1516 coro: Coroutine[asyncio.Future[Any], None, ClientResponse], 

1517 session: ClientSession, 

1518 ) -> None: 

1519 self._coro = coro 

1520 self._resp: ClientResponse | None = None 

1521 self._session = session 

1522 

1523 async def __aenter__(self) -> ClientResponse: 

1524 try: 

1525 self._resp = await self._coro 

1526 except BaseException: 

1527 await self._session.close() 

1528 raise 

1529 else: 

1530 return self._resp 

1531 

1532 async def __aexit__( 

1533 self, 

1534 exc_type: type[BaseException] | None, 

1535 exc: BaseException | None, 

1536 tb: TracebackType | None, 

1537 ) -> None: 

1538 assert self._resp is not None 

1539 self._resp.close() 

1540 await self._session.close() 

1541 

1542 

1543if sys.version_info >= (3, 11) and TYPE_CHECKING: 

1544 

1545 def request( 

1546 method: str, 

1547 url: StrOrURL, 

1548 *, 

1549 version: HttpVersion = http.HttpVersion11, 

1550 connector: BaseConnector | None = None, 

1551 **kwargs: Unpack[_RequestOptions], 

1552 ) -> _SessionRequestContextManager: ... 

1553 

1554else: 

1555 

1556 def request( 

1557 method: str, 

1558 url: StrOrURL, 

1559 *, 

1560 version: HttpVersion = http.HttpVersion11, 

1561 connector: BaseConnector | None = None, 

1562 **kwargs: Any, 

1563 ) -> _SessionRequestContextManager: 

1564 """Constructs and sends a request. 

1565 

1566 Returns response object. 

1567 method - HTTP method 

1568 url - request url 

1569 params - (optional) Dictionary or bytes to be sent in the query 

1570 string of the new request 

1571 data - (optional) Dictionary, bytes, or file-like object to 

1572 send in the body of the request 

1573 json - (optional) Any json compatible python object 

1574 headers - (optional) Dictionary of HTTP Headers to send with 

1575 the request 

1576 cookies - (optional) Dict object to send with the request 

1577 allow_redirects - (optional) If set to False, do not follow 

1578 redirects 

1579 version - Request HTTP version. 

1580 compress - Set to True if request has to be compressed 

1581 with deflate encoding. 

1582 chunked - Set to chunk size for chunked transfer encoding. 

1583 expect100 - Expect 100-continue response from server. 

1584 connector - BaseConnector sub-class instance to support 

1585 connection pooling. 

1586 read_until_eof - Read response until eof if response 

1587 does not have Content-Length header. 

1588 loop - Optional event loop. 

1589 timeout - Optional ClientTimeout settings structure, 5min 

1590 total timeout by default. 

1591 Usage:: 

1592 >>> import aiohttp 

1593 >>> async with aiohttp.request('GET', 'http://python.org/') as resp: 

1594 ... print(resp) 

1595 ... data = await resp.read() 

1596 <ClientResponse(https://www.python.org/) [200 OK]> 

1597 """ 

1598 connector_owner = False 

1599 if connector is None: 

1600 connector_owner = True 

1601 connector = TCPConnector(force_close=True) 

1602 

1603 session = ClientSession( 

1604 cookies=kwargs.pop("cookies", None), 

1605 version=version, 

1606 timeout=kwargs.pop("timeout", sentinel), 

1607 connector=connector, 

1608 connector_owner=connector_owner, 

1609 ) 

1610 

1611 return _SessionRequestContextManager( 

1612 session._request(method, url, **kwargs), 

1613 session, 

1614 )