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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

680 statements  

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

2 

3import asyncio 

4import base64 

5import hashlib 

6import json 

7import os 

8import sys 

9import traceback 

10import warnings 

11from collections.abc import ( 

12 Awaitable, 

13 Callable, 

14 Coroutine, 

15 Generator, 

16 Iterable, 

17 Sequence, 

18) 

19from contextlib import suppress 

20from types import TracebackType 

21from typing import ( 

22 TYPE_CHECKING, 

23 Any, 

24 Final, 

25 Generic, 

26 Literal, 

27 TypedDict, 

28 TypeVar, 

29 overload, 

30) 

31 

32import attr 

33from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr 

34from yarl import URL 

35 

36from . import hdrs, http, payload 

37from ._websocket.reader import WebSocketDataQueue 

38from .abc import AbstractCookieJar 

39from .client_exceptions import ( 

40 ClientConnectionError, 

41 ClientConnectionResetError, 

42 ClientConnectorCertificateError, 

43 ClientConnectorDNSError, 

44 ClientConnectorError, 

45 ClientConnectorSSLError, 

46 ClientError, 

47 ClientHttpProxyError, 

48 ClientOSError, 

49 ClientPayloadError, 

50 ClientProxyConnectionError, 

51 ClientResponseError, 

52 ClientSSLError, 

53 ConnectionTimeoutError, 

54 ContentTypeError, 

55 InvalidURL, 

56 InvalidUrlClientError, 

57 InvalidUrlRedirectClientError, 

58 NonHttpUrlClientError, 

59 NonHttpUrlRedirectClientError, 

60 RedirectClientError, 

61 ServerConnectionError, 

62 ServerDisconnectedError, 

63 ServerFingerprintMismatch, 

64 ServerTimeoutError, 

65 SocketTimeoutError, 

66 TooManyRedirects, 

67 WSMessageTypeError, 

68 WSServerHandshakeError, 

69) 

70from .client_middlewares import ClientMiddlewareType, build_client_middlewares 

71from .client_reqrep import ( 

72 ClientRequest as ClientRequest, 

73 ClientResponse as ClientResponse, 

74 Fingerprint as Fingerprint, 

75 RequestInfo as RequestInfo, 

76 _merge_ssl_params, 

77) 

78from .client_ws import ( 

79 DEFAULT_WS_CLIENT_TIMEOUT, 

80 ClientWebSocketResponse as ClientWebSocketResponse, 

81 ClientWSTimeout as ClientWSTimeout, 

82) 

83from .connector import ( 

84 HTTP_AND_EMPTY_SCHEMA_SET, 

85 BaseConnector as BaseConnector, 

86 NamedPipeConnector as NamedPipeConnector, 

87 TCPConnector as TCPConnector, 

88 UnixConnector as UnixConnector, 

89) 

90from .cookiejar import CookieJar 

91from .helpers import ( 

92 _SENTINEL, 

93 DEBUG, 

94 DEFAULT_CHUNK_SIZE, 

95 EMPTY_BODY_METHODS, 

96 BasicAuth, 

97 TimeoutHandle, 

98 basicauth_from_netrc, 

99 get_env_proxy_for_url, 

100 netrc_from_env, 

101 sentinel, 

102 strip_auth_from_url, 

103) 

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

105from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse 

106from .tracing import Trace, TraceConfig 

107from .typedefs import ( 

108 JSONBytesEncoder, 

109 JSONEncoder, 

110 LooseCookies, 

111 LooseHeaders, 

112 Query, 

113 StrOrURL, 

114) 

115 

116__all__ = ( 

117 # client_exceptions 

118 "ClientConnectionError", 

119 "ClientConnectionResetError", 

120 "ClientConnectorCertificateError", 

121 "ClientConnectorDNSError", 

122 "ClientConnectorError", 

123 "ClientConnectorSSLError", 

124 "ClientError", 

125 "ClientHttpProxyError", 

126 "ClientOSError", 

127 "ClientPayloadError", 

128 "ClientProxyConnectionError", 

129 "ClientResponseError", 

130 "ClientSSLError", 

131 "ConnectionTimeoutError", 

132 "ContentTypeError", 

133 "InvalidURL", 

134 "InvalidUrlClientError", 

135 "RedirectClientError", 

136 "NonHttpUrlClientError", 

137 "InvalidUrlRedirectClientError", 

138 "NonHttpUrlRedirectClientError", 

139 "ServerConnectionError", 

140 "ServerDisconnectedError", 

141 "ServerFingerprintMismatch", 

142 "ServerTimeoutError", 

143 "SocketTimeoutError", 

144 "TooManyRedirects", 

145 "WSServerHandshakeError", 

146 # client_reqrep 

147 "ClientRequest", 

148 "ClientResponse", 

149 "Fingerprint", 

150 "RequestInfo", 

151 # connector 

152 "BaseConnector", 

153 "TCPConnector", 

154 "UnixConnector", 

155 "NamedPipeConnector", 

156 # client_ws 

157 "ClientWebSocketResponse", 

158 # client 

159 "ClientSession", 

160 "ClientTimeout", 

161 "ClientWSTimeout", 

162 "request", 

163 "WSMessageTypeError", 

164) 

165 

166 

167if TYPE_CHECKING: 

168 from ssl import SSLContext 

169else: 

170 SSLContext = Any 

171 

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

173 from typing import Unpack 

174 

175 

176class _RequestOptions(TypedDict, total=False): 

177 params: Query 

178 data: Any 

179 json: Any 

180 cookies: LooseCookies | None 

181 headers: LooseHeaders | None 

182 skip_auto_headers: Iterable[str] | None 

183 auth: BasicAuth | None 

184 allow_redirects: bool 

185 max_redirects: int 

186 compress: str | bool | None 

187 chunked: bool | None 

188 expect100: bool 

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

190 read_until_eof: bool 

191 proxy: StrOrURL | None 

192 proxy_auth: BasicAuth | None 

193 timeout: "ClientTimeout | _SENTINEL | None" 

194 ssl: SSLContext | bool | Fingerprint 

195 server_hostname: str | None 

196 proxy_headers: LooseHeaders | None 

197 trace_request_ctx: object 

198 read_bufsize: int | None 

199 auto_decompress: bool | None 

200 max_line_size: int | None 

201 max_field_size: int | None 

202 max_headers: int | None 

203 middlewares: Sequence[ClientMiddlewareType] | None 

204 

205 

206class _WSConnectOptions(TypedDict, total=False): 

207 method: str 

