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

491 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-08 06:40 +0000

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

2 

3import asyncio 

4import base64 

5import hashlib 

6import json 

7import os 

8import sys 

9import traceback 

10import warnings 

11from contextlib import suppress 

12from types import SimpleNamespace, TracebackType 

13from typing import ( 

14 TYPE_CHECKING, 

15 Any, 

16 Awaitable, 

17 Callable, 

18 Coroutine, 

19 Final, 

20 FrozenSet, 

21 Generator, 

22 Generic, 

23 Iterable, 

24 List, 

25 Literal, 

26 Mapping, 

27 Optional, 

28 Set, 

29 Tuple, 

30 Type, 

31 TypeVar, 

32 Union, 

33) 

34 

35import attr 

36from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr 

37from yarl import URL 

38 

39from . import hdrs, http, payload 

40from .abc import AbstractCookieJar 

41from .client_exceptions import ( 

42 ClientConnectionError as ClientConnectionError, 

43 ClientConnectorCertificateError as ClientConnectorCertificateError, 

44 ClientConnectorError as ClientConnectorError, 

45 ClientConnectorSSLError as ClientConnectorSSLError, 

46 ClientError as ClientError, 

47 ClientHttpProxyError as ClientHttpProxyError, 

48 ClientOSError as ClientOSError, 

49 ClientPayloadError as ClientPayloadError, 

50 ClientProxyConnectionError as ClientProxyConnectionError, 

51 ClientResponseError as ClientResponseError, 

52 ClientSSLError as ClientSSLError, 

53 ContentTypeError as ContentTypeError, 

54 InvalidURL as InvalidURL, 

55 ServerConnectionError as ServerConnectionError, 

56 ServerDisconnectedError as ServerDisconnectedError, 

57 ServerFingerprintMismatch as ServerFingerprintMismatch, 

58 ServerTimeoutError as ServerTimeoutError, 

59 TooManyRedirects as TooManyRedirects, 

60 WSServerHandshakeError as WSServerHandshakeError, 

61) 

62from .client_reqrep import ( 

63 ClientRequest as ClientRequest, 

64 ClientResponse as ClientResponse, 

65 Fingerprint as Fingerprint, 

66 RequestInfo as RequestInfo, 

67 _merge_ssl_params, 

68) 

69from .client_ws import ClientWebSocketResponse as ClientWebSocketResponse 

70from .connector import ( 

71 BaseConnector as BaseConnector, 

72 NamedPipeConnector as NamedPipeConnector, 

73 TCPConnector as TCPConnector, 

74 UnixConnector as UnixConnector, 

75) 

76from .cookiejar import CookieJar 

77from .helpers import ( 

78 _SENTINEL, 

79 DEBUG, 

80 BasicAuth, 

81 TimeoutHandle, 

82 ceil_timeout, 

83 get_env_proxy_for_url, 

84 get_running_loop, 

85 method_must_be_empty_body, 

86 sentinel, 

87 strip_auth_from_url, 

88) 

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

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

91from .streams import FlowControlDataQueue 

92from .tracing import Trace, TraceConfig 

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

94 

95__all__ = ( 

96 # client_exceptions 

97 "ClientConnectionError", 

98 "ClientConnectorCertificateError", 

99 "ClientConnectorError", 

100 "ClientConnectorSSLError", 

101 "ClientError", 

102 "ClientHttpProxyError", 

103 "ClientOSError", 

104 "ClientPayloadError", 

105 "ClientProxyConnectionError", 

106 "ClientResponseError", 

107 "ClientSSLError", 

108 "ContentTypeError", 

109 "InvalidURL", 

110 "ServerConnectionError", 

111 "ServerDisconnectedError", 

112 "ServerFingerprintMismatch", 

113 "ServerTimeoutError", 

114 "TooManyRedirects", 

115 "WSServerHandshakeError", 

116 # client_reqrep 

117 "ClientRequest", 

118 "ClientResponse", 

119 "Fingerprint", 

120 "RequestInfo", 

121 # connector 

122 "BaseConnector", 

123 "TCPConnector", 

124 "UnixConnector", 

125 "NamedPipeConnector", 

126 # client_ws 

127 "ClientWebSocketResponse", 

128 # client 

129 "ClientSession", 

130 "ClientTimeout", 

131 "request", 

132) 

133 

134 

135if TYPE_CHECKING: 

136 from ssl import SSLContext 

137else: 

138 SSLContext = None 

139 

140 

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

142class ClientTimeout: 

143 total: Optional[float] = None 

144 connect: Optional[float] = None 

145 sock_read: Optional[float] = None 

146 sock_connect: Optional[float] = None 

147 ceil_threshold: float = 5 

148 

149 # pool_queue_timeout: Optional[float] = None 

