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

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

611 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 middlewares: Optional[Sequence[ClientMiddlewareType]] 

199 

200 

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

202class ClientTimeout: 

203 total: Optional[float] = None 

204 connect: Optional[float] = None 

205 sock_read: Optional[float] = None 

206 sock_connect: Optional[float] = None 

207 ceil_threshold: float = 5 

208 

209 # pool_queue_timeout: Optional[float] = None 

210 # dns_resolution_timeout: Optional[float] = None 

211 # socket_connect_timeout: Optional[float] = None 

212 # connection_acquiring_timeout: Optional[float] = None 

213 # new_connection_timeout: Optional[float] = None 

214 # http_header_timeout: Optional[float] = None 

215 # response_body_timeout: Optional[float] = None 

216 

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

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

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

220 # to overwrite the defaults 

221 

222 

223# 5 Minute default read timeout 

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

225 

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

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

228 

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

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

231 

232 

233class ClientSession: 

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

235 

236 ATTRS = frozenset( 

237 [ 

238 "_base_url", 

239 "_base_url_origin", 

240 "_source_traceback", 

241 "_connector", 

242 "_loop", 

243 "_cookie_jar", 

244 "_connector_owner", 

245 "_default_auth", 

246 "_version", 

247 "_json_serialize", 

248 "_requote_redirect_url", 

249 "_timeout", 

250 "_raise_for_status", 

251 "_auto_decompress", 

252 "_trust_env", 

253 "_default_headers", 

254 "_skip_auto_headers", 

255 "_request_class", 

256 "_response_class", 

257 "_ws_response_class", 

258 "_trace_configs", 

259 "_read_bufsize", 

260 "_max_line_size", 

261 "_max_field_size", 

262 "_resolve_charset", 

263 "_default_proxy", 

264 "_default_proxy_auth", 

265 "_retry_connection", 

266 "_middlewares", 

267 "requote_redirect_url", 

268 ] 

269 ) 

270 

271 _source_traceback: Optional[traceback.StackSummary] = None 

272 _connector: Optional[BaseConnector] = None 

273 

274 def __init__( 

275 self, 

276 base_url: Optional[StrOrURL] = None, 

277 *, 

278 connector: Optional[BaseConnector] = None, 

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

280 cookies: Optional[LooseCookies] = None, 

281 headers: Optional[LooseHeaders] = None, 

282 proxy: Optional[StrOrURL] = None, 

283 proxy_auth: Optional[BasicAuth] = None, 

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

285 auth: Optional[BasicAuth] = None, 

286 json_serialize: JSONEncoder = json.dumps, 

287 request_class: Type[ClientRequest] = ClientRequest, 

288 response_class: Type[ClientResponse] = ClientResponse, 

289 ws_response_class: Type[ClientWebSocketResponse] = ClientWebSocketResponse, 

290 version: HttpVersion = http.HttpVersion11, 

291 cookie_jar: Optional[AbstractCookieJar] = None, 

292 connector_owner: bool = True, 

293 raise_for_status: Union[ 

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

295 ] = False, 

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

297 conn_timeout: Optional[float] = None, 

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

299 auto_decompress: bool = True, 

300 trust_env: bool = False, 

301 requote_redirect_url: bool = True, 

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

303 read_bufsize: int = 2**16, 

304 max_line_size: int = 8190, 

305 max_field_size: int = 8190, 

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

307 middlewares: Sequence[ClientMiddlewareType] = (), 

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

309 ) -> None: 

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

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

312 self._connector: Optional[BaseConnector] = None 

313 

314 if loop is None: 

315 if connector is not None: 

316 loop = connector._loop 

317 

318 loop = loop or asyncio.get_running_loop() 

319 

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

321 self._base_url: Optional[URL] = base_url 

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

323 else: 

324 self._base_url = URL(base_url) 

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

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

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

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

329 

330 if timeout is sentinel or timeout is None: 

331 self._timeout = DEFAULT_TIMEOUT 

332 if read_timeout is not sentinel: 

333 warnings.warn( 

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

335 DeprecationWarning, 

336 stacklevel=2, 

337 ) 

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

339 if conn_timeout is not None: 

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

341 warnings.warn( 

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

343 DeprecationWarning, 

344 stacklevel=2, 

345 ) 

346 else: 

347 if not isinstance(timeout, ClientTimeout): 

348 raise ValueError( 

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

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

351 ) 

352 self._timeout = timeout 

353 if read_timeout is not sentinel: 

354 raise ValueError( 

355 "read_timeout and timeout parameters " 

356 "conflict, please setup " 

357 "timeout.read" 

358 ) 

359 if conn_timeout is not None: 

360 raise ValueError( 

361 "conn_timeout and timeout parameters " 

362 "conflict, please setup " 

363 "timeout.connect" 

364 ) 

365 

366 if ssl_shutdown_timeout is not sentinel: 

367 warnings.warn( 

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

369 DeprecationWarning, 

370 stacklevel=2, 

371 ) 