208 protocols: Iterable[str] 

209 timeout: "ClientWSTimeout | _SENTINEL" 

210 receive_timeout: float | None 

211 autoclose: bool 

212 autoping: bool 

213 heartbeat: float | None 

214 auth: BasicAuth | None 

215 origin: str | None 

216 params: Query 

217 headers: LooseHeaders | None 

218 proxy: StrOrURL | None 

219 proxy_auth: BasicAuth | None 

220 ssl: SSLContext | bool | Fingerprint 

221 verify_ssl: bool | None 

222 fingerprint: bytes | None 

223 ssl_context: SSLContext | None 

224 server_hostname: str | None 

225 proxy_headers: LooseHeaders | None 

226 compress: int 

227 max_msg_size: int 

228 

229 

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

231class ClientTimeout: 

232 total: float | None = None 

233 connect: float | None = None 

234 sock_read: float | None = None 

235 sock_connect: float | None = None 

236 ceil_threshold: float = 5 

237 

238 # pool_queue_timeout: Optional[float] = None 

239 # dns_resolution_timeout: Optional[float] = None 

240 # socket_connect_timeout: Optional[float] = None 

241 # connection_acquiring_timeout: Optional[float] = None 

242 # new_connection_timeout: Optional[float] = None 

243 # http_header_timeout: Optional[float] = None 

244 # response_body_timeout: Optional[float] = None 

245 

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

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

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

249 # to overwrite the defaults 

250 

251 

252# 5 Minute default read timeout 

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

254 

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

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

257 

258_RetType_co = TypeVar( 

259 "_RetType_co", 

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

261 covariant=True, 

262) 

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

264 

265 

266class ClientSession: 

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

268 

269 ATTRS = frozenset( 

270 [ 

271 "_base_url", 

272 "_base_url_origin", 

273 "_source_traceback", 

274 "_connector", 

275 "_loop", 

276 "_cookie_jar", 

277 "_connector_owner", 

278 "_default_auth", 

279 "_version", 

280 "_json_serialize", 

281 "_json_serialize_bytes", 

282 "_requote_redirect_url", 

283 "_timeout", 

284 "_raise_for_status", 

285 "_auto_decompress", 

286 "_trust_env", 

287 "_default_headers", 

288 "_skip_auto_headers", 

289 "_request_class", 

290 "_response_class", 

291 "_ws_response_class", 

292 "_trace_configs", 

293 "_read_bufsize", 

294 "_max_line_size", 

295 "_max_field_size", 

296 "_max_headers", 

297 "_resolve_charset", 

298 "_default_proxy", 

299 "_default_proxy_auth", 

300 "_retry_connection", 

301 "_middlewares", 

302 "requote_redirect_url", 

303 ] 

304 ) 

305 

306 _source_traceback: traceback.StackSummary | None = None 

307 _connector: BaseConnector | None = None 

308 

309 def __init__( 

310 self, 

311 base_url: StrOrURL | None = None, 

312 *, 

313 connector: BaseConnector | None = None, 

314 loop: asyncio.AbstractEventLoop | None = None, 

315 cookies: LooseCookies | None = None, 

316 headers: LooseHeaders | None = None, 

317 proxy: StrOrURL | None = None, 

318 proxy_auth: BasicAuth | None = None, 

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

320 auth: BasicAuth | None = None, 

321 json_serialize: JSONEncoder = json.dumps, 

322 json_serialize_bytes: JSONBytesEncoder | None = None, 

323 request_class: type[ClientRequest] = ClientRequest, 

324 response_class: type[ClientResponse] = ClientResponse, 

325 ws_response_class: type[ClientWebSocketResponse] = ClientWebSocketResponse, 

326 version: HttpVersion = http.HttpVersion11, 

327 cookie_jar: AbstractCookieJar | None = None, 

328 connector_owner: bool = True, 

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

330 read_timeout: float | _SENTINEL = sentinel, 

331 conn_timeout: float | None = None, 

332 timeout: object | ClientTimeout = sentinel, 

333 auto_decompress: bool = True, 

334 trust_env: bool = False, 

335 requote_redirect_url: bool = True, 

336 trace_configs: list[TraceConfig] | None = None, 

337 read_bufsize: int = DEFAULT_CHUNK_SIZE, 

338 max_line_size: int = 8190, 

339 max_field_size: int = 8190, 

340 max_headers: int = 128, 

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

342 middlewares: Sequence[ClientMiddlewareType] = (), 

343 ssl_shutdown_timeout: _SENTINEL | None | float = sentinel, 

344 ) -> None: 

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

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

347 self._connector: BaseConnector | None = None 

348 

349 if loop is None: 

350 if connector is not None: 

351 loop = connector._loop 

352 

353 loop = loop or asyncio.get_running_loop() 

354 

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

356 self._base_url: URL | None = base_url 

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

358 else: 

359 self._base_url = URL(base_url) 

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

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

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

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

364 

365 if timeout is sentinel or timeout is None: 

366 self._timeout = DEFAULT_TIMEOUT 

367 if read_timeout is not sentinel: 

368 warnings.warn( 

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

370 DeprecationWarning, 

371 stacklevel=2, 

372 ) 

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

374 if conn_timeout is not None: 

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

376 warnings.warn( 

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

378 DeprecationWarning, 

379 stacklevel=2, 

380 ) 

381 else: 

382 if not isinstance(timeout, ClientTimeout): 

383 raise ValueError( 

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

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

386 ) 

387 self._timeout = timeout 

388 if read_timeout is not sentinel: 

389 raise ValueError( 

390 "read_timeout and timeout parameters " 

391 "conflict, please setup " 

392 "timeout.read" 

393 ) 

394 if conn_timeout is not None: 

395 raise ValueError( 

396 "conn_timeout and timeout parameters " 

397 "conflict, please setup " 

398 "timeout.connect" 

399 ) 

400 

401 if ssl_shutdown_timeout is not sentinel: 

402 warnings.warn( 

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

404 DeprecationWarning, 

405 stacklevel=2, 

406 ) 

407 

408 if connector is None: 

409 connector = TCPConnector( 

410 loop=loop, ssl_shutdown_timeout=ssl_shutdown_timeout 

411 ) 

412 

413 if connector._loop is not loop: 

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

415 

416 self._loop = loop 

417 

418 if loop.get_debug(): 

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

420 

421 if cookie_jar is None: 

422 cookie_jar = CookieJar(loop=loop) 

423 self._cookie_jar = cookie_jar 

424 

