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

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 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 

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

220 

221_RetType_co = TypeVar( 

222 "_RetType_co", 

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

224 covariant=True, 

225) 

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

227 

228 

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

230# ``_cached_build_client_middlewares`` cache key. 

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

232 connector = req._session._connector 

233 assert connector is not None 

234 try: 

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

236 except asyncio.TimeoutError as exc: 

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

238 

239 assert conn.protocol is not None 

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

241 try: 

242 resp = await req._send(conn) 

243 try: 

244 await resp.start(conn) 

245 except BaseException: 

246 resp.close() 

247 raise 

248 except BaseException: 

249 conn.close() 

250 raise 

251 return resp 

252 

253 

254@final 

255class ClientSession: 

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

257 

258 __slots__ = ( 

259 "_base_url", 

260 "_base_url_origin", 

261 "_source_traceback", 

262 "_connector", 

263 "_loop", 

264 "_cookie_jar", 

265 "_connector_owner", 

266 "_version", 

267 "_json_serialize", 

268 "_json_serialize_bytes", 

269 "_requote_redirect_url", 

270 "_timeout", 

271 "_raise_for_status", 

272 "_auto_decompress", 

273 "_trust_env", 

274 "_default_headers", 

275 "_skip_auto_headers", 

276 "_request_class", 

277 "_response_class", 

278 "_ws_response_class", 

279 "_trace_configs", 

280 "_read_bufsize", 

281 "_max_line_size", 

282 "_max_field_size", 

283 "_max_headers", 

284 "_resolve_charset", 

285 "_default_proxy", 

286 "_default_ssl", 

287 "_retry_connection", 

288 "_middlewares", 

289 ) 

290 

291 def __init__( 

292 self, 

293 base_url: StrOrURL | None = None, 

294 *, 

295 connector: BaseConnector | None = None, 

296 cookies: LooseCookies | None = None, 

297 headers: LooseHeaders | None = None, 

298 proxy: StrOrURL | None = None, 

299 ssl: SSLContext | bool | Fingerprint = True, 

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

301 json_serialize: JSONEncoder = json.dumps, 

302 json_serialize_bytes: JSONBytesEncoder | None = None, 

303 request_class: type[ClientRequest] = ClientRequest, 

304 response_class: type[ClientResponse] = ClientResponse, 

305 ws_response_class: type[ClientWebSocketResponse] = ClientWebSocketResponse, 

306 version: HttpVersion = http.HttpVersion11, 

307 cookie_jar: AbstractCookieJar | None = None, 

308 connector_owner: bool = True, 

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

310 timeout: _SENTINEL | ClientTimeout | None = sentinel, 

311 auto_decompress: bool = True, 

312 trust_env: bool = False, 

313 requote_redirect_url: bool = True, 

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

315 read_bufsize: int = DEFAULT_CHUNK_SIZE, 

316 max_line_size: int = 8190, 

317 max_field_size: int = 8190, 

318 max_headers: int = 128, 

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

320 middlewares: Sequence[ClientMiddlewareType] = (), 

321 ssl_shutdown_timeout: _SENTINEL | None | float = sentinel, 

322 ) -> None: 

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

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

325 self._connector: BaseConnector | None = None 

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

327 self._base_url: URL | None = base_url 

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

329 else: 

330 self._base_url = URL(base_url) 

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

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

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

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

335 

336 if not isinstance(ssl, SSL_ALLOWED_TYPES): 

337 raise TypeError( 

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

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

340 ) 

341 

342 loop = asyncio.get_running_loop() 

343 

344 if timeout is sentinel or timeout is None: 

345 timeout = ClientTimeout() 

346 if not isinstance(timeout, ClientTimeout): 

347 raise ValueError( 

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

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

350 ) 

351 self._timeout = timeout 

352 

353 if ssl_shutdown_timeout is not sentinel: 

354 warnings.warn( 

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

356 DeprecationWarning, 

357 stacklevel=2, 

358 ) 

359 

360 if connector is None: 

361 connector = TCPConnector(ssl_shutdown_timeout=ssl_shutdown_timeout) 

362 # Initialize these three attrs before raising any exception, 

363 # they are used in __del__ 

364 self._connector = connector 

365 self._loop = loop 

366 if loop.get_debug(): 

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

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

369 ) 

370 else: 

371 self._source_traceback = None 

372 

373 if connector._loop is not loop: 

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

375 