150 # dns_resolution_timeout: Optional[float] = None 

151 # socket_connect_timeout: Optional[float] = None 

152 # connection_acquiring_timeout: Optional[float] = None 

153 # new_connection_timeout: Optional[float] = None 

154 # http_header_timeout: Optional[float] = None 

155 # response_body_timeout: Optional[float] = None 

156 

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

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

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

160 # to overwrite the defaults 

161 

162 

163# 5 Minute default read timeout 

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

165 

166_RetType = TypeVar("_RetType") 

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

168 

169 

170class ClientSession: 

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

172 

173 ATTRS = frozenset( 

174 [ 

175 "_base_url", 

176 "_source_traceback", 

177 "_connector", 

178 "requote_redirect_url", 

179 "_loop", 

180 "_cookie_jar", 

181 "_connector_owner", 

182 "_default_auth", 

183 "_version", 

184 "_json_serialize", 

185 "_requote_redirect_url", 

186 "_timeout", 

187 "_raise_for_status", 

188 "_auto_decompress", 

189 "_trust_env", 

190 "_default_headers", 

191 "_skip_auto_headers", 

192 "_request_class", 

193 "_response_class", 

194 "_ws_response_class", 

195 "_trace_configs", 

196 "_read_bufsize", 

197 "_max_line_size", 

198 "_max_field_size", 

199 "_resolve_charset", 

200 ] 

201 ) 

202 

203 _source_traceback: Optional[traceback.StackSummary] = None 

204 _connector: Optional[BaseConnector] = None 

205 

206 def __init__( 

207 self, 

208 base_url: Optional[StrOrURL] = None, 

209 *, 

210 connector: Optional[BaseConnector] = None, 

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

212 cookies: Optional[LooseCookies] = None, 

213 headers: Optional[LooseHeaders] = None, 

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

215 auth: Optional[BasicAuth] = None, 

216 json_serialize: JSONEncoder = json.dumps, 

217 request_class: Type[ClientRequest] = ClientRequest, 

218 response_class: Type[ClientResponse] = ClientResponse, 

219 ws_response_class: Type[ClientWebSocketResponse] = ClientWebSocketResponse, 

220 version: HttpVersion = http.HttpVersion11, 

221 cookie_jar: Optional[AbstractCookieJar] = None, 

222 connector_owner: bool = True, 

223 raise_for_status: Union[ 

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

225 ] = False, 

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

227 conn_timeout: Optional[float] = None, 

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

229 auto_decompress: bool = True, 

230 trust_env: bool = False, 

231 requote_redirect_url: bool = True, 

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

233 read_bufsize: int = 2**16, 

234 max_line_size: int = 8190, 

235 max_field_size: int = 8190, 

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

237 ) -> None: 

238 if loop is None: 

239 if connector is not None: 

240 loop = connector._loop 

241 

242 loop = get_running_loop(loop) 

243 

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

245 self._base_url: Optional[URL] = base_url 

246 else: 

247 self._base_url = URL(base_url) 

