Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/aiohttp/client.py: 55%

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

617 statements  

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

2 

3import asyncio 

4import base64 

5import hashlib 

6import json 

7import os 

8import sys 

9import traceback 

10import warnings 

11from contextlib import suppress 

12from types import TracebackType 

13from typing import ( 

14 TYPE_CHECKING, 

15 Any, 

16 Awaitable, 

17 Callable, 

18 Coroutine, 

19 Final, 

20 FrozenSet, 

21 Generator, 

22 Generic, 

23 Iterable, 

24 List, 

25 Mapping, 

26 Optional, 

27 Sequence, 

28 Set, 

29 Tuple, 

30 Type, 

31 TypedDict, 

32 TypeVar, 

33 Union, 

34) 

35 

36import attr 

37from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr 

38from yarl import URL 

39 

40from . import hdrs, http, payload 

41from ._websocket.reader import WebSocketDataQueue 

42from .abc import AbstractCookieJar 

43from .client_exceptions import ( 

44 ClientConnectionError, 

45 ClientConnectionResetError, 

46 ClientConnectorCertificateError, 

47 ClientConnectorDNSError, 

48 ClientConnectorError, 

49 ClientConnectorSSLError, 

50 ClientError, 

51 ClientHttpProxyError, 

52 ClientOSError, 

53 ClientPayloadError, 

54 ClientProxyConnectionError, 

55 ClientResponseError, 

56 ClientSSLError, 

57 ConnectionTimeoutError, 

58 ContentTypeError, 

59 InvalidURL, 

60 InvalidUrlClientError, 

61 InvalidUrlRedirectClientError, 

62 NonHttpUrlClientError, 

63 NonHttpUrlRedirectClientError, 

64 RedirectClientError, 

65 ServerConnectionError, 

66 ServerDisconnectedError, 

67 ServerFingerprintMismatch, 

68 ServerTimeoutError, 

69 SocketTimeoutError, 

70 TooManyRedirects, 

71 WSMessageTypeError, 

72 WSServerHandshakeError, 

73) 

74from .client_middlewares import ClientMiddlewareType, build_client_middlewares 

75from .client_reqrep import ( 

76 ClientRequest as ClientRequest, 

77 ClientResponse as ClientResponse, 

78 Fingerprint as Fingerprint, 

79 RequestInfo as RequestInfo, 

80 _merge_ssl_params, 

81) 

82from .client_ws import ( 

83 DEFAULT_WS_CLIENT_TIMEOUT, 

84 ClientWebSocketResponse as ClientWebSocketResponse, 

85 ClientWSTimeout as ClientWSTimeout, 

86) 

87from .connector import ( 

88 HTTP_AND_EMPTY_SCHEMA_SET, 

89 BaseConnector as BaseConnector, 

90 NamedPipeConnector as NamedPipeConnector, 

91 TCPConnector as TCPConnector, 

92 UnixConnector as UnixConnector, 

93) 

94from .cookiejar import CookieJar 

95from .helpers import ( 

96 _SENTINEL, 

97 DEBUG, 

98 EMPTY_BODY_METHODS, 

99 BasicAuth, 

100 TimeoutHandle, 

101 basicauth_from_netrc, 

102 get_env_proxy_for_url, 

103 netrc_from_env, 

104 sentinel, 

105 strip_auth_from_url, 

106) 

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

108from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse 

109from .tracing import Trace, TraceConfig 

110from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, Query, StrOrURL 

111 

112__all__ = ( 

113 # client_exceptions 

114 "ClientConnectionError", 

115 "ClientConnectionResetError", 

116 "ClientConnectorCertificateError", 

117 "ClientConnectorDNSError", 

118 "ClientConnectorError", 

119 "ClientConnectorSSLError", 

120 "ClientError", 

121 "ClientHttpProxyError", 

122 "ClientOSError", 

123 "ClientPayloadError", 

124 "ClientProxyConnectionError", 

125 "ClientResponseError", 

126 "ClientSSLError", 

127 "ConnectionTimeoutError", 

128 "ContentTypeError", 

129 "InvalidURL", 

130 "InvalidUrlClientError", 

131 "RedirectClientError", 

132 "NonHttpUrlClientError", 

133 "InvalidUrlRedirectClientError", 

134 "NonHttpUrlRedirectClientError", 

135 "ServerConnectionError", 

136 "ServerDisconnectedError", 

137 "ServerFingerprintMismatch", 

138 "ServerTimeoutError", 

139 "SocketTimeoutError", 

140 "TooManyRedirects", 

141 "WSServerHandshakeError", 

142 # client_reqrep 

143 "ClientRequest", 

144 "ClientResponse", 

145 "Fingerprint", 

146 "RequestInfo", 

147 # connector 

148 "BaseConnector", 

149 "TCPConnector", 

150 "UnixConnector", 

151 "NamedPipeConnector", 

152 # client_ws 

153 "ClientWebSocketResponse", 

154 # client 

155 "ClientSession", 

156 "ClientTimeout", 

157 "ClientWSTimeout", 

158 "request", 

159 "WSMessageTypeError", 

160) 

161 

162 

163if TYPE_CHECKING: 

164 from ssl import SSLContext 

165else: 

166 SSLContext = None 

167 

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

169 from typing import Unpack 

170 

171 

172class _RequestOptions(TypedDict, total=False): 

173 params: Query 

174 data: Any 

175 json: Any 

176 cookies: Union[LooseCookies, None] 

177 headers: Union[LooseHeaders, None] 

178 skip_auto_headers: Union[Iterable[str], None] 

179 auth: Union[BasicAuth, None] 

180 allow_redirects: bool 

181 max_redirects: int 

182 compress: Union[str, bool, None] 

183 chunked: Union[bool, None] 

184 expect100: bool 

185 raise_for_status: Union[None, bool, Callable[[ClientResponse], Awaitable[None]]] 

186 read_until_eof: bool 

187 proxy: Union[StrOrURL, None] 

188 proxy_auth: Union[BasicAuth, None] 

189 timeout: "Union[ClientTimeout, _SENTINEL, None]" 

190 ssl: Union[SSLContext, bool, Fingerprint] 

191 server_hostname: Union[str, None] 

192 proxy_headers: Union[LooseHeaders, None] 

193 trace_request_ctx: Union[Mapping[str, Any], None] 

194 read_bufsize: Union[int, None] 

195 auto_decompress: Union[bool, None] 

