Coverage for /pythoncovmergedfiles/medio/medio/src/aiohttp/aiohttp/client.py: 31%

468 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-27 06:09 +0000

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

2 

3import asyncio 

4import base64 

5import dataclasses 

6import hashlib 

7import json 

8import os 

9import sys 

10import traceback 

11import warnings 

12from contextlib import suppress 

13from types import SimpleNamespace, TracebackType 

14from typing import ( 

15 TYPE_CHECKING, 

16 Any, 

17 Awaitable, 

18 Callable, 

19 Collection, 

20 Coroutine, 

21 Final, 

22 FrozenSet, 

23 Generator, 

24 Generic, 

25 Iterable, 

26 List, 

27 Literal, 

28 Mapping, 

29 Optional, 

30 Set, 

31 Tuple, 

32 Type, 

33 TypeVar, 

34 Union, 

35 final, 

36) 

37 

38from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr 

39from yarl import URL 

40 

41from . import hdrs, http, payload 

42from .abc import AbstractCookieJar 

43from .client_exceptions import ( 

44 ClientConnectionError, 

45 ClientConnectorCertificateError, 

46 ClientConnectorError, 

47 ClientConnectorSSLError, 

48 ClientError, 

49 ClientHttpProxyError, 

50 ClientOSError, 

51 ClientPayloadError, 

52 ClientProxyConnectionError, 

53 ClientResponseError, 

54 ClientSSLError, 

55 ContentTypeError, 

56 InvalidURL, 

57 ServerConnectionError, 

58 ServerDisconnectedError, 

59 ServerFingerprintMismatch, 

60 ServerTimeoutError, 

61 TooManyRedirects, 

62 WSServerHandshakeError, 

63) 

64from .client_reqrep import ( 

65 SSL_ALLOWED_TYPES, 

66 ClientRequest, 

67 ClientResponse, 

68 Fingerprint, 

69 RequestInfo, 

70) 

71from .client_ws import ( 

72 DEFAULT_WS_CLIENT_TIMEOUT, 

73 ClientWebSocketResponse, 

74 ClientWSTimeout, 

75) 

76from .connector import BaseConnector, NamedPipeConnector, TCPConnector, UnixConnector 

77from .cookiejar import CookieJar 

78from .helpers import ( 

79 _SENTINEL, 

80 BasicAuth, 

81 TimeoutHandle, 

82 ceil_timeout, 

83 get_env_proxy_for_url, 

84 sentinel, 

85 strip_auth_from_url, 

86) 

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

88from .http_websocket import WSHandshakeError, WSMessage, ws_ext_gen, ws_ext_parse 

89from .streams import FlowControlDataQueue 

90from .tracing import Trace, TraceConfig 

91from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, StrOrURL 

92 

93__all__ = ( 

94 # client_exceptions 

95 "ClientConnectionError", 

96 "ClientConnectorCertificateError", 

97 "ClientConnectorError", 

98 "ClientConnectorSSLError", 

99 "ClientError", 

100 "ClientHttpProxyError", 

101 "ClientOSError", 

102 "ClientPayloadError", 

103 "ClientProxyConnectionError", 

104 "ClientResponseError", 

105 "ClientSSLError", 

106 "ContentTypeError", 

107 "InvalidURL", 

108 "ServerConnectionError", 

109 "ServerDisconnectedError", 

110 "ServerFingerprintMismatch", 

111 "ServerTimeoutError", 

112 "TooManyRedirects", 

113 "WSServerHandshakeError", 

114 # client_reqrep 

115 "ClientRequest", 

116 "ClientResponse", 

117 "Fingerprint", 

118 "RequestInfo", 

119 # connector 

120 "BaseConnector", 

121 "TCPConnector", 

122 "UnixConnector", 

123 "NamedPipeConnector", 

124 # client_ws 

125 "ClientWebSocketResponse", 

126 # client 

127 "ClientSession", 

128 "ClientTimeout", 

129 "request", 

130) 

131 

132 

133if TYPE_CHECKING: 

134 from ssl import SSLContext 

135else: 

136 SSLContext = None 

137 

138 

139@dataclasses.dataclass(frozen=True) 

140class ClientTimeout: 

141 total: Optional[float] = None 

142 connect: Optional[float] = None 

143 sock_read: Optional[float] = None 

144 sock_connect: Optional[float] = None 

145 ceil_threshold: float = 5 

146 

147 # pool_queue_timeout: Optional[float] = None 

148 # dns_resolution_timeout: Optional[float] = None 

149 # socket_connect_timeout: Optional[float] = None 

150 # connection_acquiring_timeout: Optional[float] = None 

151 # new_connection_timeout: Optional[float] = None 

152 # http_header_timeout: Optional[float] = None 

153 # response_body_timeout: Optional[float] = None 

