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

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

603 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 get_env_proxy_for_url, 

102 sentinel, 

103 strip_auth_from_url, 

104) 

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

106from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse 

107from .tracing import Trace, TraceConfig 

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

109 

110__all__ = ( 

111 # client_exceptions 

112 "ClientConnectionError", 

113 "ClientConnectionResetError", 

114 "ClientConnectorCertificateError", 

115 "ClientConnectorDNSError", 

116 "ClientConnectorError", 

117 "ClientConnectorSSLError", 

118 "ClientError", 

119 "ClientHttpProxyError", 

120 "ClientOSError", 

121 "ClientPayloadError", 

122 "ClientProxyConnectionError", 

123 "ClientResponseError", 

124 "ClientSSLError", 

125 "ConnectionTimeoutError", 

126 "ContentTypeError", 

127 "InvalidURL", 

128 "InvalidUrlClientError", 

129 "RedirectClientError", 

130 "NonHttpUrlClientError", 

131 "InvalidUrlRedirectClientError", 

132 "NonHttpUrlRedirectClientError", 

133 "ServerConnectionError", 

134 "ServerDisconnectedError", 

135 "ServerFingerprintMismatch", 

136 "ServerTimeoutError", 

137 "SocketTimeoutError", 

138 "TooManyRedirects", 

139 "WSServerHandshakeError", 

140 # client_reqrep 

141 "ClientRequest", 

142 "ClientResponse", 

143 "Fingerprint", 

144 "RequestInfo", 

145 # connector 

146 "BaseConnector", 

147 "TCPConnector", 

148 "UnixConnector", 

149 "NamedPipeConnector", 

150 # client_ws 

151 "ClientWebSocketResponse", 

152 # client 

153 "ClientSession", 

154 "ClientTimeout", 

155 "ClientWSTimeout", 

156 "request", 

157 "WSMessageTypeError", 

158) 

159 

160 

161if TYPE_CHECKING: 

162 from ssl import SSLContext 

163else: 

164 SSLContext = None 

165 

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

167 from typing import Unpack 

168 

169 

170class _RequestOptions(TypedDict, total=False): 

171 params: Query 

172 data: Any 

173 json: Any 

174 cookies: Union[LooseCookies, None] 

175 headers: Union[LooseHeaders, None] 

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

177 auth: Union[BasicAuth, None] 

178 allow_redirects: bool 

179 max_redirects: int 

180 compress: Union[str, bool, None] 

181 chunked: Union[bool, None] 

182 expect100: bool 

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

184 read_until_eof: bool 

185 proxy: Union[StrOrURL, None] 

186 proxy_auth: Union[BasicAuth, None] 

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

188 ssl: Union[SSLContext, bool, Fingerprint] 

189 server_hostname: Union[str, None] 

190 proxy_headers: Union[LooseHeaders, None] 

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

192 read_bufsize: Union[int, None] 

193 auto_decompress: Union[bool, None] 

194 max_line_size: Union[int, None] 

195 max_field_size: Union[int, None] 

196 middlewares: Optional[Sequence[ClientMiddlewareType]] 

197 

198 

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

200class ClientTimeout: 

201 total: Optional[float] = None 

202 connect: Optional[float] = None 

203 sock_read: Optional[float] = None 

204 sock_connect: Optional[float] = None 

205 ceil_threshold: float = 5 

206 

207 # pool_queue_timeout: Optional[float] = None 

208 # dns_resolution_timeout: Optional[float] = None 

209 # socket_connect_timeout: Optional[float] = None 

210 # connection_acquiring_timeout: Optional[float] = None 

211 # new_connection_timeout: Optional[float] = None 

212 # http_header_timeout: Optional[float] = None 

213 # response_body_timeout: Optional[float] = None 

214 

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

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

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

218 # to overwrite the defaults 

219 

220 

221# 5 Minute default read timeout 

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

223 

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

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

226 

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

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

229 

230 

231class ClientSession: 

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

233 

234 ATTRS = frozenset( 

235 [ 

236 "_base_url", 

237 "_base_url_origin", 

238 "_source_traceback", 

239 "_connector", 

240 "_loop", 

241 "_cookie_jar", 

242 "_connector_owner", 

243 "_default_auth", 

244 "_version", 

245 "_json_serialize", 

246 "_requote_redirect_url", 

247 "_timeout", 

248 "_raise_for_status", 

249 "_auto_decompress", 

250 "_trust_env", 

251 "_default_headers", 

252 "_skip_auto_headers", 

253 "_request_class", 

254 "_response_class", 

255 "_ws_response_class", 

256 "_trace_configs", 

257 "_read_bufsize", 

258 "_max_line_size", 

259 "_max_field_size", 

260 "_resolve_charset", 

261 "_default_proxy", 

262 "_default_proxy_auth", 

263 "_retry_connection", 

264 "_middlewares", 

265 "requote_redirect_url", 

266 ] 

267 ) 

268 

269 _source_traceback: Optional[traceback.StackSummary] = None 

270 _connector: Optional[BaseConnector] = None 

271 

