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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

616 statements  

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

2 

3import asyncio 

4import base64 

5import dataclasses 

6import hashlib 

7import json 

8import os 

9import sys 

10import traceback 

11import warnings 

12from collections.abc import ( 

13 Awaitable, 

14 Callable, 

15 Collection, 

16 Coroutine, 

17 Generator, 

18 Iterable, 

19 Sequence, 

20) 

21from contextlib import suppress 

22from types import TracebackType 

23from typing import ( 

24 TYPE_CHECKING, 

25 Any, 

26 Final, 

27 Generic, 

28 Literal, 

29 TypedDict, 

30 TypeVar, 

31 final, 

32 overload, 

33) 

34 

35from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr 

36from yarl import URL, Query 

37 

38from . import hdrs, http, payload 

39from ._websocket.reader import WebSocketDataQueue 

40from .abc import AbstractCookieJar 

41from .client_exceptions import ( 

42 ClientConnectionError, 

43 ClientConnectionResetError, 

44 ClientConnectorCertificateError, 

45 ClientConnectorDNSError, 

46 ClientConnectorError, 

47 ClientConnectorSSLError, 

48 ClientError, 

49 ClientHttpProxyError, 

50 ClientOSError, 

51 ClientPayloadError, 

52 ClientProxyConnectionError, 

53 ClientResponseError, 

54 ClientSSLError, 

55 ConnectionTimeoutError, 

56 ContentTypeError, 

57 InvalidURL, 

58 InvalidUrlClientError, 

59 InvalidUrlRedirectClientError, 

60 NonHttpUrlClientError, 

61 NonHttpUrlRedirectClientError, 

62 RedirectClientError, 

63 ServerConnectionError, 

64 ServerDisconnectedError, 

65 ServerFingerprintMismatch, 

66 ServerTimeoutError, 

67 SocketTimeoutError, 

68 TooManyRedirects, 

69 WSMessageTypeError, 

70 WSServerHandshakeError, 

71) 

72from .client_middlewares import ClientMiddlewareType, build_client_middlewares 

73from .client_reqrep import ( 

74 SSL_ALLOWED_TYPES, 

75 ClientRequest, 

76 ClientResponse, 

77 Fingerprint, 

78 RequestInfo, 

79) 

80from .client_ws import ( 

81 DEFAULT_WS_CLIENT_TIMEOUT, 

82 ClientWebSocketResponse, 

83 ClientWSTimeout, 

84) 

85from .connector import ( 

86 HTTP_AND_EMPTY_SCHEMA_SET, 

87 BaseConnector, 

88 NamedPipeConnector, 

89 TCPConnector, 

90 UnixConnector, 

91) 

92from .cookiejar import CookieJar 

93from .helpers import ( 

94 _SENTINEL, 

95 DEFAULT_CHUNK_SIZE, 

96 EMPTY_BODY_METHODS, 

97 TimeoutHandle, 

98 _auth_header_from_netrc, 

99 frozen_dataclass_decorator, 

100 get_env_proxy_for_url, 

101 netrc_from_env, 

102 sentinel, 

103 strip_auth_from_url, 

104) 

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

106from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse 

107from .tracing import Trace, TraceConfig 

108from .typedefs import ( 

109 JSONBytesEncoder, 

110 JSONEncoder, 

111 LooseCookies, 

112 LooseHeaders, 

113 StrOrURL, 

114) 

115 

116__all__ = ( 

117 # client_exceptions 

118 "ClientConnectionError", 

119 "ClientConnectionResetError", 

120 "ClientConnectorCertificateError", 

121 "ClientConnectorDNSError", 

122 "ClientConnectorError", 

123 "ClientConnectorSSLError", 

124 "ClientError", 

125 "ClientHttpProxyError", 

126 "ClientOSError", 

127 "ClientPayloadError", 

128 "ClientProxyConnectionError", 

129 "ClientResponseError", 

130 "ClientSSLError", 

131 "ConnectionTimeoutError", 

132 "ContentTypeError", 

133 "InvalidURL", 

134 "InvalidUrlClientError", 

135 "RedirectClientError", 

136 "NonHttpUrlClientError", 

137 "InvalidUrlRedirectClientError", 

138 "NonHttpUrlRedirectClientError", 

139 "ServerConnectionError", 

140 "ServerDisconnectedError", 

141 "ServerFingerprintMismatch", 

142 "ServerTimeoutError", 

143 "SocketTimeoutError", 

144 "TooManyRedirects", 

145 "WSServerHandshakeError", 

146 # client_reqrep 

147 "ClientRequest", 

148 "ClientResponse", 

149 "Fingerprint", 

150 "RequestInfo", 

151 # connector 

152 "BaseConnector", 

153 "TCPConnector", 

154 "UnixConnector", 

155 "NamedPipeConnector", 

156 # client_ws 

157 "ClientWebSocketResponse", 

158 # client 

159 "ClientSession", 

160 "ClientTimeout", 

161 "ClientWSTimeout", 

162 "request", 

163 "WSMessageTypeError", 

164) 

165 

166 

167if TYPE_CHECKING: 

168 from ssl import SSLContext 

169else: 

170 SSLContext = None 

171 

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

173 from typing import Unpack 

174 

175 

176class _RequestOptions(TypedDict, total=False): 

177 params: Query 

178 data: Any 

179 json: Any 

180 cookies: LooseCookies | None 

181 headers: LooseHeaders | None 

182 skip_auto_headers: Iterable[str] | None 

183 allow_redirects: bool 

184 max_redirects: int 

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

186 chunked: bool | None 

187 expect100: bool 

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

189 read_until_eof: bool 