248 assert ( 

249 self._base_url.origin() == self._base_url 

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

251 

252 if connector is None: 

253 connector = TCPConnector(loop=loop) 

254 

255 if connector._loop is not loop: 

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

257 

258 self._loop = loop 

259 

260 if loop.get_debug(): 

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

262 

263 if cookie_jar is None: 

264 cookie_jar = CookieJar(loop=loop) 

265 self._cookie_jar = cookie_jar 

266 

267 if cookies is not None: 

268 self._cookie_jar.update_cookies(cookies) 

269 

270 self._connector = connector 

271 self._connector_owner = connector_owner 

272 self._default_auth = auth 

273 self._version = version 

274 self._json_serialize = json_serialize 

275 if timeout is sentinel: 

276 self._timeout = DEFAULT_TIMEOUT 

277 if read_timeout is not sentinel: 

278 warnings.warn( 

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

280 DeprecationWarning, 

281 stacklevel=2, 

282 ) 

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

284 if conn_timeout is not None: 

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

286 warnings.warn( 

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

288 DeprecationWarning, 

289 stacklevel=2, 

290 ) 

291 else: 

292 self._timeout = timeout # type: ignore[assignment] 

293 if read_timeout is not sentinel: 

294 raise ValueError( 

295 "read_timeout and timeout parameters " 

296 "conflict, please setup " 

297 "timeout.read" 

298 ) 

299 if conn_timeout is not None: 

300 raise ValueError( 

301 "conn_timeout and timeout parameters " 

302 "conflict, please setup " 

303 "timeout.connect" 

304 ) 

305 self._raise_for_status = raise_for_status 

306 self._auto_decompress = auto_decompress 

307 self._trust_env = trust_env 

308 self._requote_redirect_url = requote_redirect_url 

309 self._read_bufsize = read_bufsize 

310 self._max_line_size = max_line_size 

311 self._max_field_size = max_field_size 

312 

313 # Convert to list of tuples 

314 if headers: 

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

316 else: 

317 real_headers = CIMultiDict() 

318 self._default_headers: CIMultiDict[str] = real_headers 

319 if skip_auto_headers is not None: 

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

321 else: 

322 self._skip_auto_headers = frozenset() 

323 

324 self._request_class = request_class 

325 self._response_class = response_class 

326 self._ws_response_class = ws_response_class 

327 

328 self._trace_configs = trace_configs or [] 

329 for trace_config in self._trace_configs: 

330 trace_config.freeze() 

331 

332 self._resolve_charset = fallback_charset_resolver 

333 

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

335 warnings.warn( 

336 "Inheritance class {} from ClientSession " 

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

338 DeprecationWarning, 

339 stacklevel=2, 

340 ) 

341 

342 if DEBUG: 

343 

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

345 if name not in self.ATTRS: 

346 warnings.warn( 

347 "Setting custom ClientSession.{} attribute " 

348 "is discouraged".format(name), 

349 DeprecationWarning, 

350 stacklevel=2, 

351 ) 

352 super().__setattr__(name, val) 

353 

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

355 if not self.closed: 

356 kwargs = {"source": self} 

357 _warnings.warn( 

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

359 ) 

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

361 if self._source_traceback is not None: 

362 context["source_traceback"] = self._source_traceback 

363 self._loop.call_exception_handler(context) 

364 

365 def request( 

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

367 ) -> "_RequestContextManager": 

368 """Perform HTTP request.""" 

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

370 

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

372 url = URL(str_or_url) 

373 if self._base_url is None: 

374 return url 

375 else: 

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

377 return self._base_url.join(url) 

378 

379 async def _request( 

380 self, 

381 method: str, 

382 str_or_url: StrOrURL, 

383 *, 

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

385 data: Any = None, 

386 json: Any = None, 

387 cookies: Optional[LooseCookies] = None, 

388 headers: Optional[LooseHeaders] = None, 

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

390 auth: Optional[BasicAuth] = None, 

391 allow_redirects: bool = True, 

392 max_redirects: int = 10, 

393 compress: Optional[str] = None, 

394 chunked: Optional[bool] = None, 

395 expect100: bool = False, 

396 raise_for_status: Union[ 

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

398 ] = None, 

399 read_until_eof: bool = True, 

400 proxy: Optional[StrOrURL] = None, 

401 proxy_auth: Optional[BasicAuth] = None, 

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

403 verify_ssl: Optional[bool] = None, 

404 fingerprint: Optional[bytes] = None, 

405 ssl_context: Optional[SSLContext] = None, 

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

407 server_hostname: Optional[str] = None, 

408 proxy_headers: Optional[LooseHeaders] = None, 

409 trace_request_ctx: Optional[SimpleNamespace] = None, 

410 read_bufsize: Optional[int] = None, 

411 auto_decompress: Optional[bool] = None, 

412 max_line_size: Optional[int] = None, 

413 max_field_size: Optional[int] = None, 

414 ) -> ClientResponse: 

415 

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

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

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

419 

420 if self.closed: 

421 raise RuntimeError("Session is closed") 

422 

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

424 

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

426 raise ValueError( 

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

428 ) 

429 elif json is not None: 

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

431 

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

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

434 

435 redirects = 0 

436 history = [] 

437 version = self._version 

438 params = params or {} 

439 

440 # Merge with default headers and transform to CIMultiDict 

441 headers = self._prepare_headers(headers) 

442 proxy_headers = self._prepare_headers(proxy_headers) 

443 

444 try: 

445 url = self._build_url(str_or_url) 

446 except ValueError as e: 

447 raise InvalidURL(str_or_url) from e 

448 

449 skip_headers = set(self._skip_auto_headers) 

450 if skip_auto_headers is not None: 

451 for i in skip_auto_headers: 

452 skip_headers.add(istr(i)) 

453 

454 if proxy is not None: 

455 try: 

456 proxy = URL(proxy) 

457 except ValueError as e: 

458 raise InvalidURL(proxy) from e 

459 

460 if timeout is sentinel: 

461 real_timeout: ClientTimeout = self._timeout 

462 else: 

463 if not isinstance(timeout, ClientTimeout): 

464 real_timeout = ClientTimeout(total=timeout) 

465 else: 

466 real_timeout = timeout 

467 # timeout is cumulative for all request operations 

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

469 tm = TimeoutHandle( 

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

471 ) 

472 handle = tm.start() 

473 

474 if read_bufsize is None: 

475 read_bufsize = self._read_bufsize 

476 

477 if auto_decompress is None: 

478 auto_decompress = self._auto_decompress 

479 

480 if max_line_size is None: 

481 max_line_size = self._max_line_size 

482 

483 if max_field_size is None: 

484 max_field_size = self._max_field_size 

485 

486 traces = [ 

487 Trace( 

488 self, 

489 trace_config, 

490 trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx), 

491 ) 

492 for trace_config in self._trace_configs 

493 ] 