272 def __init__( 

273 self, 

274 base_url: Optional[StrOrURL] = None, 

275 *, 

276 connector: Optional[BaseConnector] = None, 

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

278 cookies: Optional[LooseCookies] = None, 

279 headers: Optional[LooseHeaders] = None, 

280 proxy: Optional[StrOrURL] = None, 

281 proxy_auth: Optional[BasicAuth] = None, 

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

283 auth: Optional[BasicAuth] = None, 

284 json_serialize: JSONEncoder = json.dumps, 

285 request_class: Type[ClientRequest] = ClientRequest, 

286 response_class: Type[ClientResponse] = ClientResponse, 

287 ws_response_class: Type[ClientWebSocketResponse] = ClientWebSocketResponse, 

288 version: HttpVersion = http.HttpVersion11, 

289 cookie_jar: Optional[AbstractCookieJar] = None, 

290 connector_owner: bool = True, 

291 raise_for_status: Union[ 

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

293 ] = False, 

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

295 conn_timeout: Optional[float] = None, 

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

297 auto_decompress: bool = True, 

298 trust_env: bool = False, 

299 requote_redirect_url: bool = True, 

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

301 read_bufsize: int = 2**16, 

302 max_line_size: int = 8190, 

303 max_field_size: int = 8190, 

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

305 middlewares: Sequence[ClientMiddlewareType] = (), 

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

307 ) -> None: 

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

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

310 self._connector: Optional[BaseConnector] = None 

311 

312 if loop is None: 

313 if connector is not None: 

314 loop = connector._loop 

315 

316 loop = loop or asyncio.get_running_loop() 

317 

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

319 self._base_url: Optional[URL] = base_url 

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

321 else: 

322 self._base_url = URL(base_url) 

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

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

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

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

327 

328 if timeout is sentinel or timeout is None: 

329 self._timeout = DEFAULT_TIMEOUT 

330 if read_timeout is not sentinel: 

331 warnings.warn( 

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

333 DeprecationWarning, 

334 stacklevel=2, 

335 ) 

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

337 if conn_timeout is not None: 

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

339 warnings.warn( 

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

341 DeprecationWarning, 

342 stacklevel=2, 

343 ) 

344 else: 

345 if not isinstance(timeout, ClientTimeout): 

346 raise ValueError( 

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

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

349 ) 

350 self._timeout = timeout 

351 if read_timeout is not sentinel: 

352 raise ValueError( 

353 "read_timeout and timeout parameters " 

354 "conflict, please setup " 

355 "timeout.read" 

356 ) 

357 if conn_timeout is not None: 

358 raise ValueError( 

359 "conn_timeout and timeout parameters " 

360 "conflict, please setup " 

361 "timeout.connect" 

362 ) 

363 

364 if ssl_shutdown_timeout is not sentinel: 

365 warnings.warn( 

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

367 DeprecationWarning, 

368 stacklevel=2, 

369 ) 

370 

371 if connector is None: 

372 connector = TCPConnector( 

373 loop=loop, ssl_shutdown_timeout=ssl_shutdown_timeout 

374 ) 

375 

376 if connector._loop is not loop: 

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

378 

379 self._loop = loop 

380 

381 if loop.get_debug(): 

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

383 

384 if cookie_jar is None: 

385 cookie_jar = CookieJar(loop=loop) 

386 self._cookie_jar = cookie_jar 

387 

388 if cookies: 

389 self._cookie_jar.update_cookies(cookies) 

390 

391 self._connector = connector 

392 self._connector_owner = connector_owner 

393 self._default_auth = auth 

394 self._version = version 

395 self._json_serialize = json_serialize 

396 self._raise_for_status = raise_for_status 

397 self._auto_decompress = auto_decompress 

398 self._trust_env = trust_env 

399 self._requote_redirect_url = requote_redirect_url 

400 self._read_bufsize = read_bufsize 

401 self._max_line_size = max_line_size 

402 self._max_field_size = max_field_size 

403 

404 # Convert to list of tuples 

405 if headers: 

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

407 else: 

408 real_headers = CIMultiDict() 

409 self._default_headers: CIMultiDict[str] = real_headers 

410 if skip_auto_headers is not None: 

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

412 else: 

413 self._skip_auto_headers = frozenset() 

414 

415 self._request_class = request_class 

416 self._response_class = response_class 

417 self._ws_response_class = ws_response_class 

418 

419 self._trace_configs = trace_configs or [] 

420 for trace_config in self._trace_configs: 

421 trace_config.freeze() 

422 

423 self._resolve_charset = fallback_charset_resolver 

424 

425 self._default_proxy = proxy 

426 self._default_proxy_auth = proxy_auth 

427 self._retry_connection: bool = True 

428 self._middlewares = middlewares 

429 

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

431 warnings.warn( 

432 "Inheritance class {} from ClientSession " 

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

434 DeprecationWarning, 

435 stacklevel=2, 

436 ) 

437 

438 if DEBUG: 

439 

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

441 if name not in self.ATTRS: 

442 warnings.warn( 

443 "Setting custom ClientSession.{} attribute " 

444 "is discouraged".format(name), 

445 DeprecationWarning, 

446 stacklevel=2, 

447 ) 

448 super().__setattr__(name, val) 

449 

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

451 if not self.closed: 

452 kwargs = {"source": self} 

453 _warnings.warn( 

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

455 ) 

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

457 if self._source_traceback is not None: 