425 if cookies: 

426 self._cookie_jar.update_cookies(cookies) 

427 

428 if auth is not None: 

429 warnings.warn( 

430 "The 'auth' parameter is deprecated and will be removed in v4;" 

431 " pass headers={'Authorization': " 

432 "aiohttp.encode_basic_auth(login, password)} instead", 

433 DeprecationWarning, 

434 stacklevel=2, 

435 ) 

436 if proxy_auth is not None: 

437 warnings.warn( 

438 "The 'proxy_auth' parameter is deprecated and will be removed in v4;" 

439 " pass proxy_headers={'Proxy-Authorization': " 

440 "aiohttp.encode_basic_auth(login, password)} instead", 

441 DeprecationWarning, 

442 stacklevel=2, 

443 ) 

444 self._connector = connector 

445 self._connector_owner = connector_owner 

446 self._default_auth = auth 

447 self._version = version 

448 self._json_serialize = json_serialize 

449 self._json_serialize_bytes = json_serialize_bytes 

450 self._raise_for_status = raise_for_status 

451 self._auto_decompress = auto_decompress 

452 self._trust_env = trust_env 

453 self._requote_redirect_url = requote_redirect_url 

454 self._read_bufsize = read_bufsize 

455 self._max_line_size = max_line_size 

456 self._max_field_size = max_field_size 

457 self._max_headers = max_headers 

458 

459 # Convert to list of tuples 

460 if headers: 

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

462 else: 

463 real_headers = CIMultiDict() 

464 self._default_headers: CIMultiDict[str] = real_headers 

465 if skip_auto_headers is not None: 

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

467 else: 

468 self._skip_auto_headers = frozenset() 

469 

470 self._request_class = request_class 

471 self._response_class = response_class 

472 self._ws_response_class = ws_response_class 

473 

474 self._trace_configs = trace_configs or [] 

475 for trace_config in self._trace_configs: 

476 trace_config.freeze() 

477 

478 self._resolve_charset = fallback_charset_resolver 

479 

480 self._default_proxy = proxy 

481 self._default_proxy_auth = proxy_auth 

482 self._retry_connection: bool = True 

483 self._middlewares = middlewares 

484 

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

486 warnings.warn( 

487 f"Inheritance class {cls.__name__} from ClientSession is discouraged", 

488 DeprecationWarning, 

489 stacklevel=2, 

490 ) 

491 

492 if DEBUG: 

493 

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

495 if name not in self.ATTRS: 

496 warnings.warn( 

497 f"Setting custom ClientSession.{name} attribute is discouraged", 

498 DeprecationWarning, 

499 stacklevel=2, 

500 ) 

501 super().__setattr__(name, val) 

502 

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

504 if not self.closed: 

505 kwargs = {"source": self} 

506 _warnings.warn( 

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

508 ) 

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

510 if self._source_traceback is not None: 

511 context["source_traceback"] = self._source_traceback 

512 self._loop.call_exception_handler(context) 

513 

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

515 

516 def request( 

517 self, 

518 method: str, 

519 url: StrOrURL, 

520 **kwargs: Unpack[_RequestOptions], 

521 ) -> "_RequestContextManager": ... 

522 

523 else: 

524 

525 def request( 

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

527 ) -> "_RequestContextManager": 

528 """Perform HTTP request.""" 

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

530 

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

532 url = URL(str_or_url) 

533 if self._base_url and not url.absolute: 

534 return self._base_url.join(url) 

535 return url 

536 

537 async def _request( 

538 self, 

539 method: str, 

540 str_or_url: StrOrURL, 

541 *, 

542 params: Query = None, 

543 data: Any = None, 

544 json: Any = None, 

545 cookies: LooseCookies | None = None, 

546 headers: LooseHeaders | None = None, 

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

548 auth: BasicAuth | None = None, 

549 allow_redirects: bool = True, 

550 max_redirects: int = 10, 

551 compress: str | bool | None = None, 

552 chunked: bool | None = None, 

553 expect100: bool = False, 

554 raise_for_status: ( 

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

556 ) = None, 

557 read_until_eof: bool = True, 

558 proxy: StrOrURL | None = None, 

559 proxy_auth: BasicAuth | None = None, 

560 timeout: ClientTimeout | _SENTINEL = sentinel, 

561 verify_ssl: bool | None = None, 

562 fingerprint: bytes | None = None, 

563 ssl_context: SSLContext | None = None, 

564 ssl: SSLContext | bool | Fingerprint = True, 

565 server_hostname: str | None = None, 

566 proxy_headers: LooseHeaders | None = None, 

567 trace_request_ctx: object = None, 

568 read_bufsize: int | None = None, 

569 auto_decompress: bool | None = None, 

570 max_line_size: int | None = None, 

571 max_field_size: int | None = None, 

572 max_headers: int | None = None, 

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

574 ) -> ClientResponse: 

575 

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

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

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

579 

580 if self.closed: 

581 raise RuntimeError("Session is closed") 

582 

583 method = method.upper() 

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

585 

586 if auth is not None: 

587 warnings.warn( 

588 "The 'auth' parameter is deprecated and will be removed in v4;" 

589 " pass headers={'Authorization': " 

590 "aiohttp.encode_basic_auth(login, password)} instead", 

591 DeprecationWarning, 

592 stacklevel=3, 

593 ) 

594 if proxy_auth is not None: 

595 warnings.warn( 

596 "The 'proxy_auth' parameter is deprecated and will be removed in v4;" 

597 " pass proxy_headers={'Proxy-Authorization': " 

598 "aiohttp.encode_basic_auth(login, password)} instead", 

599 DeprecationWarning, 

600 stacklevel=3, 

601 ) 

602 

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

604 raise ValueError( 

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

606 ) 

607 elif json is not None: 

608 if self._json_serialize_bytes is not None: 

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

610 else: 

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

612 

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

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

615 

616 redirects = 0 

617 history: list[ClientResponse] = [] 

618 version = self._version 

619 params = params or {} 

620 

621 # Merge with default headers and transform to CIMultiDict 

622 headers = self._prepare_headers(headers) 

623 

624 try: 

625 url = self._build_url(str_or_url) 

626 except ValueError as e: 

627 raise InvalidUrlClientError(str_or_url) from e 

628 

629 assert self._connector is not None 

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

631 raise NonHttpUrlClientError(url) 

632 

633 skip_headers: Iterable[istr] | None 

634 if skip_auto_headers is not None: 

635 skip_headers = { 

636 istr(i) for i in skip_auto_headers 

637 } | self._skip_auto_headers 