372 

373 if connector is None: 

374 connector = TCPConnector( 

375 loop=loop, ssl_shutdown_timeout=ssl_shutdown_timeout 

376 ) 

377 

378 if connector._loop is not loop: 

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

380 

381 self._loop = loop 

382 

383 if loop.get_debug(): 

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

385 

386 if cookie_jar is None: 

387 cookie_jar = CookieJar(loop=loop) 

388 self._cookie_jar = cookie_jar 

389 

390 if cookies: 

391 self._cookie_jar.update_cookies(cookies) 

392 

393 self._connector = connector 

394 self._connector_owner = connector_owner 

395 self._default_auth = auth 

396 self._version = version 

397 self._json_serialize = json_serialize 

398 self._raise_for_status = raise_for_status 

399 self._auto_decompress = auto_decompress 

400 self._trust_env = trust_env 

401 self._requote_redirect_url = requote_redirect_url 

402 self._read_bufsize = read_bufsize 

403 self._max_line_size = max_line_size 

404 self._max_field_size = max_field_size 

405 

406 # Convert to list of tuples 

407 if headers: 

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

409 else: 

410 real_headers = CIMultiDict() 

411 self._default_headers: CIMultiDict[str] = real_headers 

412 if skip_auto_headers is not None: 

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

414 else: 

415 self._skip_auto_headers = frozenset() 

416 

417 self._request_class = request_class 

418 self._response_class = response_class 

419 self._ws_response_class = ws_response_class 

420 

421 self._trace_configs = trace_configs or [] 

422 for trace_config in self._trace_configs: 

423 trace_config.freeze() 

424 

425 self._resolve_charset = fallback_charset_resolver 

426 

427 self._default_proxy = proxy 

428 self._default_proxy_auth = proxy_auth 

429 self._retry_connection: bool = True 

430 self._middlewares = middlewares 

431 

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

433 warnings.warn( 

434 "Inheritance class {} from ClientSession " 

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

436 DeprecationWarning, 

437 stacklevel=2, 

438 ) 

439 

440 if DEBUG: 

441 

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

443 if name not in self.ATTRS: 

444 warnings.warn( 

445 "Setting custom ClientSession.{} attribute " 

446 "is discouraged".format(name), 

447 DeprecationWarning, 

448 stacklevel=2, 

449 ) 

450 super().__setattr__(name, val) 

451 

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

453 if not self.closed: 

454 kwargs = {"source": self} 

455 _warnings.warn( 

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

457 ) 

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

459 if self._source_traceback is not None: 

460 context["source_traceback"] = self._source_traceback 

461 self._loop.call_exception_handler(context) 

462 

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

464 

465 def request( 

466 self, 

467 method: str, 

468 url: StrOrURL, 

469 **kwargs: Unpack[_RequestOptions], 

470 ) -> "_RequestContextManager": ... 

471 

472 else: 

473 

474 def request( 

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

476 ) -> "_RequestContextManager": 

477 """Perform HTTP request.""" 

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

479 

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

481 url = URL(str_or_url) 

482 if self._base_url and not url.absolute: 

483 return self._base_url.join(url) 

484 return url 

485 

486 async def _request( 

487 self, 

488 method: str, 

489 str_or_url: StrOrURL, 

490 *, 

491 params: Query = None, 

492 data: Any = None, 

493 json: Any = None, 

494 cookies: Optional[LooseCookies] = None, 

495 headers: Optional[LooseHeaders] = None, 

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

497 auth: Optional[BasicAuth] = None, 

498 allow_redirects: bool = True, 

499 max_redirects: int = 10, 

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

501 chunked: Optional[bool] = None, 

502 expect100: bool = False, 

503 raise_for_status: Union[ 

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

505 ] = None, 

506 read_until_eof: bool = True, 

507 proxy: Optional[StrOrURL] = None, 

508 proxy_auth: Optional[BasicAuth] = None, 

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

510 verify_ssl: Optional[bool] = None, 

511 fingerprint: Optional[bytes] = None, 

512 ssl_context: Optional[SSLContext] = None, 

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

514 server_hostname: Optional[str] = None, 

515 proxy_headers: Optional[LooseHeaders] = None, 

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

517 read_bufsize: Optional[int] = None, 

518 auto_decompress: Optional[bool] = None, 

519 max_line_size: Optional[int] = None, 

520 max_field_size: Optional[int] = None, 

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

522 ) -> ClientResponse: 

523 

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

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

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

527 

528 if self.closed: 

529 raise RuntimeError("Session is closed") 

530 

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

532 

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

534 raise ValueError( 

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

536 ) 

537 elif json is not None: 

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

539 

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

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

542 

543 redirects = 0 

544 history: List[ClientResponse] = [] 

545 version = self._version 

546 params = params or {} 

547 

548 # Merge with default headers and transform to CIMultiDict 

549 headers = self._prepare_headers(headers) 

550 

551 try: 

552 url = self._build_url(str_or_url) 

