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

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

624 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 Generic, 

27 Literal, 

28 TypedDict, 

29 TypeVar, 

30 final, 

31 overload, 

32) 

33 

34from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr 

35from yarl import URL, Query 

36 

37from . import hdrs, http, payload 

38from ._websocket.reader import WebSocketDataQueue 

39from .abc import AbstractCookieJar 

40from .client_exceptions import ( 

41 ClientConnectionError, 

42 ClientConnectionResetError, 

43 ClientConnectorCertificateError, 

44 ClientConnectorDNSError, 

45 ClientConnectorError, 

46 ClientConnectorSSLError, 

47 ClientError, 

48 ClientHttpProxyError, 

49 ClientOSError, 

50 ClientPayloadError, 

51 ClientProxyConnectionError, 

52 ClientResponseError, 

53 ClientSSLError, 

54 ConnectionTimeoutError, 

55 ContentTypeError, 

56 InvalidURL, 

57 InvalidUrlClientError, 

58 InvalidUrlRedirectClientError, 

59 NonHttpUrlClientError, 

60 NonHttpUrlRedirectClientError, 

61 RedirectClientError, 

62 ServerConnectionError, 

63 ServerDisconnectedError, 

64 ServerFingerprintMismatch, 

65 ServerTimeoutError, 

66 SocketTimeoutError, 

67 TooManyRedirects, 

68 WSMessageTypeError, 

69 WSServerHandshakeError, 

70) 

71from .client_middlewares import ClientMiddlewareType, _cached_build_client_middlewares 

72from .client_reqrep import ( 

73 SSL_ALLOWED_TYPES, 

74 ClientRequest, 

75 ClientResponse, 

76 ClientTimeout, 

77 Fingerprint, 

78 RequestInfo, 

79 ResponseParams, 

80) 

81from .client_ws import ( 

82 DEFAULT_WS_CLIENT_TIMEOUT, 

83 ClientWebSocketResponse, 

84 ClientWSTimeout, 

85) 

86from .connector import BaseConnector, NamedPipeConnector, TCPConnector, UnixConnector 

87from .cookiejar import CookieJar 

88from .helpers import ( 

89 _SENTINEL, 

90 DEFAULT_CHUNK_SIZE, 

91 EMPTY_BODY_METHODS, 

92 HTTP_AND_EMPTY_SCHEMA_SET, 

93 TimeoutHandle, 

94 _auth_header_from_netrc, 

95 get_env_proxy_for_url, 

96 netrc_from_env, 

97 sentinel, 

98 strip_auth_from_url, 

99) 

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

101from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse 

102from .tracing import Trace, TraceConfig 

103from .typedefs import ( 

104 JSONBytesEncoder, 

105 JSONEncoder, 

106 LooseCookies, 

107 LooseHeaders, 

108 StrOrURL, 

109) 

110 

111__all__ = ( 

112 # client_exceptions 

113 "ClientConnectionError", 

114 "ClientConnectionResetError", 

115 "ClientConnectorCertificateError", 

116 "ClientConnectorDNSError", 

117 "ClientConnectorError", 

118 "ClientConnectorSSLError", 

119 "ClientError", 

120 "ClientHttpProxyError", 

121 "ClientOSError", 

122 "ClientPayloadError", 

123 "ClientProxyConnectionError", 

124 "ClientResponseError", 

125 "ClientSSLError", 

126 "ConnectionTimeoutError", 

127 "ContentTypeError", 

128 "InvalidURL", 

129 "InvalidUrlClientError", 

130 "RedirectClientError", 

131 "NonHttpUrlClientError", 

132 "InvalidUrlRedirectClientError", 

133 "NonHttpUrlRedirectClientError", 

134 "ServerConnectionError", 

135 "ServerDisconnectedError", 

136 "ServerFingerprintMismatch", 

137 "ServerTimeoutError", 

138 "SocketTimeoutError", 

139 "TooManyRedirects", 

140 "WSServerHandshakeError", 

141 # client_reqrep 

142 "ClientRequest", 

143 "ClientResponse", 

144 "Fingerprint", 

145 "RequestInfo", 

146 # connector 

147 "BaseConnector", 

148 "TCPConnector", 

149 "UnixConnector", 

150 "NamedPipeConnector", 

151 # client_ws 

152 "ClientWebSocketResponse", 

153 # client 

154 "ClientSession", 

155 "ClientTimeout", 

156 "ClientWSTimeout", 

157 "request", 

158 "WSMessageTypeError", 

159) 

160 

161 

162if TYPE_CHECKING: 

163 from ssl import SSLContext 

164else: 

165 SSLContext = None 

166 

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

168 from typing import Unpack 

169 

170 

171class _RequestOptions(TypedDict, total=False): 

172 params: Query 

173 data: Any 

174 json: Any 

175 cookies: LooseCookies | None 

176 headers: LooseHeaders | None 

177 skip_auto_headers: Iterable[str] | None 

178 allow_redirects: bool 

179 max_redirects: int 

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

181 chunked: bool | None 

182 expect100: bool 

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

184 read_until_eof: bool 

185 proxy: StrOrURL | None 

186 timeout: "ClientTimeout | _SENTINEL | None" 

187 ssl: SSLContext | bool | Fingerprint | _SENTINEL 

188 server_hostname: str | None 

189 proxy_headers: LooseHeaders | None 

190 trace_request_ctx: object 

191 read_bufsize: int | None 

192 auto_decompress: bool | None 

193 max_line_size: int | None 

194 max_field_size: int | None 

195 max_headers: int | None 

196 middlewares: Sequence[ClientMiddlewareType] | None 

197 