494 

495 for trace in traces: 

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

497 

498 timer = tm.timer() 

499 try: 

500 with timer: 

501 while True: 

502 url, auth_from_url = strip_auth_from_url(url) 

503 if auth and auth_from_url: 

504 raise ValueError( 

505 "Cannot combine AUTH argument with " 

506 "credentials encoded in URL" 

507 ) 

508 

509 if auth is None: 

510 auth = auth_from_url 

511 if auth is None: 

512 auth = self._default_auth 

513 # It would be confusing if we support explicit 

514 # Authorization header with auth argument 

515 if ( 

516 headers is not None 

517 and auth is not None 

518 and hdrs.AUTHORIZATION in headers 

519 ): 

520 raise ValueError( 

521 "Cannot combine AUTHORIZATION header " 

522 "with AUTH argument or credentials " 

523 "encoded in URL" 

524 ) 

525 

526 all_cookies = self._cookie_jar.filter_cookies(url) 

527 

528 if cookies is not None: 

529 tmp_cookie_jar = CookieJar() 

530 tmp_cookie_jar.update_cookies(cookies) 

531 req_cookies = tmp_cookie_jar.filter_cookies(url) 

532 if req_cookies: 

533 all_cookies.load(req_cookies) 

534 

535 if proxy is not None: 

536 proxy = URL(proxy) 

537 elif self._trust_env: 

538 with suppress(LookupError): 

539 proxy, proxy_auth = get_env_proxy_for_url(url) 

540 

541 req = self._request_class( 

542 method, 

543 url, 

544 params=params, 

545 headers=headers, 

546 skip_auto_headers=skip_headers, 

547 data=data, 

548 cookies=all_cookies, 

549 auth=auth, 

550 version=version, 

551 compress=compress, 

552 chunked=chunked, 

553 expect100=expect100, 

554 loop=self._loop, 

555 response_class=self._response_class, 

556 proxy=proxy, 

557 proxy_auth=proxy_auth, 

558 timer=timer, 

559 session=self, 

560 ssl=ssl, 

561 server_hostname=server_hostname, 

562 proxy_headers=proxy_headers, 

563 traces=traces, 

564 trust_env=self.trust_env, 

565 ) 

566 

567 # connection timeout 

568 try: 

569 async with ceil_timeout( 

570 real_timeout.connect, 

571 ceil_threshold=real_timeout.ceil_threshold, 

572 ): 

573 assert self._connector is not None 

574 conn = await self._connector.connect( 

575 req, traces=traces, timeout=real_timeout 

576 ) 

577 except asyncio.TimeoutError as exc: 

578 raise ServerTimeoutError( 

579 "Connection timeout " "to host {}".format(url) 

580 ) from exc 

581 

582 assert conn.transport is not None 

583 

584 assert conn.protocol is not None 

585 conn.protocol.set_response_params( 

586 timer=timer, 

587 skip_payload=method_must_be_empty_body(method), 

588 read_until_eof=read_until_eof, 

589 auto_decompress=auto_decompress, 

590 read_timeout=real_timeout.sock_read, 

591 read_bufsize=read_bufsize, 

592 timeout_ceil_threshold=self._connector._timeout_ceil_threshold, 

593 max_line_size=max_line_size, 

594 max_field_size=max_field_size, 

595 ) 

596 

597 try: 

598 try: 

599 resp = await req.send(conn) 

600 try: 

601 await resp.start(conn) 

602 except BaseException: 

603 resp.close() 

604 raise 

605 except BaseException: 

606 conn.close() 

607 raise 

608 except ClientError: 

609 raise 

610 except OSError as exc: 

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

612 raise 

613 raise ClientOSError(*exc.args) from exc 

614 

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

616 

617 # redirects 

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

619 

620 for trace in traces: 

621 await trace.send_request_redirect( 

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

623 ) 

624 

625 redirects += 1 

626 history.append(resp) 

627 if max_redirects and redirects >= max_redirects: 

628 resp.close() 

629 raise TooManyRedirects( 

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

631 ) 

632 

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

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

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

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

637 ): 

638 method = hdrs.METH_GET 

639 data = None 

640 if headers.get(hdrs.CONTENT_LENGTH): 

641 headers.pop(hdrs.CONTENT_LENGTH) 

642 

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

644 hdrs.URI 

645 ) 

646 if r_url is None: 

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

648 break 

649 else: 