553 except ValueError as e: 

554 raise InvalidUrlClientError(str_or_url) from e 

555 

556 assert self._connector is not None 

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

558 raise NonHttpUrlClientError(url) 

559 

560 skip_headers: Optional[Iterable[istr]] 

561 if skip_auto_headers is not None: 

562 skip_headers = { 

563 istr(i) for i in skip_auto_headers 

564 } | self._skip_auto_headers 

565 elif self._skip_auto_headers: 

566 skip_headers = self._skip_auto_headers 

567 else: 

568 skip_headers = None 

569 

570 if proxy is None: 

571 proxy = self._default_proxy 

572 if proxy_auth is None: 

573 proxy_auth = self._default_proxy_auth 

574 

575 if proxy is None: 

576 proxy_headers = None 

577 else: 

578 proxy_headers = self._prepare_headers(proxy_headers) 

579 try: 

580 proxy = URL(proxy) 

581 except ValueError as e: 

582 raise InvalidURL(proxy) from e 

583 

584 if timeout is sentinel: 

585 real_timeout: ClientTimeout = self._timeout 

586 else: 

587 if not isinstance(timeout, ClientTimeout): 

588 real_timeout = ClientTimeout(total=timeout) 

589 else: 

590 real_timeout = timeout 

591 # timeout is cumulative for all request operations 

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

593 tm = TimeoutHandle( 

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

595 ) 

596 handle = tm.start() 

597 

598 if read_bufsize is None: 

599 read_bufsize = self._read_bufsize 

600 

601 if auto_decompress is None: 

602 auto_decompress = self._auto_decompress 

603 

604 if max_line_size is None: 

605 max_line_size = self._max_line_size 

606 

607 if max_field_size is None: 

608 max_field_size = self._max_field_size 

609 

610 traces = [ 

611 Trace( 

612 self, 

613 trace_config, 

614 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), 

615 ) 

616 for trace_config in self._trace_configs 

617 ] 

618 

619 for trace in traces: 

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

621 

622 timer = tm.timer() 

623 try: 

624 with timer: 

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

626 retry_persistent_connection = ( 

627 self._retry_connection and method in IDEMPOTENT_METHODS 

628 ) 

629 while True: 

630 url, auth_from_url = strip_auth_from_url(url) 

631 if not url.raw_host: 

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

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

634 err_exc_cls = ( 

635 InvalidUrlRedirectClientError 

636 if redirects 

637 else InvalidUrlClientError 

638 ) 

639 raise err_exc_cls(url) 

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

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

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

643 if not history and (auth and auth_from_url): 

644 raise ValueError( 

645 "Cannot combine AUTH argument with " 

646 "credentials encoded in URL" 

647 ) 

648 

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

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

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

652 auth = auth_from_url 

653 

654 if ( 

655 auth is None 

656 and self._default_auth 

657 and ( 

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

659 ) 

660 ): 

661 auth = self._default_auth 

662 

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

664 # Only check if NETRC environment variable is set to avoid 

665 # creating an expensive executor job unnecessarily. 

666 if ( 

667 auth is None 

668 and self._trust_env 

669 and url.host is not None 

670 and os.environ.get("NETRC") 

671 ): 

672 auth = await self._loop.run_in_executor( 

673 None, self._get_netrc_auth, url.host 

674 ) 

675 

676 # It would be confusing if we support explicit 

677 # Authorization header with auth argument 

678 if ( 

679 headers is not None 

680 and auth is not None 

681 and hdrs.AUTHORIZATION in headers 

682 ): 

683 raise ValueError( 

684 "Cannot combine AUTHORIZATION header " 

685 "with AUTH argument or credentials " 

686 "encoded in URL" 

687 ) 

688 

689 all_cookies = self._cookie_jar.filter_cookies(url) 

690 

691 if cookies is not None: 

692 tmp_cookie_jar = CookieJar( 

693 quote_cookie=self._cookie_jar.quote_cookie 

694 ) 

695 tmp_cookie_jar.update_cookies(cookies) 

696 req_cookies = tmp_cookie_jar.filter_cookies(url) 

697 if req_cookies: 

698 all_cookies.load(req_cookies) 

699 

700 proxy_: Optional[URL] = None 

701 if proxy is not None: 

702 proxy_ = URL(proxy) 

703 elif self._trust_env: 

704 with suppress(LookupError): 

705 proxy_, proxy_auth = await asyncio.to_thread( 

706 get_env_proxy_for_url, url 

707 ) 

708 

709 req = self._request_class( 

710 method, 

711 url, 

712 params=params, 

713 headers=headers, 

714 skip_auto_headers=skip_headers, 

715 data=data, 

716 cookies=all_cookies, 

717 auth=auth, 

718 version=version, 

719 compress=compress, 

720 chunked=chunked, 

721 expect100=expect100, 

722 loop=self._loop, 

723 response_class=self._response_class, 

724 proxy=proxy_, 

725 proxy_auth=proxy_auth, 

726 timer=timer, 

727 session=self, 

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

729 server_hostname=server_hostname, 

730 proxy_headers=proxy_headers, 

731 traces=traces, 

732 trust_env=self.trust_env, 

733 ) 