198 

199class _WSConnectOptions(TypedDict, total=False): 

200 method: str 

201 protocols: Collection[str] 

202 timeout: "ClientWSTimeout | _SENTINEL" 

203 receive_timeout: float | None 

204 autoclose: bool 

205 autoping: bool 

206 heartbeat: float | None 

207 origin: str | None 

208 params: Query 

209 headers: LooseHeaders | None 

210 proxy: StrOrURL | None 

211 ssl: SSLContext | bool | Fingerprint | _SENTINEL 

212 server_hostname: str | None 

213 proxy_headers: LooseHeaders | None 

214 compress: int 

215 max_msg_size: int 

216 

217 

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

219# https://www.rfc-editor.org/info/rfc10008/#section-1-12 

220IDEMPOTENT_METHODS = frozenset( 

221 {"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE", "QUERY"} 

222) 

223 

224_RetType_co = TypeVar( 

225 "_RetType_co", 

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

227 covariant=True, 

228) 

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

230 

231 

232# Module-level (not a closure) so it has a stable identity for the 

233# ``_cached_build_client_middlewares`` cache key. 

234async def _connect_and_send_request(req: ClientRequest) -> ClientResponse: 

235 connector = req._session._connector 

236 assert connector is not None 

237 try: 

238 conn = await connector.connect(req, traces=req._traces, timeout=req._timeout) 

239 except asyncio.TimeoutError as exc: 

240 raise ConnectionTimeoutError(f"Connection timeout to host {req.url}") from exc 

241 

242 assert conn.protocol is not None 

243 conn.protocol.set_response_params(**req._response_params) 

244 try: 

245 resp = await req._send(conn) 

246 try: 

247 await resp.start(conn) 

248 except BaseException: 

249 resp.close() 

250 raise 

251 except BaseException: 

252 conn.close() 

253 raise 

254 return resp 

255 

256 

257@final 

258class ClientSession: 

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

260 

261 __slots__ = ( 

262 "_base_url", 

263 "_base_url_origin", 

264 "_source_traceback", 

265 "_connector", 

266 "_loop", 

267 "_cookie_jar", 

268 "_connector_owner", 

269 "_version", 

270 "_json_serialize", 

271 "_json_serialize_bytes", 

272 "_requote_redirect_url", 

273 "_timeout", 

274 "_raise_for_status", 

275 "_auto_decompress", 

276 "_trust_env", 

277 "_default_headers", 

278 "_skip_auto_headers", 

279 "_request_class", 

280 "_response_class", 

281 "_ws_response_class", 

282 "_trace_configs", 

283 "_read_bufsize", 

284 "_max_line_size", 

285 "_max_field_size", 

286 "_max_headers", 

287 "_resolve_charset", 

288 "_default_proxy", 

289 "_default_ssl", 

290 "_retry_connection", 

291 "_middlewares", 

292 ) 

293 

294 def __init__( 

295 self, 

296 base_url: StrOrURL | None = None, 

297 *, 

298 connector: BaseConnector | None = None, 

299 cookies: LooseCookies | None = None, 

300 headers: LooseHeaders | None = None, 

301 proxy: StrOrURL | None = None, 

302 ssl: SSLContext | bool | Fingerprint = True, 

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

304 json_serialize: JSONEncoder = json.dumps, 

305 json_serialize_bytes: JSONBytesEncoder | None = None, 

306 request_class: type[ClientRequest] = ClientRequest, 

307 response_class: type[ClientResponse] = ClientResponse, 

308 ws_response_class: type[ClientWebSocketResponse] = ClientWebSocketResponse, 

309 version: HttpVersion = http.HttpVersion11, 

310 cookie_jar: AbstractCookieJar | None = None, 

311 connector_owner: bool = True, 

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

313 timeout: _SENTINEL | ClientTimeout | None = sentinel, 

314 auto_decompress: bool = True, 

315 trust_env: bool = False, 

316 requote_redirect_url: bool = True, 

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

318 read_bufsize: int = DEFAULT_CHUNK_SIZE, 

319 max_line_size: int = 8190, 

320 max_field_size: int = 8190, 

321 max_headers: int = 128, 

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

323 middlewares: Sequence[ClientMiddlewareType] = (), 

324 ssl_shutdown_timeout: _SENTINEL | None | float = sentinel, 

325 ) -> None: 

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

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

328 self._connector: BaseConnector | None = None 

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

330 self._base_url: URL | None = base_url 

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

332 else: 

333 self._base_url = URL(base_url) 

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

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

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

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

338 

339 if not isinstance(ssl, SSL_ALLOWED_TYPES): 

340 raise TypeError( 

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

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

343 ) 

344 

345 loop = asyncio.get_running_loop() 

346 

347 if timeout is sentinel or timeout is None: 

348 timeout = ClientTimeout() 

349 if not isinstance(timeout, ClientTimeout): 

350 raise ValueError( 

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

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

353 ) 

354 self._timeout = timeout 

355 

356 if ssl_shutdown_timeout is not sentinel: 

357 warnings.warn( 

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

359 DeprecationWarning, 

360 stacklevel=2, 

361 ) 

362 

363 if connector is None: 

364 connector = TCPConnector(ssl_shutdown_timeout=ssl_shutdown_timeout) 

365 # Initialize these three attrs before raising any exception, 

366 # they are used in __del__ 

367 self._connector = connector 

368 self._loop = loop 

369 if loop.get_debug(): 

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

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

372 ) 

373 else: 

374 self._source_traceback = None 

375 

376 if connector._loop is not loop: 

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

378 

379 if cookie_jar is None: 

380 cookie_jar = CookieJar() 