154 

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

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

157 # - or use https://docs.python.org/3/library/dataclasses.html#dataclasses.replace 

158 # to overwrite the defaults 

159 

160 

161# 5 Minute default read timeout 

162DEFAULT_TIMEOUT: Final[ClientTimeout] = ClientTimeout(total=5 * 60) 

163 

164_RetType = TypeVar("_RetType") 

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

166 

167 

168@final 

169class ClientSession: 

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

171 

172 __slots__ = ( 

173 "_base_url", 

174 "_source_traceback", 

175 "_connector", 

176 "_loop", 

177 "_cookie_jar", 

178 "_connector_owner", 

179 "_default_auth", 

180 "_version", 

181 "_json_serialize", 

182 "_requote_redirect_url", 

183 "_timeout", 

184 "_raise_for_status", 

185 "_auto_decompress", 

186 "_trust_env", 

187 "_default_headers", 

188 "_skip_auto_headers", 

189 "_request_class", 

190 "_response_class", 

191 "_ws_response_class", 

192 "_trace_configs", 

193 "_read_bufsize", 

194 "_max_line_size", 

195 "_max_field_size", 

196 "_resolve_charset", 

197 ) 

198 

199 def __init__( 

200 self, 

201 base_url: Optional[StrOrURL] = None, 

202 *, 

203 connector: Optional[BaseConnector] = None, 

204 cookies: Optional[LooseCookies] = None, 

205 headers: Optional[LooseHeaders] = None, 

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

207 auth: Optional[BasicAuth] = None, 

208 json_serialize: JSONEncoder = json.dumps, 

209 request_class: Type[ClientRequest] = ClientRequest, 

210 response_class: Type[ClientResponse] = ClientResponse, 

211 ws_response_class: Type[ClientWebSocketResponse] = ClientWebSocketResponse, 

212 version: HttpVersion = http.HttpVersion11, 

213 cookie_jar: Optional[AbstractCookieJar] = None, 

214 connector_owner: bool = True, 

215 raise_for_status: Union[ 

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

217 ] = False, 

218 timeout: Union[_SENTINEL, ClientTimeout, None] = sentinel, 

219 auto_decompress: bool = True, 

220 trust_env: bool = False, 

221 requote_redirect_url: bool = True, 

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

223 read_bufsize: int = 2**16, 

224 max_line_size: int = 8190, 

225 max_field_size: int = 8190, 

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

227 ) -> None: 

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

229 self._base_url: Optional[URL] = base_url 

230 else: 

231 self._base_url = URL(base_url) 

232 assert ( 

233 self._base_url.origin() == self._base_url 

234 ), "Only absolute URLs without path part are supported" 

235 

236 loop = asyncio.get_running_loop() 

237 

238 if connector is None: 

239 connector = TCPConnector() 

240 

241 # Initialize these three attrs before raising any exception, 

242 # they are used in __del__ 

243 self._connector: Optional[BaseConnector] = connector 

244 self._loop = loop 

245 if loop.get_debug(): 

246 self._source_traceback: Optional[ 

247 traceback.StackSummary 

248 ] = traceback.extract_stack(sys._getframe(1)) 

249 else: 

250 self._source_traceback = None 

251 

252 if connector._loop is not loop: 

253 raise RuntimeError("Session and connector have to use same event loop") 

254 

255 if cookie_jar is None: 

256 cookie_jar = CookieJar() 

257 self._cookie_jar = cookie_jar 

258 

259 if cookies is not None: 

260 self._cookie_jar.update_cookies(cookies) 

261 

262 self._connector_owner = connector_owner 

263 self._default_auth = auth 

264 self._version = version 

265 self._json_serialize = json_serialize 

266 if timeout is sentinel or timeout is None: 

267 self._timeout = DEFAULT_TIMEOUT 

268 else: 

269 self._timeout = timeout 

270 self._raise_for_status = raise_for_status 

271 self._auto_decompress = auto_decompress 

272 self._trust_env = trust_env 

273 self._requote_redirect_url = requote_redirect_url 

274 self._read_bufsize = read_bufsize 

275 self._max_line_size = max_line_size 

276 self._max_field_size = max_field_size 

277 

278 # Convert to list of tuples 

279 if headers: 

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

281 else: 

282 real_headers = CIMultiDict() 

283 self._default_headers: CIMultiDict[str] = real_headers 

284 if skip_auto_headers is not None: 

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

286 else: 

287 self._skip_auto_headers = frozenset() 

288 

289 self._request_class = request_class 

290 self._response_class = response_class 

291 self._ws_response_class = ws_response_class 

292 

293 self._trace_configs = trace_configs or [] 

294 for trace_config in self._trace_configs: 

295 trace_config.freeze() 

296 