734 

735 async def _connect_and_send_request( 

736 req: ClientRequest, 

737 ) -> ClientResponse: 

738 # connection timeout 

739 assert self._connector is not None 

740 try: 

741 conn = await self._connector.connect( 

742 req, traces=traces, timeout=real_timeout 

743 ) 

744 except asyncio.TimeoutError as exc: 

745 raise ConnectionTimeoutError( 

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

747 ) from exc 

748 

749 assert conn.protocol is not None 

750 conn.protocol.set_response_params( 

751 timer=timer, 

752 skip_payload=req.method in EMPTY_BODY_METHODS, 

753 read_until_eof=read_until_eof, 

754 auto_decompress=auto_decompress, 

755 read_timeout=real_timeout.sock_read, 

756 read_bufsize=read_bufsize, 

757 timeout_ceil_threshold=self._connector._timeout_ceil_threshold, 

758 max_line_size=max_line_size, 

759 max_field_size=max_field_size, 

760 ) 

761 try: 

762 resp = await req.send(conn) 

763 try: 

764 await resp.start(conn) 

765 except BaseException: 

766 resp.close() 

767 raise 

768 except BaseException: 

769 conn.close() 

770 raise 

771 return resp 

772 

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

774 effective_middlewares = ( 

775 self._middlewares if middlewares is None else middlewares 

776 ) 

777 

778 if effective_middlewares: 

779 handler = build_client_middlewares( 

780 _connect_and_send_request, effective_middlewares 

781 ) 

782 else: 

783 handler = _connect_and_send_request 

784 

785 try: 

786 resp = await handler(req) 

787 # Client connector errors should not be retried 

788 except ( 

789 ConnectionTimeoutError, 

790 ClientConnectorError, 

791 ClientConnectorCertificateError, 

792 ClientConnectorSSLError, 

793 ): 

794 raise 

795 except (ClientOSError, ServerDisconnectedError): 

796 if retry_persistent_connection: 

797 retry_persistent_connection = False 

798 continue 

799 raise 

800 except ClientError: 

801 raise 

802 except OSError as exc: 

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

804 raise 

805 raise ClientOSError(*exc.args) from exc 

806 

807 # Update cookies from raw headers to preserve duplicates 

808 if resp._raw_cookie_headers: 

809 self._cookie_jar.update_cookies_from_headers( 

810 resp._raw_cookie_headers, resp.url 

811 ) 

812 

813 # redirects 

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

815 

816 for trace in traces: 

817 await trace.send_request_redirect( 

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

819 ) 

820 

821 redirects += 1 

822 history.append(resp) 

823 if max_redirects and redirects >= max_redirects: 

824 if req._body is not None: 

825 await req._body.close() 

826 resp.close() 

827 raise TooManyRedirects( 

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

829 ) 

830 

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

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

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

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

835 ): 

836 method = hdrs.METH_GET 

837 data = None 

838 if headers.get(hdrs.CONTENT_LENGTH): 

839 headers.pop(hdrs.CONTENT_LENGTH) 

840 else: 

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

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

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

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

845 data = req._body 

846 

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

848 hdrs.URI 

849 ) 

850 if r_url is None: 

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

852 break 

853 else: 

854 # reading from correct redirection 

855 # response is forbidden 

856 resp.release() 

857 

858 try: 

859 parsed_redirect_url = URL( 

860 r_url, encoded=not self._requote_redirect_url 

861 ) 

862 except ValueError as e: 

863 if req._body is not None: 

864 await req._body.close() 

865 resp.close() 

866 raise InvalidUrlRedirectClientError( 

867 r_url, 

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

869 ) from e 

870 

871 scheme = parsed_redirect_url.scheme 

872 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: 

873 if req._body is not None: 

874 await req._body.close() 

875 resp.close() 

876 raise NonHttpUrlRedirectClientError(r_url) 

877 elif not scheme: 

878 parsed_redirect_url = url.join(parsed_redirect_url) 

879 

880 try: 

881 redirect_origin = parsed_redirect_url.origin() 

882 except ValueError as origin_val_err: 

883 if req._body is not None: 

884 await req._body.close() 

885 resp.close() 

886 raise InvalidUrlRedirectClientError( 

887 parsed_redirect_url, 

888 "Invalid redirect URL origin", 

889 ) from origin_val_err 

890 

891 if url.origin() != redirect_origin: 

892 auth = None 

893 headers.pop(hdrs.AUTHORIZATION, None) 

894 

895 url = parsed_redirect_url 

896 params = {} 

897 resp.release() 

898 continue 

899 

900 break 

901 

902 if req._body is not None: 

903 await req._body.close() 

904 # check response status 

905 if raise_for_status is None: 

906 raise_for_status = self._raise_for_status 

