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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

676 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 ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint) 

584 

585 if auth is not None: 

586 warnings.warn( 

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

588 " pass headers={'Authorization': " 

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

590 DeprecationWarning, 

591 stacklevel=3, 

592 ) 

593 if proxy_auth is not None: 

594 warnings.warn( 

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

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

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

598 DeprecationWarning, 

599 stacklevel=3, 

600 ) 

601 

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

603 raise ValueError( 

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

605 ) 

606 elif json is not None: 

607 if self._json_serialize_bytes is not None: 

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

609 else: 

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

611 

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

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

614 

615 redirects = 0 

616 history: list[ClientResponse] = [] 

617 version = self._version 

618 params = params or {} 

619 

620 # Merge with default headers and transform to CIMultiDict 

621 headers = self._prepare_headers(headers) 

622 

623 try: 

624 url = self._build_url(str_or_url) 

625 except ValueError as e: 

626 raise InvalidUrlClientError(str_or_url) from e 

627 

628 assert self._connector is not None 

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

630 raise NonHttpUrlClientError(url) 

631 

632 skip_headers: Iterable[istr] | None 

633 if skip_auto_headers is not None: 

634 skip_headers = { 

635 istr(i) for i in skip_auto_headers 

636 } | self._skip_auto_headers 

637 elif self._skip_auto_headers: 

638 skip_headers = self._skip_auto_headers 

639 else: 

640 skip_headers = None 

641 

642 if proxy is None: 

643 proxy = self._default_proxy 

644 if proxy_auth is None: 

645 proxy_auth = self._default_proxy_auth 

646 

647 if proxy is None: 

648 proxy_headers = None 

649 else: 

650 proxy_headers = self._prepare_headers(proxy_headers) 

651 try: 

652 proxy = URL(proxy) 

653 except ValueError as e: 

654 raise InvalidURL(proxy) from e 

655 

656 if timeout is sentinel: 

657 real_timeout: ClientTimeout = self._timeout 

658 else: 

659 if not isinstance(timeout, ClientTimeout): 

660 real_timeout = ClientTimeout(total=timeout) 

661 else: 

662 real_timeout = timeout 

663 # timeout is cumulative for all request operations 

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

665 tm = TimeoutHandle( 

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

667 ) 

668 handle = tm.start() 

669 

670 if read_bufsize is None: 

671 read_bufsize = self._read_bufsize 

672 

673 if auto_decompress is None: 

674 auto_decompress = self._auto_decompress 

675 

676 if max_line_size is None: 

677 max_line_size = self._max_line_size 

678 

679 if max_field_size is None: 

680 max_field_size = self._max_field_size 

681 

682 if max_headers is None: 

683 max_headers = self._max_headers 

684 

685 traces = [ 

686 Trace( 

687 self, 

688 trace_config, 

689 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), 

690 ) 

691 for trace_config in self._trace_configs 

692 ] 

693 

694 for trace in traces: 

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

696 

697 timer = tm.timer() 

698 try: 

699 with timer: 

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

701 retry_persistent_connection = ( 

702 self._retry_connection and method in IDEMPOTENT_METHODS 

703 ) 

704 while True: 

705 url, auth_from_url = strip_auth_from_url(url) 

706 if not url.raw_host: 

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

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

709 err_exc_cls = ( 

710 InvalidUrlRedirectClientError 

711 if redirects 

712 else InvalidUrlClientError 

713 ) 

714 raise err_exc_cls(url) 

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

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

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

718 if not history and (auth and auth_from_url): 

719 raise ValueError( 

720 "Cannot combine AUTH argument with " 

721 "credentials encoded in URL" 

722 ) 

723 

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

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

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

727 auth = auth_from_url 

728 

729 if ( 

730 auth is None 

731 and self._default_auth 

732 and ( 

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

734 ) 

735 ): 

736 auth = self._default_auth 

737 

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

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

740 auth = await self._loop.run_in_executor( 

741 None, self._get_netrc_auth, url.host 

742 ) 

743 

744 # It would be confusing if we support explicit 

745 # Authorization header with auth argument 

746 if ( 

747 headers is not None 

748 and auth is not None 

749 and hdrs.AUTHORIZATION in headers 

750 ): 