196 max_line_size: Union[int, None] 

197 max_field_size: Union[int, None] 

198 max_headers: Union[int, None] 

199 middlewares: Optional[Sequence[ClientMiddlewareType]] 

200 

201 

202@attr.s(auto_attribs=True, frozen=True, slots=True) 

203class ClientTimeout: 

204 total: Optional[float] = None 

205 connect: Optional[float] = None 

206 sock_read: Optional[float] = None 

207 sock_connect: Optional[float] = None 

208 ceil_threshold: float = 5 

209 

210 # pool_queue_timeout: Optional[float] = None 

211 # dns_resolution_timeout: Optional[float] = None 

212 # socket_connect_timeout: Optional[float] = None 

213 # connection_acquiring_timeout: Optional[float] = None 

214 # new_connection_timeout: Optional[float] = None 

215 # http_header_timeout: Optional[float] = None 

216 # response_body_timeout: Optional[float] = None 

217 

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

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

220 # - or use http://www.attrs.org/en/stable/api.html#attr.evolve 

221 # to overwrite the defaults 

222 

223 

224# 5 Minute default read timeout 

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

226 

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

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

229 

230_RetType = TypeVar("_RetType", ClientResponse, ClientWebSocketResponse) 

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

232 

233 

234class ClientSession: 

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

236 

237 ATTRS = frozenset( 

238 [ 

239 "_base_url", 

240 "_base_url_origin", 

241 "_source_traceback", 

242 "_connector", 

243 "_loop", 

244 "_cookie_jar", 

245 "_connector_owner", 

246 "_default_auth", 

247 "_version", 

248 "_json_serialize", 

249 "_requote_redirect_url", 

250 "_timeout", 

251 "_raise_for_status", 

252 "_auto_decompress", 

253 "_trust_env", 

254 "_default_headers", 

255 "_skip_auto_headers", 

256 "_request_class", 

257 "_response_class", 

258 "_ws_response_class", 

259 "_trace_configs", 

260 "_read_bufsize", 

261 "_max_line_size", 

262 "_max_field_size", 

263 "_max_headers", 

264 "_resolve_charset", 

265 "_default_proxy", 

266 "_default_proxy_auth", 

267 "_retry_connection", 

268 "_middlewares", 

269 "requote_redirect_url", 

270 ] 

271 ) 

272 

273 _source_traceback: Optional[traceback.StackSummary] = None 

274 _connector: Optional[BaseConnector] = None 

275 

276 def __init__( 

277 self, 

278 base_url: Optional[StrOrURL] = None, 

279 *, 

280 connector: Optional[BaseConnector] = None, 

281 loop: Optional[asyncio.AbstractEventLoop] = None, 

282 cookies: Optional[LooseCookies] = None, 

283 headers: Optional[LooseHeaders] = None, 

284 proxy: Optional[StrOrURL] = None, 

285 proxy_auth: Optional[BasicAuth] = None, 

286 skip_auto_headers: Optional[Iterable[str]] = None, 

287 auth: Optional[BasicAuth] = None, 

288 json_serialize: JSONEncoder = json.dumps, 

289 request_class: Type[ClientRequest] = ClientRequest, 

290 response_class: Type[ClientResponse] = ClientResponse, 

291 ws_response_class: Type[ClientWebSocketResponse] = ClientWebSocketResponse, 

292 version: HttpVersion = http.HttpVersion11, 

293 cookie_jar: Optional[AbstractCookieJar] = None, 

294 connector_owner: bool = True, 

295 raise_for_status: Union[ 

296 bool, Callable[[ClientResponse], Awaitable[None]] 

297 ] = False, 

298 read_timeout: Union[float, _SENTINEL] = sentinel, 

299 conn_timeout: Optional[float] = None, 

300 timeout: Union[object, ClientTimeout] = sentinel, 

301 auto_decompress: bool = True, 

302 trust_env: bool = False, 

303 requote_redirect_url: bool = True, 

304 trace_configs: Optional[List[TraceConfig]] = None, 

305 read_bufsize: int = 2**16, 

306 max_line_size: int = 8190, 

307 max_field_size: int = 8190, 

308 max_headers: int = 128, 

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

310 middlewares: Sequence[ClientMiddlewareType] = (), 

311 ssl_shutdown_timeout: Union[_SENTINEL, None, float] = sentinel, 

312 ) -> None: 

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

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

315 self._connector: Optional[BaseConnector] = None 

316 

317 if loop is None: 

318 if connector is not None: 

319 loop = connector._loop 

320 

321 loop = loop or asyncio.get_running_loop() 

322 

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

324 self._base_url: Optional[URL] = base_url 

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

326 else: 

327 self._base_url = URL(base_url) 

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

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

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

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

332 

333 if timeout is sentinel or timeout is None: 

334 self._timeout = DEFAULT_TIMEOUT 

335 if read_timeout is not sentinel: 

336 warnings.warn( 

337 "read_timeout is deprecated, use timeout argument instead", 

338 DeprecationWarning, 

339 stacklevel=2, 

340 ) 

341 self._timeout = attr.evolve(self._timeout, total=read_timeout) 

342 if conn_timeout is not None: 

343 self._timeout = attr.evolve(self._timeout, connect=conn_timeout) 

344 warnings.warn( 

345 "conn_timeout is deprecated, use timeout argument instead", 

346 DeprecationWarning, 

347 stacklevel=2, 

348 ) 

349 else: 

350 if not isinstance(timeout, ClientTimeout): 

351 raise ValueError( 

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

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

354 ) 

355 self._timeout = timeout 

356 if read_timeout is not sentinel: 

357 raise ValueError( 

358 "read_timeout and timeout parameters " 

359 "conflict, please setup " 

360 "timeout.read" 

361 ) 

362 if conn_timeout is not None: 

363 raise ValueError( 

364 "conn_timeout and timeout parameters " 

365 "conflict, please setup " 

366 "timeout.connect" 

367 ) 

368 

369 if ssl_shutdown_timeout is not sentinel: 

370 warnings.warn( 

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

372 DeprecationWarning, 

373 stacklevel=2, 

374 ) 

375 

376 if connector is None: 

377 connector = TCPConnector( 

378 loop=loop, ssl_shutdown_timeout=ssl_shutdown_timeout 

379 ) 

380 

381 if connector._loop is not loop: 

382 raise RuntimeError("Session and connector has to use same event loop") 