381 self._cookie_jar = cookie_jar 

382 

383 if cookies: 

384 self._cookie_jar.update_cookies(cookies) 

385 

386 self._connector_owner = connector_owner 

387 self._version = version 

388 self._json_serialize = json_serialize 

389 self._json_serialize_bytes = json_serialize_bytes 

390 self._raise_for_status = raise_for_status 

391 self._auto_decompress = auto_decompress 

392 self._trust_env = trust_env 

393 self._requote_redirect_url = requote_redirect_url 

394 self._read_bufsize = read_bufsize 

395 self._max_line_size = max_line_size 

396 self._max_field_size = max_field_size 

397 self._max_headers = max_headers 

398 

399 # Convert to list of tuples 

400 if headers: 

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

402 else: 

403 real_headers = CIMultiDict() 

404 self._default_headers: CIMultiDict[str] = real_headers 

405 if skip_auto_headers is not None: 

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

407 else: 

408 self._skip_auto_headers = frozenset() 

409 

410 self._request_class = request_class 

411 self._response_class = response_class 

412 self._ws_response_class = ws_response_class 

413 

414 self._trace_configs = trace_configs or [] 

415 for trace_config in self._trace_configs: 

416 trace_config.freeze() 

417 

418 self._resolve_charset = fallback_charset_resolver 

419 

420 self._default_proxy = proxy 

421 self._default_ssl = ssl 

422 self._retry_connection: bool = True 

423 self._middlewares = tuple(middlewares) 

424 

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

426 raise TypeError( 

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

428 ) 

429 

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

431 if not self.closed: 

432 _warnings.warn( 

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

434 ResourceWarning, 

435 source=self, 

436 ) 

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

438 if self._source_traceback is not None: 

439 context["source_traceback"] = self._source_traceback 

440 self._loop.call_exception_handler(context) 

441 

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

443 

444 def request( 

445 self, 

446 method: str, 

447 url: StrOrURL, 

448 **kwargs: Unpack[_RequestOptions], 

449 ) -> "_RequestContextManager": ... 

450 

451 else: 

452 

453 def request( 

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

455 ) -> "_RequestContextManager": 

456 """Perform HTTP request.""" 

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

458 

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

460 url = URL(str_or_url) 

461 if self._base_url and not url.absolute: 

462 return self._base_url.join(url) 

463 return url 

464 

465 async def _request( 

466 self, 

467 method: str, 

468 str_or_url: StrOrURL, 

469 *, 

470 params: Query = None, 

471 data: Any = None, 

472 json: Any = None, 

473 cookies: LooseCookies | None = None, 

474 headers: LooseHeaders | None = None, 

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

476 allow_redirects: bool = True, 

477 max_redirects: int = 10, 

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

479 chunked: bool | None = None, 

480 expect100: bool = False, 

481 raise_for_status: ( 

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

483 ) = None, 

484 read_until_eof: bool = True, 

485 proxy: StrOrURL | None = None, 

486 timeout: ClientTimeout | _SENTINEL | None = sentinel, 

487 ssl: SSLContext | bool | Fingerprint | _SENTINEL = sentinel, 

488 server_hostname: str | None = None, 

489 proxy_headers: LooseHeaders | None = None, 

490 trace_request_ctx: object = None, 

491 read_bufsize: int | None = None, 

492 auto_decompress: bool | None = None, 

493 max_line_size: int | None = None, 

494 max_field_size: int | None = None, 

495 max_headers: int | None = None, 

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

497 ) -> ClientResponse: 

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

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

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

501 

502 if self.closed: 

503 raise RuntimeError("Session is closed") 

504 

505 method = method.upper() 

506 

507 if ssl is sentinel: 

508 ssl = self._default_ssl 

509 if not isinstance(ssl, SSL_ALLOWED_TYPES): 

510 raise TypeError( 

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

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

513 ) 

514 

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

516 raise ValueError( 

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

518 ) 

519 elif json is not None: 

520 if self._json_serialize_bytes is not None: 

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

522 else: 

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

524 

525 redirects = 0 

526 history: list[ClientResponse] = [] 

527 version = self._version 

528 params = params or {} 

529 

530 # Merge with default headers and transform to CIMultiDict 

531 headers = self._prepare_headers(headers) 

532 

533 try: 

534 url = self._build_url(str_or_url) 

535 except ValueError as e: 

536 raise InvalidUrlClientError(str_or_url) from e 

537 

538 assert self._connector is not None 

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

540 raise NonHttpUrlClientError(url) 

541 

542 skip_headers: Iterable[istr] | None 

543 if skip_auto_headers is not None: 

544 skip_headers = { 

545 istr(i) for i in skip_auto_headers 

546 } | self._skip_auto_headers 

547 elif self._skip_auto_headers: 

548 skip_headers = self._skip_auto_headers 

549 else: 

550 skip_headers = None 

551 

552 if proxy is None: 

553 proxy = self._default_proxy 

554 

555 resolved_proxy_headers: CIMultiDict[str] | None 

556 if proxy is None: 

557 resolved_proxy_headers = None 

558 else: 

559 resolved_proxy_headers = self._prepare_headers(proxy_headers) 

560 try: 

561 proxy = URL(proxy) 

562 except ValueError as e: 

563 raise InvalidURL(proxy) from e 

564 

565 if timeout is sentinel or timeout is None: 

566 real_timeout: ClientTimeout = self._timeout 

567 else: 

568 real_timeout = timeout 

569 # timeout is cumulative for all request operations 

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

571 tm = TimeoutHandle( 

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

573 ) 

574 handle = tm.start() 

575 