907 

908 if raise_for_status is None: 

909 pass 

910 elif callable(raise_for_status): 

911 await raise_for_status(resp) 

912 elif raise_for_status: 

913 resp.raise_for_status() 

914 

915 # register connection 

916 if handle is not None: 

917 if resp.connection is not None: 

918 resp.connection.add_callback(handle.cancel) 

919 else: 

920 handle.cancel() 

921 

922 resp._history = tuple(history) 

923 

924 for trace in traces: 

925 await trace.send_request_end( 

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

927 ) 

928 return resp 

929 

930 except BaseException as e: 

931 # cleanup timer 

932 tm.close() 

933 if handle: 

934 handle.cancel() 

935 handle = None 

936 

937 for trace in traces: 

938 await trace.send_request_exception( 

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

940 ) 

941 raise 

942 

943 def ws_connect( 

944 self, 

945 url: StrOrURL, 

946 *, 

947 method: str = hdrs.METH_GET, 

948 protocols: Iterable[str] = (), 

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

950 receive_timeout: Optional[float] = None, 

951 autoclose: bool = True, 

952 autoping: bool = True, 

953 heartbeat: Optional[float] = None, 

954 auth: Optional[BasicAuth] = None, 

955 origin: Optional[str] = None, 

956 params: Query = None, 

957 headers: Optional[LooseHeaders] = None, 

958 proxy: Optional[StrOrURL] = None, 

959 proxy_auth: Optional[BasicAuth] = None, 

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

961 verify_ssl: Optional[bool] = None, 

962 fingerprint: Optional[bytes] = None, 

963 ssl_context: Optional[SSLContext] = None, 

964 server_hostname: Optional[str] = None, 

965 proxy_headers: Optional[LooseHeaders] = None, 

966 compress: int = 0, 

967 max_msg_size: int = 4 * 1024 * 1024, 

968 ) -> "_WSRequestContextManager": 

969 """Initiate websocket connection.""" 

970 return _WSRequestContextManager( 

971 self._ws_connect( 

972 url, 

973 method=method, 

974 protocols=protocols, 

975 timeout=timeout, 

976 receive_timeout=receive_timeout, 

977 autoclose=autoclose, 

978 autoping=autoping, 

979 heartbeat=heartbeat, 

980 auth=auth, 

981 origin=origin, 

982 params=params, 

983 headers=headers, 

984 proxy=proxy, 

985 proxy_auth=proxy_auth, 

986 ssl=ssl, 

987 verify_ssl=verify_ssl, 

988 fingerprint=fingerprint, 

989 ssl_context=ssl_context, 

990 server_hostname=server_hostname, 

991 proxy_headers=proxy_headers, 

992 compress=compress, 

993 max_msg_size=max_msg_size, 

994 ) 

995 ) 

996 

997 async def _ws_connect( 

998 self, 

999 url: StrOrURL, 

1000 *, 

1001 method: str = hdrs.METH_GET, 

1002 protocols: Iterable[str] = (), 

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

1004 receive_timeout: Optional[float] = None, 

1005 autoclose: bool = True, 

1006 autoping: bool = True, 

1007 heartbeat: Optional[float] = None, 

1008 auth: Optional[BasicAuth] = None, 

1009 origin: Optional[str] = None, 

1010 params: Query = None, 

1011 headers: Optional[LooseHeaders] = None, 

1012 proxy: Optional[StrOrURL] = None, 

1013 proxy_auth: Optional[BasicAuth] = None, 

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

1015 verify_ssl: Optional[bool] = None, 

1016 fingerprint: Optional[bytes] = None, 

1017 ssl_context: Optional[SSLContext] = None, 

1018 server_hostname: Optional[str] = None, 

1019 proxy_headers: Optional[LooseHeaders] = None, 

1020 compress: int = 0, 

1021 max_msg_size: int = 4 * 1024 * 1024, 

1022 ) -> ClientWebSocketResponse: 

1023 if timeout is not sentinel: 

1024 if isinstance(timeout, ClientWSTimeout): 

1025 ws_timeout = timeout 

1026 else: 

1027 warnings.warn( 

1028 "parameter 'timeout' of type 'float' " 

1029 "is deprecated, please use " 

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

1031 DeprecationWarning, 

1032 stacklevel=2, 

1033 ) 

1034 ws_timeout = ClientWSTimeout(ws_close=timeout) 

1035 else: 

1036 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT 

1037 if receive_timeout is not None: 

1038 warnings.warn( 

1039 "float parameter 'receive_timeout' " 

1040 "is deprecated, please use parameter " 

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

1042 DeprecationWarning, 

1043 stacklevel=2, 

1044 ) 

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

1046 

1047 if headers is None: 

1048 real_headers: CIMultiDict[str] = CIMultiDict() 

1049 else: 

1050 real_headers = CIMultiDict(headers) 

1051 