383 

384 self._loop = loop 

385 

386 if loop.get_debug(): 

387 self._source_traceback = traceback.extract_stack(sys._getframe(1)) 

388 

389 if cookie_jar is None: 

390 cookie_jar = CookieJar(loop=loop) 

391 self._cookie_jar = cookie_jar 

392 

393 if cookies: 

394 self._cookie_jar.update_cookies(cookies) 

395 

396 self._connector = connector 

397 self._connector_owner = connector_owner 

398 self._default_auth = auth 

399 self._version = version 

400 self._json_serialize = json_serialize 

401 self._raise_for_status = raise_for_status 

402 self._auto_decompress = auto_decompress 

403 self._trust_env = trust_env 

404 self._requote_redirect_url = requote_redirect_url 

405 self._read_bufsize = read_bufsize 

406 self._max_line_size = max_line_size 

407 self._max_field_size = max_field_size 

408 self._max_headers = max_headers 

409 

410 # Convert to list of tuples 

411 if headers: 

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

413 else: 

414 real_headers = CIMultiDict() 

415 self._default_headers: CIMultiDict[str] = real_headers 

416 if skip_auto_headers is not None: 

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

418 else: 

419 self._skip_auto_headers = frozenset() 

420 

421 self._request_class = request_class 

422 self._response_class = response_class 

423 self._ws_response_class = ws_response_class 

424 

425 self._trace_configs = trace_configs or [] 

426 for trace_config in self._trace_configs: 

427 trace_config.freeze() 

428 

429 self._resolve_charset = fallback_charset_resolver 

430 

431 self._default_proxy = proxy 

432 self._default_proxy_auth = proxy_auth 

433 self._retry_connection: bool = True 

434 self._middlewares = middlewares 

435 

436 def __init_subclass__(cls: Type["ClientSession"]) -> None: 

437 warnings.warn( 

438 "Inheritance class {} from ClientSession " 

439 "is discouraged".format(cls.__name__), 

440 DeprecationWarning, 

441 stacklevel=2, 

442 ) 

443 

444 if DEBUG: 

445 

446 def __setattr__(self, name: str, val: Any) -> None: 

447 if name not in self.ATTRS: 

448 warnings.warn( 

449 "Setting custom ClientSession.{} attribute " 

450 "is discouraged".format(name), 

451 DeprecationWarning, 

452 stacklevel=2, 

453 ) 

454 super().__setattr__(name, val) 

455 

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

457 if not self.closed: 

458 kwargs = {"source": self} 

459 _warnings.warn( 

460 f"Unclosed client session {self!r}", ResourceWarning, **kwargs 

461 ) 

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

463 if self._source_traceback is not None: 

464 context["source_traceback"] = self._source_traceback 

465 self._loop.call_exception_handler(context) 

466 

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

468 

469 def request( 

470 self, 

471 method: str, 

472 url: StrOrURL, 

473 **kwargs: Unpack[_RequestOptions], 

474 ) -> "_RequestContextManager": ... 

475 

476 else: 

477 

478 def request( 

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

480 ) -> "_RequestContextManager": 

481 """Perform HTTP request.""" 

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

483 

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

485 url = URL(str_or_url) 

486 if self._base_url and not url.absolute: 

487 return self._base_url.join(url) 

488 return url 

489 

490 async def _request( 

491 self, 

492 method: str, 

493 str_or_url: StrOrURL, 

494 *, 

495 params: Query = None, 

496 data: Any = None, 

497 json: Any = None, 

498 cookies: Optional[LooseCookies] = None, 

499 headers: Optional[LooseHeaders] = None, 

500 skip_auto_headers: Optional[Iterable[str]] = None, 

501 auth: Optional[BasicAuth] = None, 

502 allow_redirects: bool = True, 

503 max_redirects: int = 10, 

504 compress: Union[str, bool, None] = None, 

505 chunked: Optional[bool] = None, 

506 expect100: bool = False, 

507 raise_for_status: Union[ 

508 None, bool, Callable[[ClientResponse], Awaitable[None]] 

509 ] = None, 

510 read_until_eof: bool = True, 

511 proxy: Optional[StrOrURL] = None, 

512 proxy_auth: Optional[BasicAuth] = None, 

513 timeout: Union[ClientTimeout, _SENTINEL] = sentinel, 

514 verify_ssl: Optional[bool] = None, 

515 fingerprint: Optional[bytes] = None, 

516 ssl_context: Optional[SSLContext] = None, 

517 ssl: Union[SSLContext, bool, Fingerprint] = True, 

518 server_hostname: Optional[str] = None, 

519 proxy_headers: Optional[LooseHeaders] = None, 

520 trace_request_ctx: Optional[Mapping[str, Any]] = None, 

521 read_bufsize: Optional[int] = None, 

522 auto_decompress: Optional[bool] = None, 

523 max_line_size: Optional[int] = None, 

524 max_field_size: Optional[int] = None, 

525 max_headers: Optional[int] = None, 

526 middlewares: Optional[Sequence[ClientMiddlewareType]] = None, 

527 ) -> ClientResponse: 

528 

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

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

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

532 

533 if self.closed: 

534 raise RuntimeError("Session is closed") 

535 

536 ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) 

537 

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

539 raise ValueError( 

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

541 ) 

542 elif json is not None: 

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

544 

545 if not isinstance(chunked, bool) and chunked is not None: 

546 warnings.warn("Chunk size is deprecated #1615", DeprecationWarning) 

547 

548 redirects = 0 

549 history: List[ClientResponse] = [] 

550 version = self._version 

551 params = params or {} 

552 

553 # Merge with default headers and transform to CIMultiDict 

554 headers = self._prepare_headers(headers) 

555 

556 try: 

557 url = self._build_url(str_or_url) 

558 except ValueError as e: 

559 raise InvalidUrlClientError(str_or_url) from e 

560 

561 assert self._connector is not None 

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

563 raise NonHttpUrlClientError(url) 

564 

565 skip_headers: Optional[Iterable[istr]] 

566 if skip_auto_headers is not None: 

567 skip_headers = { 

568 istr(i) for i in skip_auto_headers 

569 } | self._skip_auto_headers 

570 elif self._skip_auto_headers: 

571 skip_headers = self._skip_auto_headers 

572 else: 

573 skip_headers = None 

574 

575 if proxy is None: 

576 proxy = self._default_proxy 