458 context["source_traceback"] = self._source_traceback 

459 self._loop.call_exception_handler(context) 

460 

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

462 

463 def request( 

464 self, 

465 method: str, 

466 url: StrOrURL, 

467 **kwargs: Unpack[_RequestOptions], 

468 ) -> "_RequestContextManager": ... 

469 

470 else: 

471 

472 def request( 

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

474 ) -> "_RequestContextManager": 

475 """Perform HTTP request.""" 

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

477 

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

479 url = URL(str_or_url) 

480 if self._base_url and not url.absolute: 

481 return self._base_url.join(url) 

482 return url 

483 

484 async def _request( 

485 self, 

486 method: str, 

487 str_or_url: StrOrURL, 

488 *, 

489 params: Query = None, 

490 data: Any = None, 

491 json: Any = None, 

492 cookies: Optional[LooseCookies] = None, 

493 headers: Optional[LooseHeaders] = None, 

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

495 auth: Optional[BasicAuth] = None, 

496 allow_redirects: bool = True, 

497 max_redirects: int = 10, 

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

499 chunked: Optional[bool] = None, 

500 expect100: bool = False, 

501 raise_for_status: Union[ 

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

503 ] = None, 

504 read_until_eof: bool = True, 

505 proxy: Optional[StrOrURL] = None, 

506 proxy_auth: Optional[BasicAuth] = None, 

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

508 verify_ssl: Optional[bool] = None, 

509 fingerprint: Optional[bytes] = None, 

510 ssl_context: Optional[SSLContext] = None, 

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

512 server_hostname: Optional[str] = None, 

513 proxy_headers: Optional[LooseHeaders] = None, 

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

515 read_bufsize: Optional[int] = None, 

516 auto_decompress: Optional[bool] = None, 

517 max_line_size: Optional[int] = None, 

518 max_field_size: Optional[int] = None, 

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

520 ) -> ClientResponse: 

521 

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

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

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

525 

526 if self.closed: 

527 raise RuntimeError("Session is closed") 

528 

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

530 

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

532 raise ValueError( 

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

534 ) 

535 elif json is not None: 

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

537 

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

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

540 

541 redirects = 0 

542 history: List[ClientResponse] = [] 

543 version = self._version 

544 params = params or {} 

545 

546 # Merge with default headers and transform to CIMultiDict 

547 headers = self._prepare_headers(headers) 

548 

549 try: 

550 url = self._build_url(str_or_url) 

551 except ValueError as e: 

552 raise InvalidUrlClientError(str_or_url) from e 

553 

554 assert self._connector is not None 

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

556 raise NonHttpUrlClientError(url) 

557 

558 skip_headers: Optional[Iterable[istr]] 

559 if skip_auto_headers is not None: 

560 skip_headers = { 

561 istr(i) for i in skip_auto_headers 

562 } | self._skip_auto_headers 

563 elif self._skip_auto_headers: 

564 skip_headers = self._skip_auto_headers 

565 else: 

566 skip_headers = None 

567 

568 if proxy is None: 

569 proxy = self._default_proxy 

570 if proxy_auth is None: 

571 proxy_auth = self._default_proxy_auth 

572 

573 if proxy is None: 

574 proxy_headers = None 

575 else: 

576 proxy_headers = self._prepare_headers(proxy_headers) 

577 try: 

578 proxy = URL(proxy) 

579 except ValueError as e: 

580 raise InvalidURL(proxy) from e 

581 

582 if timeout is sentinel: 

583 real_timeout: ClientTimeout = self._timeout 

584 else: 

585 if not isinstance(timeout, ClientTimeout): 

586 real_timeout = ClientTimeout(total=timeout) 

587 else: 

588 real_timeout = timeout 

589 # timeout is cumulative for all request operations 

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

591 tm = TimeoutHandle( 

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

593 ) 

594 handle = tm.start() 

595 

596 if read_bufsize is None: 

597 read_bufsize = self._read_bufsize 

598 

599 if auto_decompress is None: 

600 auto_decompress = self._auto_decompress 

601 

602 if max_line_size is None: 

603 max_line_size = self._max_line_size 

604 

605 if max_field_size is None: 

606 max_field_size = self._max_field_size 

607 

608 traces = [ 

609 Trace( 

610 self, 

611 trace_config, 

612 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), 

613 ) 

614 for trace_config in self._trace_configs 

615 ] 

616 

617 for trace in traces: 

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

619 

620 timer = tm.timer() 

621 try: 

622 with timer: 

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

624 retry_persistent_connection = ( 

625 self._retry_connection and method in IDEMPOTENT_METHODS 

626 ) 

627 while True: 

628 url, auth_from_url = strip_auth_from_url(url) 

629 if not url.raw_host: 

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

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

632 err_exc_cls = ( 

633 InvalidUrlRedirectClientError 

634 if redirects 

635 else InvalidUrlClientError 

636 ) 

637 raise err_exc_cls(url) 

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

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

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

641 if not history and (auth and auth_from_url): 

642 raise ValueError( 

643 "Cannot combine AUTH argument with " 

644 "credentials encoded in URL" 

645 ) 

646 

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

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

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

650 auth = auth_from_url 

651 