297 self._resolve_charset = fallback_charset_resolver 

298 

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

300 raise TypeError( 

301 "Inheritance class {} from ClientSession " 

302 "is forbidden".format(cls.__name__) 

303 ) 

304 

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

306 if not self.closed: 

307 _warnings.warn( 

308 f"Unclosed client session {self!r}", 

309 ResourceWarning, 

310 source=self, 

311 ) 

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

313 if self._source_traceback is not None: 

314 context["source_traceback"] = self._source_traceback 

315 self._loop.call_exception_handler(context) 

316 

317 def request( 

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

319 ) -> "_RequestContextManager": 

320 """Perform HTTP request.""" 

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

322 

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

324 url = URL(str_or_url) 

325 if self._base_url is None: 

326 return url 

327 else: 

328 assert not url.is_absolute() and url.path.startswith("/") 

329 return self._base_url.join(url) 

330 

331 async def _request( 

332 self, 

333 method: str, 

334 str_or_url: StrOrURL, 

335 *, 

336 params: Optional[Mapping[str, str]] = None, 

337 data: Any = None, 

338 json: Any = None, 

339 cookies: Optional[LooseCookies] = None, 

340 headers: Optional[LooseHeaders] = None, 

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

342 auth: Optional[BasicAuth] = None, 

343 allow_redirects: bool = True, 

344 max_redirects: int = 10, 

345 compress: Optional[str] = None, 

346 chunked: Optional[bool] = None, 

347 expect100: bool = False, 

348 raise_for_status: Union[ 

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

350 ] = None, 

351 read_until_eof: bool = True, 

352 proxy: Optional[StrOrURL] = None, 

353 proxy_auth: Optional[BasicAuth] = None, 

354 timeout: Union[ClientTimeout, _SENTINEL, None] = sentinel, 

355 ssl: Optional[Union[SSLContext, Literal[False], Fingerprint]] = None, 

356 server_hostname: Optional[str] = None, 

357 proxy_headers: Optional[LooseHeaders] = None, 

358 trace_request_ctx: Optional[SimpleNamespace] = None, 

359 read_bufsize: Optional[int] = None, 

360 auto_decompress: Optional[bool] = None, 

361 max_line_size: Optional[int] = None, 

362 max_field_size: Optional[int] = None, 

363 ) -> ClientResponse: 

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

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

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

367 

368 if self.closed: 

369 raise RuntimeError("Session is closed") 

370 

371 if not isinstance(ssl, SSL_ALLOWED_TYPES): 

372 raise TypeError( 

373 "ssl should be SSLContext, bool, Fingerprint, " 

374 "or None, got {!r} instead.".format(ssl) 

375 ) 

376 

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

378 raise ValueError( 

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

380 ) 

381 elif json is not None: 

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

383 

384 redirects = 0 

385 history = [] 

386 version = self._version 

387 params = params or {} 

388 

389 # Merge with default headers and transform to CIMultiDict 

390 headers = self._prepare_headers(headers) 

391 proxy_headers = self._prepare_headers(proxy_headers) 

392 

393 try: 

394 url = self._build_url(str_or_url) 

395 except ValueError as e: 

396 raise InvalidURL(str_or_url) from e 

397 

398 skip_headers = set(self._skip_auto_headers) 

399 if skip_auto_headers is not None: 

400 for i in skip_auto_headers: 

401 skip_headers.add(istr(i)) 

402 

403 if proxy is not None: 

404 try: 

405 proxy = URL(proxy) 

406 except ValueError as e: 

407 raise InvalidURL(proxy) from e 

408 

409 if timeout is sentinel or timeout is None: 

410 real_timeout: ClientTimeout = self._timeout 

411 else: 

412 real_timeout = timeout 

413 # timeout is cumulative for all request operations 

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

415 tm = TimeoutHandle( 

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

417 ) 

418 handle = tm.start() 

419 

420 if read_bufsize is None: 

421 read_bufsize = self._read_bufsize 

422 

423 if auto_decompress is None: 

424 auto_decompress = self._auto_decompress 

425 

426 if max_line_size is None: 

427 max_line_size = self._max_line_size 

428 

429 if max_field_size is None: 

430 max_field_size = self._max_field_size 

431 

432 traces = [ 

433 Trace( 

434 self, 

435 trace_config, 

436 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), 

437 ) 

438 for trace_config in self._trace_configs 

439 ] 

440 

441 for trace in traces: 

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

443 

444 timer = tm.timer() 

445 try: 

446 with timer: 

447 while True: 

448 url, auth_from_url = strip_auth_from_url(url) 

449 if auth and auth_from_url: 

450 raise ValueError( 

451 "Cannot combine AUTH argument with " 

452 "credentials encoded in URL" 

453 ) 

454 

455 if auth is None: 