576 if read_bufsize is None: 

577 read_bufsize = self._read_bufsize 

578 

579 if auto_decompress is None: 

580 auto_decompress = self._auto_decompress 

581 

582 if max_line_size is None: 

583 max_line_size = self._max_line_size 

584 

585 if max_field_size is None: 

586 max_field_size = self._max_field_size 

587 

588 if max_headers is None: 

589 max_headers = self._max_headers 

590 

591 traces = [ 

592 Trace( 

593 self, 

594 trace_config, 

595 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), 

596 ) 

597 for trace_config in self._trace_configs 

598 ] 

599 

600 for trace in traces: 

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

602 

603 timer = tm.timer() 

604 req: ClientRequest | None = None 

605 resp: ClientResponse | None = None 

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 

624 if auth_from_url is not None: 

625 # URL-embedded credentials override any Authorization 

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

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

628 # shadow an explicit Authorization header. 

629 if not history and hdrs.AUTHORIZATION in headers: 

630 raise ValueError( 

631 "Cannot combine AUTHORIZATION header with " 

632 "credentials encoded in URL" 

633 ) 

634 headers[hdrs.AUTHORIZATION] = auth_from_url 

635 elif ( 

636 self._trust_env 

637 and url.host is not None 

638 and hdrs.AUTHORIZATION not in headers 

639 ): 

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

641 netrc_auth = await self._loop.run_in_executor( 

642 None, self._get_netrc_auth, url.host 

643 ) 

644 if netrc_auth is not None: 

645 headers[hdrs.AUTHORIZATION] = netrc_auth 

646 

647 all_cookies = self._cookie_jar.filter_cookies(url) 

648 

649 if cookies is not None: 

650 tmp_cookie_jar = CookieJar( 

651 unsafe=self._cookie_jar.unsafe, 

652 quote_cookie=self._cookie_jar.quote_cookie, 

653 ) 

654 tmp_cookie_jar.update_cookies(cookies) 

655 req_cookies = tmp_cookie_jar.filter_cookies(url) 

656 if req_cookies: 

657 all_cookies.load(req_cookies) 

658 

659 proxy_: URL | None = None 

660 if proxy is not None: 

661 proxy_ = URL(proxy) 

662 elif self._trust_env: 

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

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

665 resolved_proxy_headers = None 

666 with suppress(LookupError): 

667 proxy_, env_proxy_auth = await asyncio.to_thread( 

668 get_env_proxy_for_url, url 

669 ) 

670 if env_proxy_auth is not None: 

671 resolved_proxy_headers = CIMultiDict( 

672 {hdrs.PROXY_AUTHORIZATION: env_proxy_auth} 

673 ) 

674 

675 response_params: ResponseParams = { 

676 "timer": timer, 

677 "skip_payload": method in EMPTY_BODY_METHODS, 

678 "read_until_eof": read_until_eof, 

679 "auto_decompress": auto_decompress, 

680 "read_timeout": real_timeout.sock_read, 

681 "read_bufsize": read_bufsize, 

682 "timeout_ceil_threshold": self._connector._timeout_ceil_threshold, 

683 "max_line_size": max_line_size, 

684 "max_field_size": max_field_size, 

685 "max_headers": max_headers, 

686 } 

687 

688 req = self._request_class( 

689 method, 

690 url, 

691 params=params, 

692 headers=headers, 

693 skip_auto_headers=skip_headers, 

694 data=data, 

695 cookies=all_cookies, 

696 version=version, 

697 compress=compress, 

698 chunked=chunked, 

699 expect100=expect100, 

700 loop=self._loop, 

701 response_class=self._response_class, 

702 proxy=proxy_, 

703 response_params=response_params, 

704 timer=timer, 

705 timeout=real_timeout, 

706 session=self, 

707 ssl=ssl, 

708 server_hostname=server_hostname, 

709 proxy_headers=resolved_proxy_headers, 

710 traces=traces, 

711 trust_env=self.trust_env, 

712 ) 

713 

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

715 effective_middlewares = ( 

716 self._middlewares if middlewares is None else tuple(middlewares) 

717 ) 

718 

719 if effective_middlewares: 

720 handler = _cached_build_client_middlewares( 

721 _connect_and_send_request, effective_middlewares 

722 ) 

723 else: 

724 handler = _connect_and_send_request 

725 

726 try: 

727 resp = await handler(req) 

728 # Client connector errors should not be retried 

729 except ( 

730 ConnectionTimeoutError, 

731 ClientConnectorError, 

732 ClientConnectorCertificateError, 

733 ClientConnectorSSLError, 

734 ): 

735 raise 

736 except (ClientOSError, ServerDisconnectedError): 

737 if not retry_persistent_connection: 

738 raise 

739 retry_persistent_connection = False 

740 if data is not None: 

741 # Rebuilding from `data` would resend only the unread 

742 # remainder of a file object; reuse the payload, which 

743 # rewinds itself once the cancelled writer has settled. 

744 await req._close() 

745 if req._body.consumed: 

746 raise 

747 data = req._body 

748 continue 

749 except ClientError: 

750 raise 

751 except OSError as exc: 

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

753 raise 

754 raise ClientOSError(*exc.args) from exc 

755 

756 # Update cookies from raw headers to preserve duplicates 

757 if resp._raw_cookie_headers: 

758 self._cookie_jar.update_cookies_from_headers( 

759 resp._raw_cookie_headers, resp.url 

760 ) 

761 

762 # redirects 

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

764 for trace in traces: 

765 await trace.send_request_redirect( 

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

767 ) 

768 

769 redirects += 1 

770 history.append(resp) 