638 elif self._skip_auto_headers: 

639 skip_headers = self._skip_auto_headers 

640 else: 

641 skip_headers = None 

642 

643 if proxy is None: 

644 proxy = self._default_proxy 

645 if proxy_auth is None: 

646 proxy_auth = self._default_proxy_auth 

647 

648 if proxy is None: 

649 proxy_headers = None 

650 else: 

651 proxy_headers = self._prepare_headers(proxy_headers) 

652 try: 

653 proxy = URL(proxy) 

654 except ValueError as e: 

655 raise InvalidURL(proxy) from e 

656 

657 if timeout is sentinel: 

658 real_timeout: ClientTimeout = self._timeout 

659 else: 

660 if not isinstance(timeout, ClientTimeout): 

661 real_timeout = ClientTimeout(total=timeout) 

662 else: 

663 real_timeout = timeout 

664 # timeout is cumulative for all request operations 

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

666 tm = TimeoutHandle( 

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

668 ) 

669 handle = tm.start() 

670 

671 if read_bufsize is None: 

672 read_bufsize = self._read_bufsize 

673 

674 if auto_decompress is None: 

675 auto_decompress = self._auto_decompress 

676 

677 if max_line_size is None: 

678 max_line_size = self._max_line_size 

679 

680 if max_field_size is None: 

681 max_field_size = self._max_field_size 

682 

683 if max_headers is None: 

684 max_headers = self._max_headers 

685 

686 traces = [ 

687 Trace( 

688 self, 

689 trace_config, 

690 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), 

691 ) 

692 for trace_config in self._trace_configs 

693 ] 

694 

695 for trace in traces: 

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

697 

698 timer = tm.timer() 

699 req: ClientRequest | None = None 

700 try: 

701 with timer: 

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

703 retry_persistent_connection = ( 

704 self._retry_connection and method in IDEMPOTENT_METHODS 

705 ) 

706 while True: 

707 url, auth_from_url = strip_auth_from_url(url) 

708 if not url.raw_host: 

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

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

711 err_exc_cls = ( 

712 InvalidUrlRedirectClientError 

713 if redirects 

714 else InvalidUrlClientError 

715 ) 

716 raise err_exc_cls(url) 

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

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

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

720 if not history and (auth and auth_from_url): 

721 raise ValueError( 

722 "Cannot combine AUTH argument with " 

723 "credentials encoded in URL" 

724 ) 

725 

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

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

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

729 auth = auth_from_url 

730 

731 if ( 

732 auth is None 

733 and self._default_auth 

734 and ( 

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

736 ) 

737 ): 

738 auth = self._default_auth 

739 

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

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

742 auth = await self._loop.run_in_executor( 

743 None, self._get_netrc_auth, url.host 

744 ) 

745 

746 # It would be confusing if we support explicit 

747 # Authorization header with auth argument 

748 if ( 

749 headers is not None 

750 and auth is not None 

751 and hdrs.AUTHORIZATION in headers 

752 ): 

753 raise ValueError( 

754 "Cannot combine AUTHORIZATION header " 

755 "with AUTH argument or credentials " 

756 "encoded in URL" 

757 ) 

758 

759 all_cookies = self._cookie_jar.filter_cookies(url) 

760 

761 if cookies is not None: 

762 tmp_cookie_jar = CookieJar( 

763 unsafe=self._cookie_jar.unsafe, 

764 quote_cookie=self._cookie_jar.quote_cookie, 

765 ) 

766 tmp_cookie_jar.update_cookies(cookies) 

767 req_cookies = tmp_cookie_jar.filter_cookies(url) 

768 if req_cookies: 

769 all_cookies.load(req_cookies) 

770 

771 proxy_: URL | None = None 

772 if proxy is not None: 

773 proxy_ = URL(proxy) 

774 elif self._trust_env: 

775 with suppress(LookupError): 

776 proxy_, proxy_auth = await asyncio.to_thread( 

777 get_env_proxy_for_url, url 

778 ) 

779 

780 req = self._request_class( 

781 method, 

782 url, 

783 params=params, 

784 headers=headers, 

785 skip_auto_headers=skip_headers, 

786 data=data, 

787 cookies=all_cookies, 

788 auth=auth, 

789 version=version, 

790 compress=compress, 

791 chunked=chunked, 

792 expect100=expect100, 

793 loop=self._loop, 

794 response_class=self._response_class, 

795 proxy=proxy_, 

796 proxy_auth=proxy_auth, 

797 timer=timer, 

798 session=self, 

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

800 server_hostname=server_hostname, 

801 proxy_headers=proxy_headers, 

802 traces=traces, 

803 trust_env=self.trust_env, 

804 ) 

805 

806 async def _connect_and_send_request( 

807 req: ClientRequest, 

808 ) -> ClientResponse: 

809 # connection timeout 

810 assert self._connector is not None 

811 try: 

812 conn = await self._connector.connect( 

813 req, traces=traces, timeout=real_timeout 

814 ) 

815 except asyncio.TimeoutError as exc: 

816 raise ConnectionTimeoutError( 

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

818 ) from exc 

819 

820 assert conn.protocol is not None 

821 conn.protocol.set_response_params( 

822 timer=timer, 

823 skip_payload=req.method in EMPTY_BODY_METHODS, 

824 read_until_eof=read_until_eof, 

825 auto_decompress=auto_decompress, 

826 read_timeout=real_timeout.sock_read, 

827 read_bufsize=read_bufsize, 

828 timeout_ceil_threshold=self._connector._timeout_ceil_threshold, 

829 max_line_size=max_line_size, 

830 max_field_size=max_field_size, 

831 max_headers=max_headers, 

832 ) 

833 try: 

834 resp = await req.send(conn) 

835 try: 

836 await resp.start(conn) 

837 except BaseException: 

838 resp.close() 

839 raise 

840 except BaseException: 

841 conn.close() 

842 raise 

843 return resp 

844 

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

846 effective_middlewares = ( 

847 self._middlewares if middlewares is None else middlewares 

848 ) 

849 

850 if effective_middlewares: 

851 handler = build_client_middlewares( 

852 _connect_and_send_request, effective_middlewares 

853 ) 

854 else: 

855 handler = _connect_and_send_request 

856 

857 try: 

858 resp = await handler(req) 

859 # Client connector errors should not be retried 

860 except ( 

861 ConnectionTimeoutError, 

862 ClientConnectorError, 

863 ClientConnectorCertificateError, 

864 ClientConnectorSSLError, 

865 ): 