577 if proxy_auth is None: 

578 proxy_auth = self._default_proxy_auth 

579 

580 if proxy is None: 

581 proxy_headers = None 

582 else: 

583 proxy_headers = self._prepare_headers(proxy_headers) 

584 try: 

585 proxy = URL(proxy) 

586 except ValueError as e: 

587 raise InvalidURL(proxy) from e 

588 

589 if timeout is sentinel: 

590 real_timeout: ClientTimeout = self._timeout 

591 else: 

592 if not isinstance(timeout, ClientTimeout): 

593 real_timeout = ClientTimeout(total=timeout) 

594 else: 

595 real_timeout = timeout 

596 # timeout is cumulative for all request operations 

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

598 tm = TimeoutHandle( 

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

600 ) 

601 handle = tm.start() 

602 

603 if read_bufsize is None: 

604 read_bufsize = self._read_bufsize 

605 

606 if auto_decompress is None: 

607 auto_decompress = self._auto_decompress 

608 

609 if max_line_size is None: 

610 max_line_size = self._max_line_size 

611 

612 if max_field_size is None: 

613 max_field_size = self._max_field_size 

614 

615 if max_headers is None: 

616 max_headers = self._max_headers 

617 

618 traces = [ 

619 Trace( 

620 self, 

621 trace_config, 

622 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), 

623 ) 

624 for trace_config in self._trace_configs 

625 ] 

626 

627 for trace in traces: 

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

629 

630 timer = tm.timer() 

631 try: 

632 with timer: 

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

634 retry_persistent_connection = ( 

635 self._retry_connection and method in IDEMPOTENT_METHODS 

636 ) 

637 while True: 

638 url, auth_from_url = strip_auth_from_url(url) 

639 if not url.raw_host: 

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

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

642 err_exc_cls = ( 

643 InvalidUrlRedirectClientError 

644 if redirects 

645 else InvalidUrlClientError 

646 ) 

647 raise err_exc_cls(url) 

648 # If `auth` was passed for an already authenticated URL, 

649 # disallow only if this is the initial URL; this is to avoid issues 

650 # with sketchy redirects that are not the caller's responsibility 

651 if not history and (auth and auth_from_url): 

652 raise ValueError( 

653 "Cannot combine AUTH argument with " 

654 "credentials encoded in URL" 

655 ) 

656 

657 # Override the auth with the one from the URL only if we 

658 # have no auth, or if we got an auth from a redirect URL 

659 if auth is None or (history and auth_from_url is not None): 

660 auth = auth_from_url 

661 

662 if ( 

663 auth is None 

664 and self._default_auth 

665 and ( 

666 not self._base_url or self._base_url_origin == url.origin() 

667 ) 

668 ): 

669 auth = self._default_auth 

670 

671 # Try netrc if auth is still None and trust_env is enabled. 

672 if auth is None and self._trust_env and url.host is not None: 

673 auth = await self._loop.run_in_executor( 

674 None, self._get_netrc_auth, url.host 

675 ) 

676 

677 # It would be confusing if we support explicit 

678 # Authorization header with auth argument 

679 if ( 

680 headers is not None 

681 and auth is not None 

682 and hdrs.AUTHORIZATION in headers 

683 ): 

684 raise ValueError( 

685 "Cannot combine AUTHORIZATION header " 

686 "with AUTH argument or credentials " 

687 "encoded in URL" 

688 ) 

689 

690 all_cookies = self._cookie_jar.filter_cookies(url) 

691 

692 if cookies is not None: 

693 tmp_cookie_jar = CookieJar( 

694 quote_cookie=self._cookie_jar.quote_cookie 

695 ) 

696 tmp_cookie_jar.update_cookies(cookies) 

697 req_cookies = tmp_cookie_jar.filter_cookies(url) 

698 if req_cookies: 

699 all_cookies.load(req_cookies) 

700 

701 proxy_: Optional[URL] = None 

702 if proxy is not None: 

703 proxy_ = URL(proxy) 

704 elif self._trust_env: 

705 with suppress(LookupError): 

706 proxy_, proxy_auth = await asyncio.to_thread( 

707 get_env_proxy_for_url, url 

708 ) 

709 

710 req = self._request_class( 

711 method, 

712 url, 

713 params=params, 

714 headers=headers, 

715 skip_auto_headers=skip_headers, 

716 data=data, 

717 cookies=all_cookies, 

718 auth=auth, 

719 version=version, 

720 compress=compress, 

721 chunked=chunked, 

722 expect100=expect100, 

723 loop=self._loop, 

724 response_class=self._response_class, 

725 proxy=proxy_, 

726 proxy_auth=proxy_auth, 

727 timer=timer, 

728 session=self, 

729 ssl=ssl if ssl is not None else True, 

730 server_hostname=server_hostname, 

731 proxy_headers=proxy_headers, 

732 traces=traces, 

733 trust_env=self.trust_env, 

734 ) 

735 

736 async def _connect_and_send_request( 

737 req: ClientRequest, 

738 ) -> ClientResponse: 

739 # connection timeout 

740 assert self._connector is not None 

741 try: 

742 conn = await self._connector.connect( 

743 req, traces=traces, timeout=real_timeout 

744 ) 

745 except asyncio.TimeoutError as exc: 

746 raise ConnectionTimeoutError( 

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

748 ) from exc 

749 

750 assert conn.protocol is not None 

751 conn.protocol.set_response_params( 

752 timer=timer, 

753 skip_payload=req.method in EMPTY_BODY_METHODS, 

754 read_until_eof=read_until_eof, 

755 auto_decompress=auto_decompress, 

756 read_timeout=real_timeout.sock_read, 

757 read_bufsize=read_bufsize, 

758 timeout_ceil_threshold=self._connector._timeout_ceil_threshold, 

759 max_line_size=max_line_size, 

760 max_field_size=max_field_size, 

761 max_headers=max_headers, 

762 ) 

763 try: 

764 resp = await req.send(conn) 

765 try: 

766 await resp.start(conn) 

767 except BaseException: 

768 resp.close() 

769 raise 

770 except BaseException: 

771 conn.close() 

772 raise 

773 return resp 

774 

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

776 effective_middlewares = ( 

777 self._middlewares if middlewares is None else middlewares 

778 ) 

779 

780 if effective_middlewares: 

781 handler = build_client_middlewares( 

782 _connect_and_send_request, effective_middlewares 

783 ) 