652 if ( 

653 auth is None 

654 and self._default_auth 

655 and ( 

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

657 ) 

658 ): 

659 auth = self._default_auth 

660 # It would be confusing if we support explicit 

661 # Authorization header with auth argument 

662 if ( 

663 headers is not None 

664 and auth is not None 

665 and hdrs.AUTHORIZATION in headers 

666 ): 

667 raise ValueError( 

668 "Cannot combine AUTHORIZATION header " 

669 "with AUTH argument or credentials " 

670 "encoded in URL" 

671 ) 

672 

673 all_cookies = self._cookie_jar.filter_cookies(url) 

674 

675 if cookies is not None: 

676 tmp_cookie_jar = CookieJar( 

677 quote_cookie=self._cookie_jar.quote_cookie 

678 ) 

679 tmp_cookie_jar.update_cookies(cookies) 

680 req_cookies = tmp_cookie_jar.filter_cookies(url) 

681 if req_cookies: 

682 all_cookies.load(req_cookies) 

683 

684 proxy_: Optional[URL] = None 

685 if proxy is not None: 

686 proxy_ = URL(proxy) 

687 elif self._trust_env: 

688 with suppress(LookupError): 

689 proxy_, proxy_auth = await asyncio.to_thread( 

690 get_env_proxy_for_url, url 

691 ) 

692 

693 req = self._request_class( 

694 method, 

695 url, 

696 params=params, 

697 headers=headers, 

698 skip_auto_headers=skip_headers, 

699 data=data, 

700 cookies=all_cookies, 

701 auth=auth, 

702 version=version, 

703 compress=compress, 

704 chunked=chunked, 

705 expect100=expect100, 

706 loop=self._loop, 

707 response_class=self._response_class, 

708 proxy=proxy_, 

709 proxy_auth=proxy_auth, 

710 timer=timer, 

711 session=self, 

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

713 server_hostname=server_hostname, 

714 proxy_headers=proxy_headers, 

715 traces=traces, 

716 trust_env=self.trust_env, 

717 ) 

718 

719 async def _connect_and_send_request( 

720 req: ClientRequest, 

721 ) -> ClientResponse: 

722 # connection timeout 

723 assert self._connector is not None 

724 try: 

725 conn = await self._connector.connect( 

726 req, traces=traces, timeout=real_timeout 

727 ) 

728 except asyncio.TimeoutError as exc: 

729 raise ConnectionTimeoutError( 

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

731 ) from exc 

732 

733 assert conn.protocol is not None 

734 conn.protocol.set_response_params( 

735 timer=timer, 

736 skip_payload=req.method in EMPTY_BODY_METHODS, 

737 read_until_eof=read_until_eof, 

738 auto_decompress=auto_decompress, 

739 read_timeout=real_timeout.sock_read, 

740 read_bufsize=read_bufsize, 

741 timeout_ceil_threshold=self._connector._timeout_ceil_threshold, 

742 max_line_size=max_line_size, 

743 max_field_size=max_field_size, 

744 ) 

745 try: 

746 resp = await req.send(conn) 

747 try: 

748 await resp.start(conn) 

749 except BaseException: 

750 resp.close() 

751 raise 

752 except BaseException: 

753 conn.close() 

754 raise 

755 return resp 

756 

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

758 effective_middlewares = ( 

759 self._middlewares if middlewares is None else middlewares 

760 ) 

761 

762 if effective_middlewares: 

763 handler = build_client_middlewares( 

764 _connect_and_send_request, effective_middlewares 

765 ) 

766 else: 

767 handler = _connect_and_send_request 

768 

769 try: 

770 resp = await handler(req) 

771 # Client connector errors should not be retried 

772 except ( 

773 ConnectionTimeoutError, 

774 ClientConnectorError, 

775 ClientConnectorCertificateError, 

776 ClientConnectorSSLError, 

777 ): 

778 raise 

779 except (ClientOSError, ServerDisconnectedError): 

780 if retry_persistent_connection: 

781 retry_persistent_connection = False 

782 continue 

783 raise 

784 except ClientError: 

785 raise 

786 except OSError as exc: 

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

788 raise 

789 raise ClientOSError(*exc.args) from exc 

790 

791 # Update cookies from raw headers to preserve duplicates 

792 if resp._raw_cookie_headers: 

793 self._cookie_jar.update_cookies_from_headers( 

794 resp._raw_cookie_headers, resp.url 

795 ) 

796 

797 # redirects 

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

799 

800 for trace in traces: 

801 await trace.send_request_redirect( 

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

803 ) 

804 

805 redirects += 1 

806 history.append(resp) 

807 if max_redirects and redirects >= max_redirects: 

808 if req._body is not None: 

809 await req._body.close() 

810 resp.close() 

811 raise TooManyRedirects( 

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

813 ) 

814 

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

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

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

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

819 ): 

820 method = hdrs.METH_GET 

821 data = None 

822 if headers.get(hdrs.CONTENT_LENGTH): 

823 headers.pop(hdrs.CONTENT_LENGTH) 

824 else: 

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

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

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

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

829 data = req._body 

830 

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

832 hdrs.URI 

833 ) 

834 if r_url is None: 

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

836 break 

837 else: 

838 # reading from correct redirection 

839 # response is forbidden 