190 proxy: StrOrURL | None 

191 timeout: "ClientTimeout | _SENTINEL | None" 

192 ssl: SSLContext | bool | Fingerprint 

193 server_hostname: str | None 

194 proxy_headers: LooseHeaders | None 

195 trace_request_ctx: object 

196 read_bufsize: int | None 

197 auto_decompress: bool | None 

198 max_line_size: int | None 

199 max_field_size: int | None 

200 max_headers: int | None 

201 middlewares: Sequence[ClientMiddlewareType] | None 

202 

203 

204class _WSConnectOptions(TypedDict, total=False): 

205 method: str 

206 protocols: Collection[str] 

207 timeout: "ClientWSTimeout | _SENTINEL" 

208 receive_timeout: float | None 

209 autoclose: bool 

210 autoping: bool 

211 heartbeat: float | None 

212 origin: str | None 

213 params: Query 

214 headers: LooseHeaders | None 

215 proxy: StrOrURL | None 

216 ssl: SSLContext | bool | Fingerprint 

217 server_hostname: str | None 

218 proxy_headers: LooseHeaders | None 

219 compress: int 

220 max_msg_size: int 

221 

222 

223@frozen_dataclass_decorator 

224class ClientTimeout: 

225 total: float | None = None 

226 connect: float | None = None 

227 sock_read: float | None = None 

228 sock_connect: float | None = None 

229 ceil_threshold: float = 5 

230 

231 # pool_queue_timeout: Optional[float] = None 

232 # dns_resolution_timeout: Optional[float] = None 

233 # socket_connect_timeout: Optional[float] = None 

234 # connection_acquiring_timeout: Optional[float] = None 

235 # new_connection_timeout: Optional[float] = None 

236 # http_header_timeout: Optional[float] = None 

237 # response_body_timeout: Optional[float] = None 

238 

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

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

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

242 # to overwrite the defaults 

243 

244 def __post_init__(self) -> None: 

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

246 raise ValueError( 

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

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

249 "use None instead." 

250 ) 

251 

252 

253# 5 Minute default read timeout 

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

255 

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

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

258 

259_RetType_co = TypeVar( 

260 "_RetType_co", 

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

262 covariant=True, 

263) 

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

265 

266 

267@final 

268class ClientSession: 

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

270 

271 __slots__ = ( 

272 "_base_url", 

273 "_base_url_origin", 

274 "_source_traceback", 

275 "_connector", 

276 "_loop", 

277 "_cookie_jar", 

278 "_connector_owner", 

279 "_version", 

280 "_json_serialize", 

281 "_json_serialize_bytes", 

282 "_requote_redirect_url", 

283 "_timeout", 

284 "_raise_for_status", 

285 "_auto_decompress", 

286 "_trust_env", 

287 "_default_headers", 

288 "_skip_auto_headers", 

289 "_request_class", 

290 "_response_class", 

291 "_ws_response_class", 

292 "_trace_configs", 

293 "_read_bufsize", 

294 "_max_line_size", 

295 "_max_field_size", 

296 "_max_headers", 

297 "_resolve_charset", 

298 "_default_proxy", 

299 "_retry_connection", 

300 "_middlewares", 

301 ) 

302 

303 def __init__( 

304 self, 

305 base_url: StrOrURL | None = None, 

306 *, 

307 connector: BaseConnector | None = None, 

308 cookies: LooseCookies | None = None, 

309 headers: LooseHeaders | None = None, 

310 proxy: StrOrURL | None = None, 

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

312 json_serialize: JSONEncoder = json.dumps, 

313 json_serialize_bytes: JSONBytesEncoder | None = None, 

314 request_class: type[ClientRequest] = ClientRequest, 

315 response_class: type[ClientResponse] = ClientResponse, 

316 ws_response_class: type[ClientWebSocketResponse] = ClientWebSocketResponse, 

317 version: HttpVersion = http.HttpVersion11, 

318 cookie_jar: AbstractCookieJar | None = None, 

319 connector_owner: bool = True, 

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

321 timeout: _SENTINEL | ClientTimeout | None = sentinel, 

322 auto_decompress: bool = True, 

323 trust_env: bool = False, 

324 requote_redirect_url: bool = True, 

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

326 read_bufsize: int = DEFAULT_CHUNK_SIZE, 

327 max_line_size: int = 8190, 

328 max_field_size: int = 8190, 

329 max_headers: int = 128, 

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

331 middlewares: Sequence[ClientMiddlewareType] = (), 

332 ssl_shutdown_timeout: _SENTINEL | None | float = sentinel, 

333 ) -> None: 

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

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

336 self._connector: BaseConnector | None = None 

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

338 self._base_url: URL | None = base_url 

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

340 else: 

341 self._base_url = URL(base_url) 

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

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

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

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

346 

347 loop = asyncio.get_running_loop() 

348 

349 if timeout is sentinel or timeout is None: 

350 timeout = DEFAULT_TIMEOUT 

351 if not isinstance(timeout, ClientTimeout): 

352 raise ValueError( 

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

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

355 ) 

356 self._timeout = timeout 

357 

358 if ssl_shutdown_timeout is not sentinel: 

359 warnings.warn( 

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

361 DeprecationWarning, 

362 stacklevel=2, 

363 ) 

364 

365 if connector is None: 

366 connector = TCPConnector(ssl_shutdown_timeout=ssl_shutdown_timeout) 

367 # Initialize these three attrs before raising any exception, 

368 # they are used in __del__ 

369 self._connector = connector 

370 self._loop = loop 

371 if loop.get_debug(): 

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

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

374 ) 

375 else: 

376 self._source_traceback = None 