866 raise 

867 except (ClientOSError, ServerDisconnectedError): 

868 if retry_persistent_connection: 

869 retry_persistent_connection = False 

870 continue 

871 raise 

872 except ClientError: 

873 raise 

874 except OSError as exc: 

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

876 raise 

877 raise ClientOSError(*exc.args) from exc 

878 

879 # Update cookies from raw headers to preserve duplicates 

880 if resp._raw_cookie_headers: 

881 self._cookie_jar.update_cookies_from_headers( 

882 resp._raw_cookie_headers, resp.url 

883 ) 

884 

885 # redirects 

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

887 

888 for trace in traces: 

889 await trace.send_request_redirect( 

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

891 ) 

892 

893 redirects += 1 

894 history.append(resp) 

895 if max_redirects and redirects >= max_redirects: 

896 if req._body is not None: 

897 await req._body.close() 

898 resp.close() 

899 raise TooManyRedirects( 

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

901 ) 

902 

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

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

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

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

907 ): 

908 method = hdrs.METH_GET 

909 data = None 

910 if headers.get(hdrs.CONTENT_LENGTH): 

911 headers.pop(hdrs.CONTENT_LENGTH) 

912 else: 

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

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

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

916 # Use the existing payload to avoid recreating it from 

917 # a potentially consumed file. 

918 # 

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

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

921 if req._body is not None and req._body.consumed: 

922 resp.close() 

923 raise ClientPayloadError( 

924 "Cannot follow redirect with a consumed request " 

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

926 "or set allow_redirects=False." 

927 ) 

928 data = req._body 

929 

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

931 hdrs.URI 

932 ) 

933 if r_url is None: 

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

935 break 

936 else: 

937 # reading from correct redirection 

938 # response is forbidden 

939 resp.release() 

940 

941 try: 

942 parsed_redirect_url = URL( 

943 r_url, encoded=not self._requote_redirect_url 

944 ) 

945 except ValueError as e: 

946 if req._body is not None: 

947 await req._body.close() 

948 resp.close() 

949 raise InvalidUrlRedirectClientError( 

950 r_url, 

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

952 ) from e 

953 

954 scheme = parsed_redirect_url.scheme 

955 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: 

956 if req._body is not None: 

957 await req._body.close() 

958 resp.close() 

959 raise NonHttpUrlRedirectClientError(r_url) 

960 elif not scheme: 

961 parsed_redirect_url = url.join(parsed_redirect_url) 

962 

963 try: 

964 redirect_origin = parsed_redirect_url.origin() 

965 except ValueError as origin_val_err: 

966 if req._body is not None: 

967 await req._body.close() 

968 resp.close() 

969 raise InvalidUrlRedirectClientError( 

970 parsed_redirect_url, 

971 "Invalid redirect URL origin", 

972 ) from origin_val_err 

973 

974 if url.origin() != redirect_origin: 

975 auth = None 

976 cookies = None 

977 headers.popall(hdrs.AUTHORIZATION, None) 

978 headers.popall(hdrs.COOKIE, None) 

979 headers.popall(hdrs.PROXY_AUTHORIZATION, None) 

980 

981 url = parsed_redirect_url 

982 params = {} 

983 resp.release() 

984 continue 

985 

986 break 

987 

988 if req._body is not None: 

989 await req._body.close() 

990 # check response status 

991 if raise_for_status is None: 

992 raise_for_status = self._raise_for_status 

993 

994 if raise_for_status is None: 

995 pass 

996 elif callable(raise_for_status): 

997 await raise_for_status(resp) 

998 elif raise_for_status: 

999 resp.raise_for_status() 

1000 

1001 # register connection 

1002 if handle is not None: 

1003 if resp.connection is not None: 

1004 resp.connection.add_callback(handle.cancel) 

1005 else: 

1006 handle.cancel() 

1007 

1008 resp._history = tuple(history) 

1009 

1010 for trace in traces: 

1011 await trace.send_request_end( 

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

1013 ) 

1014 return resp 

1015 

1016 except BaseException as e: 

1017 # cleanup timer 

1018 tm.close() 

1019 if handle: 

1020 handle.cancel() 

1021 handle = None 

1022 

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

1024 await req._body.close() 

1025 

1026 for trace in traces: 

1027 await trace.send_request_exception( 

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

1029 ) 

1030 raise 

1031 

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

1033 

1034 @overload 