840 resp.release() 

841 

842 try: 

843 parsed_redirect_url = URL( 

844 r_url, encoded=not self._requote_redirect_url 

845 ) 

846 except ValueError as e: 

847 if req._body is not None: 

848 await req._body.close() 

849 resp.close() 

850 raise InvalidUrlRedirectClientError( 

851 r_url, 

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

853 ) from e 

854 

855 scheme = parsed_redirect_url.scheme 

856 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: 

857 if req._body is not None: 

858 await req._body.close() 

859 resp.close() 

860 raise NonHttpUrlRedirectClientError(r_url) 

861 elif not scheme: 

862 parsed_redirect_url = url.join(parsed_redirect_url) 

863 

864 try: 

865 redirect_origin = parsed_redirect_url.origin() 

866 except ValueError as origin_val_err: 

867 if req._body is not None: 

868 await req._body.close() 

869 resp.close() 

870 raise InvalidUrlRedirectClientError( 

871 parsed_redirect_url, 

872 "Invalid redirect URL origin", 

873 ) from origin_val_err 

874 

875 if url.origin() != redirect_origin: 

876 auth = None 

877 headers.pop(hdrs.AUTHORIZATION, None) 

878 

879 url = parsed_redirect_url 

880 params = {} 

881 resp.release() 

882 continue 

883 

884 break 

885 

886 if req._body is not None: 

887 await req._body.close() 

888 # check response status 

889 if raise_for_status is None: 

890 raise_for_status = self._raise_for_status 

891 

892 if raise_for_status is None: 

893 pass 

894 elif callable(raise_for_status): 

895 await raise_for_status(resp) 

896 elif raise_for_status: 

897 resp.raise_for_status() 

898 

899 # register connection 

900 if handle is not None: 

901 if resp.connection is not None: 

902 resp.connection.add_callback(handle.cancel) 

903 else: 

904 handle.cancel() 

905 

906 resp._history = tuple(history) 

907 

908 for trace in traces: 

909 await trace.send_request_end( 

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

911 ) 

912 return resp 

913 

914 except BaseException as e: 

915 # cleanup timer 

916 tm.close() 

917 if handle: 

918 handle.cancel() 

919 handle = None 

920 

921 for trace in traces: 

922 await trace.send_request_exception( 

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

924 ) 

925 raise 

926 

927 def ws_connect( 

928 self, 

929 url: StrOrURL, 

930 *, 

931 method: str = hdrs.METH_GET, 

932 protocols: Iterable[str] = (), 

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

934 receive_timeout: Optional[float] = None, 

935 autoclose: bool = True, 

936 autoping: bool = True, 

937 heartbeat: Optional[float] = None, 

938 auth: Optional[BasicAuth] = None, 

939 origin: Optional[str] = None, 

940 params: Query = None, 

941 headers: Optional[LooseHeaders] = None, 

942 proxy: Optional[StrOrURL] = None, 

943 proxy_auth: Optional[BasicAuth] = None, 

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

945 verify_ssl: Optional[bool] = None, 

946 fingerprint: Optional[bytes] = None, 

947 ssl_context: Optional[SSLContext] = None, 

948 server_hostname: Optional[str] = None, 

949 proxy_headers: Optional[LooseHeaders] = None, 

950 compress: int = 0, 

951 max_msg_size: int = 4 * 1024 * 1024, 

952 ) -> "_WSRequestContextManager": 

953 """Initiate websocket connection.""" 

954 return _WSRequestContextManager( 

955 self._ws_connect( 

956 url, 

957 method=method, 

958 protocols=protocols, 

959 timeout=timeout, 

960 receive_timeout=receive_timeout, 

961 autoclose=autoclose, 

962 autoping=autoping, 

963 heartbeat=heartbeat, 

964 auth=auth, 

965 origin=origin, 

966 params=params, 

967 headers=headers, 

968 proxy=proxy, 

969 proxy_auth=proxy_auth, 

970 ssl=ssl, 

971 verify_ssl=verify_ssl, 

972 fingerprint=fingerprint, 

973 ssl_context=ssl_context, 

974 server_hostname=server_hostname, 

975 proxy_headers=proxy_headers, 

976 compress=compress, 

977 max_msg_size=max_msg_size, 

978 ) 

979 ) 

980 

981 async def _ws_connect( 

982 self, 

983 url: StrOrURL, 

984 *, 

985 method: str = hdrs.METH_GET, 

986 protocols: Iterable[str] = (), 

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

988 receive_timeout: Optional[float] = None, 

989 autoclose: bool = True, 

990 autoping: bool = True, 

991 heartbeat: Optional[float] = None, 

992 auth: Optional[BasicAuth] = None, 

993 origin: Optional[str] = None, 

994 params: Query = None, 

995 headers: Optional[LooseHeaders] = None, 

996 proxy: Optional[StrOrURL] = None, 

997 proxy_auth: Optional[BasicAuth] = None, 

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

999 verify_ssl: Optional[bool] = None, 

1000 fingerprint: Optional[bytes] = None, 

1001 ssl_context: Optional[SSLContext] = None, 

1002 server_hostname: Optional[str] = None, 

1003 proxy_headers: Optional[LooseHeaders] = None, 

1004 compress: int = 0, 

1005 max_msg_size: int = 4 * 1024 * 1024, 

1006 ) -> ClientWebSocketResponse: 

