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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

708 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 UploadAbortedError, 

68 WSMessageTypeError, 

69 WSServerHandshakeError, 

70) 

71from .client_middlewares import ClientMiddlewareType, build_client_middlewares 

72from .client_reqrep import ( 

73 ClientRequest as ClientRequest, 

74 ClientResponse as ClientResponse, 

75 Fingerprint as Fingerprint, 

76 RequestInfo as RequestInfo, 

77 UploadTracker, 

78 _merge_ssl_params, 

79) 

80from .client_ws import ( 

81 DEFAULT_WS_CLIENT_TIMEOUT, 

82 ClientWebSocketResponse as ClientWebSocketResponse, 

83 ClientWSTimeout as ClientWSTimeout, 

84) 

85from .connector import ( 

86 HTTP_AND_EMPTY_SCHEMA_SET, 

87 BaseConnector as BaseConnector, 

88 NamedPipeConnector as NamedPipeConnector, 

89 TCPConnector as TCPConnector, 

90 UnixConnector as UnixConnector, 

91) 

92from .cookiejar import CookieJar, _UnlimitedCookieJar 

93from .helpers import ( 

94 _SENTINEL, 

95 DEBUG, 

96 DEFAULT_CHUNK_SIZE, 

97 EMPTY_BODY_METHODS, 

98 BasicAuth, 

99 TimeoutHandle, 

100 basicauth_from_netrc, 

101 get_env_proxy_for_url, 

102 netrc_from_env, 

103 sentinel, 

104 strip_auth_from_url, 

105) 

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

107from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse 

108from .tracing import Trace, TraceConfig 

109from .typedefs import ( 

110 JSONBytesEncoder, 

111 JSONEncoder, 

112 LooseCookies, 

113 LooseHeaders, 

114 Query, 

115 StrOrURL, 

116) 

117 

118__all__ = ( 

119 # client_exceptions 

120 "ClientConnectionError", 

121 "ClientConnectionResetError", 

122 "ClientConnectorCertificateError", 

123 "ClientConnectorDNSError", 

124 "ClientConnectorError", 

125 "ClientConnectorSSLError", 

126 "ClientError", 

127 "ClientHttpProxyError", 

128 "ClientOSError", 

129 "ClientPayloadError", 

130 "ClientProxyConnectionError", 

131 "ClientResponseError", 

132 "ClientSSLError", 

133 "ConnectionTimeoutError", 

134 "ContentTypeError", 

135 "InvalidURL", 

136 "InvalidUrlClientError", 

137 "RedirectClientError", 

138 "NonHttpUrlClientError", 

139 "InvalidUrlRedirectClientError", 

140 "NonHttpUrlRedirectClientError", 

141 "ServerConnectionError", 

142 "ServerDisconnectedError", 

143 "ServerFingerprintMismatch", 

144 "ServerTimeoutError", 

145 "SocketTimeoutError", 

146 "TooManyRedirects", 

147 "UploadAbortedError", 

148 "WSServerHandshakeError", 

149 # client_reqrep 

150 "ClientRequest", 

151 "ClientResponse", 

152 "Fingerprint", 

153 "RequestInfo", 

154 "UploadTracker", 

155 # connector 

156 "BaseConnector", 

157 "TCPConnector", 

158 "UnixConnector", 

159 "NamedPipeConnector", 

160 # client_ws 

161 "ClientWebSocketResponse", 

162 # client 

163 "ClientSession", 

164 "ClientTimeout", 

165 "ClientWSTimeout", 

166 "request", 

167 "WSMessageTypeError", 

168) 

169 

170 

171if TYPE_CHECKING: 

172 from ssl import SSLContext 

173else: 

174 SSLContext = Any 

175 

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

177 from typing import Unpack 

178 

179 

180# URL parsers strip leading and trailing C0 control characters and spaces and 

181# drop tabs and newlines before splitting a URL. 

182_C0_CONTROL_OR_SPACE = "".join(map(chr, range(0x21))) 

183_REMOVE_TAB_OR_NEWLINE = str.maketrans("", "", "\t\n\r") 

184 

185 

186def _has_no_authority(location: str, scheme: str) -> bool: 

187 """Tell if a URL with a scheme has no "//" after the scheme. 

188 

189 Browsers read a backslash like a slash there for http and https. 

190 """ 

191 location = location.strip(_C0_CONTROL_OR_SPACE).translate(_REMOVE_TAB_OR_NEWLINE) 

192 rest = location[len(scheme) + 1 : len(scheme) + 3] 

193 return rest[:1] not in ("/", "\\") or rest[1:] not in ("/", "\\") 

194 

195 

196class _RequestOptions(TypedDict, total=False): 

197 params: Query 

198 data: Any 

199 json: Any 

200 cookies: LooseCookies | None 

201 headers: LooseHeaders | None 

202 skip_auto_headers: Iterable[str] | None 

203 auth: BasicAuth | None 

204 allow_redirects: bool 

205 max_redirects: int 

206 compress: str | bool | None 

207 chunked: bool | None 

208 expect100: bool 

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

210 read_until_eof: bool 

211 proxy: StrOrURL | None 

212 proxy_auth: BasicAuth | None 

213 timeout: "ClientTimeout | _SENTINEL | None" 

214 ssl: SSLContext | bool | Fingerprint 

215 server_hostname: str | None 

216 proxy_headers: LooseHeaders | None 

217 trace_request_ctx: object 

218 read_bufsize: int | None 

219 auto_decompress: bool | None 

220 max_line_size: int | None 

221 max_field_size: int | None 

222 max_headers: int | None 

223 middlewares: Sequence[ClientMiddlewareType] | None 

224 upload_tracker: UploadTracker | None 

225 

226 

227class _WSConnectOptions(TypedDict, total=False): 

228 method: str 

229 protocols: Iterable[str] 

230 timeout: "ClientWSTimeout | _SENTINEL" 

231 receive_timeout: float | None 

232 autoclose: bool 

233 autoping: bool 

234 heartbeat: float | None 

235 auth: BasicAuth | None 

236 origin: str | None 

237 params: Query 

238 headers: LooseHeaders | None 

239 proxy: StrOrURL | None 

240 proxy_auth: BasicAuth | None 

241 ssl: SSLContext | bool | Fingerprint 

242 verify_ssl: bool | None 

243 fingerprint: bytes | None 

244 ssl_context: SSLContext | None 

245 server_hostname: str | None 

246 proxy_headers: LooseHeaders | None 

247 compress: int 

248 max_msg_size: int 

249 