784 else: 

785 handler = _connect_and_send_request 

786 

787 try: 

788 resp = await handler(req) 

789 # Client connector errors should not be retried 

790 except ( 

791 ConnectionTimeoutError, 

792 ClientConnectorError, 

793 ClientConnectorCertificateError, 

794 ClientConnectorSSLError, 

795 ): 

796 raise 

797 except (ClientOSError, ServerDisconnectedError): 

798 if retry_persistent_connection: 

799 retry_persistent_connection = False 

800 continue 

801 raise 

802 except ClientError: 

803 raise 

804 except OSError as exc: 

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

806 raise 

807 raise ClientOSError(*exc.args) from exc 

808 

809 # Update cookies from raw headers to preserve duplicates 

810 if resp._raw_cookie_headers: 

811 self._cookie_jar.update_cookies_from_headers( 

812 resp._raw_cookie_headers, resp.url 

813 ) 

814 

815 # redirects 

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

817 

818 for trace in traces: 

819 await trace.send_request_redirect( 

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

821 ) 

822 

823 redirects += 1 

824 history.append(resp) 

825 if max_redirects and redirects >= max_redirects: 

826 if req._body is not None: 

827 await req._body.close() 

828 resp.close() 

829 raise TooManyRedirects( 

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

831 ) 

832 

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

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

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

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

837 ): 

838 method = hdrs.METH_GET 

839 data = None 

840 if headers.get(hdrs.CONTENT_LENGTH): 

841 headers.pop(hdrs.CONTENT_LENGTH) 

842 else: 

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

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

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

846 # Use the existing payload to avoid recreating it from a potentially consumed file 

847 data = req._body 

848 

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

850 hdrs.URI 

851 ) 

852 if r_url is None: 

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

854 break 

855 else: 

856 # reading from correct redirection 

857 # response is forbidden 

858 resp.release() 

859 

860 try: 

861 parsed_redirect_url = URL( 

862 r_url, encoded=not self._requote_redirect_url 

863 ) 

864 except ValueError as e: 

865 if req._body is not None: 

866 await req._body.close() 

867 resp.close() 

868 raise InvalidUrlRedirectClientError( 

869 r_url, 

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

871 ) from e 

872 

873 scheme = parsed_redirect_url.scheme 

874 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: 

875 if req._body is not None: 

876 await req._body.close() 

877 resp.close() 

878 raise NonHttpUrlRedirectClientError(r_url) 

879 elif not scheme: 

880 parsed_redirect_url = url.join(parsed_redirect_url) 

881 

882 try: 

883 redirect_origin = parsed_redirect_url.origin() 

884 except ValueError as origin_val_err: 

885 if req._body is not None: 

886 await req._body.close() 

887 resp.close() 

888 raise InvalidUrlRedirectClientError( 

889 parsed_redirect_url, 

890 "Invalid redirect URL origin", 

891 ) from origin_val_err 

892 

893 if url.origin() != redirect_origin: 

894 auth = None 

895 headers.pop(hdrs.AUTHORIZATION, None) 

896 headers.pop(hdrs.COOKIE, None) 

897 headers.pop(hdrs.PROXY_AUTHORIZATION, None) 

898 

899 url = parsed_redirect_url 

900 params = {} 

901 resp.release() 

902 continue 

903 

904 break 

905 

906 if req._body is not None: 

907 await req._body.close() 

908 # check response status 

909 if raise_for_status is None: 

910 raise_for_status = self._raise_for_status 

911 

912 if raise_for_status is None: 

913 pass 

914 elif callable(raise_for_status): 

915 await raise_for_status(resp) 

916 elif raise_for_status: 

917 resp.raise_for_status() 

918 

919 # register connection 

920 if handle is not None: 

921 if resp.connection is not None: 

922 resp.connection.add_callback(handle.cancel) 

923 else: 

924 handle.cancel() 

925 

926 resp._history = tuple(history) 

927 

928 for trace in traces: 

929 await trace.send_request_end( 

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

931 ) 

932 return resp 

933 

934 except BaseException as e: 

935 # cleanup timer 

936 tm.close() 

937 if handle: 

938 handle.cancel() 

939 handle = None 

940 

941 for trace in traces: 

942 await trace.send_request_exception( 

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

944 ) 

945 raise 

946 

947 def ws_connect( 

948 self, 

949 url: StrOrURL, 

950 *, 

951 method: str = hdrs.METH_GET, 

952 protocols: Iterable[str] = (), 

953 timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel, 

954 receive_timeout: Optional[float] = None, 

955 autoclose: bool = True, 

956 autoping: bool = True, 

957 heartbeat: Optional[float] = None, 

958 auth: Optional[BasicAuth] = None, 

959 origin: Optional[str] = None, 

960 params: Query = None, 

961 headers: Optional[LooseHeaders] = None, 

962 proxy: Optional[StrOrURL] = None, 

963 proxy_auth: Optional[BasicAuth] = None, 

964 ssl: Union[SSLContext, bool, Fingerprint] = True, 

965 verify_ssl: Optional[bool] = None, 

966 fingerprint: Optional[bytes] = None, 

967 ssl_context: Optional[SSLContext] = None, 

968 server_hostname: Optional[str] = None, 

969 proxy_headers: Optional[LooseHeaders] = None, 

970 compress: int = 0, 

971 max_msg_size: int = 4 * 1024 * 1024, 

972 ) -> "_WSRequestContextManager": 

973 """Initiate websocket connection.""" 

974 return _WSRequestContextManager( 

975 self._ws_connect( 

976 url, 

977 method=method, 

978 protocols=protocols, 

979 timeout=timeout, 

980 receive_timeout=receive_timeout, 

981 autoclose=autoclose, 

982 autoping=autoping, 

983 heartbeat=heartbeat, 

984 auth=auth, 

985 origin=origin, 

986 params=params, 

987 headers=headers, 

988 proxy=proxy, 

989 proxy_auth=proxy_auth, 

990 ssl=ssl, 

991 verify_ssl=verify_ssl, 

992 fingerprint=fingerprint, 

993 ssl_context=ssl_context, 

994 server_hostname=server_hostname, 

995 proxy_headers=proxy_headers, 

996 compress=compress, 

997 max_msg_size=max_msg_size, 

998 ) 

999 ) 

1000 