1007 if timeout is not sentinel: 

1008 if isinstance(timeout, ClientWSTimeout): 

1009 ws_timeout = timeout 

1010 else: 

1011 warnings.warn( 

1012 "parameter 'timeout' of type 'float' " 

1013 "is deprecated, please use " 

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

1015 DeprecationWarning, 

1016 stacklevel=2, 

1017 ) 

1018 ws_timeout = ClientWSTimeout(ws_close=timeout) 

1019 else: 

1020 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT 

1021 if receive_timeout is not None: 

1022 warnings.warn( 

1023 "float parameter 'receive_timeout' " 

1024 "is deprecated, please use parameter " 

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

1026 DeprecationWarning, 

1027 stacklevel=2, 

1028 ) 

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

1030 

1031 if headers is None: 

1032 real_headers: CIMultiDict[str] = CIMultiDict() 

1033 else: 

1034 real_headers = CIMultiDict(headers) 

1035 

1036 default_headers = { 

1037 hdrs.UPGRADE: "websocket", 

1038 hdrs.CONNECTION: "Upgrade", 

1039 hdrs.SEC_WEBSOCKET_VERSION: "13", 

1040 } 

1041 

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

1043 real_headers.setdefault(key, value) 

1044 

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

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

1047 

1048 if protocols: 

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

1050 if origin is not None: 

1051 real_headers[hdrs.ORIGIN] = origin 

1052 if compress: 

1053 extstr = ws_ext_gen(compress=compress) 

1054 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

1055 

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

1057 if ssl is None: 

1058 warnings.warn( 

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

1060 DeprecationWarning, 

1061 stacklevel=2, 

1062 ) 

1063 ssl = True 

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

1065 

1066 # send request 

1067 resp = await self.request( 

1068 method, 

1069 url, 

1070 params=params, 

1071 headers=real_headers, 

1072 read_until_eof=False, 

1073 auth=auth, 

1074 proxy=proxy, 

1075 proxy_auth=proxy_auth, 

1076 ssl=ssl, 

1077 server_hostname=server_hostname, 

1078 proxy_headers=proxy_headers, 

1079 ) 

1080 

1081 try: 

1082 # check handshake 

1083 if resp.status != 101: 

1084 raise WSServerHandshakeError( 

1085 resp.request_info, 

1086 resp.history, 

1087 message="Invalid response status", 

1088 status=resp.status, 

1089 headers=resp.headers, 

1090 ) 

1091 

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

1093 raise WSServerHandshakeError( 

1094 resp.request_info, 

1095 resp.history, 

1096 message="Invalid upgrade header", 

1097 status=resp.status, 

1098 headers=resp.headers, 

1099 ) 

1100 

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

1102 raise WSServerHandshakeError( 

1103 resp.request_info, 

1104 resp.history, 

1105 message="Invalid connection header", 

1106 status=resp.status, 

1107 headers=resp.headers, 

1108 ) 

1109 

1110 # key calculation 

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

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

1113 if r_key != match: 

1114 raise WSServerHandshakeError( 

1115 resp.request_info, 

1116 resp.history, 

1117 message="Invalid challenge response", 

1118 status=resp.status, 

1119 headers=resp.headers, 

1120 ) 

1121 

1122 # websocket protocol 

1123 protocol = None 

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

1125 resp_protocols = [ 

1126 proto.strip() 

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

1128 ] 

1129 

1130 for proto in resp_protocols: 

1131 if proto in protocols: 

1132 protocol = proto 

1133 break 

1134 

1135 # websocket compress 

1136 notakeover = False 

1137 if compress: 

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

1139 if compress_hdrs: 

1140 try: 

1141 compress, notakeover = ws_ext_parse(compress_hdrs) 

1142 except WSHandshakeError as exc: 

1143 raise WSServerHandshakeError( 

1144 resp.request_info, 

1145 resp.history, 

1146 message=exc.args[0], 

1147 status=resp.status, 

1148 headers=resp.headers, 

1149 ) from exc 

1150 else: 

1151 compress = 0 

1152 notakeover = False 

1153 

1154 conn = resp.connection 

1155 assert conn is not None 

1156 conn_proto = conn.protocol 

1157 assert conn_proto is not None 

1158 

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

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

1161 if ws_timeout.ws_receive is None: 

1162 # Reset regardless 

1163 conn_proto.read_timeout = None 

1164 elif conn_proto.read_timeout is not None: 

1165 conn_proto.read_timeout = max( 

1166 ws_timeout.ws_receive, conn_proto.read_timeout 

1167 ) 

1168 

1169 transport = conn.transport 

1170 assert transport is not None 

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

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

1173 writer = WebSocketWriter( 

1174 conn_proto, 

1175 transport, 

1176 use_mask=True, 

1177 compress=compress, 

1178 notakeover=notakeover, 

1179 ) 

1180 except BaseException: 

1181 resp.close() 

1182 raise 

1183 else: 