250 

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

252class ClientTimeout: 

253 total: float | None = None 

254 connect: float | None = None 

255 sock_read: float | None = None 

256 sock_connect: float | None = None 

257 ceil_threshold: float = 5 

258 

259 # pool_queue_timeout: Optional[float] = None 

260 # dns_resolution_timeout: Optional[float] = None 

261 # socket_connect_timeout: Optional[float] = None 

262 # connection_acquiring_timeout: Optional[float] = None 

263 # new_connection_timeout: Optional[float] = None 

264 # http_header_timeout: Optional[float] = None 

265 # response_body_timeout: Optional[float] = None 

266 

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

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

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

270 # to overwrite the defaults 

271 

272 

273# 5 Minute default read timeout 

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

275 

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

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

278 

279_RetType_co = TypeVar( 

280 "_RetType_co", 

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

282 covariant=True, 

283) 

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

285 

286 

287class ClientSession: 

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

289 

290 ATTRS = frozenset( 

291 [ 

292 "_base_url", 

293 "_base_url_origin", 

294 "_source_traceback", 

295 "_connector", 

296 "_loop", 

297 "_cookie_jar", 

298 "_connector_owner", 

299 "_default_auth", 

300 "_version", 

301 "_json_serialize", 

302 "_json_serialize_bytes", 

303 "_requote_redirect_url", 

304 "_timeout", 

305 "_raise_for_status", 

306 "_auto_decompress", 

307 "_trust_env", 

308 "_default_headers", 

309 "_skip_auto_headers", 

310 "_request_class", 

311 "_response_class", 

312 "_ws_response_class", 

313 "_trace_configs", 

314 "_read_bufsize", 

315 "_max_line_size", 

316 "_max_field_size", 

317 "_max_headers", 

318 "_resolve_charset", 

319 "_default_proxy", 

320 "_default_proxy_auth", 

321 "_retry_connection", 

322 "_middlewares", 

323 "requote_redirect_url", 

324 ] 

325 ) 

326 

327 _source_traceback: traceback.StackSummary | None = None 

328 _connector: BaseConnector | None = None 

329 

330 def __init__( 

331 self, 

332 base_url: StrOrURL | None = None, 

333 *, 

334 connector: BaseConnector | None = None, 

335 loop: asyncio.AbstractEventLoop | None = None, 

336 cookies: LooseCookies | None = None, 

337 headers: LooseHeaders | None = None, 

338 proxy: StrOrURL | None = None, 

339 proxy_auth: BasicAuth | None = None, 

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

341 auth: BasicAuth | None = None, 

342 json_serialize: JSONEncoder = json.dumps, 

343 json_serialize_bytes: JSONBytesEncoder | None = None, 

344 request_class: type[ClientRequest] = ClientRequest, 

345 response_class: type[ClientResponse] = ClientResponse, 

346 ws_response_class: type[ClientWebSocketResponse] = ClientWebSocketResponse, 

347 version: HttpVersion = http.HttpVersion11, 

348 cookie_jar: AbstractCookieJar | None = None, 

349 connector_owner: bool = True, 

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

351 read_timeout: float | _SENTINEL = sentinel, 

352 conn_timeout: float | None = None, 

353 timeout: object | ClientTimeout = sentinel, 

354 auto_decompress: bool = True, 

355 trust_env: bool = False, 

356 requote_redirect_url: bool = True, 

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

358 read_bufsize: int = DEFAULT_CHUNK_SIZE, 

359 max_line_size: int = 8190, 

360 max_field_size: int = 8190, 

361 max_headers: int = 128, 

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

363 middlewares: Sequence[ClientMiddlewareType] = (), 

364 ssl_shutdown_timeout: _SENTINEL | None | float = sentinel, 

365 ) -> None: 

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

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

368 self._connector: BaseConnector | None = None 

369 

370 if loop is None: 

371 if connector is not None: 

372 loop = connector._loop 

373 

374 loop = loop or asyncio.get_running_loop() 

375 

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

377 self._base_url: URL | None = base_url 

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

379 else: 

380 self._base_url = URL(base_url) 

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

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

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

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

385 

386 if timeout is sentinel or timeout is None: 

387 self._timeout = DEFAULT_TIMEOUT 

388 if read_timeout is not sentinel: 

389 warnings.warn( 

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

391 DeprecationWarning, 

392 stacklevel=2, 

393 ) 

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

395 if conn_timeout is not None: 

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

397 warnings.warn( 

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

399 DeprecationWarning, 

400 stacklevel=2, 

401 ) 

402 else: 

403 if not isinstance(timeout, ClientTimeout): 

404 raise ValueError( 

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

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

407 ) 

408 self._timeout = timeout 

409 if read_timeout is not sentinel: 

410 raise ValueError( 

411 "read_timeout and timeout parameters " 

412 "conflict, please setup " 

413 "timeout.read" 

414 ) 

415 if conn_timeout is not None: 

416 raise ValueError( 

417 "conn_timeout and timeout parameters " 

418 "conflict, please setup " 

419 "timeout.connect" 

420 ) 

421 

422 if ssl_shutdown_timeout is not sentinel: 

423 warnings.warn( 

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

425 DeprecationWarning, 

426 stacklevel=2, 

427 ) 

428 

429 if connector is None: 

430 connector = TCPConnector( 

431 loop=loop, ssl_shutdown_timeout=ssl_shutdown_timeout 

432 ) 

433 

434 if connector._loop is not loop: 

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

436 

437 self._loop = loop 

438 

439 if loop.get_debug(): 

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

441 

442 if cookie_jar is None: 

443 cookie_jar = CookieJar(loop=loop) 

444 self._cookie_jar = cookie_jar 

445 

446 if cookies: 

447 self._cookie_jar.update_cookies(cookies) 

448 

449 if auth is not None: 

450 warnings.warn( 

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

452 " pass headers={'Authorization': " 

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

454 DeprecationWarning, 

455 stacklevel=2, 

456 ) 

457 if proxy_auth is not None: 

458 warnings.warn( 

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

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

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

462 DeprecationWarning, 

463 stacklevel=2, 

464 ) 

465 self._connector = connector 

466 self._connector_owner = connector_owner 

467 self._default_auth = auth 

468 self._version = version 

469 self._json_serialize = json_serialize 

470 self._json_serialize_bytes = json_serialize_bytes 

471 self._raise_for_status = raise_for_status 

472 self._auto_decompress = auto_decompress 

473 self._trust_env = trust_env 

474 self._requote_redirect_url = requote_redirect_url 