456 auth = auth_from_url 

457 if auth is None: 

458 auth = self._default_auth 

459 # It would be confusing if we support explicit 

460 # Authorization header with auth argument 

461 if auth is not None and hdrs.AUTHORIZATION in headers: 

462 raise ValueError( 

463 "Cannot combine AUTHORIZATION header " 

464 "with AUTH argument or credentials " 

465 "encoded in URL" 

466 ) 

467 

468 all_cookies = self._cookie_jar.filter_cookies(url) 

469 

470 if cookies is not None: 

471 tmp_cookie_jar = CookieJar() 

472 tmp_cookie_jar.update_cookies(cookies) 

473 req_cookies = tmp_cookie_jar.filter_cookies(url) 

474 if req_cookies: 

475 all_cookies.load(req_cookies) 

476 

477 if proxy is not None: 

478 proxy = URL(proxy) 

479 elif self._trust_env: 

480 with suppress(LookupError): 

481 proxy, proxy_auth = get_env_proxy_for_url(url) 

482 

483 req = self._request_class( 

484 method, 

485 url, 

486 params=params, 

487 headers=headers, 

488 skip_auto_headers=skip_headers, 

489 data=data, 

490 cookies=all_cookies, 

491 auth=auth, 

492 version=version, 

493 compress=compress, 

494 chunked=chunked, 

495 expect100=expect100, 

496 loop=self._loop, 

497 response_class=self._response_class, 

498 proxy=proxy, 

499 proxy_auth=proxy_auth, 

500 timer=timer, 

501 session=self, 

502 ssl=ssl, 

503 server_hostname=server_hostname, 

504 proxy_headers=proxy_headers, 

505 traces=traces, 

506 trust_env=self.trust_env, 

507 ) 

508 

509 # connection timeout 

510 try: 

511 async with ceil_timeout( 

512 real_timeout.connect, 

513 ceil_threshold=real_timeout.ceil_threshold, 

514 ): 

515 assert self._connector is not None 

516 conn = await self._connector.connect( 

517 req, traces=traces, timeout=real_timeout 

518 ) 

519 except asyncio.TimeoutError as exc: 

520 raise ServerTimeoutError( 

521 f"Connection timeout to host {url}" 

522 ) from exc 

523 

524 assert conn.transport is not None 

525 

526 assert conn.protocol is not None 

527 conn.protocol.set_response_params( 

528 timer=timer, 

529 skip_payload=method.upper() == "HEAD", 

530 read_until_eof=read_until_eof, 

531 auto_decompress=auto_decompress, 

532 read_timeout=real_timeout.sock_read, 

533 read_bufsize=read_bufsize, 

534 timeout_ceil_threshold=self._connector._timeout_ceil_threshold, 

535 max_line_size=max_line_size, 

536 max_field_size=max_field_size, 

537 ) 

538 

539 try: 

540 try: 

541 resp = await req.send(conn) 

542 try: 

543 await resp.start(conn) 

544 except BaseException: 

545 resp.close() 

546 raise 

547 except BaseException: 

548 conn.close() 

549 raise 

550 except ClientError: 

551 raise 

552 except OSError as exc: 

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

554 raise 

555 raise ClientOSError(*exc.args) from exc 

556 

557 self._cookie_jar.update_cookies(resp.cookies, resp.url) 

558 

559 # redirects 

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

561 for trace in traces: 

562 await trace.send_request_redirect( 

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

564 ) 

565 

566 redirects += 1 

567 history.append(resp) 

568 if max_redirects and redirects >= max_redirects: 

569 resp.close() 

570 raise TooManyRedirects( 

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

572 ) 

573 

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

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

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

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

578 ): 

579 method = hdrs.METH_GET 

580 data = None 

581 if headers.get(hdrs.CONTENT_LENGTH): 

582 headers.pop(hdrs.CONTENT_LENGTH) 

583 

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

585 hdrs.URI 

586 ) 

587 if r_url is None: 

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

589 break 

590 else: 

591 # reading from correct redirection 

592 # response is forbidden 

593 resp.release() 

594 

595 try: 

596 parsed_url = URL( 

597 r_url, encoded=not self._requote_redirect_url 

598 ) 

599 

600 except ValueError as e: 

601 raise InvalidURL(r_url) from e 

602 

603 scheme = parsed_url.scheme 

604 if scheme not in ("http", "https", ""): 

605 resp.close() 

606 raise ValueError("Can redirect only to http or https") 

607 elif not scheme: 

608 parsed_url = url.join(parsed_url) 

609 

610 is_same_host_https_redirect = ( 

611 url.host == parsed_url.host 

612 and parsed_url.scheme == "https" 

613 and url.scheme == "http" 

614 ) 

615 