771 if max_redirects and redirects >= max_redirects: 

772 if req._body is not None: 

773 await req._body.close() 

774 resp.close() 

775 raise TooManyRedirects( 

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

777 ) 

778 

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

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

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

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

783 ): 

784 method = hdrs.METH_GET 

785 data = None 

786 if headers.get(hdrs.CONTENT_LENGTH): 

787 headers.pop(hdrs.CONTENT_LENGTH) 

788 else: 

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

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

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

792 # Use the existing payload to avoid recreating it from 

793 # a potentially consumed file. 

794 # 

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

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

797 if req._body.consumed: 

798 resp.close() 

799 raise ClientPayloadError( 

800 "Cannot follow redirect with a consumed request " 

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

802 "or set allow_redirects=False." 

803 ) 

804 data = req._body 

805 

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

807 hdrs.URI 

808 ) 

809 if r_url is None: 

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

811 break 

812 else: 

813 # reading from correct redirection 

814 # response is forbidden 

815 resp.release() 

816 

817 try: 

818 parsed_redirect_url = URL( 

819 r_url, encoded=not self._requote_redirect_url 

820 ) 

821 except ValueError as e: 

822 if req._body is not None: 

823 await req._body.close() 

824 resp.close() 

825 raise InvalidUrlRedirectClientError( 

826 r_url, 

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

828 ) from e 

829 

830 scheme = parsed_redirect_url.scheme 

831 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: 

832 if req._body is not None: 

833 await req._body.close() 

834 resp.close() 

835 raise NonHttpUrlRedirectClientError(r_url) 

836 elif not scheme: 

837 parsed_redirect_url = url.join(parsed_redirect_url) 

838 

839 try: 

840 redirect_origin = parsed_redirect_url.origin() 

841 except ValueError as origin_val_err: 

842 if req._body is not None: 

843 await req._body.close() 

844 resp.close() 

845 raise InvalidUrlRedirectClientError( 

846 parsed_redirect_url, 

847 "Invalid redirect URL origin", 

848 ) from origin_val_err 

849 

850 if url.origin() != redirect_origin: 

851 cookies = None 

852 headers.popall(hdrs.AUTHORIZATION, None) 

853 headers.popall(hdrs.COOKIE, None) 

854 headers.popall(hdrs.PROXY_AUTHORIZATION, None) 

855 

856 url = parsed_redirect_url 

857 params = {} 

858 resp.release() 

859 continue 

860 

861 break 

862 

863 if req._body is not None: 

864 await req._body.close() 

865 # check response status 

866 if raise_for_status is None: 

867 raise_for_status = self._raise_for_status 

868 

869 if callable(raise_for_status): 

870 await raise_for_status(resp) 

871 elif raise_for_status: 

872 resp.raise_for_status() 

873 

874 # register connection 

875 if handle is not None: 

876 if resp.connection is not None: 

877 resp.connection.add_callback(handle.cancel) 

878 else: 

879 handle.cancel() 

880 

881 resp._history = tuple(history) 

882 

883 for trace in traces: 

884 await trace.send_request_end( 

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

886 ) 

887 return resp 

888 

889 except BaseException as e: 

890 # cleanup timer 

891 tm.close() 

892 if handle: 

893 handle.cancel() 

894 handle = None 

895 

896 if resp is not None: 

897 # A failure occurred after the response was received. 

898 resp.close() 

899 

900 if req is not None and req._body is not None: 

901 await req._body.close() 

902 

903 for trace in traces: 

904 await trace.send_request_exception( 

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

906 ) 

907 raise 

908 

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

910 

911 @overload 