376 if cookie_jar is None: 

377 cookie_jar = CookieJar() 

378 self._cookie_jar = cookie_jar 

379 

380 if cookies: 

381 self._cookie_jar.update_cookies(cookies) 

382 

383 self._connector_owner = connector_owner 

384 self._version = version 

385 self._json_serialize = json_serialize 

386 self._json_serialize_bytes = json_serialize_bytes 

387 self._raise_for_status = raise_for_status 

388 self._auto_decompress = auto_decompress 

389 self._trust_env = trust_env 

390 self._requote_redirect_url = requote_redirect_url 

391 self._read_bufsize = read_bufsize 

392 self._max_line_size = max_line_size 

393 self._max_field_size = max_field_size 

394 self._max_headers = max_headers 

395 

396 # Convert to list of tuples 

397 if headers: 

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

399 else: 

400 real_headers = CIMultiDict() 

401 self._default_headers: CIMultiDict[str] = real_headers 

402 if skip_auto_headers is not None: 

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

404 else: 

405 self._skip_auto_headers = frozenset() 

406 

407 self._request_class = request_class 

408 self._response_class = response_class 

409 self._ws_response_class = ws_response_class 

410 

411 self._trace_configs = trace_configs or [] 

412 for trace_config in self._trace_configs: 

413 trace_config.freeze() 

414 

415 self._resolve_charset = fallback_charset_resolver 

416 

417 self._default_proxy = proxy 

418 self._default_ssl = ssl 

419 self._retry_connection: bool = True 

420 self._middlewares = tuple(middlewares) 

421 

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

423 raise TypeError( 

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

425 ) 

426 

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

428 if not self.closed: 

429 _warnings.warn( 

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

431 ResourceWarning, 

432 source=self, 

433 ) 

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

435 if self._source_traceback is not None: 

436 context["source_traceback"] = self._source_traceback 

437 self._loop.call_exception_handler(context) 

438 

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

440 

441 def request( 

442 self, 

443 method: str, 

444 url: StrOrURL, 

445 **kwargs: Unpack[_RequestOptions], 

446 ) -> "_RequestContextManager": ... 

447 

448 else: 

449 

450 def request( 

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

452 ) -> "_RequestContextManager": 

453 """Perform HTTP request.""" 

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

455 

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

457 url = URL(str_or_url) 

458 if self._base_url and not url.absolute: 

459 return self._base_url.join(url) 

460 return url 

461 

462 async def _request( 

463 self, 

464 method: str, 

465 str_or_url: StrOrURL, 

466 *, 

467 params: Query = None, 

468 data: Any = None, 

469 json: Any = None, 

470 cookies: LooseCookies | None = None, 

471 headers: LooseHeaders | None = None, 

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

473 allow_redirects: bool = True, 

474 max_redirects: int = 10, 

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

476 chunked: bool | None = None, 

477 expect100: bool = False, 

478 raise_for_status: ( 

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

480 ) = None, 

481 read_until_eof: bool = True, 

482 proxy: StrOrURL | None = None, 

483 timeout: ClientTimeout | _SENTINEL | None = sentinel, 

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

485 server_hostname: str | None = None, 

486 proxy_headers: LooseHeaders | None = None, 

487 trace_request_ctx: object = None, 

488 read_bufsize: int | None = None, 

489 auto_decompress: bool | None = None, 

490 max_line_size: int | None = None, 

491 max_field_size: int | None = None, 

492 max_headers: int | None = None, 

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

494 ) -> ClientResponse: 

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

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

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

498 

499 if self.closed: 

500 raise RuntimeError("Session is closed") 

501 

502 method = method.upper() 

503 

504 if ssl is sentinel: 

505 ssl = self._default_ssl 

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 req: ClientRequest | None = None 

602 try: 

603 with timer: 

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

605 retry_persistent_connection = ( 

606 self._retry_connection and method in IDEMPOTENT_METHODS 

607 ) 

608 while True: 

609 url, auth_from_url = strip_auth_from_url(url) 

610 if not url.raw_host: 

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

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

613 err_exc_cls = ( 

614 InvalidUrlRedirectClientError 

615 if redirects 

616 else InvalidUrlClientError 

617 ) 

618 raise err_exc_cls(url) 

619 

620 if auth_from_url is not None: 

621 # URL-embedded credentials override any Authorization 

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

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

624 # shadow an explicit Authorization header. 