650 # reading from correct redirection 

651 # response is forbidden 

652 resp.release() 

653 

654 try: 

655 parsed_url = URL( 

656 r_url, encoded=not self._requote_redirect_url 

657 ) 

658 

659 except ValueError as e: 

660 raise InvalidURL(r_url) from e 

661 

662 scheme = parsed_url.scheme 

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

664 resp.close() 

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

666 elif not scheme: 

667 parsed_url = url.join(parsed_url) 

668 

669 if url.origin() != parsed_url.origin(): 

670 auth = None 

671 headers.pop(hdrs.AUTHORIZATION, None) 

672 

673 url = parsed_url 

674 params = {} 

675 resp.release() 

676 continue 

677 

678 break 

679 

680 # check response status 

681 if raise_for_status is None: 

682 raise_for_status = self._raise_for_status 

683 

684 if raise_for_status is None: 

685 pass 

686 elif callable(raise_for_status): 

687 await raise_for_status(resp) 

688 elif raise_for_status: 

689 resp.raise_for_status() 

690 

691 # register connection 

692 if handle is not None: 

693 if resp.connection is not None: 

694 resp.connection.add_callback(handle.cancel) 

695 else: 

696 handle.cancel() 

697 

698 resp._history = tuple(history) 

699 

700 for trace in traces: 

701 await trace.send_request_end( 

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

703 ) 

704 return resp 

705 

706 except BaseException as e: 

707 # cleanup timer 

708 tm.close() 

709 if handle: 

710 handle.cancel() 

711 handle = None 

712 

713 for trace in traces: 

714 await trace.send_request_exception( 

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

716 ) 

717 raise 

718 

719 def ws_connect( 

720 self, 

721 url: StrOrURL, 

722 *, 

723 method: str = hdrs.METH_GET, 

724 protocols: Iterable[str] = (), 

725 timeout: float = 10.0, 

726 receive_timeout: Optional[float] = None, 

727 autoclose: bool = True, 

728 autoping: bool = True, 

729 heartbeat: Optional[float] = None, 

730 auth: Optional[BasicAuth] = None, 

731 origin: Optional[str] = None, 

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

733 headers: Optional[LooseHeaders] = None, 

734 proxy: Optional[StrOrURL] = None, 

735 proxy_auth: Optional[BasicAuth] = None, 

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

737 verify_ssl: Optional[bool] = None, 

738 fingerprint: Optional[bytes] = None, 

739 ssl_context: Optional[SSLContext] = None, 

740 proxy_headers: Optional[LooseHeaders] = None, 

741 compress: int = 0, 

742 max_msg_size: int = 4 * 1024 * 1024, 

743 ) -> "_WSRequestContextManager": 

744 """Initiate websocket connection.""" 

745 return _WSRequestContextManager( 

746 self._ws_connect( 

747 url, 

748 method=method, 

749 protocols=protocols, 

750 timeout=timeout, 

751 receive_timeout=receive_timeout, 

752 autoclose=autoclose, 

753 autoping=autoping, 

754 heartbeat=heartbeat, 

755 auth=auth, 

756 origin=origin, 

757 params=params, 

758 headers=headers, 

759 proxy=proxy, 

760 proxy_auth=proxy_auth, 

761 ssl=ssl, 

762 verify_ssl=verify_ssl, 

763 fingerprint=fingerprint, 

764 ssl_context=ssl_context, 

765 proxy_headers=proxy_headers, 

766 compress=compress, 

767 max_msg_size=max_msg_size, 

768 ) 

769 ) 

770 

771 async def _ws_connect( 

772 self, 

773 url: StrOrURL, 

774 *, 

775 method: str = hdrs.METH_GET, 

776 protocols: Iterable[str] = (), 

777 timeout: float = 10.0, 

778 receive_timeout: Optional[float] = None, 

779 autoclose: bool = True, 

780 autoping: bool = True, 

781 heartbeat: Optional[float] = None, 

782 auth: Optional[BasicAuth] = None, 

783 origin: Optional[str] = None, 

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

785 headers: Optional[LooseHeaders] = None, 

786 proxy: Optional[StrOrURL] = None, 

787 proxy_auth: Optional[BasicAuth] = None, 

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

789 verify_ssl: Optional[bool] = None, 

790 fingerprint: Optional[bytes] = None, 

791 ssl_context: Optional[SSLContext] = None, 

792 proxy_headers: Optional[LooseHeaders] = None, 

793 compress: int = 0, 

794 max_msg_size: int = 4 * 1024 * 1024, 

795 ) -> ClientWebSocketResponse: 

796 

797 if headers is None: 

798 real_headers: CIMultiDict[str] = CIMultiDict() 

799 else: 

800 real_headers = CIMultiDict(headers) 

801 