912 def ws_connect( 

913 self, 

914 url: StrOrURL, 

915 *, 

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

917 **kwargs: Unpack[_WSConnectOptions], 

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

919 

920 @overload 

921 def ws_connect( 

922 self, 

923 url: StrOrURL, 

924 *, 

925 decode_text: Literal[False], 

926 **kwargs: Unpack[_WSConnectOptions], 

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

928 

929 @overload 

930 def ws_connect( 

931 self, 

932 url: StrOrURL, 

933 *, 

934 decode_text: bool = ..., 

935 **kwargs: Unpack[_WSConnectOptions], 

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

937 

938 def ws_connect( 

939 self, 

940 url: StrOrURL, 

941 *, 

942 method: str = hdrs.METH_GET, 

943 protocols: Collection[str] = (), 

944 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

945 receive_timeout: float | None = None, 

946 autoclose: bool = True, 

947 autoping: bool = True, 

948 heartbeat: float | None = None, 

949 origin: str | None = None, 

950 params: Query = None, 

951 headers: LooseHeaders | None = None, 

952 proxy: StrOrURL | None = None, 

953 ssl: SSLContext | bool | Fingerprint | _SENTINEL = sentinel, 

954 server_hostname: str | None = None, 

955 proxy_headers: LooseHeaders | None = None, 

956 compress: int = 0, 

957 max_msg_size: int = 4 * 1024 * 1024, 

958 decode_text: bool = True, 

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

960 """Initiate websocket connection.""" 

961 return _WSRequestContextManager( 

962 self._ws_connect( 

963 url, 

964 method=method, 

965 protocols=protocols, 

966 timeout=timeout, 

967 receive_timeout=receive_timeout, 

968 autoclose=autoclose, 

969 autoping=autoping, 

970 heartbeat=heartbeat, 

971 origin=origin, 

972 params=params, 

973 headers=headers, 

974 proxy=proxy, 

975 ssl=ssl, 

976 server_hostname=server_hostname, 

977 proxy_headers=proxy_headers, 

978 compress=compress, 

979 max_msg_size=max_msg_size, 

980 decode_text=decode_text, 

981 ) 

982 ) 

983 

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

985 

986 @overload 

987 async def _ws_connect( 

988 self, 

989 url: StrOrURL, 

990 *, 

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

992 **kwargs: Unpack[_WSConnectOptions], 

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

994 

995 @overload 

996 async def _ws_connect( 

997 self, 

998 url: StrOrURL, 

999 *, 

1000 decode_text: Literal[False], 

1001 **kwargs: Unpack[_WSConnectOptions], 

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

1003 

1004 @overload 

1005 async def _ws_connect( 

1006 self, 

1007 url: StrOrURL, 

1008 *, 

1009 decode_text: bool = ..., 

1010 **kwargs: Unpack[_WSConnectOptions], 

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

1012 

1013 async def _ws_connect( 

1014 self, 

1015 url: StrOrURL, 

1016 *, 

1017 method: str = hdrs.METH_GET, 

1018 protocols: Collection[str] = (), 

1019 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

1020 receive_timeout: float | None = None, 

1021 autoclose: bool = True, 

1022 autoping: bool = True, 

1023 heartbeat: float | None = None, 

1024 origin: str | None = None, 

1025 params: Query = None, 

1026 headers: LooseHeaders | None = None, 

1027 proxy: StrOrURL | None = None, 

1028 ssl: SSLContext | bool | Fingerprint | _SENTINEL = sentinel, 

1029 server_hostname: str | None = None, 

1030 proxy_headers: LooseHeaders | None = None, 

1031 compress: int = 0, 

1032 max_msg_size: int = 4 * 1024 * 1024, 

1033 decode_text: bool = True, 

1034 ) -> "ClientWebSocketResponse[bool]": 

1035 if timeout is not sentinel: 

1036 if isinstance(timeout, ClientWSTimeout): 

1037 ws_timeout = timeout 

1038 else: 

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

1040 "parameter 'timeout' of type 'float' " 

1041 "is deprecated, please use " 

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

1043 DeprecationWarning, 

1044 stacklevel=2, 

1045 ) 

1046 ws_timeout = ClientWSTimeout(ws_close=timeout) 

1047 else: 

1048 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT 

1049 if receive_timeout is not None: 

1050 warnings.warn( 

1051 "float parameter 'receive_timeout' " 

1052 "is deprecated, please use parameter " 

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

1054 DeprecationWarning, 

1055 stacklevel=2, 

1056 ) 

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

1058 

1059 if headers is None: 

1060 real_headers: CIMultiDict[str] = CIMultiDict() 

1061 else: 

1062 real_headers = CIMultiDict(headers) 

1063 

1064 default_headers = { 

1065 hdrs.UPGRADE: "websocket", 

1066 hdrs.CONNECTION: "Upgrade", 

1067 hdrs.SEC_WEBSOCKET_VERSION: "13", 

1068 } 

1069 

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

1071 real_headers.setdefault(key, value) 

1072 

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

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

1075 

1076 if protocols: 

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

1078 if origin is not None: 

1079 real_headers[hdrs.ORIGIN] = origin 

1080 if compress: 

1081 extstr = ws_ext_gen(compress=compress) 

1082 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

1083 

1084 if ssl is not sentinel and not isinstance(ssl, SSL_ALLOWED_TYPES): 

1085 raise TypeError( 

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

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

1088 ) 

1089 

1090 # send request 

1091 resp = await self.request( 

1092 method, 

1093 url, 

1094 params=params, 

1095 headers=real_headers, 

1096 read_until_eof=False, 

1097 proxy=proxy, 

1098 ssl=ssl, 

1099 server_hostname=server_hostname, 

1100 proxy_headers=proxy_headers, 

1101 ) 

1102 

1103 try: 

1104 # check handshake 

1105 if resp.status != 101: 

1106 raise WSServerHandshakeError( 

1107 resp.request_info, 

1108 resp.history, 

1109 message="Invalid response status", 

1110 status=resp.status, 

1111 headers=resp.headers, 

1112 ) 

1113 

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

1115 raise WSServerHandshakeError( 

1116 resp.request_info, 

1117 resp.history, 

1118 message="Invalid upgrade header", 

1119 status=resp.status, 

1120 headers=resp.headers, 

1121 ) 

1122 

1123 if not resp._upgraded: 

1124 raise WSServerHandshakeError( 

1125 resp.request_info, 

1126 resp.history, 

1127 message="Invalid connection header", 

1128 status=resp.status, 

1129 headers=resp.headers, 

1130 ) 

1131 

1132 # key calculation 

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

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

1135 if r_key != match: 

1136 raise WSServerHandshakeError( 

1137 resp.request_info, 

1138 resp.history, 

1139 message="Invalid challenge response", 

1140 status=resp.status, 

1141 headers=resp.headers, 

1142 ) 

1143 

1144 # websocket protocol 

1145 protocol = None 

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

1147 resp_protocols = [ 

1148 proto.strip() 

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

1150 ] 

1151 

1152 for proto in resp_protocols: 

1153 if proto in protocols: 

1154 protocol = proto 

1155 break 

1156 

1157 # websocket compress 

1158 notakeover = False 

1159 if compress: 

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

1161 if compress_hdrs: 

1162 try: 

1163 compress, notakeover = ws_ext_parse(compress_hdrs) 

1164 except WSHandshakeError as exc: 

1165 raise WSServerHandshakeError( 

1166 resp.request_info, 

1167 resp.history, 

1168 message=exc.args[0], 

1169 status=resp.status, 

1170 headers=resp.headers, 

1171 ) from exc 

1172 else: 

1173 compress = 0 

1174 notakeover = False 

1175 

1176 conn = resp.connection 

1177 assert conn is not None 

1178 conn_proto = conn.protocol 

1179 assert conn_proto is not None 

1180 

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

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

1183 if ws_timeout.ws_receive is None: 

1184 # Reset regardless 

1185 conn_proto.read_timeout = None 

1186 elif conn_proto.read_timeout is not None: 

1187 conn_proto.read_timeout = max( 

1188 ws_timeout.ws_receive, conn_proto.read_timeout 

1189 ) 

1190 

1191 transport = conn.transport 

1192 assert transport is not None 

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

1194 writer = WebSocketWriter( 

1195 conn_proto, 

1196 transport, 

1197 use_mask=True, 

1198 compress=compress, 

1199 notakeover=notakeover, 

1200 ) 

1201 except BaseException: 

1202 resp.close() 

1203 raise 

1204 else: 

1205 ws_resp = self._ws_response_class( 

1206 reader, 

1207 writer, 

1208 protocol, 

1209 resp, 

1210 ws_timeout, 

1211 autoclose, 

1212 autoping, 

1213 self._loop, 

1214 heartbeat=heartbeat, 

1215 compress=compress, 

1216 client_notakeover=notakeover, 

1217 ) 

1218 ws_resp._parser = WebSocketReader( 

1219 reader, 

1220 max_msg_size, 

1221 compress=bool(compress), 

1222 decode_text=decode_text, 

1223 ) 

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

1225 conn_proto.set_parser(ws_resp._parser, reader, data_received_cb=cb) 

1226 return ws_resp 

1227 

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

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

1230 # Convert headers to MultiDict 

1231 result = CIMultiDict(self._default_headers) 

1232 if headers: 

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

1234 headers = CIMultiDict(headers) 

1235 added_names: set[str] = set() 

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

1237 if key in added_names: 

1238 result.add(key, value) 

1239 else: 

1240 result[key] = value 

1241 added_names.add(key) 

1242 return result 

1243 

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

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

1246 

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

1248 event loop. 

1249 """ 

1250 netrc_obj = netrc_from_env() 

1251 try: 

1252 return _auth_header_from_netrc(netrc_obj, host) 

1253 except LookupError: 

1254 return None 

1255 

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

1257 

1258 def get( 

1259 self, 

1260 url: StrOrURL, 

1261 **kwargs: Unpack[_RequestOptions], 

1262 ) -> "_RequestContextManager": ... 

1263 

1264 def options( 

1265 self, 

1266 url: StrOrURL, 

1267 **kwargs: Unpack[_RequestOptions], 

1268 ) -> "_RequestContextManager": ... 

1269 

1270 def head( 

1271 self, 

1272 url: StrOrURL, 

1273 **kwargs: Unpack[_RequestOptions], 

1274 ) -> "_RequestContextManager": ... 

1275 

1276 def post( 

1277 self, 

1278 url: StrOrURL, 

1279 **kwargs: Unpack[_RequestOptions], 

1280 ) -> "_RequestContextManager": ... 

1281 

1282 def put( 

1283 self, 

1284 url: StrOrURL, 

1285 **kwargs: Unpack[_RequestOptions], 

1286 ) -> "_RequestContextManager": ... 

1287 

1288 def patch( 

1289 self, 

1290 url: StrOrURL, 

1291 **kwargs: Unpack[_RequestOptions], 

1292 ) -> "_RequestContextManager": ... 

1293 

1294 def delete( 

1295 self, 

1296 url: StrOrURL, 

1297 **kwargs: Unpack[_RequestOptions], 

1298 ) -> "_RequestContextManager": ... 

1299 

1300 else: 

1301 

1302 def get( 

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

1304 ) -> "_RequestContextManager": 

1305 """Perform HTTP GET request.""" 

1306 return _RequestContextManager( 

1307 self._request( 

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

1309 ) 

1310 ) 

1311 

1312 def options( 

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

1314 ) -> "_RequestContextManager": 

1315 """Perform HTTP OPTIONS request.""" 

1316 return _RequestContextManager( 

1317 self._request( 

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

1319 ) 

1320 ) 

1321 

1322 def head( 

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

1324 ) -> "_RequestContextManager": 

1325 """Perform HTTP HEAD request.""" 

1326 return _RequestContextManager( 

1327 self._request( 

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

1329 ) 

1330 ) 

1331 

1332 def post( 

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

1334 ) -> "_RequestContextManager": 

1335 """Perform HTTP POST request.""" 

1336 return _RequestContextManager( 

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

1338 ) 

1339 

1340 def put( 

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

1342 ) -> "_RequestContextManager": 

1343 """Perform HTTP PUT request.""" 

1344 return _RequestContextManager( 

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

1346 ) 

1347 

1348 def patch( 

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

1350 ) -> "_RequestContextManager": 

1351 """Perform HTTP PATCH request.""" 

1352 return _RequestContextManager( 

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

1354 ) 

1355 

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

1357 """Perform HTTP DELETE request.""" 

1358 return _RequestContextManager( 

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

1360 ) 

1361 

1362 async def close(self) -> None: 

1363 """Close underlying connector. 

1364 

1365 Release all acquired resources. 

1366 """ 

1367 if not self.closed: 

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

1369 await self._connector.close() 

1370 self._connector = None 

1371 

1372 @property 

1373 def closed(self) -> bool: 

1374 """Is client session closed. 

1375 

1376 A readonly property. 

1377 """ 

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

1379 

1380 @property 

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

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

1383 return self._connector 

1384 

1385 @property 

1386 def cookie_jar(self) -> AbstractCookieJar: 

1387 """The session cookies.""" 

1388 return self._cookie_jar 

1389 

1390 @property 

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

1392 """The session HTTP protocol version.""" 

1393 return self._version 

1394 

1395 @property 

1396 def requote_redirect_url(self) -> bool: 

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

1398 return self._requote_redirect_url 

1399 

1400 @property 

1401 def timeout(self) -> ClientTimeout: 

1402 """Timeout for the session.""" 

1403 return self._timeout 

1404 

1405 @property 

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

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

1408 return self._default_headers 

1409 

1410 @property 

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

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

1413 return self._skip_auto_headers 

1414 

1415 @property 

1416 def json_serialize(self) -> JSONEncoder: 

1417 """Json serializer callable""" 

1418 return self._json_serialize 

1419 

1420 @property 

1421 def connector_owner(self) -> bool: 

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

1423 return self._connector_owner 

1424 

1425 @property 

1426 def raise_for_status( 

1427 self, 

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

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

1430 return self._raise_for_status 

1431 

1432 @property 

1433 def auto_decompress(self) -> bool: 

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

1435 return self._auto_decompress 

1436 

1437 @property 

1438 def trust_env(self) -> bool: 

1439 """ 

1440 Should proxies information from environment or netrc be trusted. 

1441 

1442 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1443 or ~/.netrc file if present. 

1444 """ 

1445 return self._trust_env 

1446 

1447 @property 

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

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

1450 return self._trace_configs 

1451 

1452 def detach(self) -> None: 

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

1454 

1455 Session is switched to closed state anyway. 

1456 """ 

1457 self._connector = None 

1458 

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

1460 return self 

1461 

1462 async def __aexit__( 

1463 self, 

1464 exc_type: type[BaseException] | None, 

1465 exc_val: BaseException | None, 

1466 exc_tb: TracebackType | None, 

1467 ) -> None: 

1468 await self.close() 

1469 

1470 

1471class _BaseRequestContextManager( 

1472 Coroutine[Any, Any, _RetType_co], Generic[_RetType_co] 

1473): 

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

1475 

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

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

1478 

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

1480 return self._coro.send(arg) 

1481 

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

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

1484 

1485 def close(self) -> None: 

1486 return self._coro.close() 

1487 

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

1489 ret = self._coro.__await__() 

1490 return ret 

1491 

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

1493 return self.__await__() 

1494 

1495 async def __aenter__(self) -> _RetType_co: 

1496 self._resp: _RetType_co = await self._coro 

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

1498 

1499 async def __aexit__( 

1500 self, 

1501 exc_type: type[BaseException] | None, 

1502 exc: BaseException | None, 

1503 tb: TracebackType | None, 

1504 ) -> None: 

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

1506 

1507 

1508_RequestContextManager = _BaseRequestContextManager[ClientResponse] 

1509_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse[bool]] 

1510 

1511 

1512class _SessionRequestContextManager: 

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

1514 

1515 def __init__( 

1516 self, 

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

1518 session: ClientSession, 

1519 ) -> None: 

1520 self._coro = coro 

1521 self._resp: ClientResponse | None = None 

1522 self._session = session 

1523 

1524 async def __aenter__(self) -> ClientResponse: 

1525 try: 

1526 self._resp = await self._coro 

1527 except BaseException: 

1528 await self._session.close() 

1529 raise 

1530 else: 

1531 return self._resp 

1532 

1533 async def __aexit__( 

1534 self, 

1535 exc_type: type[BaseException] | None, 

1536 exc: BaseException | None, 

1537 tb: TracebackType | None, 

1538 ) -> None: 

1539 assert self._resp is not None 

1540 self._resp.close() 

1541 await self._session.close() 

1542 

1543 

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

1545 

1546 def request( 

1547 method: str, 

1548 url: StrOrURL, 

1549 *, 

1550 version: HttpVersion = http.HttpVersion11, 

1551 connector: BaseConnector | None = None, 

1552 **kwargs: Unpack[_RequestOptions], 

1553 ) -> _SessionRequestContextManager: ... 

1554 

1555else: 

1556 

1557 def request( 

1558 method: str, 

1559 url: StrOrURL, 

1560 *, 

1561 version: HttpVersion = http.HttpVersion11, 

1562 connector: BaseConnector | None = None, 

1563 **kwargs: Any, 

1564 ) -> _SessionRequestContextManager: 

1565 """Constructs and sends a request. 

1566 

1567 Returns response object. 

1568 method - HTTP method 

1569 url - request url 

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

1571 string of the new request 

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

1573 send in the body of the request 

1574 json - (optional) Any json compatible python object 

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

1576 the request 

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

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

1579 redirects 

1580 version - Request HTTP version. 

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

1582 with deflate encoding. 

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

1584 expect100 - Expect 100-continue response from server. 

1585 connector - BaseConnector sub-class instance to support 

1586 connection pooling. 

1587 read_until_eof - Read response until eof if response 

1588 does not have Content-Length header. 

1589 loop - Optional event loop. 

1590 timeout - Optional ClientTimeout settings structure, 5min 

1591 total timeout by default. 

1592 Usage:: 

1593 >>> import aiohttp 

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

1595 ... print(resp) 

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

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

1598 """ 

1599 connector_owner = False 

1600 if connector is None: 

1601 connector_owner = True 

1602 connector = TCPConnector(force_close=True) 

1603 

1604 session = ClientSession( 

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

1606 version=version, 

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

1608 connector=connector, 

1609 connector_owner=connector_owner, 

1610 ) 

1611 

1612 return _SessionRequestContextManager( 

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

1614 session, 

1615 )