625 if not history and hdrs.AUTHORIZATION in headers: 

626 raise ValueError( 

627 "Cannot combine AUTHORIZATION header with " 

628 "credentials encoded in URL" 

629 ) 

630 headers[hdrs.AUTHORIZATION] = auth_from_url 

631 elif ( 

632 self._trust_env 

633 and url.host is not None 

634 and hdrs.AUTHORIZATION not in headers 

635 ): 

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

637 netrc_auth = await self._loop.run_in_executor( 

638 None, self._get_netrc_auth, url.host 

639 ) 

640 if netrc_auth is not None: 

641 headers[hdrs.AUTHORIZATION] = netrc_auth 

642 

643 all_cookies = self._cookie_jar.filter_cookies(url) 

644 

645 if cookies is not None: 

646 tmp_cookie_jar = CookieJar( 

647 unsafe=self._cookie_jar.unsafe, 

648 quote_cookie=self._cookie_jar.quote_cookie, 

649 ) 

650 tmp_cookie_jar.update_cookies(cookies) 

651 req_cookies = tmp_cookie_jar.filter_cookies(url) 

652 if req_cookies: 

653 all_cookies.load(req_cookies) 

654 

655 proxy_: URL | None = None 

656 if proxy is not None: 

657 proxy_ = URL(proxy) 

658 elif self._trust_env: 

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

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

661 resolved_proxy_headers = None 

662 with suppress(LookupError): 

663 proxy_, env_proxy_auth = await asyncio.to_thread( 

664 get_env_proxy_for_url, url 

665 ) 

666 if env_proxy_auth is not None: 

667 resolved_proxy_headers = CIMultiDict( 

668 {hdrs.PROXY_AUTHORIZATION: env_proxy_auth} 

669 ) 

670 

671 response_params: ResponseParams = { 

672 "timer": timer, 

673 "skip_payload": method in EMPTY_BODY_METHODS, 

674 "read_until_eof": read_until_eof, 

675 "auto_decompress": auto_decompress, 

676 "read_timeout": real_timeout.sock_read, 

677 "read_bufsize": read_bufsize, 

678 "timeout_ceil_threshold": self._connector._timeout_ceil_threshold, 

679 "max_line_size": max_line_size, 

680 "max_field_size": max_field_size, 

681 "max_headers": max_headers, 

682 } 

683 

684 req = self._request_class( 

685 method, 

686 url, 

687 params=params, 

688 headers=headers, 

689 skip_auto_headers=skip_headers, 

690 data=data, 

691 cookies=all_cookies, 

692 version=version, 

693 compress=compress, 

694 chunked=chunked, 

695 expect100=expect100, 

696 loop=self._loop, 

697 response_class=self._response_class, 

698 proxy=proxy_, 

699 response_params=response_params, 

700 timer=timer, 

701 timeout=real_timeout, 

702 session=self, 

703 ssl=ssl, 

704 server_hostname=server_hostname, 

705 proxy_headers=resolved_proxy_headers, 

706 traces=traces, 

707 trust_env=self.trust_env, 

708 ) 

709 

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

711 effective_middlewares = ( 

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

713 ) 

714 

715 if effective_middlewares: 

716 handler = _cached_build_client_middlewares( 

717 _connect_and_send_request, effective_middlewares 

718 ) 

719 else: 

720 handler = _connect_and_send_request 

721 

722 try: 

723 resp = await handler(req) 

724 # Client connector errors should not be retried 

725 except ( 

726 ConnectionTimeoutError, 

727 ClientConnectorError, 

728 ClientConnectorCertificateError, 

729 ClientConnectorSSLError, 

730 ): 

731 raise 

732 except (ClientOSError, ServerDisconnectedError): 

733 if retry_persistent_connection: 

734 retry_persistent_connection = False 

735 continue 

736 raise 

737 except ClientError: 

738 raise 

739 except OSError as exc: 

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

741 raise 

742 raise ClientOSError(*exc.args) from exc 

743 

744 # Update cookies from raw headers to preserve duplicates 

745 if resp._raw_cookie_headers: 

746 self._cookie_jar.update_cookies_from_headers( 

747 resp._raw_cookie_headers, resp.url 

748 ) 

749 

750 # redirects 

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

752 for trace in traces: 

753 await trace.send_request_redirect( 

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

755 ) 

756 

757 redirects += 1 

758 history.append(resp) 

759 if max_redirects and redirects >= max_redirects: 