802 default_headers = { 

803 hdrs.UPGRADE: "websocket", 

804 hdrs.CONNECTION: "Upgrade", 

805 hdrs.SEC_WEBSOCKET_VERSION: "13", 

806 } 

807 

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

809 real_headers.setdefault(key, value) 

810 

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

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

813 

814 if protocols: 

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

816 if origin is not None: 

817 real_headers[hdrs.ORIGIN] = origin 

818 if compress: 

819 extstr = ws_ext_gen(compress=compress) 

820 real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr 

821 

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

823 

824 # send request 

825 resp = await self.request( 

826 method, 

827 url, 

828 params=params, 

829 headers=real_headers, 

830 read_until_eof=False, 

831 auth=auth, 

832 proxy=proxy, 

833 proxy_auth=proxy_auth, 

834 ssl=ssl, 

835 proxy_headers=proxy_headers, 

836 ) 

837 

838 try: 

839 # check handshake 

840 if resp.status != 101: 

841 raise WSServerHandshakeError( 

842 resp.request_info, 

843 resp.history, 

844 message="Invalid response status", 

845 status=resp.status, 

846 headers=resp.headers, 

847 ) 

848 

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

850 raise WSServerHandshakeError( 

851 resp.request_info, 

852 resp.history, 

853 message="Invalid upgrade header", 

854 status=resp.status, 

855 headers=resp.headers, 

856 ) 

857 

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

859 raise WSServerHandshakeError( 

860 resp.request_info, 

861 resp.history, 

862 message="Invalid connection header", 

863 status=resp.status, 

864 headers=resp.headers, 

865 ) 

866 

867 # key calculation 

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

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

870 if r_key != match: 

871 raise WSServerHandshakeError( 

872 resp.request_info, 

873 resp.history, 

874 message="Invalid challenge response", 

875 status=resp.status, 

876 headers=resp.headers, 

877 ) 

878 

879 # websocket protocol 

880 protocol = None 

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

882 resp_protocols = [ 

883 proto.strip() 

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

885 ] 

886 

887 for proto in resp_protocols: 

888 if proto in protocols: 

889 protocol = proto 

890 break 

891 

892 # websocket compress 

893 notakeover = False 

894 if compress: 

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

896 if compress_hdrs: 

897 try: 

898 compress, notakeover = ws_ext_parse(compress_hdrs) 

899 except WSHandshakeError as exc: 

900 raise WSServerHandshakeError( 

901 resp.request_info, 

902 resp.history, 

903 message=exc.args[0], 

904 status=resp.status, 

905 headers=resp.headers, 

906 ) from exc 

907 else: 

908 compress = 0 

909 notakeover = False 

910 

911 conn = resp.connection 

912 assert conn is not None 

913 conn_proto = conn.protocol 

914 assert conn_proto is not None 

915 transport = conn.transport 

916 assert transport is not None 

917 reader: FlowControlDataQueue[WSMessage] = FlowControlDataQueue( 

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

919 ) 

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

921 writer = WebSocketWriter( 

922 conn_proto, 

923 transport, 

924 use_mask=True, 

925 compress=compress, 

926 notakeover=notakeover, 

927 ) 

928 except BaseException: 

929 resp.close() 

930 raise 

931 else: 

932 return self._ws_response_class( 

933 reader, 

934 writer, 

935 protocol, 

936 resp, 

937 timeout, 

938 autoclose, 

939 autoping, 

940 self._loop, 

941 receive_timeout=receive_timeout, 

942 heartbeat=heartbeat, 

943 compress=compress, 

944 client_notakeover=notakeover, 

945 ) 

946 

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

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

949 # Convert headers to MultiDict 

950 result = CIMultiDict(self._default_headers) 

951 if headers: 

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

953 headers = CIMultiDict(headers) 

954 added_names: Set[str] = set() 

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

956 if key in added_names: 

957 result.add(key, value) 

958 else: 

959 result[key] = value 

960 added_names.add(key) 

961 return result 

962 

963 def get( 

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

965 ) -> "_RequestContextManager": 

966 """Perform HTTP GET request.""" 

967 return _RequestContextManager( 

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

969 ) 

970 

971 def options( 

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

973 ) -> "_RequestContextManager": 

974 """Perform HTTP OPTIONS request.""" 

975 return _RequestContextManager( 

976 self._request( 

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

978 ) 

979 ) 

980 

981 def head( 

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

983 ) -> "_RequestContextManager": 

984 """Perform HTTP HEAD request.""" 

985 return _RequestContextManager( 

986 self._request( 

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

988 ) 

989 ) 

990 

991 def post( 

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

993 ) -> "_RequestContextManager": 

994 """Perform HTTP POST request.""" 

995 return _RequestContextManager( 

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

997 ) 

998 