475 self._read_bufsize = read_bufsize 

476 self._max_line_size = max_line_size 

477 self._max_field_size = max_field_size 

478 self._max_headers = max_headers 

479 

480 # Convert to list of tuples 

481 if headers: 

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

483 else: 

484 real_headers = CIMultiDict() 

485 self._default_headers: CIMultiDict[str] = real_headers 

486 if skip_auto_headers is not None: 

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

488 else: 

489 self._skip_auto_headers = frozenset() 

490 

491 self._request_class = request_class 

492 self._response_class = response_class 

493 self._ws_response_class = ws_response_class 

494 

495 self._trace_configs = trace_configs or [] 

496 for trace_config in self._trace_configs: 

497 trace_config.freeze() 

498 

499 self._resolve_charset = fallback_charset_resolver 

500 

501 self._default_proxy = proxy 

502 self._default_proxy_auth = proxy_auth 

503 self._retry_connection: bool = True 

504 self._middlewares = middlewares 

505 

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

507 warnings.warn( 

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

509 DeprecationWarning, 

510 stacklevel=2, 

511 ) 

512 

513 if DEBUG: 

514 

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

516 if name not in self.ATTRS: 

517 warnings.warn( 

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

519 DeprecationWarning, 

520 stacklevel=2, 

521 ) 

522 super().__setattr__(name, val) 

523 

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

525 if not self.closed: 

526 kwargs = {"source": self} 

527 _warnings.warn( 

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

529 ) 

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

531 if self._source_traceback is not None: 

532 context["source_traceback"] = self._source_traceback 

533 self._loop.call_exception_handler(context) 

534 

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

536 

537 def request( 

538 self, 

539 method: str, 

540 url: StrOrURL, 

541 **kwargs: Unpack[_RequestOptions], 

542 ) -> "_RequestContextManager": ... 

543 

544 else: 

545 

546 def request( 

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

548 ) -> "_RequestContextManager": 

549 """Perform HTTP request.""" 

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

551 

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

553 url = URL(str_or_url) 

554 if self._base_url and not url.absolute: 

555 return self._base_url.join(url) 

556 return url 

557 

558 async def _request( 

559 self, 

560 method: str, 

561 str_or_url: StrOrURL, 

562 *, 

563 params: Query = None, 

564 data: Any = None, 

565 json: Any = None, 

566 cookies: LooseCookies | None = None, 

567 headers: LooseHeaders | None = None, 

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

569 auth: BasicAuth | None = None, 

570 allow_redirects: bool = True, 

571 max_redirects: int = 10, 

572 compress: str | bool | None = None, 

573 chunked: bool | None = None, 

574 expect100: bool = False, 

575 raise_for_status: ( 

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

577 ) = None, 

578 read_until_eof: bool = True, 

579 proxy: StrOrURL | None = None, 

580 proxy_auth: BasicAuth | None = None, 

581 timeout: ClientTimeout | _SENTINEL = sentinel, 

582 verify_ssl: bool | None = None, 

583 fingerprint: bytes | None = None, 

584 ssl_context: SSLContext | None = None, 

585 ssl: SSLContext | bool | Fingerprint = True, 

586 server_hostname: str | None = None, 

587 proxy_headers: LooseHeaders | None = None, 

588 trace_request_ctx: object = None, 

589 read_bufsize: int | None = None, 

590 auto_decompress: bool | None = None, 

591 max_line_size: int | None = None, 

592 max_field_size: int | None = None, 

593 max_headers: int | None = None, 

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

595 upload_tracker: UploadTracker | None = None, 

596 ) -> ClientResponse: 

597 

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

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

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

601 

602 # Bound outside the settle-guaranteeing try block: a rebind error 

603 # must not settle a tracker owned by another request. 

604 if upload_tracker is not None: 

605 upload_tracker._bind() 

606 

607 tm: TimeoutHandle | None = None 

608 handle: asyncio.TimerHandle | None = None 

609 # Only traces that saw send_request_start; they must also see a terminal event. 

610 traces: list[Trace] = [] 

611 req: ClientRequest | None = None 

612 resp: ClientResponse | None = None 

613 try: 

614 if self.closed: 

615 raise RuntimeError("Session is closed") 

616 

617 method = method.upper() 

618 

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

620 

621 if auth is not None: 

622 warnings.warn( 

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

624 " pass headers={'Authorization': " 

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

626 DeprecationWarning, 

627 stacklevel=3, 

628 ) 

629 if proxy_auth is not None: 

630 warnings.warn( 

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

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

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

634 DeprecationWarning, 

635 stacklevel=3, 

636 ) 

637 

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

639 raise ValueError( 

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

641 ) 

642 elif json is not None: 

643 if self._json_serialize_bytes is not None: 

644 data = payload.JsonBytesPayload( 

645 json, dumps=self._json_serialize_bytes 

646 ) 

647 else: 

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

649 

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

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

652 

653 redirects = 0 

654 history: list[ClientResponse] = [] 

655 version = self._version 

656 params = params or {} 

657 

658 # Merge with default headers and transform to CIMultiDict 

659 headers = self._prepare_headers(headers) 

660 

661 try: 

662 url = self._build_url(str_or_url) 

663 except ValueError as e: 

664 raise InvalidUrlClientError(str_or_url) from e 

665 

666 assert self._connector is not None 

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

668 raise NonHttpUrlClientError(url) 

669 

670 skip_headers: Iterable[istr] | None 

671 if skip_auto_headers is not None: 

672 skip_headers = { 

673 istr(i) for i in skip_auto_headers 

674 } | self._skip_auto_headers 

675 elif self._skip_auto_headers: 

676 skip_headers = self._skip_auto_headers 

677 else: 

678 skip_headers = None 

679 

680 if proxy is None: 

681 proxy = self._default_proxy 

682 if proxy_auth is None: 

683 proxy_auth = self._default_proxy_auth 

684 

685 if proxy is None: 

686 proxy_headers = None 

687 else: 

688 proxy_headers = self._prepare_headers(proxy_headers) 

689 try: 

690 proxy = URL(proxy) 

691 except ValueError as e: 

692 raise InvalidURL(proxy) from e 

693 

694 if timeout is sentinel: 

695 real_timeout: ClientTimeout = self._timeout 

696 else: 

697 if not isinstance(timeout, ClientTimeout): 

698 real_timeout = ClientTimeout(total=timeout) 

699 else: 

700 real_timeout = timeout 

701 # timeout is cumulative for all request operations 

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