760 if req._body is not None: 

761 await req._body.close() 

762 resp.close() 

763 raise TooManyRedirects( 

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

765 ) 

766 

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

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

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

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

771 ): 

772 method = hdrs.METH_GET 

773 data = None 

774 if headers.get(hdrs.CONTENT_LENGTH): 

775 headers.pop(hdrs.CONTENT_LENGTH) 

776 else: 

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

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

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

780 # Use the existing payload to avoid recreating it from 

781 # a potentially consumed file. 

782 # 

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

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

785 if req._body.consumed: 

786 resp.close() 

787 raise ClientPayloadError( 

788 "Cannot follow redirect with a consumed request " 

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

790 "or set allow_redirects=False." 

791 ) 

792 data = req._body 

793 

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

795 hdrs.URI 

796 ) 

797 if r_url is None: 

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

799 break 

800 else: 

801 # reading from correct redirection 

802 # response is forbidden 

803 resp.release() 

804 

805 try: 

806 parsed_redirect_url = URL( 

807 r_url, encoded=not self._requote_redirect_url 

808 ) 

809 except ValueError as e: 

810 if req._body is not None: 

811 await req._body.close() 

812 resp.close() 

813 raise InvalidUrlRedirectClientError( 

814 r_url, 

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

816 ) from e 

817 

818 scheme = parsed_redirect_url.scheme 

819 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: 

820 if req._body is not None: 

821 await req._body.close() 

822 resp.close() 

823 raise NonHttpUrlRedirectClientError(r_url) 

824 elif not scheme: 

825 parsed_redirect_url = url.join(parsed_redirect_url) 

826 

827 try: 

828 redirect_origin = parsed_redirect_url.origin() 

829 except ValueError as origin_val_err: 

830 if req._body is not None: 

831 await req._body.close() 

832 resp.close() 

833 raise InvalidUrlRedirectClientError( 

834 parsed_redirect_url, 

835 "Invalid redirect URL origin", 

836 ) from origin_val_err 

837 

838 if url.origin() != redirect_origin: 

839 cookies = None 

840 headers.popall(hdrs.AUTHORIZATION, None) 

841 headers.popall(hdrs.COOKIE, None) 

842 headers.popall(hdrs.PROXY_AUTHORIZATION, None) 

843 

844 url = parsed_redirect_url 

845 params = {} 

846 resp.release() 

847 continue 

848 

849 break 

850 

851 if req._body is not None: 

852 await req._body.close() 

853 # check response status 

854 if raise_for_status is None: 

855 raise_for_status = self._raise_for_status 

856 

857 if callable(raise_for_status): 

858 await raise_for_status(resp) 

859 elif raise_for_status: 

860 resp.raise_for_status() 

861 

862 # register connection 

863 if handle is not None: 

864 if resp.connection is not None: 

865 resp.connection.add_callback(handle.cancel) 

866 else: 

867 handle.cancel() 

868 

869 resp._history = tuple(history) 

870 

871 for trace in traces: 

872 await trace.send_request_end( 

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

874 ) 

875 return resp 

876 

877 except BaseException as e: 

878 # cleanup timer 

879 tm.close() 

880 if handle: 

881 handle.cancel() 

882 handle = None 

883 

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

885 await req._body.close() 

886 

887 for trace in traces: 

888 await trace.send_request_exception( 

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

890 ) 

891 raise 

892 

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

894 

895 @overload 