1001 async def _ws_connect( 

1002 self, 

1003 url: StrOrURL, 

1004 *, 

1005 method: str = hdrs.METH_GET, 

1006 protocols: Iterable[str] = (), 

1007 timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel, 

1008 receive_timeout: Optional[float] = None, 

1009 autoclose: bool = True, 

1010 autoping: bool = True, 

1011 heartbeat: Optional[float] = None, 

1012 auth: Optional[BasicAuth] = None, 

1013 origin: Optional[str] = None, 

1014 params: Query = None, 

1015 headers: Optional[LooseHeaders] = None, 

1016 proxy: Optional[StrOrURL] = None, 

1017 proxy_auth: Optional[BasicAuth] = None, 

1018 ssl: Union[SSLContext, bool, Fingerprint] = True, 

1019 verify_ssl: Optional[bool] = None, 

1020 fingerprint: Optional[bytes] = None, 

1021 ssl_context: Optional[SSLContext] = None, 

1022 server_hostname: Optional[str] = None, 

1023 proxy_headers: Optional[LooseHeaders] = None, 

1024 compress: int = 0, 

1025 max_msg_size: int = 4 * 1024 * 1024, 

1026 ) -> ClientWebSocketResponse: 

1027 if timeout is not sentinel: 

1028 if isinstance(timeout, ClientWSTimeout): 

1029 ws_timeout = timeout 

1030 else: 

1031 warnings.warn( 

1032 "parameter 'timeout' of type 'float' " 

1033 "is deprecated, please use " 

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

1035 DeprecationWarning, 

1036 stacklevel=2, 

1037 ) 

1038 ws_timeout = ClientWSTimeout(ws_close=timeout) 

1039 else: 

1040 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT 

1041 if receive_timeout is not None: 

1042 warnings.warn( 

1043 "float parameter 'receive_timeout' " 

1044 "is deprecated, please use parameter " 

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

1046 DeprecationWarning, 

1047 stacklevel=2, 

1048 ) 

1049 ws_timeout = attr.evolve(ws_timeout, ws_receive=receive_timeout) 

1050 

1051 if headers is None: 

1052 real_headers: CIMultiDict[str] = CIMultiDict() 

1053 else: 

1054 real_headers = CIMultiDict(headers) 

1055 

1056 default_headers = { 

1057 hdrs.UPGRADE: "websocket", 

1058 hdrs.CONNECTION: "Upgrade", 

1059 hdrs.SEC_WEBSOCKET_VERSION: "13", 

1060 } 

1061 

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

1063 real_headers.setdefault(key, value) 

1064 

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

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

1067 

1068 if protocols: 

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

1070 if origin is not None: 

1071 real_headers[hdrs.ORIGIN] = origin 

1072 if compress: 

1073 extstr = ws_ext_gen(compress=compress) 

1074 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

1075 

1076 # For the sake of backward compatibility, if user passes in None, convert it to True 

1077 if ssl is None: 

1078 warnings.warn( 

1079 "ssl=None is deprecated, please use ssl=True", 

1080 DeprecationWarning, 

1081 stacklevel=2, 

1082 ) 

1083 ssl = True 

1084 ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) 

1085 

1086 # send request 

1087 resp = await self.request( 

1088 method, 

1089 url, 

1090 params=params, 

1091 headers=real_headers, 

1092 read_until_eof=False, 

1093 auth=auth, 

1094 proxy=proxy, 

1095 proxy_auth=proxy_auth, 

1096 ssl=ssl, 

1097 server_hostname=server_hostname, 

1098 proxy_headers=proxy_headers, 

1099 ) 

1100 

1101 try: 

1102 # check handshake 

1103 if resp.status != 101: 

1104 raise WSServerHandshakeError( 

1105 resp.request_info, 

1106 resp.history, 

1107 message="Invalid response status", 

1108 status=resp.status, 

1109 headers=resp.headers, 

1110 ) 

1111 

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

1113 raise WSServerHandshakeError( 

1114 resp.request_info, 

1115 resp.history, 

1116 message="Invalid upgrade header", 

1117 status=resp.status, 

1118 headers=resp.headers, 

1119 ) 

1120 

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

1122 raise WSServerHandshakeError( 

1123 resp.request_info, 

1124 resp.history, 

1125 message="Invalid connection header", 

1126 status=resp.status, 

1127 headers=resp.headers, 

1128 ) 

1129 

1130 # key calculation 

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

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

1133 if r_key != match: 

1134 raise WSServerHandshakeError( 

1135 resp.request_info, 

1136 resp.history, 

1137 message="Invalid challenge response", 

1138 status=resp.status, 

1139 headers=resp.headers, 

1140 ) 

1141 

1142 # websocket protocol 

1143 protocol = None 

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

1145 resp_protocols = [ 

1146 proto.strip() 

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

1148 ] 

1149 

1150 for proto in resp_protocols: 

1151 if proto in protocols: 

1152 protocol = proto 

1153 break 

1154 

1155 # websocket compress 

1156 notakeover = False 

1157 if compress: 

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

1159 if compress_hdrs: 

1160 try: 

1161 compress, notakeover = ws_ext_parse(compress_hdrs) 

1162 except WSHandshakeError as exc: 

1163 raise WSServerHandshakeError( 

1164 resp.request_info, 

1165 resp.history, 

1166 message=exc.args[0], 

1167 status=resp.status, 

1168 headers=resp.headers, 

1169 ) from exc 

1170 else: 

1171 compress = 0 

1172 notakeover = False 

1173 

1174 conn = resp.connection 

1175 assert conn is not None 

1176 conn_proto = conn.protocol 

1177 assert conn_proto is not None 

1178 

1179 # For WS connection the read_timeout must be either receive_timeout or greater 

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

1181 if ws_timeout.ws_receive is None: 

1182 # Reset regardless 

1183 conn_proto.read_timeout = None 

1184 elif conn_proto.read_timeout is not None: 

1185 conn_proto.read_timeout = max( 

1186 ws_timeout.ws_receive, conn_proto.read_timeout 

1187 ) 

1188 

1189 transport = conn.transport 

1190 assert transport is not None 

1191 reader = WebSocketDataQueue(conn_proto, 2**16, loop=self._loop) 

1192 conn_proto.set_parser(WebSocketReader(reader, max_msg_size), reader) 

1193 writer = WebSocketWriter( 

1194 conn_proto, 

1195 transport, 

1196 use_mask=True, 

1197 compress=compress, 

1198 notakeover=notakeover, 

1199 ) 