1184 return self._ws_response_class( 

1185 reader, 

1186 writer, 

1187 protocol, 

1188 resp, 

1189 ws_timeout, 

1190 autoclose, 

1191 autoping, 

1192 self._loop, 

1193 heartbeat=heartbeat, 

1194 compress=compress, 

1195 client_notakeover=notakeover, 

1196 ) 

1197 

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

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

1200 # Convert headers to MultiDict 

1201 result = CIMultiDict(self._default_headers) 

1202 if headers: 

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

1204 headers = CIMultiDict(headers) 

1205 added_names: Set[str] = set() 

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

1207 if key in added_names: 

1208 result.add(key, value) 

1209 else: 

1210 result[key] = value 

1211 added_names.add(key) 

1212 return result 

1213 

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

1215 

1216 def get( 

1217 self, 

1218 url: StrOrURL, 

1219 **kwargs: Unpack[_RequestOptions], 

1220 ) -> "_RequestContextManager": ... 

1221 

1222 def options( 

1223 self, 

1224 url: StrOrURL, 

1225 **kwargs: Unpack[_RequestOptions], 

1226 ) -> "_RequestContextManager": ... 

1227 

1228 def head( 

1229 self, 

1230 url: StrOrURL, 

1231 **kwargs: Unpack[_RequestOptions], 

1232 ) -> "_RequestContextManager": ... 

1233 

1234 def post( 

1235 self, 

1236 url: StrOrURL, 

1237 **kwargs: Unpack[_RequestOptions], 

1238 ) -> "_RequestContextManager": ... 

1239 

1240 def put( 

1241 self, 

1242 url: StrOrURL, 

1243 **kwargs: Unpack[_RequestOptions], 

1244 ) -> "_RequestContextManager": ... 

1245 

1246 def patch( 

1247 self, 

1248 url: StrOrURL, 

1249 **kwargs: Unpack[_RequestOptions], 

1250 ) -> "_RequestContextManager": ... 

1251 

1252 def delete( 

1253 self, 

1254 url: StrOrURL, 

1255 **kwargs: Unpack[_RequestOptions], 

1256 ) -> "_RequestContextManager": ... 

1257 

1258 else: 

1259 

1260 def get( 

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

1262 ) -> "_RequestContextManager": 

1263 """Perform HTTP GET request.""" 

1264 return _RequestContextManager( 

1265 self._request( 

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

1267 ) 

1268 ) 

1269 

1270 def options( 

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

1272 ) -> "_RequestContextManager": 

1273 """Perform HTTP OPTIONS request.""" 

1274 return _RequestContextManager( 

1275 self._request( 

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

1277 ) 

1278 ) 

1279 

1280 def head( 

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

1282 ) -> "_RequestContextManager": 

1283 """Perform HTTP HEAD request.""" 

1284 return _RequestContextManager( 

1285 self._request( 

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

1287 ) 

1288 ) 

1289 

1290 def post( 

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

1292 ) -> "_RequestContextManager": 

1293 """Perform HTTP POST request.""" 

1294 return _RequestContextManager( 

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

1296 ) 

1297 

1298 def put( 

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

1300 ) -> "_RequestContextManager": 

1301 """Perform HTTP PUT request.""" 

1302 return _RequestContextManager( 

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

1304 ) 

1305 

1306 def patch( 

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

1308 ) -> "_RequestContextManager": 

1309 """Perform HTTP PATCH request.""" 

1310 return _RequestContextManager( 

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

1312 ) 

1313 

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

1315 """Perform HTTP DELETE request.""" 

1316 return _RequestContextManager( 

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

1318 ) 

1319 

1320 async def close(self) -> None: 

1321 """Close underlying connector. 

1322 

1323 Release all acquired resources. 

1324 """ 

1325 if not self.closed: 

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

1327 await self._connector.close() 

1328 self._connector = None 

1329 

1330 @property 

1331 def closed(self) -> bool: 

1332 """Is client session closed. 

1333 

1334 A readonly property. 

1335 """ 

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

1337 

1338 @property 

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

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

1341 return self._connector 

1342 

1343 @property 

1344 def cookie_jar(self) -> AbstractCookieJar: 

1345 """The session cookies.""" 

1346 return self._cookie_jar 

1347 

1348 @property 

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

1350 """The session HTTP protocol version.""" 

1351 return self._version 

1352 

1353 @property 

1354 def requote_redirect_url(self) -> bool: 

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

1356 return self._requote_redirect_url 

1357 

1358 @requote_redirect_url.setter 

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

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

1361 warnings.warn( 

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

1363 DeprecationWarning, 

1364 stacklevel=2, 

1365 ) 

1366 self._requote_redirect_url = val 

1367 

1368 @property 

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

1370 """Session's loop.""" 

1371 warnings.warn( 

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

1373 ) 

1374 return self._loop 

1375 

1376 @property 

1377 def timeout(self) -> ClientTimeout: 

1378 """Timeout for the session.""" 

1379 return self._timeout 

1380 

1381 @property 

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

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

1384 return self._default_headers 

1385 

1386 @property 

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

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

1389 return self._skip_auto_headers 

1390 

1391 @property 

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

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

1394 return self._default_auth 

1395 

1396 @property 

1397 def json_serialize(self) -> JSONEncoder: 

1398 """Json serializer callable""" 

1399 return self._json_serialize 

1400 

1401 @property 