751 raise ValueError( 

752 "Cannot combine AUTHORIZATION header " 

753 "with AUTH argument or credentials " 

754 "encoded in URL" 

755 ) 

756 

757 all_cookies = self._cookie_jar.filter_cookies(url) 

758 

759 if cookies is not None: 

760 tmp_cookie_jar = CookieJar( 

761 unsafe=self._cookie_jar.unsafe, 

762 quote_cookie=self._cookie_jar.quote_cookie, 

763 ) 

764 tmp_cookie_jar.update_cookies(cookies) 

765 req_cookies = tmp_cookie_jar.filter_cookies(url) 

766 if req_cookies: 

767 all_cookies.load(req_cookies) 

768 

769 proxy_: URL | None = None 

770 if proxy is not None: 

771 proxy_ = URL(proxy) 

772 elif self._trust_env: 

773 with suppress(LookupError): 

774 proxy_, proxy_auth = await asyncio.to_thread( 

775 get_env_proxy_for_url, url 

776 ) 

777 

778 req = self._request_class( 

779 method, 

780 url, 

781 params=params, 

782 headers=headers, 

783 skip_auto_headers=skip_headers, 

784 data=data, 

785 cookies=all_cookies, 

786 auth=auth, 

787 version=version, 

788 compress=compress, 

789 chunked=chunked, 

790 expect100=expect100, 

791 loop=self._loop, 

792 response_class=self._response_class, 

793 proxy=proxy_, 

794 proxy_auth=proxy_auth, 

795 timer=timer, 

796 session=self, 

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

798 server_hostname=server_hostname, 

799 proxy_headers=proxy_headers, 

800 traces=traces, 

801 trust_env=self.trust_env, 

802 ) 

803 

804 async def _connect_and_send_request( 

805 req: ClientRequest, 

806 ) -> ClientResponse: 

807 # connection timeout 

808 assert self._connector is not None 

809 try: 

810 conn = await self._connector.connect( 

811 req, traces=traces, timeout=real_timeout 

812 ) 

813 except asyncio.TimeoutError as exc: 

814 raise ConnectionTimeoutError( 

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

816 ) from exc 

817 

818 assert conn.protocol is not None 

819 conn.protocol.set_response_params( 

820 timer=timer, 

821 skip_payload=req.method in EMPTY_BODY_METHODS, 

822 read_until_eof=read_until_eof, 

823 auto_decompress=auto_decompress, 

824 read_timeout=real_timeout.sock_read, 

825 read_bufsize=read_bufsize, 

826 timeout_ceil_threshold=self._connector._timeout_ceil_threshold, 

827 max_line_size=max_line_size, 

828 max_field_size=max_field_size, 

829 max_headers=max_headers, 

830 ) 

831 try: 

832 resp = await req.send(conn) 

833 try: 

834 await resp.start(conn) 

835 except BaseException: 

836 resp.close() 

837 raise 

838 except BaseException: 

839 conn.close() 

840 raise 

841 return resp 

842 

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

844 effective_middlewares = ( 

845 self._middlewares if middlewares is None else middlewares 

846 ) 

847 

848 if effective_middlewares: 

849 handler = build_client_middlewares( 

850 _connect_and_send_request, effective_middlewares 

851 ) 

852 else: 

853 handler = _connect_and_send_request 

854 

855 try: 

856 resp = await handler(req) 

857 # Client connector errors should not be retried 

858 except ( 

859 ConnectionTimeoutError, 

860 ClientConnectorError, 

861 ClientConnectorCertificateError, 

862 ClientConnectorSSLError, 

863 ): 

864 raise 

865 except (ClientOSError, ServerDisconnectedError): 

866 if retry_persistent_connection: 

867 retry_persistent_connection = False 

868 continue 

869 raise 

870 except ClientError: 

871 raise 

872 except OSError as exc: 

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

874 raise 

875 raise ClientOSError(*exc.args) from exc 

876 

877 # Update cookies from raw headers to preserve duplicates 

878 if resp._raw_cookie_headers: 

879 self._cookie_jar.update_cookies_from_headers( 

880 resp._raw_cookie_headers, resp.url 

881 ) 