1052 default_headers = { 

1053 hdrs.UPGRADE: "websocket", 

1054 hdrs.CONNECTION: "Upgrade", 

1055 hdrs.SEC_WEBSOCKET_VERSION: "13", 

1056 } 

1057 

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

1059 real_headers.setdefault(key, value) 

1060 

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

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

1063 

1064 if protocols: 

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

1066 if origin is not None: 

1067 real_headers[hdrs.ORIGIN] = origin 

1068 if compress: 

1069 extstr = ws_ext_gen(compress=compress) 

1070 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

1071 

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

1073 if ssl is None: 

1074 warnings.warn( 

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

1076 DeprecationWarning, 

1077 stacklevel=2, 

1078 ) 

1079 ssl = True 

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

1081 

1082 # send request 

1083 resp = await self.request( 

1084 method, 

1085 url, 

1086 params=params, 

1087 headers=real_headers, 

1088 read_until_eof=False, 

1089 auth=auth, 

1090 proxy=proxy, 

1091 proxy_auth=proxy_auth, 

1092 ssl=ssl, 

1093 server_hostname=server_hostname, 

1094 proxy_headers=proxy_headers, 

1095 ) 

1096 

1097 try: 

1098 # check handshake 

1099 if resp.status != 101: 

1100 raise WSServerHandshakeError( 

1101 resp.request_info, 

1102 resp.history, 

1103 message="Invalid response status", 

1104 status=resp.status, 

1105 headers=resp.headers, 

1106 ) 

1107 

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

1109 raise WSServerHandshakeError( 

1110 resp.request_info, 

1111 resp.history, 

1112 message="Invalid upgrade header", 

1113 status=resp.status, 

1114 headers=resp.headers, 

1115 ) 

1116 

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

1118 raise WSServerHandshakeError( 

1119 resp.request_info, 

1120 resp.history, 

1121 message="Invalid connection header", 

1122 status=resp.status, 

1123 headers=resp.headers, 

1124 ) 

1125 

1126 # key calculation 

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

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

1129 if r_key != match: 

1130 raise WSServerHandshakeError( 

1131 resp.request_info, 

1132 resp.history, 

1133 message="Invalid challenge response", 

1134 status=resp.status, 

1135 headers=resp.headers, 

1136 ) 

1137 

1138 # websocket protocol 

1139 protocol = None 

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

1141 resp_protocols = [ 

1142 proto.strip() 

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

1144 ] 

1145 

1146 for proto in resp_protocols: 

1147 if proto in protocols: 

1148 protocol = proto 

1149 break 

1150 

1151 # websocket compress 

1152 notakeover = False 

1153 if compress: 

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

1155 if compress_hdrs: 

1156 try: 

1157 compress, notakeover = ws_ext_parse(compress_hdrs) 

1158 except WSHandshakeError as exc: 

1159 raise WSServerHandshakeError( 

1160 resp.request_info, 

1161 resp.history, 

1162 message=exc.args[0], 

1163 status=resp.status, 

1164 headers=resp.headers, 

1165 ) from exc 

1166 else: 

1167 compress = 0 

1168 notakeover = False 

1169 

1170 conn = resp.connection 

1171 assert conn is not None 

1172 conn_proto = conn.protocol 

1173 assert conn_proto is not None 

1174 

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

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

1177 if ws_timeout.ws_receive is None: 

1178 # Reset regardless 

1179 conn_proto.read_timeout = None 

1180 elif conn_proto.read_timeout is not None: 

1181 conn_proto.read_timeout = max( 

1182 ws_timeout.ws_receive, conn_proto.read_timeout 

1183 ) 

1184 

1185 transport = conn.transport 

1186 assert transport is not None 

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

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

1189 writer = WebSocketWriter( 

1190 conn_proto, 

1191 transport, 

1192 use_mask=True, 

1193 compress=compress, 

1194 notakeover=notakeover, 

1195 ) 

1196 except BaseException: 

1197 resp.close() 

1198 raise 

1199 else: 

1200 return self._ws_response_class( 

1201 reader, 

1202 writer, 

1203 protocol, 

1204 resp, 

1205 ws_timeout, 

1206 autoclose, 

1207 autoping, 

1208 self._loop, 

1209 heartbeat=heartbeat, 

1210 compress=compress, 

1211 client_notakeover=notakeover, 

1212 ) 

1213 

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

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

1216 # Convert headers to MultiDict 

1217 result = CIMultiDict(self._default_headers) 

1218 if headers: 

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

1220 headers = CIMultiDict(headers) 

1221 added_names: Set[str] = set() 

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

1223 if key in added_names: 

1224 result.add(key, value) 

1225 else: 

1226 result[key] = value 

1227 added_names.add(key) 

1228 return result 

1229 

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

1231 """ 

1232 Get auth from netrc for the given host. 

1233 

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

1235 blocking I/O in the event loop. 

1236 """ 

1237 netrc_obj = netrc_from_env() 

1238 try: 

1239 return basicauth_from_netrc(netrc_obj, host) 

1240 except LookupError: 