999 def put( 

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

1001 ) -> "_RequestContextManager": 

1002 """Perform HTTP PUT request.""" 

1003 return _RequestContextManager( 

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

1005 ) 

1006 

1007 def patch( 

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

1009 ) -> "_RequestContextManager": 

1010 """Perform HTTP PATCH request.""" 

1011 return _RequestContextManager( 

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

1013 ) 

1014 

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

1016 """Perform HTTP DELETE request.""" 

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

1018 

1019 async def close(self) -> None: 

1020 """Close underlying connector. 

1021 

1022 Release all acquired resources. 

1023 """ 

1024 if not self.closed: 

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

1026 await self._connector.close() 

1027 self._connector = None 

1028 

1029 @property 

1030 def closed(self) -> bool: 

1031 """Is client session closed. 

1032 

1033 A readonly property. 

1034 """ 

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

1036 

1037 @property 

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

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

1040 return self._connector 

1041 

1042 @property 

1043 def cookie_jar(self) -> AbstractCookieJar: 

1044 """The session cookies.""" 

1045 return self._cookie_jar 

1046 

1047 @property 

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

1049 """The session HTTP protocol version.""" 

1050 return self._version 

1051 

1052 @property 

1053 def requote_redirect_url(self) -> bool: 

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

1055 return self._requote_redirect_url 

1056 

1057 @requote_redirect_url.setter 

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

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

1060 warnings.warn( 

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

1062 DeprecationWarning, 

1063 stacklevel=2, 

1064 ) 

1065 self._requote_redirect_url = val 

1066 

1067 @property 

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

1069 """Session's loop.""" 

1070 warnings.warn( 

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

1072 ) 

1073 return self._loop 

1074 

1075 @property 

1076 def timeout(self) -> ClientTimeout: 

1077 """Timeout for the session.""" 

1078 return self._timeout 

1079 

1080 @property 

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

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

1083 return self._default_headers 

1084 

1085 @property 

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

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

1088 return self._skip_auto_headers 

1089 

1090 @property 

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

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

1093 return self._default_auth 

1094 

1095 @property 

1096 def json_serialize(self) -> JSONEncoder: 

1097 """Json serializer callable""" 

1098 return self._json_serialize 

1099 

1100 @property 

1101 def connector_owner(self) -> bool: 

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

1103 return self._connector_owner 

1104 

1105 @property 