703 tm = TimeoutHandle( 

704 self._loop, 

705 real_timeout.total, 

706 ceil_threshold=real_timeout.ceil_threshold, 

707 ) 

708 handle = tm.start() 

709 

710 if read_bufsize is None: 

711 read_bufsize = self._read_bufsize 

712 

713 if auto_decompress is None: 

714 auto_decompress = self._auto_decompress 

715 

716 if max_line_size is None: 

717 max_line_size = self._max_line_size 

718 

719 if max_field_size is None: 

720 max_field_size = self._max_field_size 

721 

722 if max_headers is None: 

723 max_headers = self._max_headers 

724 

725 for trace_config in self._trace_configs: 

726 trace = Trace( 

727 self, 

728 trace_config, 

729 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), 

730 ) 

731 await trace.send_request_start( 

732 method, url.update_query(params), headers 

733 ) 

734 traces.append(trace) 

735 

736 timer = tm.timer() 

737 with timer: 

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

739 retry_persistent_connection = ( 

740 self._retry_connection and method in IDEMPOTENT_METHODS 

741 ) 

742 while True: 

743 url, auth_from_url = strip_auth_from_url(url) 

744 if not url.raw_host: 

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

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

747 err_exc_cls = ( 

748 InvalidUrlRedirectClientError 

749 if redirects 

750 else InvalidUrlClientError 

751 ) 

752 raise err_exc_cls(url) 

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

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

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

756 if not history and (auth and auth_from_url): 

757 raise ValueError( 

758 "Cannot combine AUTH argument with " 

759 "credentials encoded in URL" 

760 ) 

761 

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

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

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

765 auth = auth_from_url 

766 

767 if ( 

768 auth is None 

769 and self._default_auth 

770 and ( 

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

772 ) 

773 ): 

774 auth = self._default_auth 

775 

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

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

778 auth = await self._loop.run_in_executor( 

779 None, self._get_netrc_auth, url.host 

780 ) 

781 

782 # It would be confusing if we support explicit 

783 # Authorization header with auth argument 

784 if ( 

785 headers is not None 

786 and auth is not None 

787 and hdrs.AUTHORIZATION in headers 

788 ): 

789 raise ValueError( 

790 "Cannot combine AUTHORIZATION header " 

791 "with AUTH argument or credentials " 

792 "encoded in URL" 

793 ) 

794 

795 all_cookies = self._cookie_jar.filter_cookies(url) 

796 

797 if cookies is not None: 

798 tmp_cookie_jar = _UnlimitedCookieJar( 

799 unsafe=self._cookie_jar.unsafe, 

800 quote_cookie=self._cookie_jar.quote_cookie, 

801 treat_as_secure_origin=frozenset( 

802 self._cookie_jar.treat_as_secure_origin 

803 ), 

804 ) 

805 tmp_cookie_jar.update_cookies(cookies) 

806 req_cookies = tmp_cookie_jar.filter_cookies(url) 

807 if req_cookies: 

808 all_cookies.load(req_cookies) 

809 

810 proxy_: URL | None = None 

811 if proxy is not None: 

812 proxy_ = URL(proxy) 

813 elif self._trust_env: 

814 with suppress(LookupError): 

815 proxy_, proxy_auth = await asyncio.to_thread( 

816 get_env_proxy_for_url, url 

817 ) 

818 

819 req = self._request_class( 

820 method, 

821 url, 

822 params=params, 

823 headers=headers, 

824 skip_auto_headers=skip_headers, 

825 data=data, 

826 cookies=all_cookies, 

827 auth=auth, 

828 version=version, 

829 compress=compress, 

830 chunked=chunked, 

831 expect100=expect100, 

832 loop=self._loop, 

833 response_class=self._response_class, 

834 proxy=proxy_, 

835 proxy_auth=proxy_auth, 

836 timer=timer, 

837 session=self, 

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

839 server_hostname=server_hostname, 

840 proxy_headers=proxy_headers, 

841 traces=traces, 

842 trust_env=self.trust_env, 

843 ) 

844 req._upload_tracker = upload_tracker 

845 

846 async def _connect_and_send_request( 

847 req: ClientRequest, 

848 ) -> ClientResponse: 

849 # connection timeout 

850 assert self._connector is not None 

851 if (tracker := req._upload_tracker) is not None: 

852 req._upload_gen = tracker._attempt_started() 

853 try: 

854 conn = await self._connector.connect( 

855 req, traces=traces, timeout=real_timeout 

856 ) 

857 except asyncio.TimeoutError as exc: 

858 raise ConnectionTimeoutError( 

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

860 ) from exc 

861 

862 assert conn.protocol is not None 

863 conn.protocol.set_response_params( 

864 timer=timer, 

865 skip_payload=req.method in EMPTY_BODY_METHODS, 

866 read_until_eof=read_until_eof, 

867 auto_decompress=auto_decompress, 

868 read_timeout=real_timeout.sock_read, 

869 read_bufsize=read_bufsize, 

870 timeout_ceil_threshold=self._connector._timeout_ceil_threshold, 

871 max_line_size=max_line_size, 

872 max_field_size=max_field_size, 

873 max_headers=max_headers, 

874 ) 

875 try: 

876 resp = await req.send(conn) 

877 try: 

878 await resp.start(conn) 

879 except BaseException: 

880 resp.close() 

881 raise 

882 except BaseException: 

883 conn.close() 

884 raise 

885 return resp 

886 

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

888 effective_middlewares = ( 

889 self._middlewares if middlewares is None else middlewares 

890 ) 

891 

892 if effective_middlewares: 

893 handler = build_client_middlewares( 

894 _connect_and_send_request, effective_middlewares 

895 ) 

896 else: 

897 handler = _connect_and_send_request 

898 

899 try: 

900 resp = await handler(req) 

901 # Client connector errors should not be retried 

902 except ( 

903 ConnectionTimeoutError, 

904 ClientConnectorError, 

905 ClientConnectorCertificateError, 

906 ClientConnectorSSLError, 

907 ): 

908 raise 

909 except (ClientOSError, ServerDisconnectedError): 

910 if not retry_persistent_connection: 

911 raise 

912 retry_persistent_connection = False 

913 if data is not None: 

914 # Rebuilding from `data` would resend only the unread 

915 # remainder of a file object; reuse the payload, which 

916 # rewinds itself once the cancelled writer has settled. 

917 await req.close() 

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

919 raise 

920 data = req._body 

921 continue 

922 except ClientError: 

923 raise 

924 except OSError as exc: 

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

926 raise 

927 raise ClientOSError(*exc.args) from exc 