377 

378 if connector._loop is not loop: 

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

380 

381 if cookie_jar is None: 

382 cookie_jar = CookieJar() 

383 self._cookie_jar = cookie_jar 

384 

385 if cookies: 

386 self._cookie_jar.update_cookies(cookies) 

387 

388 self._connector_owner = connector_owner 

389 self._version = version 

390 self._json_serialize = json_serialize 

391 self._json_serialize_bytes = json_serialize_bytes 

392 self._raise_for_status = raise_for_status 

393 self._auto_decompress = auto_decompress 

394 self._trust_env = trust_env 

395 self._requote_redirect_url = requote_redirect_url 

396 self._read_bufsize = read_bufsize 

397 self._max_line_size = max_line_size 

398 self._max_field_size = max_field_size 

399 self._max_headers = max_headers 

400 

401 # Convert to list of tuples 

402 if headers: 

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

404 else: 

405 real_headers = CIMultiDict() 

406 self._default_headers: CIMultiDict[str] = real_headers 

407 if skip_auto_headers is not None: 

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

409 else: 

410 self._skip_auto_headers = frozenset() 

411 

412 self._request_class = request_class 

413 self._response_class = response_class 

414 self._ws_response_class = ws_response_class 

415 

416 self._trace_configs = trace_configs or [] 

417 for trace_config in self._trace_configs: 

418 trace_config.freeze() 

419 

420 self._resolve_charset = fallback_charset_resolver 

421 

422 self._default_proxy = proxy 

423 self._retry_connection: bool = True 

424 self._middlewares = middlewares 

425 

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

427 raise TypeError( 

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

429 ) 

430 

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

432 if not self.closed: 

433 _warnings.warn( 

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

435 ResourceWarning, 

436 source=self, 

437 ) 

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

439 if self._source_traceback is not None: 

440 context["source_traceback"] = self._source_traceback 

441 self._loop.call_exception_handler(context) 

442 

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

444 

445 def request( 

446 self, 

447 method: str, 

448 url: StrOrURL, 

449 **kwargs: Unpack[_RequestOptions], 

450 ) -> "_RequestContextManager": ... 

451 

452 else: 

453 

454 def request( 

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

456 ) -> "_RequestContextManager": 

457 """Perform HTTP request.""" 

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

459 

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

461 url = URL(str_or_url) 

462 if self._base_url and not url.absolute: 

463 return self._base_url.join(url) 

464 return url 

465 

466 async def _request( 

467 self, 

468 method: str, 

469 str_or_url: StrOrURL, 

470 *, 

471 params: Query = None, 

472 data: Any = None, 

473 json: Any = None, 

474 cookies: LooseCookies | None = None, 

475 headers: LooseHeaders | None = None, 

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

477 allow_redirects: bool = True, 

478 max_redirects: int = 10, 

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

480 chunked: bool | None = None, 

481 expect100: bool = False, 

482 raise_for_status: ( 

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

484 ) = None, 

485 read_until_eof: bool = True, 

486 proxy: StrOrURL | None = None, 

487 timeout: ClientTimeout | _SENTINEL | None = sentinel, 

488 ssl: SSLContext | bool | Fingerprint = True, 

489 server_hostname: str | None = None, 

490 proxy_headers: LooseHeaders | None = None, 

491 trace_request_ctx: object = None, 

492 read_bufsize: int | None = None, 

493 auto_decompress: bool | None = None, 

494 max_line_size: int | None = None, 

495 max_field_size: int | None = None, 

496 max_headers: int | None = None, 

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

498 ) -> ClientResponse: 

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

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

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

502 

503 if self.closed: 

504 raise RuntimeError("Session is closed") 

505 

506 if not isinstance(ssl, SSL_ALLOWED_TYPES): 

507 raise TypeError( 

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

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

510 ) 

511 

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

513 raise ValueError( 

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

515 ) 

516 elif json is not None: 

517 if self._json_serialize_bytes is not None: 

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

519 else: 

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

521 

522 redirects = 0 

523 history: list[ClientResponse] = [] 

524 version = self._version 

525 params = params or {} 

526 

527 # Merge with default headers and transform to CIMultiDict 

528 headers = self._prepare_headers(headers) 

529 

530 try: 

531 url = self._build_url(str_or_url) 

532 except ValueError as e: 

533 raise InvalidUrlClientError(str_or_url) from e 

534 

535 assert self._connector is not None 

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

537 raise NonHttpUrlClientError(url) 

538 

539 skip_headers: Iterable[istr] | None 

540 if skip_auto_headers is not None: 

541 skip_headers = { 

542 istr(i) for i in skip_auto_headers 

543 } | self._skip_auto_headers 

544 elif self._skip_auto_headers: 

545 skip_headers = self._skip_auto_headers 

546 else: 

547 skip_headers = None 

548 

549 if proxy is None: 

550 proxy = self._default_proxy 

551 

552 if proxy is None: 

553 proxy_headers = None 

554 else: 

555 proxy_headers = self._prepare_headers(proxy_headers) 

556 try: 

557 proxy = URL(proxy) 

558 except ValueError as e: 

559 raise InvalidURL(proxy) from e 

560 

561 if timeout is sentinel or timeout is None: 

562 real_timeout: ClientTimeout = self._timeout 

563 else: 

564 real_timeout = timeout 

565 # timeout is cumulative for all request operations 

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

567 tm = TimeoutHandle( 

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

569 ) 

570 handle = tm.start() 

571 

572 if read_bufsize is None: 

573 read_bufsize = self._read_bufsize 

574 

575 if auto_decompress is None: 

576 auto_decompress = self._auto_decompress 

577 