1200 except BaseException: 

1201 resp.close() 

1202 raise 

1203 else: 

1204 return self._ws_response_class( 

1205 reader, 

1206 writer, 

1207 protocol, 

1208 resp, 

1209 ws_timeout, 

1210 autoclose, 

1211 autoping, 

1212 self._loop, 

1213 heartbeat=heartbeat, 

1214 compress=compress, 

1215 client_notakeover=notakeover, 

1216 ) 

1217 

1218 def _prepare_headers(self, headers: Optional[LooseHeaders]) -> "CIMultiDict[str]": 

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

1220 # Convert headers to MultiDict 

1221 result = CIMultiDict(self._default_headers) 

1222 if headers: 

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

1224 headers = CIMultiDict(headers) 

1225 added_names: Set[str] = set() 

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

1227 if key in added_names: 

1228 result.add(key, value) 

1229 else: 

1230 result[key] = value 

1231 added_names.add(key) 

1232 return result 

1233 

1234 def _get_netrc_auth(self, host: str) -> Optional[BasicAuth]: 

1235 """ 

1236 Get auth from netrc for the given host. 

1237 

1238 This method is designed to be called in an executor to avoid 

1239 blocking I/O in the event loop. 

1240 """ 

1241 netrc_obj = netrc_from_env() 

1242 try: 

1243 return basicauth_from_netrc(netrc_obj, host) 

1244 except LookupError: 

1245 return None 

1246 

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

1248 

1249 def get( 

1250 self, 

1251 url: StrOrURL, 

1252 **kwargs: Unpack[_RequestOptions], 

1253 ) -> "_RequestContextManager": ... 

1254 

1255 def options( 

1256 self, 

1257 url: StrOrURL, 

1258 **kwargs: Unpack[_RequestOptions], 

1259 ) -> "_RequestContextManager": ... 

1260 

1261 def head( 

1262 self, 

1263 url: StrOrURL, 

1264 **kwargs: Unpack[_RequestOptions], 

1265 ) -> "_RequestContextManager": ... 

1266 

1267 def post( 

1268 self, 

1269 url: StrOrURL, 

1270 **kwargs: Unpack[_RequestOptions], 

1271 ) -> "_RequestContextManager": ... 

1272 

1273 def put( 

1274 self, 

1275 url: StrOrURL, 

1276 **kwargs: Unpack[_RequestOptions], 

1277 ) -> "_RequestContextManager": ... 

1278 

1279 def patch( 

1280 self, 

1281 url: StrOrURL, 

1282 **kwargs: Unpack[_RequestOptions], 

1283 ) -> "_RequestContextManager": ... 

1284 

1285 def delete( 

1286 self, 

1287 url: StrOrURL, 

1288 **kwargs: Unpack[_RequestOptions], 

1289 ) -> "_RequestContextManager": ... 

1290 

1291 else: 

1292 

1293 def get( 

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

1295 ) -> "_RequestContextManager": 

1296 """Perform HTTP GET request.""" 

1297 return _RequestContextManager( 

1298 self._request( 

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

1300 ) 

1301 ) 

1302 

1303 def options( 

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

1305 ) -> "_RequestContextManager": 

1306 """Perform HTTP OPTIONS request.""" 

1307 return _RequestContextManager( 

1308 self._request( 

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

1310 ) 

1311 ) 

1312 

1313 def head( 

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

1315 ) -> "_RequestContextManager": 

1316 """Perform HTTP HEAD request.""" 

1317 return _RequestContextManager( 

1318 self._request( 

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

1320 ) 

1321 ) 

1322 

1323 def post( 

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

1325 ) -> "_RequestContextManager": 

1326 """Perform HTTP POST request.""" 

1327 return _RequestContextManager( 

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

1329 ) 

1330 

1331 def put( 

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

1333 ) -> "_RequestContextManager": 

1334 """Perform HTTP PUT request.""" 

1335 return _RequestContextManager( 

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

1337 ) 

1338 

1339 def patch( 

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

1341 ) -> "_RequestContextManager": 

1342 """Perform HTTP PATCH request.""" 

1343 return _RequestContextManager( 

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

1345 ) 

1346 

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

1348 """Perform HTTP DELETE request.""" 

1349 return _RequestContextManager( 

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

1351 ) 

1352 

1353 async def close(self) -> None: 

1354 """Close underlying connector. 

1355 

1356 Release all acquired resources. 

1357 """ 

1358 if not self.closed: 

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

1360 await self._connector.close() 

1361 self._connector = None 

1362 

1363 @property 

1364 def closed(self) -> bool: 

1365 """Is client session closed. 

1366 

1367 A readonly property. 

1368 """ 

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

1370 

1371 @property 

1372 def connector(self) -> Optional[BaseConnector]: 

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

1374 return self._connector 

1375 

1376 @property 

1377 def cookie_jar(self) -> AbstractCookieJar: 

1378 """The session cookies.""" 

1379 return self._cookie_jar 

1380 

1381 @property 

1382 def version(self) -> Tuple[int, int]: 

1383 """The session HTTP protocol version.""" 

1384 return self._version 

1385 

1386 @property 

1387 def requote_redirect_url(self) -> bool: 

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

1389 return self._requote_redirect_url 

1390 

1391 @requote_redirect_url.setter 

1392 def requote_redirect_url(self, val: bool) -> None: 

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

1394 warnings.warn( 

1395 "session.requote_redirect_url modification is deprecated #2778", 

1396 DeprecationWarning, 

1397 stacklevel=2, 

1398 ) 

1399 self._requote_redirect_url = val 

1400 

1401 @property 

1402 def loop(self) -> asyncio.AbstractEventLoop: 

1403 """Session's loop.""" 

1404 warnings.warn( 

1405 "client.loop property is deprecated", DeprecationWarning, stacklevel=2 

1406 ) 

1407 return self._loop 

1408 

1409 @property 

1410 def timeout(self) -> ClientTimeout: 

1411 """Timeout for the session.""" 

1412 return self._timeout 

1413 

1414 @property 

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

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

1417 return self._default_headers 

1418 

1419 @property 

1420 def skip_auto_headers(self) -> FrozenSet[istr]: 

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

1422 return self._skip_auto_headers 

1423 

1424 @property 

1425 def auth(self) -> Optional[BasicAuth]: 

1426 """An object that represents HTTP Basic Authorization""" 