616 if ( 

617 url.origin() != parsed_url.origin() 

618 and not is_same_host_https_redirect 

619 ): 

620 auth = None 

621 headers.pop(hdrs.AUTHORIZATION, None) 

622 

623 url = parsed_url 

624 params = {} 

625 resp.release() 

626 continue 

627 

628 break 

629 

630 # check response status 

631 if raise_for_status is None: 

632 raise_for_status = self._raise_for_status 

633 

634 if raise_for_status is None: 

635 pass 

636 elif callable(raise_for_status): 

637 await raise_for_status(resp) 

638 elif raise_for_status: 

639 resp.raise_for_status() 

640 

641 # register connection 

642 if handle is not None: 

643 if resp.connection is not None: 

644 resp.connection.add_callback(handle.cancel) 

645 else: 

646 handle.cancel() 

647 

648 resp._history = tuple(history) 

649 

650 for trace in traces: 

651 await trace.send_request_end( 

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

653 ) 

654 return resp 

655 

656 except BaseException as e: 

657 # cleanup timer 

658 tm.close() 

659 if handle: 

660 handle.cancel() 

661 handle = None 

662 

663 for trace in traces: 

664 await trace.send_request_exception( 

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

666 ) 

667 raise 

668 

669 def ws_connect( 

670 self, 

671 url: StrOrURL, 

672 *, 

673 method: str = hdrs.METH_GET, 

674 protocols: Collection[str] = (), 

675 timeout: Union[ClientWSTimeout, float, _SENTINEL, None] = sentinel, 

676 receive_timeout: Optional[float] = None, 

677 autoclose: bool = True, 

678 autoping: bool = True, 

679 heartbeat: Optional[float] = None, 

680 auth: Optional[BasicAuth] = None, 

681 origin: Optional[str] = None, 

682 params: Optional[Mapping[str, str]] = None, 

683 headers: Optional[LooseHeaders] = None, 

684 proxy: Optional[StrOrURL] = None, 

685 proxy_auth: Optional[BasicAuth] = None, 

686 ssl: Union[SSLContext, Literal[False], None, Fingerprint] = None, 

687 proxy_headers: Optional[LooseHeaders] = None, 

688 compress: int = 0, 

689 max_msg_size: int = 4 * 1024 * 1024, 

690 ) -> "_WSRequestContextManager": 

691 """Initiate websocket connection.""" 

692 return _WSRequestContextManager( 

693 self._ws_connect( 

694 url, 

695 method=method, 

696 protocols=protocols, 

697 timeout=timeout, 

698 receive_timeout=receive_timeout, 

699 autoclose=autoclose, 

700 autoping=autoping, 

701 heartbeat=heartbeat, 

702 auth=auth, 

703 origin=origin, 

704 params=params, 

705 headers=headers, 

706 proxy=proxy, 

707 proxy_auth=proxy_auth, 

708 ssl=ssl, 

709 proxy_headers=proxy_headers, 

710 compress=compress, 

711 max_msg_size=max_msg_size, 

712 ) 

713 ) 

714 

715 async def _ws_connect( 

716 self, 

717 url: StrOrURL, 

718 *, 

719 method: str = hdrs.METH_GET, 

720 protocols: Collection[str] = (), 

721 timeout: Union[ClientWSTimeout, float, _SENTINEL, None] = sentinel, 

722 receive_timeout: Optional[float] = None, 

723 autoclose: bool = True, 

724 autoping: bool = True, 

725 heartbeat: Optional[float] = None, 

726 auth: Optional[BasicAuth] = None, 

727 origin: Optional[str] = None, 

728 params: Optional[Mapping[str, str]] = None, 

729 headers: Optional[LooseHeaders] = None, 

730 proxy: Optional[StrOrURL] = None, 

731 proxy_auth: Optional[BasicAuth] = None, 

732 ssl: Union[SSLContext, Literal[False], None, Fingerprint] = None, 

733 proxy_headers: Optional[LooseHeaders] = None, 

734 compress: int = 0, 

735 max_msg_size: int = 4 * 1024 * 1024, 

736 ) -> ClientWebSocketResponse: 

737 if timeout is sentinel or timeout is None: 

738 ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT 

739 else: 

740 if isinstance(timeout, ClientWSTimeout): 

741 ws_timeout = timeout 

742 else: 

743 warnings.warn( 

744 "parameter 'timeout' of type 'float' " 

745 "is deprecated, please use " 

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

747 DeprecationWarning, 

748 stacklevel=2, 

749 ) 

750 ws_timeout = ClientWSTimeout(ws_close=timeout) 

751 

752 if receive_timeout is not None: 

753 warnings.warn( 

754 "float parameter 'receive_timeout' " 

755 "is deprecated, please use parameter " 

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

757 DeprecationWarning, 

758 stacklevel=2, 

759 ) 