578 if max_line_size is None: 

579 max_line_size = self._max_line_size 

580 

581 if max_field_size is None: 

582 max_field_size = self._max_field_size 

583 

584 if max_headers is None: 

585 max_headers = self._max_headers 

586 

587 traces = [ 

588 Trace( 

589 self, 

590 trace_config, 

591 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), 

592 ) 

593 for trace_config in self._trace_configs 

594 ] 

595 

596 for trace in traces: 

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

598 

599 timer = tm.timer() 

600 try: 

601 with timer: 

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

603 retry_persistent_connection = ( 

604 self._retry_connection and method in IDEMPOTENT_METHODS 

605 ) 

606 while True: 

607 url, auth_from_url = strip_auth_from_url(url) 

608 if not url.raw_host: 

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

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

611 err_exc_cls = ( 

612 InvalidUrlRedirectClientError 

613 if redirects 

614 else InvalidUrlClientError 

615 ) 

616 raise err_exc_cls(url) 

617 

618 if auth_from_url is not None: 

619 # URL-embedded credentials override any Authorization 

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

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

622 # shadow an explicit Authorization header. 

623 if not history and hdrs.AUTHORIZATION in headers: 

624 raise ValueError( 

625 "Cannot combine AUTHORIZATION header with " 

626 "credentials encoded in URL" 

627 ) 

628 headers[hdrs.AUTHORIZATION] = auth_from_url 

629 elif ( 

630 self._trust_env 

631 and url.host is not None 

632 and hdrs.AUTHORIZATION not in headers 

633 ): 

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

635 netrc_auth = await self._loop.run_in_executor( 

636 None, self._get_netrc_auth, url.host 

637 ) 

638 if netrc_auth is not None: 

639 headers[hdrs.AUTHORIZATION] = netrc_auth 

640 

641 all_cookies = self._cookie_jar.filter_cookies(url) 

642 

643 if cookies is not None: 

644 tmp_cookie_jar = CookieJar( 

645 unsafe=self._cookie_jar.unsafe, 

646 quote_cookie=self._cookie_jar.quote_cookie, 

647 ) 

648 tmp_cookie_jar.update_cookies(cookies) 

649 req_cookies = tmp_cookie_jar.filter_cookies(url) 

650 if req_cookies: 

651 all_cookies.load(req_cookies) 

652 

653 proxy_: URL | None = None 

654 if proxy is not None: 

655 proxy_ = URL(proxy) 

656 elif self._trust_env: 

657 with suppress(LookupError): 

658 proxy_, env_proxy_auth = await asyncio.to_thread( 

659 get_env_proxy_for_url, url 

660 ) 

661 if env_proxy_auth is not None and ( 

662 proxy_headers is None 

663 or hdrs.PROXY_AUTHORIZATION not in proxy_headers 

664 ): 

665 proxy_headers = proxy_headers or CIMultiDict() 

666 proxy_headers[hdrs.PROXY_AUTHORIZATION] = env_proxy_auth 

667 

668 req = self._request_class( 

669 method, 

670 url, 

671 params=params, 

672 headers=headers, 

673 skip_auto_headers=skip_headers, 

674 data=data, 

675 cookies=all_cookies, 

676 version=version, 

677 compress=compress, 

678 chunked=chunked, 

679 expect100=expect100, 

680 loop=self._loop, 

681 response_class=self._response_class, 

682 proxy=proxy_, 

683 timer=timer, 

684 session=self, 

685 ssl=ssl, 

686 server_hostname=server_hostname, 

687 proxy_headers=proxy_headers, 

688 traces=traces, 

689 trust_env=self.trust_env, 

690 ) 

691 

692 async def _connect_and_send_request( 

693 req: ClientRequest, 

694 ) -> ClientResponse: 

695 # connection timeout 

696 assert self._connector is not None 

697 try: 

698 conn = await self._connector.connect( 

699 req, traces=traces, timeout=real_timeout 

700 ) 

701 except asyncio.TimeoutError as exc: 

702 raise ConnectionTimeoutError( 

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

704 ) from exc 

705 

706 assert conn.protocol is not None 

707 conn.protocol.set_response_params( 

708 timer=timer, 

709 skip_payload=req.method in EMPTY_BODY_METHODS, 

710 read_until_eof=read_until_eof, 

711 auto_decompress=auto_decompress, 

712 read_timeout=real_timeout.sock_read, 

713 read_bufsize=read_bufsize, 

714 timeout_ceil_threshold=self._connector._timeout_ceil_threshold, 

715 max_line_size=max_line_size, 

716 max_field_size=max_field_size, 

717 max_headers=max_headers, 

718 ) 

719 try: 

720 resp = await req._send(conn) 

721 try: 

722 await resp.start(conn) 

723 except BaseException: 

724 resp.close() 

725 raise 

726 except BaseException: 

727 conn.close() 

728 raise 

729 return resp 

730 

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

732 effective_middlewares = ( 

733 self._middlewares if middlewares is None else middlewares 

734 ) 

735 

736 if effective_middlewares: 

737 handler = build_client_middlewares( 

738 _connect_and_send_request, effective_middlewares 

739 ) 

740 else: 

741 handler = _connect_and_send_request 

742 

743 try: 

744 resp = await handler(req) 

745 # Client connector errors should not be retried 

746 except ( 

747 ConnectionTimeoutError, 

748 ClientConnectorError, 

749 ClientConnectorCertificateError, 

750 ClientConnectorSSLError, 

751 ): 

752 raise 

753 except (ClientOSError, ServerDisconnectedError): 

754 if retry_persistent_connection: 

755 retry_persistent_connection = False 