928 

929 # Update cookies from raw headers to preserve duplicates 

930 if resp._raw_cookie_headers: 

931 self._cookie_jar.update_cookies_from_headers( 

932 resp._raw_cookie_headers, resp.url 

933 ) 

934 

935 # redirects 

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

937 

938 for trace in traces: 

939 await trace.send_request_redirect( 

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

941 ) 

942 

943 redirects += 1 

944 history.append(resp) 

945 if max_redirects and redirects >= max_redirects: 

946 if req._body is not None: 

947 await req._body.close() 

948 resp.close() 

949 raise TooManyRedirects( 

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

951 ) 

952 

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

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

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

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

957 ): 

958 method = hdrs.METH_GET 

959 data = None 

960 if headers.get(hdrs.CONTENT_LENGTH): 

961 headers.pop(hdrs.CONTENT_LENGTH) 

962 else: 

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

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

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

966 # Use the existing payload to avoid recreating it from 

967 # a potentially consumed file. 

968 # 

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

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

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

972 resp.close() 

973 raise ClientPayloadError( 

974 "Cannot follow redirect with a consumed request " 

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

976 "or set allow_redirects=False." 

977 ) 

978 data = req._body 

979 

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

981 hdrs.URI 

982 ) 

983 if r_url is None: 

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

985 break 

986 else: 

987 # reading from correct redirection 

988 # response is forbidden 

989 resp.release() 

990 

991 try: 

992 parsed_redirect_url = URL( 

993 r_url, encoded=not self._requote_redirect_url 

994 ) 

995 except ValueError as e: 

996 if req._body is not None: 

997 await req._body.close() 

998 resp.close() 

999 raise InvalidUrlRedirectClientError( 

1000 r_url, 

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

1002 ) from e 

1003 

1004 scheme = parsed_redirect_url.scheme 

1005 if scheme not in HTTP_AND_EMPTY_SCHEMA_SET: 

1006 if req._body is not None: 

1007 await req._body.close() 

1008 resp.close() 

1009 raise NonHttpUrlRedirectClientError(r_url) 

1010 elif not scheme or ( 

1011 scheme == url.scheme and _has_no_authority(r_url, scheme) 

1012 ): 

1013 # "http:/path" or "http:path" is a reference to 

1014 # the current URL, as browsers resolve it. 

1015 parsed_redirect_url = url.join(parsed_redirect_url) 

1016 

1017 try: 

1018 redirect_origin = parsed_redirect_url.origin() 

1019 except ValueError as origin_val_err: 

1020 if req._body is not None: 

1021 await req._body.close() 

1022 resp.close() 

1023 raise InvalidUrlRedirectClientError( 

1024 parsed_redirect_url, 

1025 "Invalid redirect URL origin", 

1026 ) from origin_val_err 

1027 

1028 if url.origin() != redirect_origin: 

1029 auth = None 

1030 cookies = None 

1031 headers.popall(hdrs.AUTHORIZATION, None) 

1032 headers.popall(hdrs.COOKIE, None) 

1033 headers.popall(hdrs.PROXY_AUTHORIZATION, None) 

1034 

1035 url = parsed_redirect_url 

1036 params = {} 

1037 resp.release() 

1038 continue 

1039 

1040 break 

1041 

1042 if req._body is not None: 

1043 await req._body.close() 

1044 # check response status 

1045 if raise_for_status is None: 

1046 raise_for_status = self._raise_for_status 

1047 

1048 if raise_for_status is None: 

1049 pass 

1050 elif callable(raise_for_status): 

1051 await raise_for_status(resp) 

1052 elif raise_for_status: 

1053 resp.raise_for_status() 

1054 

1055 # register connection 

1056 if handle is not None: 

1057 if resp.connection is not None: 

1058 resp.connection.add_callback(handle.cancel) 

1059 else: 

1060 handle.cancel() 

1061 

1062 resp._history = tuple(history) 

1063 

1064 for trace in traces: 

1065 await trace.send_request_end( 

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

1067 ) 

1068 if upload_tracker is not None: 

1069 upload_tracker._finalize() 

1070 return resp 

1071 

1072 except BaseException as e: 

1073 # cleanup timer 

1074 if tm is not None: 

1075 tm.close() 

1076 if handle: 

1077 handle.cancel() 

1078 

1079 if upload_tracker is not None: 

1080 upload_tracker._finalize() 

1081 

1082 if resp is not None: 

1083 # A failure occurred after the response was received. 

1084 resp.close() 

1085 

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

1087 await req._body.close() 

1088 

1089 for trace in traces: 

1090 await trace.send_request_exception( 

1091 method, url.update_query(params), headers, e # type: ignore[arg-type] 

1092 ) 

1093 raise 

1094 

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

1096 

1097 @overload 