1241 return None 

1242 

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

1244 

1245 def get( 

1246 self, 

1247 url: StrOrURL, 

1248 **kwargs: Unpack[_RequestOptions], 

1249 ) -> "_RequestContextManager": ... 

1250 

1251 def options( 

1252 self, 

1253 url: StrOrURL, 

1254 **kwargs: Unpack[_RequestOptions], 

1255 ) -> "_RequestContextManager": ... 

1256 

1257 def head( 

1258 self, 

1259 url: StrOrURL, 

1260 **kwargs: Unpack[_RequestOptions], 

1261 ) -> "_RequestContextManager": ... 

1262 

1263 def post( 

1264 self, 

1265 url: StrOrURL, 

1266 **kwargs: Unpack[_RequestOptions], 

1267 ) -> "_RequestContextManager": ... 

1268 

1269 def put( 

1270 self, 

1271 url: StrOrURL, 

1272 **kwargs: Unpack[_RequestOptions], 

1273 ) -> "_RequestContextManager": ... 

1274 

1275 def patch( 

1276 self, 

1277 url: StrOrURL, 

1278 **kwargs: Unpack[_RequestOptions], 

1279 ) -> "_RequestContextManager": ... 

1280 

1281 def delete( 

1282 self, 

1283 url: StrOrURL, 

1284 **kwargs: Unpack[_RequestOptions], 

1285 ) -> "_RequestContextManager": ... 

1286 

1287 else: 

1288 

1289 def get( 

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

1291 ) -> "_RequestContextManager": 

1292 """Perform HTTP GET request.""" 

1293 return _RequestContextManager( 

1294 self._request( 

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

1296 ) 

1297 ) 

1298 

1299 def options( 

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

1301 ) -> "_RequestContextManager": 

1302 """Perform HTTP OPTIONS request.""" 

1303 return _RequestContextManager( 

1304 self._request( 

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

1306 ) 

1307 ) 

1308 

1309 def head( 

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

1311 ) -> "_RequestContextManager": 

1312 """Perform HTTP HEAD request.""" 

1313 return _RequestContextManager( 

1314 self._request( 

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

1316 ) 

1317 ) 

1318 

1319 def post( 

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

1321 ) -> "_RequestContextManager": 

1322 """Perform HTTP POST request.""" 

1323 return _RequestContextManager( 

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

1325 ) 

1326 

1327 def put( 

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

1329 ) -> "_RequestContextManager": 

1330 """Perform HTTP PUT request.""" 

1331 return _RequestContextManager( 

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

1333 ) 

1334 

1335 def patch( 

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

1337 ) -> "_RequestContextManager": 

1338 """Perform HTTP PATCH request.""" 

1339 return _RequestContextManager( 

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

1341 ) 

1342 

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

1344 """Perform HTTP DELETE request.""" 

1345 return _RequestContextManager( 

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

1347 ) 

1348 

1349 async def close(self) -> None: 

1350 """Close underlying connector. 

1351 

1352 Release all acquired resources. 

1353 """ 

1354 if not self.closed: 

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

1356 await self._connector.close() 

1357 self._connector = None 

1358 

1359 @property 

1360 def closed(self) -> bool: 

1361 """Is client session closed. 

1362 

1363 A readonly property. 

1364 """ 

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

1366 

1367 @property 

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

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

1370 return self._connector 

1371 

1372 @property 

1373 def cookie_jar(self) -> AbstractCookieJar: 

1374 """The session cookies.""" 

1375 return self._cookie_jar 

1376 

1377 @property 

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

1379 """The session HTTP protocol version.""" 

1380 return self._version 

1381 

1382 @property 

1383 def requote_redirect_url(self) -> bool: 

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

1385 return self._requote_redirect_url 

1386 

1387 @requote_redirect_url.setter 

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

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

1390 warnings.warn( 

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

1392 DeprecationWarning, 

1393 stacklevel=2, 

1394 ) 

1395 self._requote_redirect_url = val 

1396 

1397 @property 

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

1399 """Session's loop.""" 

1400 warnings.warn( 

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

1402 ) 

1403 return self._loop 

1404 

1405 @property 

1406 def timeout(self) -> ClientTimeout: 

1407 """Timeout for the session.""" 

1408 return self._timeout 

1409 

1410 @property 

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

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

1413 return self._default_headers 

1414 

1415 @property 

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

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

1418 return self._skip_auto_headers 

1419 

1420 @property 

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

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

1423 return self._default_auth 

1424 

1425 @property 

1426 def json_serialize(self) -> JSONEncoder: 

1427 """Json serializer callable""" 

1428 return self._json_serialize 

1429 

1430 @property 

1431 def connector_owner(self) -> bool: 

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

1433 return self._connector_owner 

1434 

1435 @property 