1035 def ws_connect( 

1036 self, 

1037 url: StrOrURL, 

1038 *, 

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

1040 **kwargs: Unpack[_WSConnectOptions], 

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

1042 

1043 @overload 

1044 def ws_connect( 

1045 self, 

1046 url: StrOrURL, 

1047 *, 

1048 decode_text: Literal[False], 

1049 **kwargs: Unpack[_WSConnectOptions], 

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

1051 

1052 @overload 

1053 def ws_connect( 

1054 self, 

1055 url: StrOrURL, 

1056 *, 

1057 decode_text: bool = ..., 

1058 **kwargs: Unpack[_WSConnectOptions], 

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

1060 

1061 def ws_connect( 

1062 self, 

1063 url: StrOrURL, 

1064 *, 

1065 method: str = hdrs.METH_GET, 

1066 protocols: Iterable[str] = (), 

1067 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

1068 receive_timeout: float | None = None, 

1069 autoclose: bool = True, 

1070 autoping: bool = True, 

1071 heartbeat: float | None = None, 

1072 auth: BasicAuth | None = None, 

1073 origin: str | None = None, 

1074 params: Query = None, 

1075 headers: LooseHeaders | None = None, 

1076 proxy: StrOrURL | None = None, 

1077 proxy_auth: BasicAuth | None = None, 

1078 ssl: SSLContext | bool | Fingerprint = True, 

1079 verify_ssl: bool | None = None, 

1080 fingerprint: bytes | None = None, 

1081 ssl_context: SSLContext | None = None, 

1082 server_hostname: str | None = None, 

1083 proxy_headers: LooseHeaders | None = None, 

1084 compress: int = 0, 

1085 max_msg_size: int = 4 * 1024 * 1024, 

1086 decode_text: bool = True, 

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

1088 """Initiate websocket connection.""" 

1089 return _WSRequestContextManager( 

1090 self._ws_connect( 

1091 url, 

1092 method=method, 

1093 protocols=protocols, 

1094 timeout=timeout, 

1095 receive_timeout=receive_timeout, 

1096 autoclose=autoclose, 

1097 autoping=autoping, 

1098 heartbeat=heartbeat, 

1099 auth=auth, 

1100 origin=origin, 

1101 params=params, 

1102 headers=headers, 

1103 proxy=proxy, 

1104 proxy_auth=proxy_auth, 

1105 ssl=ssl, 

1106 verify_ssl=verify_ssl, 

1107 fingerprint=fingerprint, 

1108 ssl_context=ssl_context, 

1109 server_hostname=server_hostname, 

1110 proxy_headers=proxy_headers, 

1111 compress=compress, 

1112 max_msg_size=max_msg_size, 

1113 decode_text=decode_text, 

1114 ) 

1115 ) 

1116 

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

1118 

1119 @overload 

1120 async def _ws_connect( 

1121 self, 

1122 url: StrOrURL, 

1123 *, 

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

1125 **kwargs: Unpack[_WSConnectOptions], 

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

1127 

1128 @overload 

1129 async def _ws_connect( 

1130 self, 

1131 url: StrOrURL, 

1132 *, 

1133 decode_text: Literal[False], 

1134 **kwargs: Unpack[_WSConnectOptions], 

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

1136 

1137 @overload 

1138 async def _ws_connect( 

1139 self, 

1140 url: StrOrURL, 

1141 *, 

1142 decode_text: bool = ..., 

1143 **kwargs: Unpack[_WSConnectOptions], 

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

1145 

1146 async def _ws_connect( 

1147 self, 

1148 url: StrOrURL, 

1149 *, 

1150 method: str = hdrs.METH_GET, 

1151 protocols: Iterable[str] = (), 

1152 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

1153 receive_timeout: float | None = None, 

1154 autoclose: bool = True, 

1155 autoping: bool = True, 

1156 heartbeat: float | None = None, 

1157 auth: BasicAuth | None = None, 

1158 origin: str | None = None, 

1159 params: Query = None, 

1160 headers: LooseHeaders | None = None, 

1161 proxy: StrOrURL | None = None, 

1162 proxy_auth: BasicAuth | None = None, 

1163 ssl: SSLContext | bool | Fingerprint = True, 

1164 verify_ssl: bool | None = None, 

1165 fingerprint: bytes | None = None, 

1166 ssl_context: SSLContext | None = None, 

1167 server_hostname: str | None = None, 

1168 proxy_headers: LooseHeaders | None = None, 

1169 compress: int = 0, 

1170 max_msg_size: int = 4 * 1024 * 1024, 

1171 decode_text: bool = True, 

1172 ) -> "ClientWebSocketResponse[bool]": 

1173 if auth is not None: 

1174 warnings.warn( 

1175 "The 'auth' parameter is deprecated and will be removed in v4;" 

1176 " pass headers={'Authorization': " 

1177 "aiohttp.encode_basic_auth(login, password)} instead", 

1178 DeprecationWarning, 

1179 stacklevel=3, 

1180 ) 

1181 if proxy_auth is not None: 

1182 warnings.warn( 

1183 "The 'proxy_auth' parameter is deprecated and will be removed in v4;" 

1184 " pass proxy_headers={'Proxy-Authorization': " 

1185 "aiohttp.encode_basic_auth(login, password)} instead", 

1186 DeprecationWarning, 

1187 stacklevel=3, 

1188 ) 

1189 if timeout is not sentinel: 

1190 if isinstance(timeout, ClientWSTimeout): 

1191 ws_timeout = timeout 

1192 else: 

1193 warnings.warn( 

1194 "parameter 'timeout' of type 'float' " 

1195 "is deprecated, please use " 

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

1197 DeprecationWarning, 

1198 stacklevel=2, 

1199 ) 

1200 ws_timeout = ClientWSTimeout(ws_close=timeout) 

1201 else: 

1202 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT 

1203 if receive_timeout is not None: 

1204 warnings.warn( 

1205 "float parameter 'receive_timeout' " 

1206 "is deprecated, please use parameter " 

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

1208 DeprecationWarning, 

1209 stacklevel=2, 

1210 ) 

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

1212 

1213 if headers is None: 

1214 real_headers: CIMultiDict[str] = CIMultiDict() 

1215 else: 

1216 real_headers = CIMultiDict(headers) 

1217 

1218 default_headers = { 

1219 hdrs.UPGRADE: "websocket", 

1220 hdrs.CONNECTION: "Upgrade", 

1221 hdrs.SEC_WEBSOCKET_VERSION: "13", 

1222 } 

1223 

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

1225 real_headers.setdefault(key, value) 

1226 

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

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

1229 

1230 if protocols: 

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

1232 if origin is not None: 

1233 real_headers[hdrs.ORIGIN] = origin 

1234 if compress: 

1235 extstr = ws_ext_gen(compress=compress) 

1236 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

1237 

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

1239 if ssl is None: 

1240 warnings.warn( 

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

1242 DeprecationWarning, 

1243 stacklevel=2, 

1244 ) 

1245 ssl = True 

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

1247 

1248 # send request 

1249 resp = await self.request( 

1250 method, 

1251 url, 

1252 params=params, 

1253 headers=real_headers, 

1254 read_until_eof=False, 

1255 auth=auth, 

1256 proxy=proxy, 

1257 proxy_auth=proxy_auth, 

1258 ssl=ssl, 

1259 server_hostname=server_hostname, 

1260 proxy_headers=proxy_headers, 

1261 ) 

1262 

1263 try: 

1264 # check handshake 

1265 if resp.status != 101: 

1266 raise WSServerHandshakeError( 

1267 resp.request_info, 

1268 resp.history, 

1269 message="Invalid response status", 

1270 status=resp.status, 

1271 headers=resp.headers, 

1272 ) 

1273 

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

1275 raise WSServerHandshakeError( 

1276 resp.request_info, 

1277 resp.history, 

1278 message="Invalid upgrade header", 

1279 status=resp.status, 

1280 headers=resp.headers, 

1281 ) 

1282 

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

1284 raise WSServerHandshakeError( 

1285 resp.request_info, 

1286 resp.history, 

1287 message="Invalid connection header", 

1288 status=resp.status, 

1289 headers=resp.headers, 

1290 ) 

1291 

1292 # key calculation 

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

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

1295 if r_key != match: 

1296 raise WSServerHandshakeError( 

1297 resp.request_info, 

1298 resp.history, 

1299 message="Invalid challenge response", 

1300 status=resp.status, 

1301 headers=resp.headers, 

1302 ) 

1303 

1304 # websocket protocol 

1305 protocol = None 

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

1307 resp_protocols = [ 

1308 proto.strip() 

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

1310 ] 

1311 

1312 for proto in resp_protocols: 

1313 if proto in protocols: 

1314 protocol = proto 

1315 break 

1316 

1317 # websocket compress 

1318 notakeover = False 

1319 if compress: 

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

1321 if compress_hdrs: 

1322 try: 

1323 compress, notakeover = ws_ext_parse(compress_hdrs) 

1324 except WSHandshakeError as exc: 

1325 raise WSServerHandshakeError( 

1326 resp.request_info, 

1327 resp.history, 

1328 message=exc.args[0], 

1329 status=resp.status, 

1330 headers=resp.headers, 

1331 ) from exc 

1332 else: 

1333 compress = 0 

1334 notakeover = False 

1335 

1336 conn = resp.connection 

1337 assert conn is not None 

1338 conn_proto = conn.protocol 

1339 assert conn_proto is not None 

1340 

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

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

1343 if ws_timeout.ws_receive is None: 

1344 # Reset regardless 

1345 conn_proto.read_timeout = None 

1346 elif conn_proto.read_timeout is not None: 

1347 conn_proto.read_timeout = max( 

1348 ws_timeout.ws_receive, conn_proto.read_timeout 

1349 ) 

1350 

1351 transport = conn.transport 

1352 assert transport is not None 

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

1354 writer = WebSocketWriter( 

1355 conn_proto, 

1356 transport, 

1357 use_mask=True, 

1358 compress=compress, 

1359 notakeover=notakeover, 

1360 ) 

1361 except BaseException: 

1362 resp.close() 

1363 raise 

1364 else: 

1365 ws_resp = self._ws_response_class( 

1366 reader, 

1367 writer, 

1368 protocol, 

1369 resp, 

1370 ws_timeout, 

1371 autoclose, 

1372 autoping, 

1373 self._loop, 

1374 heartbeat=heartbeat, 

1375 compress=compress, 

1376 client_notakeover=notakeover, 

1377 ) 

1378 parser = WebSocketReader( 

1379 reader, 

1380 max_msg_size, 

1381 compress=bool(compress), 

1382 decode_text=decode_text, 

1383 ) 

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

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

1386 return ws_resp 

1387 

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

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

1390 # Convert headers to MultiDict 

1391 result = CIMultiDict(self._default_headers) 

1392 if headers: 

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

1394 headers = CIMultiDict(headers) 

1395 added_names: set[str] = set() 

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

1397 if key in added_names: 

1398 result.add(key, value) 

1399 else: 

1400 result[key] = value 

1401 added_names.add(key) 

1402 return result 

1403 

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

1405 """ 

1406 Get auth from netrc for the given host. 

1407 

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

1409 blocking I/O in the event loop. 

1410 """ 

1411 netrc_obj = netrc_from_env() 

1412 try: 

1413 return basicauth_from_netrc(netrc_obj, host) 

1414 except LookupError: 

1415 return None 

1416 

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

1418 

1419 def get( 

1420 self, 

1421 url: StrOrURL, 

1422 **kwargs: Unpack[_RequestOptions], 

1423 ) -> "_RequestContextManager": ... 

1424 

1425 def options( 

1426 self, 

1427 url: StrOrURL, 

1428 **kwargs: Unpack[_RequestOptions], 

1429 ) -> "_RequestContextManager": ... 

1430 

1431 def head( 

1432 self, 

1433 url: StrOrURL, 

1434 **kwargs: Unpack[_RequestOptions], 

1435 ) -> "_RequestContextManager": ... 

1436 

1437 def post( 

1438 self, 

1439 url: StrOrURL, 

1440 **kwargs: Unpack[_RequestOptions], 

1441 ) -> "_RequestContextManager": ... 

1442 

1443 def put( 

1444 self, 

1445 url: StrOrURL, 

1446 **kwargs: Unpack[_RequestOptions], 

1447 ) -> "_RequestContextManager": ... 

1448 

1449 def patch( 

1450 self, 

1451 url: StrOrURL, 

1452 **kwargs: Unpack[_RequestOptions], 

1453 ) -> "_RequestContextManager": ... 

1454 

1455 def delete( 

1456 self, 

1457 url: StrOrURL, 

1458 **kwargs: Unpack[_RequestOptions], 

1459 ) -> "_RequestContextManager": ... 

1460 

1461 else: 

1462 

1463 def get( 

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

1465 ) -> "_RequestContextManager": 

1466 """Perform HTTP GET request.""" 

1467 return _RequestContextManager( 

1468 self._request( 

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

1470 ) 

1471 ) 

1472 

1473 def options( 

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

1475 ) -> "_RequestContextManager": 

1476 """Perform HTTP OPTIONS request.""" 

1477 return _RequestContextManager( 

1478 self._request( 

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

1480 ) 

1481 ) 

1482 

1483 def head( 

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

1485 ) -> "_RequestContextManager": 

1486 """Perform HTTP HEAD request.""" 

1487 return _RequestContextManager( 

1488 self._request( 

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

1490 ) 

1491 ) 

1492 

1493 def post( 

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

1495 ) -> "_RequestContextManager": 

1496 """Perform HTTP POST request.""" 

1497 return _RequestContextManager( 

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

1499 ) 

1500 

1501 def put( 

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

1503 ) -> "_RequestContextManager": 

1504 """Perform HTTP PUT request.""" 

1505 return _RequestContextManager( 

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

1507 ) 

1508 

1509 def patch( 

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

1511 ) -> "_RequestContextManager": 

1512 """Perform HTTP PATCH request.""" 

1513 return _RequestContextManager( 

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

1515 ) 

1516 

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

1518 """Perform HTTP DELETE request.""" 

1519 return _RequestContextManager( 

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

1521 ) 

1522 

1523 async def close(self) -> None: 

1524 """Close underlying connector. 

1525 

1526 Release all acquired resources. 

1527 """ 

1528 if not self.closed: 

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

1530 await self._connector.close() 

1531 self._connector = None 

1532 

1533 @property 

1534 def closed(self) -> bool: 

1535 """Is client session closed. 

1536 

1537 A readonly property. 

1538 """ 

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

1540 

1541 @property 

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

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

1544 return self._connector 

1545 

1546 @property 

1547 def cookie_jar(self) -> AbstractCookieJar: 

1548 """The session cookies.""" 

1549 return self._cookie_jar 

1550 

1551 @property 

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

1553 """The session HTTP protocol version.""" 

1554 return self._version 

1555 

1556 @property 

1557 def requote_redirect_url(self) -> bool: 

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

1559 return self._requote_redirect_url 

1560 

1561 @requote_redirect_url.setter 

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

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

1564 warnings.warn( 

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

1566 DeprecationWarning, 

1567 stacklevel=2, 

1568 ) 

1569 self._requote_redirect_url = val 

1570 

1571 @property 

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

1573 """Session's loop.""" 

1574 warnings.warn( 

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

1576 ) 

1577 return self._loop 

1578 

1579 @property 

1580 def timeout(self) -> ClientTimeout: 

1581 """Timeout for the session.""" 

1582 return self._timeout 

1583 

1584 @property 

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

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

1587 return self._default_headers 

1588 

1589 @property 

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

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

1592 return self._skip_auto_headers 

1593 

1594 @property 

1595 def auth(self) -> BasicAuth | None: 

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

1597 return self._default_auth 

1598 

1599 @property 

1600 def json_serialize(self) -> JSONEncoder: 

1601 """Json serializer callable""" 

1602 return self._json_serialize 

1603 

1604 @property 

1605 def connector_owner(self) -> bool: 

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

1607 return self._connector_owner 

1608 

1609 @property 

1610 def raise_for_status( 

1611 self, 

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

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

1614 return self._raise_for_status 

1615 

1616 @property 

1617 def auto_decompress(self) -> bool: 

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

1619 return self._auto_decompress 

1620 

1621 @property 

1622 def trust_env(self) -> bool: 

1623 """ 

1624 Should proxies information from environment or netrc be trusted. 

1625 

1626 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1627 or ~/.netrc file if present. 

1628 """ 

1629 return self._trust_env 

1630 

1631 @property 

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

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

1634 return self._trace_configs 

1635 

1636 def detach(self) -> None: 

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

1638 

1639 Session is switched to closed state anyway. 

1640 """ 

1641 self._connector = None 

1642 

1643 def __enter__(self) -> None: 

1644 raise TypeError("Use async with instead") 

1645 

1646 def __exit__( 

1647 self, 

1648 exc_type: type[BaseException] | None, 

1649 exc_val: BaseException | None, 

1650 exc_tb: TracebackType | None, 

1651 ) -> None: 

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

1653 pass # pragma: no cover 

1654 

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

1656 return self 

1657 

1658 async def __aexit__( 

1659 self, 

1660 exc_type: type[BaseException] | None, 

1661 exc_val: BaseException | None, 

1662 exc_tb: TracebackType | None, 

1663 ) -> None: 

1664 await self.close() 

1665 

1666 

1667class _BaseRequestContextManager( 

1668 Coroutine[Any, Any, _RetType_co], Generic[_RetType_co] 

1669): 

1670 

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

1672 

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

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

1675 

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

1677 return self._coro.send(arg) 

1678 

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

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

1681 

1682 def close(self) -> None: 

1683 return self._coro.close() 

1684 

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

1686 ret = self._coro.__await__() 

1687 return ret 

1688 

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

1690 return self.__await__() 

1691 

1692 async def __aenter__(self) -> _RetType_co: 

1693 self._resp: _RetType_co = await self._coro 

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

1695 

1696 async def __aexit__( 

1697 self, 

1698 exc_type: type[BaseException] | None, 

1699 exc: BaseException | None, 

1700 tb: TracebackType | None, 

1701 ) -> None: 

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

1703 

1704 

1705_RequestContextManager = _BaseRequestContextManager[ClientResponse] 

1706_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse[bool]] 

1707 

1708 

1709class _SessionRequestContextManager: 

1710 

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

1712 

1713 def __init__( 

1714 self, 

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

1716 session: ClientSession, 

1717 ) -> None: 

1718 self._coro = coro 

1719 self._resp: ClientResponse | None = None 

1720 self._session = session 

1721 

1722 async def __aenter__(self) -> ClientResponse: 

1723 try: 

1724 self._resp = await self._coro 

1725 except BaseException: 

1726 await self._session.close() 

1727 raise 

1728 else: 

1729 return self._resp 

1730 

1731 async def __aexit__( 

1732 self, 

1733 exc_type: type[BaseException] | None, 

1734 exc: BaseException | None, 

1735 tb: TracebackType | None, 

1736 ) -> None: 

1737 assert self._resp is not None 

1738 self._resp.close() 

1739 await self._session.close() 

1740 

1741 

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

1743 

1744 def request( 

1745 method: str, 

1746 url: StrOrURL, 

1747 *, 

1748 version: HttpVersion = http.HttpVersion11, 

1749 connector: BaseConnector | None = None, 

1750 loop: asyncio.AbstractEventLoop | None = None, 

1751 **kwargs: Unpack[_RequestOptions], 

1752 ) -> _SessionRequestContextManager: ... 

1753 

1754else: 

1755 

1756 def request( 

1757 method: str, 

1758 url: StrOrURL, 

1759 *, 

1760 version: HttpVersion = http.HttpVersion11, 

1761 connector: BaseConnector | None = None, 

1762 loop: asyncio.AbstractEventLoop | None = None, 

1763 **kwargs: Any, 

1764 ) -> _SessionRequestContextManager: 

1765 """Constructs and sends a request. 

1766 

1767 Returns response object. 

1768 method - HTTP method 

1769 url - request url 

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

1771 string of the new request 

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

1773 send in the body of the request 

1774 json - (optional) Any json compatible python object 

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

1776 the request 

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

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

1779 auth - aiohttp.helpers.BasicAuth 

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

1781 redirects 

1782 version - Request HTTP version. 

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

1784 with deflate encoding. 

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

1786 expect100 - Expect 100-continue response from server. 

1787 connector - BaseConnector sub-class instance to support 

1788 connection pooling. 

1789 read_until_eof - Read response until eof if response 

1790 does not have Content-Length header. 

1791 loop - Optional event loop. 

1792 timeout - Optional ClientTimeout settings structure, 5min 

1793 total timeout by default. 

1794 Usage:: 

1795 >>> import aiohttp 

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

1797 ... print(resp) 

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

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

1800 """ 

1801 connector_owner = False 

1802 if connector is None: 

1803 connector_owner = True 

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

1805 

1806 session = ClientSession( 

1807 loop=loop, 

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

1809 version=version, 

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

1811 connector=connector, 

1812 connector_owner=connector_owner, 

1813 ) 

1814 

1815 return _SessionRequestContextManager( 

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

1817 session, 

1818 )