1402 def connector_owner(self) -> bool: 

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

1404 return self._connector_owner 

1405 

1406 @property 

1407 def raise_for_status( 

1408 self, 

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

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

1411 return self._raise_for_status 

1412 

1413 @property 

1414 def auto_decompress(self) -> bool: 

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

1416 return self._auto_decompress 

1417 

1418 @property 

1419 def trust_env(self) -> bool: 

1420 """ 

1421 Should proxies information from environment or netrc be trusted. 

1422 

1423 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1424 or ~/.netrc file if present. 

1425 """ 

1426 return self._trust_env 

1427 

1428 @property 

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

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

1431 return self._trace_configs 

1432 

1433 def detach(self) -> None: 

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

1435 

1436 Session is switched to closed state anyway. 

1437 """ 

1438 self._connector = None 

1439 

1440 def __enter__(self) -> None: 

1441 raise TypeError("Use async with instead") 

1442 

1443 def __exit__( 

1444 self, 

1445 exc_type: Optional[Type[BaseException]], 

1446 exc_val: Optional[BaseException], 

1447 exc_tb: Optional[TracebackType], 

1448 ) -> None: 

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

1450 pass # pragma: no cover 

1451 

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

1453 return self 

1454 

1455 async def __aexit__( 

1456 self, 

1457 exc_type: Optional[Type[BaseException]], 

1458 exc_val: Optional[BaseException], 

1459 exc_tb: Optional[TracebackType], 

1460 ) -> None: 

1461 await self.close() 

1462 

1463 

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

1465 

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

1467 

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

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

1470 

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

1472 return self._coro.send(arg) 

1473 

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

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

1476 

1477 def close(self) -> None: 

1478 return self._coro.close() 

1479 

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

1481 ret = self._coro.__await__() 

1482 return ret 

1483 

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

1485 return self.__await__() 

1486 

1487 async def __aenter__(self) -> _RetType: 

1488 self._resp: _RetType = await self._coro 

1489 return await self._resp.__aenter__() 

1490 

1491 async def __aexit__( 

1492 self, 

1493 exc_type: Optional[Type[BaseException]], 

1494 exc: Optional[BaseException], 

1495 tb: Optional[TracebackType], 

1496 ) -> None: 

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

1498 

1499 

1500_RequestContextManager = _BaseRequestContextManager[ClientResponse] 

1501_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse] 

1502 

1503 

1504class _SessionRequestContextManager: 

1505 

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

1507 

1508 def __init__( 

1509 self, 

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

1511 session: ClientSession, 

1512 ) -> None: 

1513 self._coro = coro 

1514 self._resp: Optional[ClientResponse] = None 

1515 self._session = session 

1516 

1517 async def __aenter__(self) -> ClientResponse: 

1518 try: 

1519 self._resp = await self._coro 

1520 except BaseException: 

1521 await self._session.close() 

1522 raise 

1523 else: 

1524 return self._resp 

1525 

1526 async def __aexit__( 

1527 self, 

1528 exc_type: Optional[Type[BaseException]], 

1529 exc: Optional[BaseException], 

1530 tb: Optional[TracebackType], 

1531 ) -> None: 

1532 assert self._resp is not None 

1533 self._resp.close() 

1534 await self._session.close() 

1535 

1536 

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

1538 

1539 def request( 

1540 method: str, 

1541 url: StrOrURL, 

1542 *, 

1543 version: HttpVersion = http.HttpVersion11, 

1544 connector: Optional[BaseConnector] = None, 

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

1546 **kwargs: Unpack[_RequestOptions], 

1547 ) -> _SessionRequestContextManager: ... 

1548 

1549else: 

1550 

1551 def request( 

1552 method: str, 

1553 url: StrOrURL, 

1554 *, 

1555 version: HttpVersion = http.HttpVersion11, 

1556 connector: Optional[BaseConnector] = None, 

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

1558 **kwargs: Any, 

1559 ) -> _SessionRequestContextManager: 

1560 """Constructs and sends a request. 

1561 

1562 Returns response object. 

1563 method - HTTP method 

1564 url - request url 

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

1566 string of the new request 

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

1568 send in the body of the request 

1569 json - (optional) Any json compatible python object 

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

1571 the request 

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

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

1574 auth - aiohttp.helpers.BasicAuth 

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

1576 redirects 

1577 version - Request HTTP version. 

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

1579 with deflate encoding. 

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

1581 expect100 - Expect 100-continue response from server. 

1582 connector - BaseConnector sub-class instance to support 

1583 connection pooling. 

1584 read_until_eof - Read response until eof if response 

1585 does not have Content-Length header. 

1586 loop - Optional event loop. 

1587 timeout - Optional ClientTimeout settings structure, 5min 

1588 total timeout by default. 

1589 Usage:: 

1590 >>> import aiohttp 

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

1592 ... print(resp) 

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

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

1595 """ 

1596 connector_owner = False 

1597 if connector is None: 

1598 connector_owner = True 

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

1600 

1601 session = ClientSession( 

1602 loop=loop, 

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

1604 version=version, 

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

1606 connector=connector, 

1607 connector_owner=connector_owner, 

1608 ) 

1609 

1610 return _SessionRequestContextManager( 

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

1612 session, 

1613 )