756 continue 

757 raise 

758 except ClientError: 

759 raise 

760 except OSError as exc: 

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

762 raise 

763 raise ClientOSError(*exc.args) from exc 

764 

765 # Update cookies from raw headers to preserve duplicates 

766 if resp._raw_cookie_headers: 

767 self._cookie_jar.update_cookies_from_headers( 

768 resp._raw_cookie_headers, resp.url 

769 ) 

770 

771 # redirects 

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

773 for trace in traces: 

774 await trace.send_request_redirect( 

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

776 ) 

777 

778 redirects += 1 

779 history.append(resp) 

780 if max_redirects and redirects >= max_redirects: 

781 if req._body is not None: 

782 await req._body.close() 

783 resp.close() 

784 raise TooManyRedirects( 

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

786 ) 

787 

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

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

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

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

792 ): 

793 method = hdrs.METH_GET 

794 data = None 

795 if headers.get(hdrs.CONTENT_LENGTH): 

796 headers.pop(hdrs.CONTENT_LENGTH) 

797 else: 

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

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

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

801 # Use the existing payload to avoid recreating it from 

802 # a potentially consumed file. 

803 # 

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

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

806 if req._body.consumed: 

807 resp.close() 

808 raise ClientPayloadError( 

809 "Cannot follow redirect with a consumed request " 

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

811 "or set allow_redirects=False." 

812 ) 

813 data = req._body 

814 

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

816 hdrs.URI 

817 ) 

818 if r_url is None: 

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

820 break 

821 else: 

822 # reading from correct redirection 

823 # response is forbidden 

824 resp.release() 

825 

826 try: 

827 parsed_redirect_url = URL( 

828 r_url, encoded=not self._requote_redirect_url 

829 ) 

830 except ValueError as e: 

831 if req._body is not None: 

832 await req._body.close() 

833 resp.close() 

834 raise InvalidUrlRedirectClientError( 

835 r_url, 

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

837 ) from e 

838 

839 scheme = parsed_redirect_url.scheme 

840 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: 

841 if req._body is not None: 

842 await req._body.close() 

843 resp.close() 

844 raise NonHttpUrlRedirectClientError(r_url) 

845 elif not scheme: 

846 parsed_redirect_url = url.join(parsed_redirect_url) 

847 

848 try: 

849 redirect_origin = parsed_redirect_url.origin() 

850 except ValueError as origin_val_err: 

851 if req._body is not None: 

852 await req._body.close() 

853 resp.close() 

854 raise InvalidUrlRedirectClientError( 

855 parsed_redirect_url, 

856 "Invalid redirect URL origin", 

857 ) from origin_val_err 

858 

859 if url.origin() != redirect_origin: 

860 cookies = None 

861 headers.pop(hdrs.AUTHORIZATION, None) 

862 headers.pop(hdrs.COOKIE, None) 

863 headers.pop(hdrs.PROXY_AUTHORIZATION, None) 

864 

865 url = parsed_redirect_url 

866 params = {} 

867 resp.release() 

868 continue 

869 

870 break 

871 

872 if req._body is not None: 

873 await req._body.close() 

874 # check response status 

875 if raise_for_status is None: 

876 raise_for_status = self._raise_for_status 

877 

878 if callable(raise_for_status): 

879 await raise_for_status(resp) 

880 elif raise_for_status: 

881 resp.raise_for_status() 

882 

883 # register connection 

884 if handle is not None: 

885 if resp.connection is not None: 

886 resp.connection.add_callback(handle.cancel) 

887 else: 

888 handle.cancel() 

889 

890 resp._history = tuple(history) 

891 

892 for trace in traces: 

893 await trace.send_request_end( 

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

895 ) 

896 return resp 

897 

898 except BaseException as e: 

899 # cleanup timer 

900 tm.close() 

901 if handle: 

902 handle.cancel() 

903 handle = None 

904 

905 for trace in traces: 

906 await trace.send_request_exception( 

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

908 ) 

909 raise 

910 

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

912 

913 @overload 