1436 def raise_for_status( 

1437 self, 

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

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

1440 return self._raise_for_status 

1441 

1442 @property 

1443 def auto_decompress(self) -> bool: 

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

1445 return self._auto_decompress 

1446 

1447 @property 

1448 def trust_env(self) -> bool: 

1449 """ 

1450 Should proxies information from environment or netrc be trusted. 

1451 

1452 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1453 or ~/.netrc file if present. 

1454 """ 

1455 return self._trust_env 

1456 

1457 @property 

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

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

1460 return self._trace_configs 

1461 

1462 def detach(self) -> None: 

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

1464 

1465 Session is switched to closed state anyway. 

1466 """ 

1467 self._connector = None 

1468 

1469 def __enter__(self) -> None: 

1470 raise TypeError("Use async with instead") 

1471 

1472 def __exit__( 

1473 self, 

1474 exc_type: Optional[Type[BaseException]], 

1475 exc_val: Optional[BaseException], 

1476 exc_tb: Optional[TracebackType], 

1477 ) -> None: 

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

1479 pass # pragma: no cover 

1480 

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

1482 return self 

1483 

1484 async def __aexit__( 

1485 self, 

1486 exc_type: Optional[Type[BaseException]], 

1487 exc_val: Optional[BaseException], 

1488 exc_tb: Optional[TracebackType], 

1489 ) -> None: 

1490 await self.close() 

1491 

1492 

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

1494 

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

1496 

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

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

1499 

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

1501 return self._coro.send(arg) 

1502 

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

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

1505 

1506 def close(self) -> None: 

1507 return self._coro.close() 

1508 

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

1510 ret = self._coro.__await__() 

1511 return ret 

1512 

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

1514 return self.__await__() 

1515 

1516 async def __aenter__(self) -> _RetType: 

1517 self._resp: _RetType = await self._coro 

1518 return await self._resp.__aenter__() 

1519 

1520 async def __aexit__( 

1521 self, 

1522 exc_type: Optional[Type[BaseException]], 

1523 exc: Optional[BaseException], 

1524 tb: Optional[TracebackType], 

1525 ) -> None: 

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

1527 

1528 

1529_RequestContextManager = _BaseRequestContextManager[ClientResponse] 

1530_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse] 

1531 

1532 

1533class _SessionRequestContextManager: 

1534 

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

1536 

1537 def __init__( 

1538 self, 

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

1540 session: ClientSession, 

1541 ) -> None: 

1542 self._coro = coro 

1543 self._resp: Optional[ClientResponse] = None 

1544 self._session = session 

1545 

1546 async def __aenter__(self) -> ClientResponse: 

1547 try: 

1548 self._resp = await self._coro 

1549 except BaseException: 

1550 await self._session.close() 

1551 raise 

1552 else: 

1553 return self._resp 

1554 

1555 async def __aexit__( 

1556 self, 

1557 exc_type: Optional[Type[BaseException]], 

1558 exc: Optional[BaseException], 

1559 tb: Optional[TracebackType], 

1560 ) -> None: 

1561 assert self._resp is not None 

1562 self._resp.close() 

1563 await self._session.close() 

1564 

1565 

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

1567 

1568 def request( 

1569 method: str, 

1570 url: StrOrURL, 

1571 *, 

1572 version: HttpVersion = http.HttpVersion11, 

1573 connector: Optional[BaseConnector] = None, 

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

1575 **kwargs: Unpack[_RequestOptions], 

1576 ) -> _SessionRequestContextManager: ... 

1577 

1578else: 

1579 

1580 def request( 

1581 method: str, 

1582 url: StrOrURL, 

1583 *, 

1584 version: HttpVersion = http.HttpVersion11, 

1585 connector: Optional[BaseConnector] = None, 

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

1587 **kwargs: Any, 

1588 ) -> _SessionRequestContextManager: 

1589 """Constructs and sends a request. 

1590 

1591 Returns response object. 

1592 method - HTTP method 

1593 url - request url 

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

1595 string of the new request 

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

1597 send in the body of the request 

1598 json - (optional) Any json compatible python object 

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

1600 the request 

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

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

1603 auth - aiohttp.helpers.BasicAuth 

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

1605 redirects 

1606 version - Request HTTP version. 

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

1608 with deflate encoding. 

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

1610 expect100 - Expect 100-continue response from server. 

1611 connector - BaseConnector sub-class instance to support 

1612 connection pooling. 

1613 read_until_eof - Read response until eof if response 

1614 does not have Content-Length header. 

1615 loop - Optional event loop. 

1616 timeout - Optional ClientTimeout settings structure, 5min 

1617 total timeout by default. 

1618 Usage:: 

1619 >>> import aiohttp 

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

1621 ... print(resp) 

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

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

1624 """ 

1625 connector_owner = False 

1626 if connector is None: 

1627 connector_owner = True 

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

1629 

1630 session = ClientSession( 

1631 loop=loop, 

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

1633 version=version, 

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

1635 connector=connector, 

1636 connector_owner=connector_owner, 

1637 ) 

1638 

1639 return _SessionRequestContextManager( 

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

1641 session, 

1642 )