1098 def ws_connect( 

1099 self, 

1100 url: StrOrURL, 

1101 *, 

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

1103 **kwargs: Unpack[_WSConnectOptions], 

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

1105 

1106 @overload 

1107 def ws_connect( 

1108 self, 

1109 url: StrOrURL, 

1110 *, 

1111 decode_text: Literal[False], 

1112 **kwargs: Unpack[_WSConnectOptions], 

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

1114 

1115 @overload 

1116 def ws_connect( 

1117 self, 

1118 url: StrOrURL, 

1119 *, 

1120 decode_text: bool = ..., 

1121 **kwargs: Unpack[_WSConnectOptions], 

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

1123 

1124 def ws_connect( 

1125 self, 

1126 url: StrOrURL, 

1127 *, 

1128 method: str = hdrs.METH_GET, 

1129 protocols: Iterable[str] = (), 

1130 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

1131 receive_timeout: float | None = None, 

1132 autoclose: bool = True, 

1133 autoping: bool = True, 

1134 heartbeat: float | None = None, 

1135 auth: BasicAuth | None = None, 

1136 origin: str | None = None, 

1137 params: Query = None, 

1138 headers: LooseHeaders | None = None, 

1139 proxy: StrOrURL | None = None, 

1140 proxy_auth: BasicAuth | None = None, 

1141 ssl: SSLContext | bool | Fingerprint = True, 

1142 verify_ssl: bool | None = None, 

1143 fingerprint: bytes | None = None, 

1144 ssl_context: SSLContext | None = None, 

1145 server_hostname: str | None = None, 

1146 proxy_headers: LooseHeaders | None = None, 

1147 compress: int = 0, 

1148 max_msg_size: int = 4 * 1024 * 1024, 

1149 decode_text: bool = True, 

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

1151 """Initiate websocket connection.""" 

1152 return _WSRequestContextManager( 

1153 self._ws_connect( 

1154 url, 

1155 method=method, 

1156 protocols=protocols, 

1157 timeout=timeout, 

1158 receive_timeout=receive_timeout, 

1159 autoclose=autoclose, 

1160 autoping=autoping, 

1161 heartbeat=heartbeat, 

1162 auth=auth, 

1163 origin=origin, 

1164 params=params, 

1165 headers=headers, 

1166 proxy=proxy, 

1167 proxy_auth=proxy_auth, 

1168 ssl=ssl, 

1169 verify_ssl=verify_ssl, 

1170 fingerprint=fingerprint, 

1171 ssl_context=ssl_context, 

1172 server_hostname=server_hostname, 

1173 proxy_headers=proxy_headers, 

1174 compress=compress, 

1175 max_msg_size=max_msg_size, 

1176 decode_text=decode_text, 

1177 ) 

1178 ) 

1179 

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

1181 

1182 @overload 

1183 async def _ws_connect( 

1184 self, 

1185 url: StrOrURL, 

1186 *, 

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

1188 **kwargs: Unpack[_WSConnectOptions], 

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

1190 

1191 @overload 

1192 async def _ws_connect( 

1193 self, 

1194 url: StrOrURL, 

1195 *, 

1196 decode_text: Literal[False], 

1197 **kwargs: Unpack[_WSConnectOptions], 

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

1199 

1200 @overload 

1201 async def _ws_connect( 

1202 self, 

1203 url: StrOrURL, 

1204 *, 

1205 decode_text: bool = ..., 

1206 **kwargs: Unpack[_WSConnectOptions], 

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

1208 

1209 async def _ws_connect( 

1210 self, 

1211 url: StrOrURL, 

1212 *, 

1213 method: str = hdrs.METH_GET, 

1214 protocols: Iterable[str] = (), 

1215 timeout: ClientWSTimeout | _SENTINEL = sentinel, 

1216 receive_timeout: float | None = None, 

1217 autoclose: bool = True, 

1218 autoping: bool = True, 

1219 heartbeat: float | None = None, 

1220 auth: BasicAuth | None = None, 

1221 origin: str | None = None, 

1222 params: Query = None, 

1223 headers: LooseHeaders | None = None, 

1224 proxy: StrOrURL | None = None, 

1225 proxy_auth: BasicAuth | None = None, 

1226 ssl: SSLContext | bool | Fingerprint = True, 

1227 verify_ssl: bool | None = None, 

1228 fingerprint: bytes | None = None, 

1229 ssl_context: SSLContext | None = None, 

1230 server_hostname: str | None = None, 

1231 proxy_headers: LooseHeaders | None = None, 

1232 compress: int = 0, 

1233 max_msg_size: int = 4 * 1024 * 1024, 

1234 decode_text: bool = True, 

1235 ) -> "ClientWebSocketResponse[bool]": 

1236 if auth is not None: 

1237 warnings.warn( 

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

1239 " pass headers={'Authorization': " 

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

1241 DeprecationWarning, 

1242 stacklevel=3, 

1243 ) 

1244 if proxy_auth is not None: 

1245 warnings.warn( 

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

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

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

1249 DeprecationWarning, 

1250 stacklevel=3, 

1251 ) 

1252 if timeout is not sentinel: 

1253 if isinstance(timeout, ClientWSTimeout): 

1254 ws_timeout = timeout 

1255 else: 

1256 warnings.warn( 

1257 "parameter 'timeout' of type 'float' " 

1258 "is deprecated, please use " 

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

1260 DeprecationWarning, 

1261 stacklevel=2, 

1262 ) 

1263 ws_timeout = ClientWSTimeout(ws_close=timeout) 

1264 else: 

1265 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT 

1266 if receive_timeout is not None: 

1267 warnings.warn( 

1268 "float parameter 'receive_timeout' " 

1269 "is deprecated, please use parameter " 

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

1271 DeprecationWarning, 

1272 stacklevel=2, 

1273 ) 

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

1275 

1276 if headers is None: 

1277 real_headers: CIMultiDict[str] = CIMultiDict() 

1278 else: 

1279 real_headers = CIMultiDict(headers) 

1280 

1281 default_headers = { 

1282 hdrs.UPGRADE: "websocket", 

1283 hdrs.CONNECTION: "Upgrade", 

1284 hdrs.SEC_WEBSOCKET_VERSION: "13", 

1285 } 

1286 

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

1288 real_headers.setdefault(key, value) 

1289 

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

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

1292 

1293 if protocols: 

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

1295 if origin is not None: 

1296 real_headers[hdrs.ORIGIN] = origin 

1297 if compress: 

1298 extstr = ws_ext_gen(compress=compress) 

1299 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

1300 

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

1302 if ssl is None: 

1303 warnings.warn( 

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

1305 DeprecationWarning, 

1306 stacklevel=2, 

1307 ) 

1308 ssl = True 

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

1310 

1311 # send request 

1312 resp = await self.request( 

1313 method, 

1314 url, 

1315 params=params, 

1316 headers=real_headers, 

1317 read_until_eof=False, 

1318 auth=auth, 

1319 proxy=proxy, 

1320 proxy_auth=proxy_auth, 

1321 ssl=ssl, 

1322 server_hostname=server_hostname, 

1323 proxy_headers=proxy_headers, 

1324 ) 

1325 

1326 try: 

1327 # check handshake 

1328 if resp.status != 101: 

1329 raise WSServerHandshakeError( 

1330 resp.request_info, 

1331 resp.history, 

1332 message="Invalid response status", 

1333 status=resp.status, 

1334 headers=resp.headers, 

1335 ) 

1336 

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

1338 raise WSServerHandshakeError( 

1339 resp.request_info, 

1340 resp.history, 

1341 message="Invalid upgrade header", 

1342 status=resp.status, 

1343 headers=resp.headers, 

1344 ) 

1345 

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

1347 raise WSServerHandshakeError( 

1348 resp.request_info, 

1349 resp.history, 

1350 message="Invalid connection header", 

1351 status=resp.status, 

1352 headers=resp.headers, 

1353 ) 

1354 

1355 # key calculation 

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

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

1358 if r_key != match: 

1359 raise WSServerHandshakeError( 

1360 resp.request_info, 

1361 resp.history, 

1362 message="Invalid challenge response", 

1363 status=resp.status, 

1364 headers=resp.headers, 

1365 ) 

1366 

1367 # websocket protocol 

1368 protocol = None 

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

1370 resp_protocols = [ 

1371 proto.strip() 

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

1373 ] 

1374 

1375 for proto in resp_protocols: 

1376 if proto in protocols: 

1377 protocol = proto 

1378 break 

1379 

1380 # websocket compress 

1381 notakeover = False 

1382 if compress: 

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

1384 if compress_hdrs: 

1385 try: 

1386 compress, notakeover = ws_ext_parse(compress_hdrs) 

1387 except WSHandshakeError as exc: 

1388 raise WSServerHandshakeError( 

1389 resp.request_info, 

1390 resp.history, 

1391 message=exc.args[0], 

1392 status=resp.status, 

1393 headers=resp.headers, 

1394 ) from exc 

1395 else: 

1396 compress = 0 

1397 notakeover = False 

1398 

1399 conn = resp.connection 

1400 assert conn is not None 

1401 conn_proto = conn.protocol 

1402 assert conn_proto is not None 

1403 

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

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

1406 if ws_timeout.ws_receive is None: 

1407 # Reset regardless 

1408 conn_proto.read_timeout = None 

1409 elif conn_proto.read_timeout is not None: 

1410 conn_proto.read_timeout = max( 

1411 ws_timeout.ws_receive, conn_proto.read_timeout 

1412 ) 

1413 

1414 transport = conn.transport 

1415 assert transport is not None 

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

1417 writer = WebSocketWriter( 

1418 conn_proto, 

1419 transport, 

1420 use_mask=True, 

1421 compress=compress, 

1422 notakeover=notakeover, 

1423 ) 

1424 except BaseException: 

1425 resp.close() 

1426 raise 

1427 else: 

1428 ws_resp = self._ws_response_class( 

1429 reader, 

1430 writer, 

1431 protocol, 

1432 resp, 

1433 ws_timeout, 

1434 autoclose, 

1435 autoping, 

1436 self._loop, 

1437 heartbeat=heartbeat, 

1438 compress=compress, 

1439 client_notakeover=notakeover, 

1440 ) 

1441 ws_resp._parser = WebSocketReader( 

1442 reader, 

1443 max_msg_size, 

1444 compress=bool(compress), 

1445 decode_text=decode_text, 

1446 ) 

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

1448 conn_proto.set_parser(ws_resp._parser, reader, data_received_cb=cb) 

1449 return ws_resp 

1450 

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

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

1453 # Convert headers to MultiDict 

1454 result = CIMultiDict(self._default_headers) 

1455 if headers: 

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

1457 headers = CIMultiDict(headers) 

1458 added_names: set[str] = set() 

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

1460 if key in added_names: 

1461 result.add(key, value) 

1462 else: 

1463 result[key] = value 

1464 added_names.add(key) 

1465 return result 

1466 

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

1468 """ 

1469 Get auth from netrc for the given host. 

1470 

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

1472 blocking I/O in the event loop. 

1473 """ 

1474 netrc_obj = netrc_from_env() 

1475 try: 

1476 return basicauth_from_netrc(netrc_obj, host) 

1477 except LookupError: 

1478 return None 

1479 

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

1481 

1482 def get( 

1483 self, 

1484 url: StrOrURL, 

1485 **kwargs: Unpack[_RequestOptions], 

1486 ) -> "_RequestContextManager": ... 

1487 

1488 def options( 

1489 self, 

1490 url: StrOrURL, 

1491 **kwargs: Unpack[_RequestOptions], 

1492 ) -> "_RequestContextManager": ... 

1493 

1494 def head( 

1495 self, 

1496 url: StrOrURL, 

1497 **kwargs: Unpack[_RequestOptions], 

1498 ) -> "_RequestContextManager": ... 

1499 

1500 def post( 

1501 self, 

1502 url: StrOrURL, 

1503 **kwargs: Unpack[_RequestOptions], 

1504 ) -> "_RequestContextManager": ... 

1505 

1506 def put( 

1507 self, 

1508 url: StrOrURL, 

1509 **kwargs: Unpack[_RequestOptions], 

1510 ) -> "_RequestContextManager": ... 

1511 

1512 def patch( 

1513 self, 

1514 url: StrOrURL, 

1515 **kwargs: Unpack[_RequestOptions], 

1516 ) -> "_RequestContextManager": ... 

1517 

1518 def delete( 

1519 self, 

1520 url: StrOrURL, 

1521 **kwargs: Unpack[_RequestOptions], 

1522 ) -> "_RequestContextManager": ... 

1523 

1524 else: 

1525 

1526 def get( 

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

1528 ) -> "_RequestContextManager": 

1529 """Perform HTTP GET request.""" 

1530 return _RequestContextManager( 

1531 self._request( 

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

1533 ) 

1534 ) 

1535 

1536 def options( 

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

1538 ) -> "_RequestContextManager": 

1539 """Perform HTTP OPTIONS request.""" 

1540 return _RequestContextManager( 

1541 self._request( 

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

1543 ) 

1544 ) 

1545 

1546 def head( 

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

1548 ) -> "_RequestContextManager": 

1549 """Perform HTTP HEAD request.""" 

1550 return _RequestContextManager( 

1551 self._request( 

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

1553 ) 

1554 ) 

1555 

1556 def post( 

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

1558 ) -> "_RequestContextManager": 

1559 """Perform HTTP POST request.""" 

1560 return _RequestContextManager( 

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

1562 ) 

1563 

1564 def put( 

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

1566 ) -> "_RequestContextManager": 

1567 """Perform HTTP PUT request.""" 

1568 return _RequestContextManager( 

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

1570 ) 

1571 

1572 def patch( 

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

1574 ) -> "_RequestContextManager": 

1575 """Perform HTTP PATCH request.""" 

1576 return _RequestContextManager( 

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

1578 ) 

1579 

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

1581 """Perform HTTP DELETE request.""" 

1582 return _RequestContextManager( 

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

1584 ) 

1585 

1586 async def close(self) -> None: 

1587 """Close underlying connector. 

1588 

1589 Release all acquired resources. 

1590 """ 

1591 if not self.closed: 

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

1593 await self._connector.close() 

1594 self._connector = None 

1595 

1596 @property 

1597 def closed(self) -> bool: 

1598 """Is client session closed. 

1599 

1600 A readonly property. 

1601 """ 

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

1603 

1604 @property 

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

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

1607 return self._connector 

1608 

1609 @property 

1610 def cookie_jar(self) -> AbstractCookieJar: 

1611 """The session cookies.""" 

1612 return self._cookie_jar 

1613 

1614 @property 

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

1616 """The session HTTP protocol version.""" 

1617 return self._version 

1618 

1619 @property 

1620 def requote_redirect_url(self) -> bool: 

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

1622 return self._requote_redirect_url 

1623 

1624 @requote_redirect_url.setter 

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

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

1627 warnings.warn( 

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

1629 DeprecationWarning, 

1630 stacklevel=2, 

1631 ) 

1632 self._requote_redirect_url = val 

1633 

1634 @property 

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

1636 """Session's loop.""" 

1637 warnings.warn( 

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

1639 ) 

1640 return self._loop 

1641 

1642 @property 

1643 def timeout(self) -> ClientTimeout: 

1644 """Timeout for the session.""" 

1645 return self._timeout 

1646 

1647 @property 

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

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

1650 return self._default_headers 

1651 

1652 @property 

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

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

1655 return self._skip_auto_headers 

1656 

1657 @property 

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

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

1660 return self._default_auth 

1661 

1662 @property 

1663 def json_serialize(self) -> JSONEncoder: 

1664 """Json serializer callable""" 

1665 return self._json_serialize 

1666 

1667 @property 

1668 def connector_owner(self) -> bool: 

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

1670 return self._connector_owner 

1671 

1672 @property 

1673 def raise_for_status( 

1674 self, 

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

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

1677 return self._raise_for_status 

1678 

1679 @property 

1680 def auto_decompress(self) -> bool: 

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

1682 return self._auto_decompress 

1683 

1684 @property 

1685 def trust_env(self) -> bool: 

1686 """ 

1687 Should proxies information from environment or netrc be trusted. 

1688 

1689 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1690 or ~/.netrc file if present. 

1691 """ 

1692 return self._trust_env 

1693 

1694 @property 

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

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

1697 return self._trace_configs 

1698 

1699 def detach(self) -> None: 

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

1701 

1702 Session is switched to closed state anyway. 

1703 """ 

1704 self._connector = None 

1705 

1706 def __enter__(self) -> None: 

1707 raise TypeError("Use async with instead") 

1708 

1709 def __exit__( 

1710 self, 

1711 exc_type: type[BaseException] | None, 

1712 exc_val: BaseException | None, 

1713 exc_tb: TracebackType | None, 

1714 ) -> None: 

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

1716 pass # pragma: no cover 

1717 

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

1719 return self 

1720 

1721 async def __aexit__( 

1722 self, 

1723 exc_type: type[BaseException] | None, 

1724 exc_val: BaseException | None, 

1725 exc_tb: TracebackType | None, 

1726 ) -> None: 

1727 await self.close() 

1728 

1729 

1730class _BaseRequestContextManager( 

1731 Coroutine[Any, Any, _RetType_co], Generic[_RetType_co] 

1732): 

1733 

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

1735 

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

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

1738 

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

1740 return self._coro.send(arg) 

1741 

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

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

1744 

1745 def close(self) -> None: 

1746 return self._coro.close() 

1747 

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

1749 ret = self._coro.__await__() 

1750 return ret 

1751 

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

1753 return self.__await__() 

1754 

1755 async def __aenter__(self) -> _RetType_co: 

1756 self._resp: _RetType_co = await self._coro 

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

1758 

1759 async def __aexit__( 

1760 self, 

1761 exc_type: type[BaseException] | None, 

1762 exc: BaseException | None, 

1763 tb: TracebackType | None, 

1764 ) -> None: 

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

1766 

1767 

1768_RequestContextManager = _BaseRequestContextManager[ClientResponse] 

1769_WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse[bool]] 

1770 

1771 

1772class _SessionRequestContextManager: 

1773 

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

1775 

1776 def __init__( 

1777 self, 

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

1779 session: ClientSession, 

1780 ) -> None: 

1781 self._coro = coro 

1782 self._resp: ClientResponse | None = None 

1783 self._session = session 

1784 

1785 async def __aenter__(self) -> ClientResponse: 

1786 try: 

1787 self._resp = await self._coro 

1788 except BaseException: 

1789 await self._session.close() 

1790 raise 

1791 else: 

1792 return self._resp 

1793 

1794 async def __aexit__( 

1795 self, 

1796 exc_type: type[BaseException] | None, 

1797 exc: BaseException | None, 

1798 tb: TracebackType | None, 

1799 ) -> None: 

1800 assert self._resp is not None 

1801 self._resp.close() 

1802 await self._session.close() 

1803 

1804 

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

1806 

1807 def request( 

1808 method: str, 

1809 url: StrOrURL, 

1810 *, 

1811 version: HttpVersion = http.HttpVersion11, 

1812 connector: BaseConnector | None = None, 

1813 loop: asyncio.AbstractEventLoop | None = None, 

1814 **kwargs: Unpack[_RequestOptions], 

1815 ) -> _SessionRequestContextManager: ... 

1816 

1817else: 

1818 

1819 def request( 

1820 method: str, 

1821 url: StrOrURL, 

1822 *, 

1823 version: HttpVersion = http.HttpVersion11, 

1824 connector: BaseConnector | None = None, 

1825 loop: asyncio.AbstractEventLoop | None = None, 

1826 **kwargs: Any, 

1827 ) -> _SessionRequestContextManager: 

1828 """Constructs and sends a request. 

1829 

1830 Returns response object. 

1831 method - HTTP method 

1832 url - request url 

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

1834 string of the new request 

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

1836 send in the body of the request 

1837 json - (optional) Any json compatible python object 

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

1839 the request 

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

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

1842 auth - aiohttp.helpers.BasicAuth 

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

1844 redirects 

1845 version - Request HTTP version. 

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

1847 with deflate encoding. 

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

1849 expect100 - Expect 100-continue response from server. 

1850 connector - BaseConnector sub-class instance to support 

1851 connection pooling. 

1852 read_until_eof - Read response until eof if response 

1853 does not have Content-Length header. 

1854 loop - Optional event loop. 

1855 timeout - Optional ClientTimeout settings structure, 5min 

1856 total timeout by default. 

1857 Usage:: 

1858 >>> import aiohttp 

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

1860 ... print(resp) 

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

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

1863 """ 

1864 connector_owner = False 

1865 if connector is None: 

1866 connector_owner = True 

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

1868 

1869 session = ClientSession( 

1870 loop=loop, 

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

1872 version=version, 

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

1874 connector=connector, 

1875 connector_owner=connector_owner, 

1876 ) 

1877 

1878 return _SessionRequestContextManager( 

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

1880 session, 

1881 )