896 def ws_connect( 

897 self, 

898 url: StrOrURL, 

899 *, 

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

901 **kwargs: Unpack[_WSConnectOptions], 

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

903 

904 @overload 

905 def ws_connect( 

906 self, 

907 url: StrOrURL, 

908 *, 

909 decode_text: Literal[False], 

910 **kwargs: Unpack[_WSConnectOptions], 

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

912 

913 @overload 

914 def ws_connect( 

915 self, 

916 url: StrOrURL, 

917 *, 

918 decode_text: bool = ..., 

919 **kwargs: Unpack[_WSConnectOptions], 

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

921 

922 def ws_connect( 

923 self, 

924 url: StrOrURL, 

925 *, 

926 method: str = hdrs.METH_GET, 

927 protocols: Collection[str] = (), 

928 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

929 receive_timeout: float | None = None, 

930 autoclose: bool = True, 

931 autoping: bool = True, 

932 heartbeat: float | None = None, 

933 origin: str | None = None, 

934 params: Query = None, 

935 headers: LooseHeaders | None = None, 

936 proxy: StrOrURL | None = None, 

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

938 server_hostname: str | None = None, 

939 proxy_headers: LooseHeaders | None = None, 

940 compress: int = 0, 

941 max_msg_size: int = 4 * 1024 * 1024, 

942 decode_text: bool = True, 

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

944 """Initiate websocket connection.""" 

945 return _WSRequestContextManager( 

946 self._ws_connect( 

947 url, 

948 method=method, 

949 protocols=protocols, 

950 timeout=timeout, 

951 receive_timeout=receive_timeout, 

952 autoclose=autoclose, 

953 autoping=autoping, 

954 heartbeat=heartbeat, 

955 origin=origin, 

956 params=params, 

957 headers=headers, 

958 proxy=proxy, 

959 ssl=ssl, 

960 server_hostname=server_hostname, 

961 proxy_headers=proxy_headers, 

962 compress=compress, 

963 max_msg_size=max_msg_size, 

964 decode_text=decode_text, 

965 ) 

966 ) 

967 

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

969 

970 @overload 

971 async def _ws_connect( 

972 self, 

973 url: StrOrURL, 

974 *, 

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

976 **kwargs: Unpack[_WSConnectOptions], 

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

978 

979 @overload 

980 async def _ws_connect( 

981 self, 

982 url: StrOrURL, 

983 *, 

984 decode_text: Literal[False], 

985 **kwargs: Unpack[_WSConnectOptions], 

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

987 

988 @overload 

989 async def _ws_connect( 

990 self, 

991 url: StrOrURL, 

992 *, 

993 decode_text: bool = ..., 

994 **kwargs: Unpack[_WSConnectOptions], 

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

996 

997 async def _ws_connect( 

998 self, 

999 url: StrOrURL, 

1000 *, 

1001 method: str = hdrs.METH_GET, 

1002 protocols: Collection[str] = (), 

1003 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

1004 receive_timeout: float | None = None, 

1005 autoclose: bool = True, 

1006 autoping: bool = True, 

1007 heartbeat: float | None = None, 

1008 origin: str | None = None, 

1009 params: Query = None, 

1010 headers: LooseHeaders | None = None, 

1011 proxy: StrOrURL | None = None, 

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

1013 server_hostname: str | None = None, 

1014 proxy_headers: LooseHeaders | None = None, 

1015 compress: int = 0, 

1016 max_msg_size: int = 4 * 1024 * 1024, 

1017 decode_text: bool = True, 

1018 ) -> "ClientWebSocketResponse[bool]": 

1019 if timeout is not sentinel: 

1020 if isinstance(timeout, ClientWSTimeout): 

1021 ws_timeout = timeout 

1022 else: 

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

1024 "parameter 'timeout' of type 'float' " 

1025 "is deprecated, please use " 

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

1027 DeprecationWarning, 

1028 stacklevel=2, 

1029 ) 

1030 ws_timeout = ClientWSTimeout(ws_close=timeout) 

1031 else: 

1032 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT 

1033 if receive_timeout is not None: 

1034 warnings.warn( 

1035 "float parameter 'receive_timeout' " 

1036 "is deprecated, please use parameter " 

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

1038 DeprecationWarning, 

1039 stacklevel=2, 

1040 ) 

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

1042 

1043 if headers is None: 

1044 real_headers: CIMultiDict[str] = CIMultiDict() 

1045 else: 

1046 real_headers = CIMultiDict(headers) 

1047 

1048 default_headers = { 

1049 hdrs.UPGRADE: "websocket", 

1050 hdrs.CONNECTION: "Upgrade", 

1051 hdrs.SEC_WEBSOCKET_VERSION: "13", 

1052 } 

1053 

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

1055 real_headers.setdefault(key, value) 

1056 

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

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

1059 

1060 if protocols: 

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

1062 if origin is not None: 

1063 real_headers[hdrs.ORIGIN] = origin 

1064 if compress: 

1065 extstr = ws_ext_gen(compress=compress) 

1066 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

1067 

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

1069 raise TypeError( 

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

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

1072 ) 

1073 

1074 # send request 

1075 resp = await self.request( 

1076 method, 

1077 url, 

1078 params=params, 

1079 headers=real_headers, 

1080 read_until_eof=False, 

1081 proxy=proxy, 

1082 ssl=ssl, 

1083 server_hostname=server_hostname, 

1084 proxy_headers=proxy_headers, 

1085 ) 

1086 

1087 try: 

1088 # check handshake 

1089 if resp.status != 101: 

1090 raise WSServerHandshakeError( 

1091 resp.request_info, 

1092 resp.history, 

1093 message="Invalid response status", 

1094 status=resp.status, 

1095 headers=resp.headers, 

1096 ) 

1097 

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

1099 raise WSServerHandshakeError( 

1100 resp.request_info, 

1101 resp.history, 

1102 message="Invalid upgrade header", 

1103 status=resp.status, 

1104 headers=resp.headers, 

1105 ) 

1106 

1107 if not resp._upgraded: 

1108 raise WSServerHandshakeError( 

1109 resp.request_info, 

1110 resp.history, 

1111 message="Invalid connection header", 

1112 status=resp.status, 

1113 headers=resp.headers, 

1114 ) 

1115 

1116 # key calculation 

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

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

1119 if r_key != match: 

1120 raise WSServerHandshakeError( 

1121 resp.request_info, 

1122 resp.history, 

1123 message="Invalid challenge response", 

1124 status=resp.status, 

1125 headers=resp.headers, 

1126 ) 

1127 

1128 # websocket protocol 

1129 protocol = None 

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

1131 resp_protocols = [ 

1132 proto.strip() 

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

1134 ] 

1135 

1136 for proto in resp_protocols: 

1137 if proto in protocols: 

1138 protocol = proto 

1139 break 

1140 

1141 # websocket compress 

1142 notakeover = False 

1143 if compress: 

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

1145 if compress_hdrs: 

1146 try: 

1147 compress, notakeover = ws_ext_parse(compress_hdrs) 

1148 except WSHandshakeError as exc: 

1149 raise WSServerHandshakeError( 

1150 resp.request_info, 

1151 resp.history, 

1152 message=exc.args[0], 

1153 status=resp.status, 

1154 headers=resp.headers, 

1155 ) from exc 

1156 else: 

1157 compress = 0 

1158 notakeover = False 

1159 

1160 conn = resp.connection 

1161 assert conn is not None 

1162 conn_proto = conn.protocol 

1163 assert conn_proto is not None 

1164 

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

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

1167 if ws_timeout.ws_receive is None: 

1168 # Reset regardless 

1169 conn_proto.read_timeout = None 

1170 elif conn_proto.read_timeout is not None: 

1171 conn_proto.read_timeout = max( 

1172 ws_timeout.ws_receive, conn_proto.read_timeout 

1173 ) 

1174 

1175 transport = conn.transport 

1176 assert transport is not None 

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

1178 writer = WebSocketWriter( 

1179 conn_proto, 

1180 transport, 

1181 use_mask=True, 

1182 compress=compress, 

1183 notakeover=notakeover, 

1184 ) 

1185 except BaseException: 

1186 resp.close() 

1187 raise 

1188 else: 

1189 ws_resp = self._ws_response_class( 

1190 reader, 

1191 writer, 

1192 protocol, 

1193 resp, 

1194 ws_timeout, 

1195 autoclose, 

1196 autoping, 

1197 self._loop, 

1198 heartbeat=heartbeat, 

1199 compress=compress, 

1200 client_notakeover=notakeover, 

1201 ) 

1202 parser = WebSocketReader( 

1203 reader, 

1204 max_msg_size, 

1205 compress=bool(compress), 

1206 decode_text=decode_text, 

1207 ) 

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

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

1210 return ws_resp 

1211 

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

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

1214 # Convert headers to MultiDict 

1215 result = CIMultiDict(self._default_headers) 

1216 if headers: 

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

1218 headers = CIMultiDict(headers) 

1219 added_names: set[str] = set() 

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

1221 if key in added_names: 

1222 result.add(key, value) 

1223 else: 

1224 result[key] = value 

1225 added_names.add(key) 

1226 return result 

1227 

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

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

1230 

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

1232 event loop. 

1233 """ 

1234 netrc_obj = netrc_from_env() 

1235 try: 

1236 return _auth_header_from_netrc(netrc_obj, host) 

1237 except LookupError: 

1238 return None 

1239 

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

1241 

1242 def get( 

1243 self, 

1244 url: StrOrURL, 

1245 **kwargs: Unpack[_RequestOptions], 

1246 ) -> "_RequestContextManager": ... 

1247 

1248 def options( 

1249 self, 

1250 url: StrOrURL, 

1251 **kwargs: Unpack[_RequestOptions], 

1252 ) -> "_RequestContextManager": ... 

1253 

1254 def head( 

1255 self, 

1256 url: StrOrURL, 

1257 **kwargs: Unpack[_RequestOptions], 

1258 ) -> "_RequestContextManager": ... 

1259 

1260 def post( 

1261 self, 

1262 url: StrOrURL, 

1263 **kwargs: Unpack[_RequestOptions], 

1264 ) -> "_RequestContextManager": ... 

1265 

1266 def put( 

1267 self, 

1268 url: StrOrURL, 

1269 **kwargs: Unpack[_RequestOptions], 

1270 ) -> "_RequestContextManager": ... 

1271 

1272 def patch( 

1273 self, 

1274 url: StrOrURL, 

1275 **kwargs: Unpack[_RequestOptions], 

1276 ) -> "_RequestContextManager": ... 

1277 

1278 def delete( 

1279 self, 

1280 url: StrOrURL, 

1281 **kwargs: Unpack[_RequestOptions], 

1282 ) -> "_RequestContextManager": ... 

1283 

1284 else: 

1285 

1286 def get( 

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

1288 ) -> "_RequestContextManager": 

1289 """Perform HTTP GET request.""" 

1290 return _RequestContextManager( 

1291 self._request( 

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

1293 ) 

1294 ) 

1295 

1296 def options( 

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

1298 ) -> "_RequestContextManager": 

1299 """Perform HTTP OPTIONS request.""" 

1300 return _RequestContextManager( 

1301 self._request( 

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

1303 ) 

1304 ) 

1305 

1306 def head( 

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

1308 ) -> "_RequestContextManager": 

1309 """Perform HTTP HEAD request.""" 

1310 return _RequestContextManager( 

1311 self._request( 

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

1313 ) 

1314 ) 

1315 

1316 def post( 

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

1318 ) -> "_RequestContextManager": 

1319 """Perform HTTP POST request.""" 

1320 return _RequestContextManager( 

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

1322 ) 

1323 

1324 def put( 

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

1326 ) -> "_RequestContextManager": 

1327 """Perform HTTP PUT request.""" 

1328 return _RequestContextManager( 

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

1330 ) 

1331 

1332 def patch( 

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

1334 ) -> "_RequestContextManager": 

1335 """Perform HTTP PATCH request.""" 

1336 return _RequestContextManager( 

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

1338 ) 

1339 

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

1341 """Perform HTTP DELETE request.""" 

1342 return _RequestContextManager( 

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

1344 ) 

1345 

1346 async def close(self) -> None: 

1347 """Close underlying connector. 

1348 

1349 Release all acquired resources. 

1350 """ 

1351 if not self.closed: 

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

1353 await self._connector.close() 

1354 self._connector = None 

1355 

1356 @property 

1357 def closed(self) -> bool: 

1358 """Is client session closed. 

1359 

1360 A readonly property. 

1361 """ 

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

1363 

1364 @property 

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

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

1367 return self._connector 

1368 

1369 @property 

1370 def cookie_jar(self) -> AbstractCookieJar: 

1371 """The session cookies.""" 

1372 return self._cookie_jar 

1373 

1374 @property 

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

1376 """The session HTTP protocol version.""" 

1377 return self._version 

1378 

1379 @property 

1380 def requote_redirect_url(self) -> bool: 

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

1382 return self._requote_redirect_url 

1383 

1384 @property 

1385 def timeout(self) -> ClientTimeout: 

1386 """Timeout for the session.""" 

1387 return self._timeout 

1388 

1389 @property 

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

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

1392 return self._default_headers 

1393 

1394 @property 

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

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

1397 return self._skip_auto_headers 

1398 

1399 @property 

1400 def json_serialize(self) -> JSONEncoder: 

1401 """Json serializer callable""" 

1402 return self._json_serialize 

1403 

1404 @property 

1405 def connector_owner(self) -> bool: 

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

1407 return self._connector_owner 

1408 

1409 @property 

1410 def raise_for_status( 

1411 self, 

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

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

1414 return self._raise_for_status 

1415 

1416 @property 

1417 def auto_decompress(self) -> bool: 

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

1419 return self._auto_decompress 

1420 

1421 @property 

1422 def trust_env(self) -> bool: 

1423 """ 

1424 Should proxies information from environment or netrc be trusted. 

1425 

1426 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1427 or ~/.netrc file if present. 

1428 """ 

1429 return self._trust_env 

1430 

1431 @property 

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

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

1434 return self._trace_configs 

1435 

1436 def detach(self) -> None: 

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

1438 

1439 Session is switched to closed state anyway. 

1440 """ 

1441 self._connector = None 

1442 

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

1444 return self 

1445 

1446 async def __aexit__( 

1447 self, 

1448 exc_type: type[BaseException] | None, 

1449 exc_val: BaseException | None, 

1450 exc_tb: TracebackType | None, 

1451 ) -> None: 

1452 await self.close() 

1453 

1454 

1455class _BaseRequestContextManager( 

1456 Coroutine[Any, Any, _RetType_co], Generic[_RetType_co] 

1457): 

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

1459 

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

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

1462 

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

1464 return self._coro.send(arg) 

1465 

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

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

1468 

1469 def close(self) -> None: 

1470 return self._coro.close() 

1471 

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

1473 ret = self._coro.__await__() 

1474 return ret 

1475 

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

1477 return self.__await__() 

1478 

1479 async def __aenter__(self) -> _RetType_co: 

1480 self._resp: _RetType_co = await self._coro 

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

1482 

1483 async def __aexit__( 

1484 self, 

1485 exc_type: type[BaseException] | None, 

1486 exc: BaseException | None, 

1487 tb: TracebackType | None, 

1488 ) -> None: 

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

1490 

1491 

1492_RequestContextManager = _BaseRequestContextManager[ClientResponse] 

1493_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse[bool]] 

1494 

1495 

1496class _SessionRequestContextManager: 

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

1498 

1499 def __init__( 

1500 self, 

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

1502 session: ClientSession, 

1503 ) -> None: 

1504 self._coro = coro 

1505 self._resp: ClientResponse | None = None 

1506 self._session = session 

1507 

1508 async def __aenter__(self) -> ClientResponse: 

1509 try: 

1510 self._resp = await self._coro 

1511 except BaseException: 

1512 await self._session.close() 

1513 raise 

1514 else: 

1515 return self._resp 

1516 

1517 async def __aexit__( 

1518 self, 

1519 exc_type: type[BaseException] | None, 

1520 exc: BaseException | None, 

1521 tb: TracebackType | None, 

1522 ) -> None: 

1523 assert self._resp is not None 

1524 self._resp.close() 

1525 await self._session.close() 

1526 

1527 

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

1529 

1530 def request( 

1531 method: str, 

1532 url: StrOrURL, 

1533 *, 

1534 version: HttpVersion = http.HttpVersion11, 

1535 connector: BaseConnector | None = None, 

1536 **kwargs: Unpack[_RequestOptions], 

1537 ) -> _SessionRequestContextManager: ... 

1538 

1539else: 

1540 

1541 def request( 

1542 method: str, 

1543 url: StrOrURL, 

1544 *, 

1545 version: HttpVersion = http.HttpVersion11, 

1546 connector: BaseConnector | None = None, 

1547 **kwargs: Any, 

1548 ) -> _SessionRequestContextManager: 

1549 """Constructs and sends a request. 

1550 

1551 Returns response object. 

1552 method - HTTP method 

1553 url - request url 

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

1555 string of the new request 

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

1557 send in the body of the request 

1558 json - (optional) Any json compatible python object 

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

1560 the request 

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

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

1563 redirects 

1564 version - Request HTTP version. 

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

1566 with deflate encoding. 

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

1568 expect100 - Expect 100-continue response from server. 

1569 connector - BaseConnector sub-class instance to support 

1570 connection pooling. 

1571 read_until_eof - Read response until eof if response 

1572 does not have Content-Length header. 

1573 loop - Optional event loop. 

1574 timeout - Optional ClientTimeout settings structure, 5min 

1575 total timeout by default. 

1576 Usage:: 

1577 >>> import aiohttp 

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

1579 ... print(resp) 

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

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

1582 """ 

1583 connector_owner = False 

1584 if connector is None: 

1585 connector_owner = True 

1586 connector = TCPConnector(force_close=True) 

1587 

1588 session = ClientSession( 

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

1590 version=version, 

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

1592 connector=connector, 

1593 connector_owner=connector_owner, 

1594 ) 

1595 

1596 return _SessionRequestContextManager( 

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

1598 session, 

1599 )