760 ws_timeout = dataclasses.replace(ws_timeout, ws_receive=receive_timeout) 

761 

762 if headers is None: 

763 real_headers: CIMultiDict[str] = CIMultiDict() 

764 else: 

765 real_headers = CIMultiDict(headers) 

766 

767 default_headers = { 

768 hdrs.UPGRADE: "websocket", 

769 hdrs.CONNECTION: "Upgrade", 

770 hdrs.SEC_WEBSOCKET_VERSION: "13", 

771 } 

772 

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

774 real_headers.setdefault(key, value) 

775 

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

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

778 

779 if protocols: 

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

781 if origin is not None: 

782 real_headers[hdrs.ORIGIN] = origin 

783 if compress: 

784 extstr = ws_ext_gen(compress=compress) 

785 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

786 

787 if not isinstance(ssl, SSL_ALLOWED_TYPES): 

788 raise TypeError( 

789 "ssl should be SSLContext, bool, Fingerprint, " 

790 "or None, got {!r} instead.".format(ssl) 

791 ) 

792 

793 # send request 

794 resp = await self.request( 

795 method, 

796 url, 

797 params=params, 

798 headers=real_headers, 

799 read_until_eof=False, 

800 auth=auth, 

801 proxy=proxy, 

802 proxy_auth=proxy_auth, 

803 ssl=ssl, 

804 proxy_headers=proxy_headers, 

805 ) 

806 

807 try: 

808 # check handshake 

809 if resp.status != 101: 

810 raise WSServerHandshakeError( 

811 resp.request_info, 

812 resp.history, 

813 message="Invalid response status", 

814 status=resp.status, 

815 headers=resp.headers, 

816 ) 

817 

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

819 raise WSServerHandshakeError( 

820 resp.request_info, 

821 resp.history, 

822 message="Invalid upgrade header", 

823 status=resp.status, 

824 headers=resp.headers, 

825 ) 

826 

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

828 raise WSServerHandshakeError( 

829 resp.request_info, 

830 resp.history, 

831 message="Invalid connection header", 

832 status=resp.status, 

833 headers=resp.headers, 

834 ) 

835 

836 # key calculation 

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

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

839 if r_key != match: 

840 raise WSServerHandshakeError( 

841 resp.request_info, 

842 resp.history, 

843 message="Invalid challenge response", 

844 status=resp.status, 

845 headers=resp.headers, 

846 ) 

847 

848 # websocket protocol 

849 protocol = None 

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

851 resp_protocols = [ 

852 proto.strip() 

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

854 ] 

855 

856 for proto in resp_protocols: 

857 if proto in protocols: 

858 protocol = proto 

859 break 

860 

861 # websocket compress 

862 notakeover = False 

863 if compress: 

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

865 if compress_hdrs: 

866 try: 

867 compress, notakeover = ws_ext_parse(compress_hdrs) 

868 except WSHandshakeError as exc: 

869 raise WSServerHandshakeError( 

870 resp.request_info, 

871 resp.history, 

872 message=exc.args[0], 

873 status=resp.status, 

874 headers=resp.headers, 

875 ) from exc 

876 else: 

877 compress = 0 

878 notakeover = False 

879 

880 conn = resp.connection 

881 assert conn is not None 

882 conn_proto = conn.protocol 

883 assert conn_proto is not None 

884 transport = conn.transport 

885 assert transport is not None 

886 reader: FlowControlDataQueue[WSMessage] = FlowControlDataQueue( 

887 conn_proto, 2**16, loop=self._loop 

888 ) 

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

890 writer = WebSocketWriter( 

891 conn_proto, 

892 transport, 

893 use_mask=True, 

894 compress=compress, 

895 notakeover=notakeover, 

896 ) 

897 except BaseException: 

898 resp.close() 

899 raise 

900 else: 

901 return self._ws_response_class( 

902 reader, 

903 writer, 

904 protocol, 

905 resp, 

906 ws_timeout, 

907 autoclose, 

908 autoping, 

909 self._loop, 

910 heartbeat=heartbeat, 

911 compress=compress, 

912 client_notakeover=notakeover, 

913 ) 

914 

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

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

917 # Convert headers to MultiDict 

918 result = CIMultiDict(self._default_headers) 

919 if headers: 

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

921 headers = CIMultiDict(headers) 

922 added_names: Set[str] = set() 

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

924 if key in added_names: 

925 result.add(key, value) 

926 else: 

927 result[key] = value 

928 added_names.add(key) 

929 return result 

930 

931 def get( 

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

933 ) -> "_RequestContextManager": 

934 """Perform HTTP GET request.""" 

935 return _RequestContextManager( 

936 self._request(hdrs.METH_GET, url, allow_redirects=allow_redirects, **kwargs) 

937 ) 