914 def ws_connect( 

915 self, 

916 url: StrOrURL, 

917 *, 

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

919 **kwargs: Unpack[_WSConnectOptions], 

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

921 

922 @overload 

923 def ws_connect( 

924 self, 

925 url: StrOrURL, 

926 *, 

927 decode_text: Literal[False], 

928 **kwargs: Unpack[_WSConnectOptions], 

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

930 

931 @overload 

932 def ws_connect( 

933 self, 

934 url: StrOrURL, 

935 *, 

936 decode_text: bool = ..., 

937 **kwargs: Unpack[_WSConnectOptions], 

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

939 

940 def ws_connect( 

941 self, 

942 url: StrOrURL, 

943 *, 

944 method: str = hdrs.METH_GET, 

945 protocols: Collection[str] = (), 

946 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

947 receive_timeout: float | None = None, 

948 autoclose: bool = True, 

949 autoping: bool = True, 

950 heartbeat: float | None = None, 

951 origin: str | None = None, 

952 params: Query = None, 

953 headers: LooseHeaders | None = None, 

954 proxy: StrOrURL | None = None, 

955 ssl: SSLContext | bool | Fingerprint = True, 

956 server_hostname: str | None = None, 

957 proxy_headers: LooseHeaders | None = None, 

958 compress: int = 0, 

959 max_msg_size: int = 4 * 1024 * 1024, 

960 decode_text: bool = True, 

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

962 """Initiate websocket connection.""" 

963 return _WSRequestContextManager( 

964 self._ws_connect( 

965 url, 

966 method=method, 

967 protocols=protocols, 

968 timeout=timeout, 

969 receive_timeout=receive_timeout, 

970 autoclose=autoclose, 

971 autoping=autoping, 

972 heartbeat=heartbeat, 

973 origin=origin, 

974 params=params, 

975 headers=headers, 

976 proxy=proxy, 

977 ssl=ssl, 

978 server_hostname=server_hostname, 

979 proxy_headers=proxy_headers, 

980 compress=compress, 

981 max_msg_size=max_msg_size, 

982 decode_text=decode_text, 

983 ) 

984 ) 

985 

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

987 

988 @overload 

989 async def _ws_connect( 

990 self, 

991 url: StrOrURL, 

992 *, 

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

994 **kwargs: Unpack[_WSConnectOptions], 

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

996 

997 @overload 

998 async def _ws_connect( 

999 self, 

1000 url: StrOrURL, 

1001 *, 

1002 decode_text: Literal[False], 

1003 **kwargs: Unpack[_WSConnectOptions], 

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

1005 

1006 @overload 

1007 async def _ws_connect( 

1008 self, 

1009 url: StrOrURL, 

1010 *, 

1011 decode_text: bool = ..., 

1012 **kwargs: Unpack[_WSConnectOptions], 

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

1014 

1015 async def _ws_connect( 

1016 self, 

1017 url: StrOrURL, 

1018 *, 

1019 method: str = hdrs.METH_GET, 

1020 protocols: Collection[str] = (), 

1021 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

1022 receive_timeout: float | None = None, 

1023 autoclose: bool = True, 

1024 autoping: bool = True, 

1025 heartbeat: float | None = None, 

1026 origin: str | None = None, 

1027 params: Query = None, 

1028 headers: LooseHeaders | None = None, 

1029 proxy: StrOrURL | None = None, 

1030 ssl: SSLContext | bool | Fingerprint = True, 

1031 server_hostname: str | None = None, 

1032 proxy_headers: LooseHeaders | None = None, 

1033 compress: int = 0, 

1034 max_msg_size: int = 4 * 1024 * 1024, 

1035 decode_text: bool = True, 

1036 ) -> "ClientWebSocketResponse[bool]": 

1037 if timeout is not sentinel: 

1038 if isinstance(timeout, ClientWSTimeout): 

1039 ws_timeout = timeout 

1040 else: 

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

1042 "parameter 'timeout' of type 'float' " 

1043 "is deprecated, please use " 

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

1045 DeprecationWarning, 

1046 stacklevel=2, 

1047 ) 

1048 ws_timeout = ClientWSTimeout(ws_close=timeout) 

1049 else: 

1050 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT 

1051 if receive_timeout is not None: 

1052 warnings.warn( 

1053 "float parameter 'receive_timeout' " 

1054 "is deprecated, please use parameter " 

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

1056 DeprecationWarning, 

1057 stacklevel=2, 

1058 ) 

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

1060 

1061 if headers is None: 

1062 real_headers: CIMultiDict[str] = CIMultiDict() 

1063 else: 

1064 real_headers = CIMultiDict(headers) 

1065 

1066 default_headers = { 

1067 hdrs.UPGRADE: "websocket", 

1068 hdrs.CONNECTION: "Upgrade", 

1069 hdrs.SEC_WEBSOCKET_VERSION: "13", 

1070 } 

1071 

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

1073 real_headers.setdefault(key, value) 

1074 

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

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

1077 

1078 if protocols: 

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

1080 if origin is not None: 

1081 real_headers[hdrs.ORIGIN] = origin 

1082 if compress: 

1083 extstr = ws_ext_gen(compress=compress) 

1084 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

1085 

1086 if not isinstance(ssl, SSL_ALLOWED_TYPES): 

1087 raise TypeError( 

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

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

1090 ) 

1091 

1092 # send request 

1093 resp = await self.request( 

1094 method, 

1095 url, 

1096 params=params, 

1097 headers=real_headers, 

1098 read_until_eof=False, 

1099 proxy=proxy, 

1100 ssl=ssl, 

1101 server_hostname=server_hostname, 

1102 proxy_headers=proxy_headers, 

1103 ) 

1104 

1105 try: 

1106 # check handshake 

1107 if resp.status != 101: 

1108 raise WSServerHandshakeError( 

1109 resp.request_info, 

1110 resp.history, 

1111 message="Invalid response status", 

1112 status=resp.status, 

1113 headers=resp.headers, 

1114 ) 

1115 

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

1117 raise WSServerHandshakeError( 

1118 resp.request_info, 

1119 resp.history, 

1120 message="Invalid upgrade header", 

1121 status=resp.status, 

1122 headers=resp.headers, 

1123 ) 

1124 

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

1126 raise WSServerHandshakeError( 

1127 resp.request_info, 

1128 resp.history, 

1129 message="Invalid connection header", 

1130 status=resp.status, 

1131 headers=resp.headers, 

1132 ) 

1133 

1134 # key calculation 

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

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

1137 if r_key != match: 

1138 raise WSServerHandshakeError( 

1139 resp.request_info, 

1140 resp.history, 

1141 message="Invalid challenge response", 

1142 status=resp.status, 

1143 headers=resp.headers, 

1144 ) 

1145 

1146 # websocket protocol 

1147 protocol = None 

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

1149 resp_protocols = [ 

1150 proto.strip() 

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

1152 ] 

1153 

1154 for proto in resp_protocols: 

1155 if proto in protocols: 

1156 protocol = proto 

1157 break 

1158 

1159 # websocket compress 

1160 notakeover = False 

1161 if compress: 

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

1163 if compress_hdrs: 

1164 try: 

1165 compress, notakeover = ws_ext_parse(compress_hdrs) 

1166 except WSHandshakeError as exc: 

1167 raise WSServerHandshakeError( 

1168 resp.request_info, 

1169 resp.history, 

1170 message=exc.args[0], 

1171 status=resp.status, 

1172 headers=resp.headers, 

1173 ) from exc 

1174 else: 

1175 compress = 0 

1176 notakeover = False 

1177 

1178 conn = resp.connection 

1179 assert conn is not None 

1180 conn_proto = conn.protocol 

1181 assert conn_proto is not None 

1182 

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

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

1185 if ws_timeout.ws_receive is None: 

1186 # Reset regardless 

1187 conn_proto.read_timeout = None 

1188 elif conn_proto.read_timeout is not None: 

1189 conn_proto.read_timeout = max( 

1190 ws_timeout.ws_receive, conn_proto.read_timeout 

1191 ) 

1192 

1193 transport = conn.transport 

1194 assert transport is not None 

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

1196 writer = WebSocketWriter( 

1197 conn_proto, 

1198 transport, 

1199 use_mask=True, 

1200 compress=compress, 

1201 notakeover=notakeover, 

1202 ) 

1203 except BaseException: 

1204 resp.close() 

1205 raise 

1206 else: 

1207 ws_resp = self._ws_response_class( 

1208 reader, 

1209 writer, 

1210 protocol, 

1211 resp, 

1212 ws_timeout, 

1213 autoclose, 

1214 autoping, 

1215 self._loop, 

1216 heartbeat=heartbeat, 

1217 compress=compress, 

1218 client_notakeover=notakeover, 

1219 ) 

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

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

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

1223 return ws_resp 

1224 

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

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

1227 # Convert headers to MultiDict 

1228 result = CIMultiDict(self._default_headers) 

1229 if headers: 

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

1231 headers = CIMultiDict(headers) 

1232 added_names: set[str] = set() 

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

1234 if key in added_names: 

1235 result.add(key, value) 

1236 else: 

1237 result[key] = value 

1238 added_names.add(key) 

1239 return result 

1240 

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

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

1243 

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

1245 event loop. 

1246 """ 

1247 netrc_obj = netrc_from_env() 

1248 try: 

1249 return _auth_header_from_netrc(netrc_obj, host) 

1250 except LookupError: 

1251 return None 

1252 

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

1254 

1255 def get( 

1256 self, 

1257 url: StrOrURL, 

1258 **kwargs: Unpack[_RequestOptions], 

1259 ) -> "_RequestContextManager": ... 

1260 

1261 def options( 

1262 self, 

1263 url: StrOrURL, 

1264 **kwargs: Unpack[_RequestOptions], 

1265 ) -> "_RequestContextManager": ... 

1266 

1267 def head( 

1268 self, 

1269 url: StrOrURL, 

1270 **kwargs: Unpack[_RequestOptions], 

1271 ) -> "_RequestContextManager": ... 

1272 

1273 def post( 

1274 self, 

1275 url: StrOrURL, 

1276 **kwargs: Unpack[_RequestOptions], 

1277 ) -> "_RequestContextManager": ... 

1278 

1279 def put( 

1280 self, 

1281 url: StrOrURL, 

1282 **kwargs: Unpack[_RequestOptions], 

1283 ) -> "_RequestContextManager": ... 

1284 

1285 def patch( 

1286 self, 

1287 url: StrOrURL, 

1288 **kwargs: Unpack[_RequestOptions], 

1289 ) -> "_RequestContextManager": ... 

1290 

1291 def delete( 

1292 self, 

1293 url: StrOrURL, 

1294 **kwargs: Unpack[_RequestOptions], 

1295 ) -> "_RequestContextManager": ... 

1296 

1297 else: 

1298 

1299 def get( 

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

1301 ) -> "_RequestContextManager": 

1302 """Perform HTTP GET request.""" 

1303 return _RequestContextManager( 

1304 self._request( 

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

1306 ) 

1307 ) 

1308 

1309 def options( 

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

1311 ) -> "_RequestContextManager": 

1312 """Perform HTTP OPTIONS request.""" 

1313 return _RequestContextManager( 

1314 self._request( 

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

1316 ) 

1317 ) 

1318 

1319 def head( 

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

1321 ) -> "_RequestContextManager": 

1322 """Perform HTTP HEAD request.""" 

1323 return _RequestContextManager( 

1324 self._request( 

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

1326 ) 

1327 ) 

1328 

1329 def post( 

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

1331 ) -> "_RequestContextManager": 

1332 """Perform HTTP POST request.""" 

1333 return _RequestContextManager( 

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

1335 ) 

1336 

1337 def put( 

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

1339 ) -> "_RequestContextManager": 

1340 """Perform HTTP PUT request.""" 

1341 return _RequestContextManager( 

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

1343 ) 

1344 

1345 def patch( 

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

1347 ) -> "_RequestContextManager": 

1348 """Perform HTTP PATCH request.""" 

1349 return _RequestContextManager( 

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

1351 ) 

1352 

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

1354 """Perform HTTP DELETE request.""" 

1355 return _RequestContextManager( 

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

1357 ) 

1358 

1359 async def close(self) -> None: 

1360 """Close underlying connector. 

1361 

1362 Release all acquired resources. 

1363 """ 

1364 if not self.closed: 

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

1366 await self._connector.close() 

1367 self._connector = None 

1368 

1369 @property 

1370 def closed(self) -> bool: 

1371 """Is client session closed. 

1372 

1373 A readonly property. 

1374 """ 

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

1376 

1377 @property 

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

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

1380 return self._connector 

1381 

1382 @property 

1383 def cookie_jar(self) -> AbstractCookieJar: 

1384 """The session cookies.""" 

1385 return self._cookie_jar 

1386 

1387 @property 

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

1389 """The session HTTP protocol version.""" 

1390 return self._version 

1391 

1392 @property 

1393 def requote_redirect_url(self) -> bool: 

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

1395 return self._requote_redirect_url 

1396 

1397 @property 

1398 def timeout(self) -> ClientTimeout: 

1399 """Timeout for the session.""" 

1400 return self._timeout 

1401 

1402 @property 

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

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

1405 return self._default_headers 

1406 

1407 @property 

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

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

1410 return self._skip_auto_headers 

1411 

1412 @property 

1413 def json_serialize(self) -> JSONEncoder: 

1414 """Json serializer callable""" 

1415 return self._json_serialize 

1416 

1417 @property 

1418 def connector_owner(self) -> bool: 

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

1420 return self._connector_owner 

1421 

1422 @property 

1423 def raise_for_status( 

1424 self, 

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

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

1427 return self._raise_for_status 

1428 

1429 @property 

1430 def auto_decompress(self) -> bool: 

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

1432 return self._auto_decompress 

1433 

1434 @property 

1435 def trust_env(self) -> bool: 

1436 """ 

1437 Should proxies information from environment or netrc be trusted. 

1438 

1439 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1440 or ~/.netrc file if present. 

1441 """ 

1442 return self._trust_env 

1443 

1444 @property 

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

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

1447 return self._trace_configs 

1448 

1449 def detach(self) -> None: 

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

1451 

1452 Session is switched to closed state anyway. 

1453 """ 

1454 self._connector = None 

1455 

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

1457 return self 

1458 

1459 async def __aexit__( 

1460 self, 

1461 exc_type: type[BaseException] | None, 

1462 exc_val: BaseException | None, 

1463 exc_tb: TracebackType | None, 

1464 ) -> None: 

1465 await self.close() 

1466 

1467 

1468class _BaseRequestContextManager( 

1469 Coroutine[Any, Any, _RetType_co], Generic[_RetType_co] 

1470): 

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

1472 

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

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

1475 

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

1477 return self._coro.send(arg) 

1478 

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

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

1481 

1482 def close(self) -> None: 

1483 return self._coro.close() 

1484 

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

1486 ret = self._coro.__await__() 

1487 return ret 

1488 

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

1490 return self.__await__() 

1491 

1492 async def __aenter__(self) -> _RetType_co: 

1493 self._resp: _RetType_co = await self._coro 

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

1495 

1496 async def __aexit__( 

1497 self, 

1498 exc_type: type[BaseException] | None, 

1499 exc: BaseException | None, 

1500 tb: TracebackType | None, 

1501 ) -> None: 

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

1503 

1504 

1505_RequestContextManager = _BaseRequestContextManager[ClientResponse] 

1506_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse[bool]] 

1507 

1508 

1509class _SessionRequestContextManager: 

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

1511 

1512 def __init__( 

1513 self, 

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

1515 session: ClientSession, 

1516 ) -> None: 

1517 self._coro = coro 

1518 self._resp: ClientResponse | None = None 

1519 self._session = session 

1520 

1521 async def __aenter__(self) -> ClientResponse: 

1522 try: 

1523 self._resp = await self._coro 

1524 except BaseException: 

1525 await self._session.close() 

1526 raise 

1527 else: 

1528 return self._resp 

1529 

1530 async def __aexit__( 

1531 self, 

1532 exc_type: type[BaseException] | None, 

1533 exc: BaseException | None, 

1534 tb: TracebackType | None, 

1535 ) -> None: 

1536 assert self._resp is not None 

1537 self._resp.close() 

1538 await self._session.close() 

1539 

1540 

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

1542 

1543 def request( 

1544 method: str, 

1545 url: StrOrURL, 

1546 *, 

1547 version: HttpVersion = http.HttpVersion11, 

1548 connector: BaseConnector | None = None, 

1549 **kwargs: Unpack[_RequestOptions], 

1550 ) -> _SessionRequestContextManager: ... 

1551 

1552else: 

1553 

1554 def request( 

1555 method: str, 

1556 url: StrOrURL, 

1557 *, 

1558 version: HttpVersion = http.HttpVersion11, 

1559 connector: BaseConnector | None = None, 

1560 **kwargs: Any, 

1561 ) -> _SessionRequestContextManager: 

1562 """Constructs and sends a request. 

1563 

1564 Returns response object. 

1565 method - HTTP method 

1566 url - request url 

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

1568 string of the new request 

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

1570 send in the body of the request 

1571 json - (optional) Any json compatible python object 

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

1573 the request 

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

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

1576 redirects 

1577 version - Request HTTP version. 

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

1579 with deflate encoding. 

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

1581 expect100 - Expect 100-continue response from server. 

1582 connector - BaseConnector sub-class instance to support 

1583 connection pooling. 

1584 read_until_eof - Read response until eof if response 

1585 does not have Content-Length header. 

1586 loop - Optional event loop. 

1587 timeout - Optional ClientTimeout settings structure, 5min 

1588 total timeout by default. 

1589 Usage:: 

1590 >>> import aiohttp 

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

1592 ... print(resp) 

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

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

1595 """ 

1596 connector_owner = False 

1597 if connector is None: 

1598 connector_owner = True 

1599 connector = TCPConnector(force_close=True) 

1600 

1601 session = ClientSession( 

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

1603 version=version, 

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

1605 connector=connector, 

1606 connector_owner=connector_owner, 

1607 ) 

1608 

1609 return _SessionRequestContextManager( 

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

1611 session, 

1612 )