882 

883 # redirects 

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

885 

886 for trace in traces: 

887 await trace.send_request_redirect( 

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

889 ) 

890 

891 redirects += 1 

892 history.append(resp) 

893 if max_redirects and redirects >= max_redirects: 

894 if req._body is not None: 

895 await req._body.close() 

896 resp.close() 

897 raise TooManyRedirects( 

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

899 ) 

900 

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

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

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

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

905 ): 

906 method = hdrs.METH_GET 

907 data = None 

908 if headers.get(hdrs.CONTENT_LENGTH): 

909 headers.pop(hdrs.CONTENT_LENGTH) 

910 else: 

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

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

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

914 # Use the existing payload to avoid recreating it from 

915 # a potentially consumed file. 

916 # 

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

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

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

920 resp.close() 

921 raise ClientPayloadError( 

922 "Cannot follow redirect with a consumed request " 

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

924 "or set allow_redirects=False." 

925 ) 

926 data = req._body 

927 

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

929 hdrs.URI 

930 ) 

931 if r_url is None: 

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

933 break 

934 else: 

935 # reading from correct redirection 

936 # response is forbidden 

937 resp.release() 

938 

939 try: 

940 parsed_redirect_url = URL( 

941 r_url, encoded=not self._requote_redirect_url 

942 ) 

943 except ValueError as e: 

944 if req._body is not None: 

945 await req._body.close() 

946 resp.close() 

947 raise InvalidUrlRedirectClientError( 

948 r_url, 

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

950 ) from e 

951 

952 scheme = parsed_redirect_url.scheme 

953 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: 

954 if req._body is not None: 

955 await req._body.close() 

956 resp.close() 

957 raise NonHttpUrlRedirectClientError(r_url) 

958 elif not scheme: 

959 parsed_redirect_url = url.join(parsed_redirect_url) 

960 

961 try: 

962 redirect_origin = parsed_redirect_url.origin() 

963 except ValueError as origin_val_err: 

964 if req._body is not None: 

965 await req._body.close() 

966 resp.close() 

967 raise InvalidUrlRedirectClientError( 

968 parsed_redirect_url, 

969 "Invalid redirect URL origin", 

970 ) from origin_val_err 

971 

972 if url.origin() != redirect_origin: 

973 auth = None 

974 cookies = None 

975 headers.pop(hdrs.AUTHORIZATION, None) 

976 headers.pop(hdrs.COOKIE, None) 

977 headers.pop(hdrs.PROXY_AUTHORIZATION, None) 

978 

979 url = parsed_redirect_url 

980 params = {} 

981 resp.release() 

982 continue 

983 

984 break 

985 

986 if req._body is not None: 

987 await req._body.close() 

988 # check response status 

989 if raise_for_status is None: 

990 raise_for_status = self._raise_for_status 

991 

992 if raise_for_status is None: 

993 pass 

994 elif callable(raise_for_status): 

995 await raise_for_status(resp) 

996 elif raise_for_status: 

997 resp.raise_for_status() 

998 

999 # register connection 

1000 if handle is not None: 

1001 if resp.connection is not None: 

1002 resp.connection.add_callback(handle.cancel) 

1003 else: 

1004 handle.cancel() 

1005 

1006 resp._history = tuple(history) 

1007 

1008 for trace in traces: 

1009 await trace.send_request_end( 

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

1011 ) 

1012 return resp 

1013 

1014 except BaseException as e: 

1015 # cleanup timer 

1016 tm.close() 

1017 if handle: 

1018 handle.cancel() 

1019 handle = None 

1020 

1021 for trace in traces: 

1022 await trace.send_request_exception( 

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

1024 ) 

1025 raise 

1026 

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

1028 

1029 @overload 