938 

939 def options( 

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

941 ) -> "_RequestContextManager": 

942 """Perform HTTP OPTIONS request.""" 

943 return _RequestContextManager( 

944 self._request( 

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

946 ) 

947 ) 

948 

949 def head( 

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

951 ) -> "_RequestContextManager": 

952 """Perform HTTP HEAD request.""" 

953 return _RequestContextManager( 

954 self._request( 

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

956 ) 

957 ) 

958 

959 def post( 

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

961 ) -> "_RequestContextManager": 

962 """Perform HTTP POST request.""" 

963 return _RequestContextManager( 

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

965 ) 

966 

967 def put( 

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

969 ) -> "_RequestContextManager": 

970 """Perform HTTP PUT request.""" 

971 return _RequestContextManager( 

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

973 ) 

974 

975 def patch( 

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

977 ) -> "_RequestContextManager": 

978 """Perform HTTP PATCH request.""" 

979 return _RequestContextManager( 

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

981 ) 

982 

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

984 """Perform HTTP DELETE request.""" 

985 return _RequestContextManager(self._request(hdrs.METH_DELETE, url, **kwargs)) 

986 

987 async def close(self) -> None: 

988 """Close underlying connector. 

989 

990 Release all acquired resources. 

991 """ 

992 if not self.closed: 

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

994 await self._connector.close() 

995 self._connector = None 

996 

997 @property 

998 def closed(self) -> bool: 

999 """Is client session closed. 

1000 

1001 A readonly property. 

1002 """ 

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

1004 

1005 @property 

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

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

1008 return self._connector 

1009 

1010 @property 

1011 def cookie_jar(self) -> AbstractCookieJar: 

1012 """The session cookies.""" 

1013 return self._cookie_jar 

1014 

1015 @property 

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

1017 """The session HTTP protocol version.""" 

1018 return self._version 

1019 

1020 @property 

1021 def requote_redirect_url(self) -> bool: 

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

1023 return self._requote_redirect_url 

1024 

1025 @property 

1026 def timeout(self) -> ClientTimeout: 

1027 """Timeout for the session.""" 

1028 return self._timeout 

1029 

1030 @property 

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

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

1033 return self._default_headers 

1034 

1035 @property 

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

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

1038 return self._skip_auto_headers 

1039 

1040 @property 

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

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

1043 return self._default_auth 

1044 

1045 @property 

1046 def json_serialize(self) -> JSONEncoder: 

1047 """Json serializer callable""" 

1048 return self._json_serialize 

1049 

1050 @property 

1051 def connector_owner(self) -> bool: 

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

1053 return self._connector_owner 

1054 

1055 @property 