1106 def raise_for_status( 

1107 self, 

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

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

1110 return self._raise_for_status 

1111 

1112 @property 

1113 def auto_decompress(self) -> bool: 

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

1115 return self._auto_decompress 

1116 

1117 @property 

1118 def trust_env(self) -> bool: 

1119 """ 

1120 Should proxies information from environment or netrc be trusted. 

1121 

1122 Information is from HTTP_PROXY / HTTPS_PROXY environment variables 

1123 or ~/.netrc file if present. 

1124 """ 

1125 return self._trust_env 

1126 

1127 @property 

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

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

1130 return self._trace_configs 

1131 

1132 def detach(self) -> None: 

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

1134 

1135 Session is switched to closed state anyway. 

1136 """ 

1137 self._connector = None 

1138 

1139 def __enter__(self) -> None: 

1140 raise TypeError("Use async with instead") 

1141 

1142 def __exit__( 

1143 self, 

1144 exc_type: Optional[Type[BaseException]], 

1145 exc_val: Optional[BaseException], 

1146 exc_tb: Optional[TracebackType], 

1147 ) -> None: 

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

1149 pass # pragma: no cover 

1150 

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

1152 return self 

1153 

1154 async def __aexit__( 

1155 self, 

1156 exc_type: Optional[Type[BaseException]], 

1157 exc_val: Optional[BaseException], 

1158 exc_tb: Optional[TracebackType], 

1159 ) -> None: 

1160 await self.close() 

1161 

1162 

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

1164 

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

1166 

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

1168 self._coro = coro 

1169 

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

1171 return self._coro.send(arg) 

1172 

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

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

1175 

1176 def close(self) -> None: 

1177 return self._coro.close() 

1178 

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

1180 ret = self._coro.__await__() 

1181 return ret 

1182 

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

1184 return self.__await__() 

1185 

1186 async def __aenter__(self) -> _RetType: 

1187 self._resp = await self._coro 

1188 return self._resp 

1189 

1190 

1191class _RequestContextManager(_BaseRequestContextManager[ClientResponse]): 

1192 __slots__ = () 

1193 

1194 async def __aexit__( 

1195 self, 

1196 exc_type: Optional[Type[BaseException]], 

1197 exc: Optional[BaseException], 

1198 tb: Optional[TracebackType], 

1199 ) -> None: 

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

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

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

1203 # explicitly. Otherwise connection error handling should kick in 

1204 # and close/recycle the connection as required. 

1205 self._resp.release() 

1206 await self._resp.wait_for_close() 

1207 

1208 

1209class _WSRequestContextManager(_BaseRequestContextManager[ClientWebSocketResponse]): 

1210 __slots__ = () 

1211 

1212 async def __aexit__( 

1213 self, 

1214 exc_type: Optional[Type[BaseException]], 

1215 exc: Optional[BaseException], 

1216 tb: Optional[TracebackType], 

1217 ) -> None: 

1218 await self._resp.close() 

1219 

1220 

1221class _SessionRequestContextManager: 

1222 

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

1224 

1225 def __init__( 

1226 self, 

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

1228 session: ClientSession, 

1229 ) -> None: 

1230 self._coro = coro 

1231 self._resp: Optional[ClientResponse] = None 

1232 self._session = session 

1233 

1234 async def __aenter__(self) -> ClientResponse: 

1235 try: 

1236 self._resp = await self._coro 

1237 except BaseException: 

1238 await self._session.close() 

1239 raise 

1240 else: 

1241 return self._resp 

1242 

1243 async def __aexit__( 

1244 self, 

1245 exc_type: Optional[Type[BaseException]], 

1246 exc: Optional[BaseException], 

1247 tb: Optional[TracebackType], 

1248 ) -> None: 

1249 assert self._resp is not None 

1250 self._resp.close() 

1251 await self._session.close() 

1252 

1253 

1254def request( 

1255 method: str, 

1256 url: StrOrURL, 

1257 *, 

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

1259 data: Any = None, 

1260 json: Any = None, 

1261 headers: Optional[LooseHeaders] = None, 

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

1263 auth: Optional[BasicAuth] = None, 

1264 allow_redirects: bool = True, 

1265 max_redirects: int = 10, 

1266 compress: Optional[str] = None, 

1267 chunked: Optional[bool] = None, 

1268 expect100: bool = False, 

1269 raise_for_status: Optional[bool] = None, 

1270 read_until_eof: bool = True, 

1271 proxy: Optional[StrOrURL] = None, 

1272 proxy_auth: Optional[BasicAuth] = None, 

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

1274 cookies: Optional[LooseCookies] = None, 

1275 version: HttpVersion = http.HttpVersion11, 

1276 connector: Optional[BaseConnector] = None, 

1277 read_bufsize: Optional[int] = None, 

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

1279 max_line_size: int = 8190, 

1280 max_field_size: int = 8190, 

1281) -> _SessionRequestContextManager: 

1282 """Constructs and sends a request. 

1283 

1284 Returns response object. 

1285 method - HTTP method 

1286 url - request url 

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

1288 string of the new request 

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

1290 send in the body of the request 

1291 json - (optional) Any json compatible python object 

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

1293 the request 

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

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

1296 auth - aiohttp.helpers.BasicAuth 

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

1298 redirects 

1299 version - Request HTTP version. 

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

1301 with deflate encoding. 

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

1303 expect100 - Expect 100-continue response from server. 

1304 connector - BaseConnector sub-class instance to support 

1305 connection pooling. 

1306 read_until_eof - Read response until eof if response 

1307 does not have Content-Length header. 

1308 loop - Optional event loop. 

1309 timeout - Optional ClientTimeout settings structure, 5min 

1310 total timeout by default. 

1311 Usage:: 

1312 >>> import aiohttp 

1313 >>> resp = await aiohttp.request('GET', 'http://python.org/') 

1314 >>> resp 

1315 <ClientResponse(python.org/) [200]> 

1316 >>> data = await resp.read() 

1317 """ 

1318 connector_owner = False 

1319 if connector is None: 

1320 connector_owner = True 

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

1322 

1323 session = ClientSession( 

1324 loop=loop, 

1325 cookies=cookies, 

1326 version=version, 

1327 timeout=timeout, 

1328 connector=connector, 

1329 connector_owner=connector_owner, 

1330 ) 

1331 

1332 return _SessionRequestContextManager( 

1333 session._request( 

1334 method, 

1335 url, 

1336 params=params, 

1337 data=data, 

1338 json=json, 

1339 headers=headers, 

1340 skip_auto_headers=skip_auto_headers, 

1341 auth=auth, 

1342 allow_redirects=allow_redirects, 

1343 max_redirects=max_redirects, 

1344 compress=compress, 

1345 chunked=chunked, 

1346 expect100=expect100, 

1347 raise_for_status=raise_for_status, 

1348 read_until_eof=read_until_eof, 

1349 proxy=proxy, 

1350 proxy_auth=proxy_auth, 

1351 read_bufsize=read_bufsize, 

1352 max_line_size=max_line_size, 

1353 max_field_size=max_field_size, 

1354 ), 

1355 session, 

1356 )