1030 def ws_connect( 

1031 self, 

1032 url: StrOrURL, 

1033 *, 

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

1035 **kwargs: Unpack[_WSConnectOptions], 

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

1037 

1038 @overload 

1039 def ws_connect( 

1040 self, 

1041 url: StrOrURL, 

1042 *, 

1043 decode_text: Literal[False], 

1044 **kwargs: Unpack[_WSConnectOptions], 

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

1046 

1047 @overload 

1048 def ws_connect( 

1049 self, 

1050 url: StrOrURL, 

1051 *, 

1052 decode_text: bool = ..., 

1053 **kwargs: Unpack[_WSConnectOptions], 

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

1055 

1056 def ws_connect( 

1057 self, 

1058 url: StrOrURL, 

1059 *, 

1060 method: str = hdrs.METH_GET, 

1061 protocols: Iterable[str] = (), 

1062 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

1063 receive_timeout: float | None = None, 

1064 autoclose: bool = True, 

1065 autoping: bool = True, 

1066 heartbeat: float | None = None, 

1067 auth: BasicAuth | None = None, 

1068 origin: str | None = None, 

1069 params: Query = None, 

1070 headers: LooseHeaders | None = None, 

1071 proxy: StrOrURL | None = None, 

1072 proxy_auth: BasicAuth | None = None, 

1073 ssl: SSLContext | bool | Fingerprint = True, 

1074 verify_ssl: bool | None = None, 

1075 fingerprint: bytes | None = None, 

1076 ssl_context: SSLContext | None = None, 

1077 server_hostname: str | None = None, 

1078 proxy_headers: LooseHeaders | None = None, 

1079 compress: int = 0, 

1080 max_msg_size: int = 4 * 1024 * 1024, 

1081 decode_text: bool = True, 

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

1083 """Initiate websocket connection.""" 

1084 return _WSRequestContextManager( 

1085 self._ws_connect( 

1086 url, 

1087 method=method, 

1088 protocols=protocols, 

1089 timeout=timeout, 

1090 receive_timeout=receive_timeout, 

1091 autoclose=autoclose, 

1092 autoping=autoping, 

1093 heartbeat=heartbeat, 

1094 auth=auth, 

1095 origin=origin, 

1096 params=params, 

1097 headers=headers, 

1098 proxy=proxy, 

1099 proxy_auth=proxy_auth, 

1100 ssl=ssl, 

1101 verify_ssl=verify_ssl, 

1102 fingerprint=fingerprint, 

1103 ssl_context=ssl_context, 

1104 server_hostname=server_hostname, 

1105 proxy_headers=proxy_headers, 

1106 compress=compress, 

1107 max_msg_size=max_msg_size, 

1108 decode_text=decode_text, 

1109 ) 

1110 ) 

1111 

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

1113 

1114 @overload 

1115 async def _ws_connect( 

1116 self, 

1117 url: StrOrURL, 

1118 *, 

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

1120 **kwargs: Unpack[_WSConnectOptions], 

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

1122 

1123 @overload 

1124 async def _ws_connect( 

1125 self, 

1126 url: StrOrURL, 

1127 *, 

1128 decode_text: Literal[False], 

1129 **kwargs: Unpack[_WSConnectOptions], 

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

1131 

1132 @overload 

1133 async def _ws_connect( 

1134 self, 

1135 url: StrOrURL, 

1136 *, 

1137 decode_text: bool = ..., 

1138 **kwargs: Unpack[_WSConnectOptions], 

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

1140 

1141 async def _ws_connect( 

1142 self, 

1143 url: StrOrURL, 

1144 *, 

1145 method: str = hdrs.METH_GET, 

1146 protocols: Iterable[str] = (), 

1147 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

1148 receive_timeout: float | None = None, 

1149 autoclose: bool = True, 

1150 autoping: bool = True, 

1151 heartbeat: float | None = None, 

1152 auth: BasicAuth | None = None, 

1153 origin: str | None = None, 

1154 params: Query = None, 

1155 headers: LooseHeaders | None = None, 

1156 proxy: StrOrURL | None = None, 

1157 proxy_auth: BasicAuth | None = None, 

1158 ssl: SSLContext | bool | Fingerprint = True, 

1159 verify_ssl: bool | None = None, 

1160 fingerprint: bytes | None = None, 

1161 ssl_context: SSLContext | None = None, 

1162 server_hostname: str | None = None, 

1163 proxy_headers: LooseHeaders | None = None, 

1164 compress: int = 0, 

1165 max_msg_size: int = 4 * 1024 * 1024, 

1166 decode_text: bool = True, 

1167 ) -> "ClientWebSocketResponse[bool]": 

1168 if auth is not None: 

1169 warnings.warn( 

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

1171 " pass headers={'Authorization': " 

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

1173 DeprecationWarning, 

1174 stacklevel=3, 

1175 ) 

1176 if proxy_auth is not None: 

1177 warnings.warn( 

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

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

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

1181 DeprecationWarning, 

1182 stacklevel=3, 

1183 ) 

1184 if timeout is not sentinel: 

1185 if isinstance(timeout, ClientWSTimeout): 

1186 ws_timeout = timeout 

1187 else: 

1188 warnings.warn( 

1189 "parameter 'timeout' of type 'float' " 

1190 "is deprecated, please use " 

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

1192 DeprecationWarning, 

1193 stacklevel=2, 

1194 ) 

1195 ws_timeout = ClientWSTimeout(ws_close=timeout) 

1196 else: 

1197 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT 

1198 if receive_timeout is not None: 

1199 warnings.warn( 

1200 "float parameter 'receive_timeout' " 

1201 "is deprecated, please use parameter " 

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

1203 DeprecationWarning, 

1204 stacklevel=2, 

1205 ) 

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

1207 

1208 if headers is None: 

1209 real_headers: CIMultiDict[str] = CIMultiDict() 

1210 else: 

1211 real_headers = CIMultiDict(headers) 

1212 

1213 default_headers = { 

1214 hdrs.UPGRADE: "websocket", 

1215 hdrs.CONNECTION: "Upgrade", 

1216 hdrs.SEC_WEBSOCKET_VERSION: "13", 

1217 } 

1218 

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

1220 real_headers.setdefault(key, value) 

1221 

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

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

1224 

1225 if protocols: 

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

1227 if origin is not None: 

1228 real_headers[hdrs.ORIGIN] = origin 

1229 if compress: 

1230 extstr = ws_ext_gen(compress=compress) 

1231 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

1232 

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

1234 if ssl is None: 

1235 warnings.warn( 

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

1237 DeprecationWarning, 

1238 stacklevel=2, 

1239 ) 

1240 ssl = True 

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

1242 

1243 # send request 

1244 resp = await self.request( 

1245 method, 

1246 url, 

1247 params=params, 

1248 headers=real_headers, 

1249 read_until_eof=False, 

1250 auth=auth, 

1251 proxy=proxy, 

1252 proxy_auth=proxy_auth, 

1253 ssl=ssl, 

1254 server_hostname=server_hostname, 

1255 proxy_headers=proxy_headers, 

1256 ) 

1257 

1258 try: 

1259 # check handshake 

1260 if resp.status != 101: 

1261 raise WSServerHandshakeError( 

1262 resp.request_info, 

1263 resp.history, 

1264 message="Invalid response status", 

1265 status=resp.status, 

1266 headers=resp.headers, 

1267 ) 

1268 

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

1270 raise WSServerHandshakeError( 

1271 resp.request_info, 

1272 resp.history, 

1273 message="Invalid upgrade header", 

1274 status=resp.status, 

1275 headers=resp.headers, 

1276 ) 

1277 

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

1279 raise WSServerHandshakeError( 

1280 resp.request_info, 

1281 resp.history, 

1282 message="Invalid connection header", 

1283 status=resp.status, 

1284 headers=resp.headers, 

1285 ) 

1286 

1287 # key calculation 

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

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

1290 if r_key != match: 

1291 raise WSServerHandshakeError( 

1292 resp.request_info, 

1293 resp.history, 

1294 message="Invalid challenge response", 

1295 status=resp.status, 

1296 headers=resp.headers, 

1297 ) 

1298 

1299 # websocket protocol 

1300 protocol = None 

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

1302 resp_protocols = [ 

1303 proto.strip() 

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

1305 ] 

1306 

1307 for proto in resp_protocols: 

1308 if proto in protocols: 

1309 protocol = proto 

1310 break 

1311 

1312 # websocket compress 

1313 notakeover = False 

1314 if compress: 

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

1316 if compress_hdrs: 

1317 try: 

1318 compress, notakeover = ws_ext_parse(compress_hdrs) 

1319 except WSHandshakeError as exc: 

1320 raise WSServerHandshakeError( 

1321 resp.request_info, 

1322 resp.history, 

1323 message=exc.args[0], 

1324 status=resp.status, 

1325 headers=resp.headers, 

1326 ) from exc 

1327 else: 

1328 compress = 0 

1329 notakeover = False 

1330 

1331 conn = resp.connection 

1332 assert conn is not None 

1333 conn_proto = conn.protocol 

1334 assert conn_proto is not None 

1335 

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

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

1338 if ws_timeout.ws_receive is None: 

1339 # Reset regardless 

1340 conn_proto.read_timeout = None 

1341 elif conn_proto.read_timeout is not None: 

1342 conn_proto.read_timeout = max( 

1343 ws_timeout.ws_receive, conn_proto.read_timeout 

1344 ) 

1345 

1346 transport = conn.transport 

1347 assert transport is not None 

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

1349 writer = WebSocketWriter( 

1350 conn_proto, 

1351 transport, 

1352 use_mask=True, 

1353 compress=compress, 

1354 notakeover=notakeover, 

1355 ) 

1356 except BaseException: 

1357 resp.close() 

1358 raise 

1359 else: 

1360 ws_resp = self._ws_response_class( 

1361 reader, 

1362 writer, 

1363 protocol, 

1364 resp, 

1365 ws_timeout, 

1366 autoclose, 

1367 autoping, 

1368 self._loop, 

1369 heartbeat=heartbeat, 

1370 compress=compress, 

1371 client_notakeover=notakeover, 

1372 ) 

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

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

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

1376 return ws_resp 

1377 

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

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

1380 # Convert headers to MultiDict 

1381 result = CIMultiDict(self._default_headers) 

1382 if headers: 

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

1384 headers = CIMultiDict(headers) 

1385 added_names: set[str] = set() 

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

1387 if key in added_names: 

1388 result.add(key, value) 

1389 else: 

1390 result[key] = value 

1391 added_names.add(key) 

1392 return result 

1393 

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

1395 """ 

1396 Get auth from netrc for the given host. 

1397 

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

1399 blocking I/O in the event loop. 

1400 """ 

1401 netrc_obj = netrc_from_env() 

1402 try: 

1403 return basicauth_from_netrc(netrc_obj, host) 

1404 except LookupError: 

1405 return None 

1406 

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

1408 

1409 def get( 

1410 self, 

1411 url: StrOrURL, 

1412 **kwargs: Unpack[_RequestOptions], 

1413 ) -> "_RequestContextManager": ... 

1414 

1415 def options( 

1416 self, 

1417 url: StrOrURL, 

1418 **kwargs: Unpack[_RequestOptions], 

1419 ) -> "_RequestContextManager": ... 

1420 

1421 def head( 

1422 self, 

1423 url: StrOrURL, 

1424 **kwargs: Unpack[_RequestOptions], 

1425 ) -> "_RequestContextManager": ... 

1426 

1427 def post( 

1428 self, 

1429 url: StrOrURL, 

1430 **kwargs: Unpack[_RequestOptions], 

1431 ) -> "_RequestContextManager": ... 

1432 

1433 def put( 

1434 self, 

1435 url: StrOrURL, 

1436 **kwargs: Unpack[_RequestOptions], 

1437 ) -> "_RequestContextManager": ... 

1438 

1439 def patch( 

1440 self, 

1441 url: StrOrURL, 

1442 **kwargs: Unpack[_RequestOptions], 

1443 ) -> "_RequestContextManager": ... 

1444 

1445 def delete( 

1446 self, 

1447 url: StrOrURL, 

1448 **kwargs: Unpack[_RequestOptions], 

1449 ) -> "_RequestContextManager": ... 

1450 

1451 else: 

1452 

1453 def get( 

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

1455 ) -> "_RequestContextManager": 

1456 """Perform HTTP GET request.""" 

1457 return _RequestContextManager( 

1458 self._request( 

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

1460 ) 

1461 ) 

1462 

1463 def options( 

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

1465 ) -> "_RequestContextManager": 

1466 """Perform HTTP OPTIONS request.""" 

1467 return _RequestContextManager( 

1468 self._request( 

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

1470 ) 

1471 ) 

1472 

1473 def head( 

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

1475 ) -> "_RequestContextManager": 

1476 """Perform HTTP HEAD request.""" 

1477 return _RequestContextManager( 

1478 self._request( 

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

1480 ) 

1481 ) 

1482 

1483 def post( 

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

1485 ) -> "_RequestContextManager": 

1486 """Perform HTTP POST request.""" 

1487 return _RequestContextManager( 

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

1489 ) 

1490 

1491 def put( 

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

1493 ) -> "_RequestContextManager": 

1494 """Perform HTTP PUT request.""" 

1495 return _RequestContextManager( 

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

1497 ) 

1498 

1499 def patch( 

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

1501 ) -> "_RequestContextManager": 

1502 """Perform HTTP PATCH request.""" 

1503 return _RequestContextManager( 

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

1505 ) 

1506 

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

1508 """Perform HTTP DELETE request.""" 

1509 return _RequestContextManager( 

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

1511 ) 

1512 

1513 async def close(self) -> None: 

1514 """Close underlying connector. 

1515 

1516 Release all acquired resources. 

1517 """ 

1518 if not self.closed: 

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

1520 await self._connector.close() 

1521 self._connector = None 

1522 

1523 @property 

1524 def closed(self) -> bool: 

1525 """Is client session closed. 

1526 

1527 A readonly property. 

1528 """ 

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

1530 

1531 @property 

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

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

1534 return self._connector 

1535 

1536 @property 

1537 def cookie_jar(self) -> AbstractCookieJar: 

1538 """The session cookies.""" 

1539 return self._cookie_jar 

1540 

1541 @property 

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

1543 """The session HTTP protocol version.""" 

1544 return self._version 

1545 

1546 @property 

1547 def requote_redirect_url(self) -> bool: 

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

1549 return self._requote_redirect_url 

1550 

1551 @requote_redirect_url.setter 

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

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

1554 warnings.warn( 

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

1556 DeprecationWarning, 

1557 stacklevel=2, 

1558 ) 

1559 self._requote_redirect_url = val 

1560 

1561 @property 

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

1563 """Session's loop.""" 

1564 warnings.warn( 

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

1566 ) 

1567 return self._loop 

1568 

1569 @property 

1570 def timeout(self) -> ClientTimeout: 

1571 """Timeout for the session.""" 

1572 return self._timeout 

1573 

1574 @property 

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

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

1577 return self._default_headers 

1578 

1579 @property 

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

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

1582 return self._skip_auto_headers 

1583 

1584 @property 

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

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

1587 return self._default_auth 

1588 

1589 @property 

1590 def json_serialize(self) -> JSONEncoder: 

1591 """Json serializer callable""" 

1592 return self._json_serialize 

1593 

1594 @property 

1595 def connector_owner(self) -> bool: 

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

1597 return self._connector_owner 

1598 

1599 @property 

1600 def raise_for_status( 

1601 self, 

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

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

1604 return self._raise_for_status 

1605 

1606 @property 

1607 def auto_decompress(self) -> bool: 

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

1609 return self._auto_decompress 

1610 

1611 @property 

1612 def trust_env(self) -> bool: 

1613 """ 

1614 Should proxies information from environment or netrc be trusted. 

1615 

1616 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1617 or ~/.netrc file if present. 

1618 """ 

1619 return self._trust_env 

1620 

1621 @property 

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

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

1624 return self._trace_configs 

1625 

1626 def detach(self) -> None: 

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

1628 

1629 Session is switched to closed state anyway. 

1630 """ 

1631 self._connector = None 

1632 

1633 def __enter__(self) -> None: 

1634 raise TypeError("Use async with instead") 

1635 

1636 def __exit__( 

1637 self, 

1638 exc_type: type[BaseException] | None, 

1639 exc_val: BaseException | None, 

1640 exc_tb: TracebackType | None, 

1641 ) -> None: 

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

1643 pass # pragma: no cover 

1644 

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

1646 return self 

1647 

1648 async def __aexit__( 

1649 self, 

1650 exc_type: type[BaseException] | None, 

1651 exc_val: BaseException | None, 

1652 exc_tb: TracebackType | None, 

1653 ) -> None: 

1654 await self.close() 

1655 

1656 

1657class _BaseRequestContextManager( 

1658 Coroutine[Any, Any, _RetType_co], Generic[_RetType_co] 

1659): 

1660 

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

1662 

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

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

1665 

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

1667 return self._coro.send(arg) 

1668 

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

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

1671 

1672 def close(self) -> None: 

1673 return self._coro.close() 

1674 

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

1676 ret = self._coro.__await__() 

1677 return ret 

1678 

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

1680 return self.__await__() 

1681 

1682 async def __aenter__(self) -> _RetType_co: 

1683 self._resp: _RetType_co = await self._coro 

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

1685 

1686 async def __aexit__( 

1687 self, 

1688 exc_type: type[BaseException] | None, 

1689 exc: BaseException | None, 

1690 tb: TracebackType | None, 

1691 ) -> None: 

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

1693 

1694 

1695_RequestContextManager = _BaseRequestContextManager[ClientResponse] 

1696_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse[bool]] 

1697 

1698 

1699class _SessionRequestContextManager: 

1700 

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

1702 

1703 def __init__( 

1704 self, 

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

1706 session: ClientSession, 

1707 ) -> None: 

1708 self._coro = coro 

1709 self._resp: ClientResponse | None = None 

1710 self._session = session 

1711 

1712 async def __aenter__(self) -> ClientResponse: 

1713 try: 

1714 self._resp = await self._coro 

1715 except BaseException: 

1716 await self._session.close() 

1717 raise 

1718 else: 

1719 return self._resp 

1720 

1721 async def __aexit__( 

1722 self, 

1723 exc_type: type[BaseException] | None, 

1724 exc: BaseException | None, 

1725 tb: TracebackType | None, 

1726 ) -> None: 

1727 assert self._resp is not None 

1728 self._resp.close() 

1729 await self._session.close() 

1730 

1731 

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

1733 

1734 def request( 

1735 method: str, 

1736 url: StrOrURL, 

1737 *, 

1738 version: HttpVersion = http.HttpVersion11, 

1739 connector: BaseConnector | None = None, 

1740 loop: asyncio.AbstractEventLoop | None = None, 

1741 **kwargs: Unpack[_RequestOptions], 

1742 ) -> _SessionRequestContextManager: ... 

1743 

1744else: 

1745 

1746 def request( 

1747 method: str, 

1748 url: StrOrURL, 

1749 *, 

1750 version: HttpVersion = http.HttpVersion11, 

1751 connector: BaseConnector | None = None, 

1752 loop: asyncio.AbstractEventLoop | None = None, 

1753 **kwargs: Any, 

1754 ) -> _SessionRequestContextManager: 

1755 """Constructs and sends a request. 

1756 

1757 Returns response object. 

1758 method - HTTP method 

1759 url - request url 

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

1761 string of the new request 

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

1763 send in the body of the request 

1764 json - (optional) Any json compatible python object 

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

1766 the request 

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

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

1769 auth - aiohttp.helpers.BasicAuth 

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

1771 redirects 

1772 version - Request HTTP version. 

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

1774 with deflate encoding. 

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

1776 expect100 - Expect 100-continue response from server. 

1777 connector - BaseConnector sub-class instance to support 

1778 connection pooling. 

1779 read_until_eof - Read response until eof if response 

1780 does not have Content-Length header. 

1781 loop - Optional event loop. 

1782 timeout - Optional ClientTimeout settings structure, 5min 

1783 total timeout by default. 

1784 Usage:: 

1785 >>> import aiohttp 

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

1787 ... print(resp) 

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

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

1790 """ 

1791 connector_owner = False 

1792 if connector is None: 

1793 connector_owner = True 

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

1795 

1796 session = ClientSession( 

1797 loop=loop, 

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

1799 version=version, 

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

1801 connector=connector, 

1802 connector_owner=connector_owner, 

1803 ) 

1804 

1805 return _SessionRequestContextManager( 

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

1807 session, 

1808 )