1056 def raise_for_status( 

1057 self, 

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

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

1060 return self._raise_for_status 

1061 

1062 @property 

1063 def auto_decompress(self) -> bool: 

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

1065 return self._auto_decompress 

1066 

1067 @property 

1068 def trust_env(self) -> bool: 

1069 """ 

1070 Should proxies information from environment or netrc be trusted. 

1071 

1072 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1073 or ~/.netrc file if present. 

1074 """ 

1075 return self._trust_env 

1076 

1077 @property 

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

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

1080 return self._trace_configs 

1081 

1082 def detach(self) -> None: 

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

1084 

1085 Session is switched to closed state anyway. 

1086 """ 

1087 self._connector = None 

1088 

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

1090 return self 

1091 

1092 async def __aexit__( 

1093 self, 

1094 exc_type: Optional[Type[BaseException]], 

1095 exc_val: Optional[BaseException], 

1096 exc_tb: Optional[TracebackType], 

1097 ) -> None: 

1098 await self.close() 

1099 

1100 

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

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

1103 

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

1105 self._coro = coro 

1106 

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

1108 return self._coro.send(arg) 

1109 

1110 def throw(self, arg: BaseException) -> None: # type: ignore[override] 

1111 self._coro.throw(arg) # type: ignore[unused-awaitable] 

1112 

1113 def close(self) -> None: 

1114 return self._coro.close() 

1115 

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

1117 ret = self._coro.__await__() 

1118 return ret 

1119 

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

1121 return self.__await__() 

1122 

1123 async def __aenter__(self) -> _RetType: 

1124 self._resp = await self._coro 

1125 return self._resp 

1126 

1127 

1128class _RequestContextManager(_BaseRequestContextManager[ClientResponse]): 

1129 __slots__ = () 

1130 

1131 async def __aexit__( 

1132 self, 

1133 exc_type: Optional[Type[BaseException]], 

1134 exc: Optional[BaseException], 

1135 tb: Optional[TracebackType], 

1136 ) -> None: 

1137 # We're basing behavior on the exception as it can be caused by 

1138 # user code unrelated to the status of the connection. If you 

1139 # would like to close a connection you must do that 

1140 # explicitly. Otherwise connection error handling should kick in 

1141 # and close/recycle the connection as required. 

1142 self._resp.release() 

1143 

1144 

1145class _WSRequestContextManager(_BaseRequestContextManager[ClientWebSocketResponse]): 

1146 __slots__ = () 

1147 

1148 async def __aexit__( 

1149 self, 

1150 exc_type: Optional[Type[BaseException]], 

1151 exc: Optional[BaseException], 

1152 tb: Optional[TracebackType], 

1153 ) -> None: 

1154 await self._resp.close() 

1155 

1156 

1157class _SessionRequestContextManager: 

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

1159 

1160 def __init__( 

1161 self, 

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

1163 session: ClientSession, 

1164 ) -> None: 

1165 self._coro = coro 

1166 self._resp: Optional[ClientResponse] = None 

1167 self._session = session 

1168 

1169 async def __aenter__(self) -> ClientResponse: 

1170 try: 

1171 self._resp = await self._coro 

1172 except BaseException: 

1173 await self._session.close() 

1174 raise 

1175 else: 

1176 return self._resp 

1177 

1178 async def __aexit__( 

1179 self, 

1180 exc_type: Optional[Type[BaseException]], 

1181 exc: Optional[BaseException], 

1182 tb: Optional[TracebackType], 

1183 ) -> None: 

1184 assert self._resp is not None 

1185 self._resp.close() 

1186 await self._session.close() 

1187 

1188 

1189def request( 

1190 method: str, 

1191 url: StrOrURL, 

1192 *, 

1193 params: Optional[Mapping[str, str]] = None, 

1194 data: Any = None, 

1195 json: Any = None, 

1196 headers: Optional[LooseHeaders] = None, 

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

1198 auth: Optional[BasicAuth] = None, 

1199 allow_redirects: bool = True, 

1200 max_redirects: int = 10, 

1201 compress: Optional[str] = None, 

1202 chunked: Optional[bool] = None, 

1203 expect100: bool = False, 

1204 raise_for_status: Optional[bool] = None, 

1205 read_until_eof: bool = True, 

1206 proxy: Optional[StrOrURL] = None, 

1207 proxy_auth: Optional[BasicAuth] = None, 

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

1209 cookies: Optional[LooseCookies] = None, 

1210 version: HttpVersion = http.HttpVersion11, 

1211 connector: Optional[BaseConnector] = None, 

1212 read_bufsize: Optional[int] = None, 

1213 max_line_size: int = 8190, 

1214 max_field_size: int = 8190, 

1215) -> _SessionRequestContextManager: 

1216 """Constructs and sends a request. 

1217 

1218 Returns response object. 

1219 method - HTTP method 

1220 url - request url 

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

1222 string of the new request 

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

1224 send in the body of the request 

1225 json - (optional) Any json compatible python object 

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

1227 the request 

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

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

1230 auth - aiohttp.helpers.BasicAuth 

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

1232 redirects 

1233 version - Request HTTP version. 

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

1235 with deflate encoding. 

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

1237 expect100 - Expect 100-continue response from server. 

1238 connector - BaseConnector sub-class instance to support 

1239 connection pooling. 

1240 read_until_eof - Read response until eof if response 

1241 does not have Content-Length header. 

1242 loop - Optional event loop. 

1243 timeout - Optional ClientTimeout settings structure, 5min 

1244 total timeout by default. 

1245 Usage:: 

1246 >>> import aiohttp 

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

1248 ... print(resp) 

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

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

1251 """ 

1252 connector_owner = False 

1253 if connector is None: 

1254 connector_owner = True 

1255 connector = TCPConnector(force_close=True) 

1256 

1257 session = ClientSession( 

1258 cookies=cookies, 

1259 version=version, 

1260 timeout=timeout, 

1261 connector=connector, 

1262 connector_owner=connector_owner, 

1263 ) 

1264 

1265 return _SessionRequestContextManager( 

1266 session._request( 

1267 method, 

1268 url, 

1269 params=params, 

1270 data=data, 

1271 json=json, 

1272 headers=headers, 

1273 skip_auto_headers=skip_auto_headers, 

1274 auth=auth, 

1275 allow_redirects=allow_redirects, 

1276 max_redirects=max_redirects, 

1277 compress=compress, 

1278 chunked=chunked, 

1279 expect100=expect100, 

1280 raise_for_status=raise_for_status, 

1281 read_until_eof=read_until_eof, 

1282 proxy=proxy, 

1283 proxy_auth=proxy_auth, 

1284 read_bufsize=read_bufsize, 

1285 max_line_size=max_line_size, 

1286 max_field_size=max_field_size, 

1287 ), 

1288 session, 

1289 )