1427 return self._default_auth 

1428 

1429 @property 

1430 def json_serialize(self) -> JSONEncoder: 

1431 """Json serializer callable""" 

1432 return self._json_serialize 

1433 

1434 @property 

1435 def connector_owner(self) -> bool: 

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

1437 return self._connector_owner 

1438 

1439 @property 

1440 def raise_for_status( 

1441 self, 

1442 ) -> Union[bool, Callable[[ClientResponse], Awaitable[None]]]: 

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

1444 return self._raise_for_status 

1445 

1446 @property 

1447 def auto_decompress(self) -> bool: 

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

1449 return self._auto_decompress 

1450 

1451 @property 

1452 def trust_env(self) -> bool: 

1453 """ 

1454 Should proxies information from environment or netrc be trusted. 

1455 

1456 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1457 or ~/.netrc file if present. 

1458 """ 

1459 return self._trust_env 

1460 

1461 @property 

1462 def trace_configs(self) -> List[TraceConfig]: 

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

1464 return self._trace_configs 

1465 

1466 def detach(self) -> None: 

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

1468 

1469 Session is switched to closed state anyway. 

1470 """ 

1471 self._connector = None 

1472 

1473 def __enter__(self) -> None: 

1474 raise TypeError("Use async with instead") 

1475 

1476 def __exit__( 

1477 self, 

1478 exc_type: Optional[Type[BaseException]], 

1479 exc_val: Optional[BaseException], 

1480 exc_tb: Optional[TracebackType], 

1481 ) -> None: 

1482 # __exit__ should exist in pair with __enter__ but never executed 

1483 pass # pragma: no cover 

1484 

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

1486 return self 

1487 

1488 async def __aexit__( 

1489 self, 

1490 exc_type: Optional[Type[BaseException]], 

1491 exc_val: Optional[BaseException], 

1492 exc_tb: Optional[TracebackType], 

1493 ) -> None: 

1494 await self.close() 

1495 

1496 

1497class _BaseRequestContextManager(Coroutine[Any, Any, _RetType], Generic[_RetType]): 

1498 

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

1500 

1501 def __init__(self, coro: Coroutine["asyncio.Future[Any]", None, _RetType]) -> None: 

1502 self._coro: Coroutine["asyncio.Future[Any]", None, _RetType] = coro 

1503 

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

1505 return self._coro.send(arg) 

1506 

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

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

1509 

1510 def close(self) -> None: 

1511 return self._coro.close() 

1512 

1513 def __await__(self) -> Generator[Any, None, _RetType]: 

1514 ret = self._coro.__await__() 

1515 return ret 

1516 

1517 def __iter__(self) -> Generator[Any, None, _RetType]: 

1518 return self.__await__() 

1519 

1520 async def __aenter__(self) -> _RetType: 

1521 self._resp: _RetType = await self._coro 

1522 return await self._resp.__aenter__() 

1523 

1524 async def __aexit__( 

1525 self, 

1526 exc_type: Optional[Type[BaseException]], 

1527 exc: Optional[BaseException], 

1528 tb: Optional[TracebackType], 

1529 ) -> None: 

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

1531 

1532 

1533_RequestContextManager = _BaseRequestContextManager[ClientResponse] 

1534_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse] 

1535 

1536 

1537class _SessionRequestContextManager: 

1538 

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

1540 

1541 def __init__( 

1542 self, 

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

1544 session: ClientSession, 

1545 ) -> None: 

1546 self._coro = coro 

1547 self._resp: Optional[ClientResponse] = None 

1548 self._session = session 

1549 

1550 async def __aenter__(self) -> ClientResponse: 

1551 try: 

1552 self._resp = await self._coro 

1553 except BaseException: 

1554 await self._session.close() 

1555 raise 

1556 else: 

1557 return self._resp 

1558 

1559 async def __aexit__( 

1560 self, 

1561 exc_type: Optional[Type[BaseException]], 

1562 exc: Optional[BaseException], 

1563 tb: Optional[TracebackType], 

1564 ) -> None: 

1565 assert self._resp is not None 

1566 self._resp.close() 

1567 await self._session.close() 

1568 

1569 

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

1571 

1572 def request( 

1573 method: str, 

1574 url: StrOrURL, 

1575 *, 

1576 version: HttpVersion = http.HttpVersion11, 

1577 connector: Optional[BaseConnector] = None, 

1578 loop: Optional[asyncio.AbstractEventLoop] = None, 

1579 **kwargs: Unpack[_RequestOptions], 

1580 ) -> _SessionRequestContextManager: ... 

1581 

1582else: 

1583 

1584 def request( 

1585 method: str, 

1586 url: StrOrURL, 

1587 *, 

1588 version: HttpVersion = http.HttpVersion11, 

1589 connector: Optional[BaseConnector] = None, 

1590 loop: Optional[asyncio.AbstractEventLoop] = None, 

1591 **kwargs: Any, 

1592 ) -> _SessionRequestContextManager: 

1593 """Constructs and sends a request. 

1594 

1595 Returns response object. 

1596 method - HTTP method 

1597 url - request url 

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

1599 string of the new request 

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

1601 send in the body of the request 

1602 json - (optional) Any json compatible python object 

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

1604 the request 

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

1606 auth - (optional) BasicAuth named tuple represent HTTP Basic Auth 

1607 auth - aiohttp.helpers.BasicAuth 

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

1609 redirects 

1610 version - Request HTTP version. 

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

1612 with deflate encoding. 

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

1614 expect100 - Expect 100-continue response from server. 

1615 connector - BaseConnector sub-class instance to support 

1616 connection pooling. 

1617 read_until_eof - Read response until eof if response 

1618 does not have Content-Length header. 

1619 loop - Optional event loop. 

1620 timeout - Optional ClientTimeout settings structure, 5min 

1621 total timeout by default. 

1622 Usage:: 

1623 >>> import aiohttp 

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

1625 ... print(resp) 

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

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

1628 """ 

1629 connector_owner = False 

1630 if connector is None: 

1631 connector_owner = True 

1632 connector = TCPConnector(loop=loop, force_close=True) 

1633 

1634 session = ClientSession( 

1635 loop=loop, 

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

1637 version=version, 

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

1639 connector=connector, 

1640 connector_owner=connector_owner, 

1641 ) 

1642 

1643 return _SessionRequestContextManager( 

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

1645 session, 

1646 )