client.py 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635
  1. """HTTP Client for asyncio."""
  2. import asyncio
  3. import base64
  4. import hashlib
  5. import json
  6. import os
  7. import sys
  8. import traceback
  9. import warnings
  10. from contextlib import suppress
  11. from types import TracebackType
  12. from typing import (
  13. TYPE_CHECKING,
  14. Any,
  15. Awaitable,
  16. Callable,
  17. Coroutine,
  18. Final,
  19. FrozenSet,
  20. Generator,
  21. Generic,
  22. Iterable,
  23. List,
  24. Mapping,
  25. Optional,
  26. Sequence,
  27. Set,
  28. Tuple,
  29. Type,
  30. TypedDict,
  31. TypeVar,
  32. Union,
  33. )
  34. import attr
  35. from multidict import CIMultiDict, MultiDict, MultiDictProxy, istr
  36. from yarl import URL
  37. from . import hdrs, http, payload
  38. from ._websocket.reader import WebSocketDataQueue
  39. from .abc import AbstractCookieJar
  40. from .client_exceptions import (
  41. ClientConnectionError,
  42. ClientConnectionResetError,
  43. ClientConnectorCertificateError,
  44. ClientConnectorDNSError,
  45. ClientConnectorError,
  46. ClientConnectorSSLError,
  47. ClientError,
  48. ClientHttpProxyError,
  49. ClientOSError,
  50. ClientPayloadError,
  51. ClientProxyConnectionError,
  52. ClientResponseError,
  53. ClientSSLError,
  54. ConnectionTimeoutError,
  55. ContentTypeError,
  56. InvalidURL,
  57. InvalidUrlClientError,
  58. InvalidUrlRedirectClientError,
  59. NonHttpUrlClientError,
  60. NonHttpUrlRedirectClientError,
  61. RedirectClientError,
  62. ServerConnectionError,
  63. ServerDisconnectedError,
  64. ServerFingerprintMismatch,
  65. ServerTimeoutError,
  66. SocketTimeoutError,
  67. TooManyRedirects,
  68. WSMessageTypeError,
  69. WSServerHandshakeError,
  70. )
  71. from .client_middlewares import ClientMiddlewareType, build_client_middlewares
  72. from .client_reqrep import (
  73. ClientRequest as ClientRequest,
  74. ClientResponse as ClientResponse,
  75. Fingerprint as Fingerprint,
  76. RequestInfo as RequestInfo,
  77. _merge_ssl_params,
  78. )
  79. from .client_ws import (
  80. DEFAULT_WS_CLIENT_TIMEOUT,
  81. ClientWebSocketResponse as ClientWebSocketResponse,
  82. ClientWSTimeout as ClientWSTimeout,
  83. )
  84. from .connector import (
  85. HTTP_AND_EMPTY_SCHEMA_SET,
  86. BaseConnector as BaseConnector,
  87. NamedPipeConnector as NamedPipeConnector,
  88. TCPConnector as TCPConnector,
  89. UnixConnector as UnixConnector,
  90. )
  91. from .cookiejar import CookieJar
  92. from .helpers import (
  93. _SENTINEL,
  94. DEBUG,
  95. EMPTY_BODY_METHODS,
  96. BasicAuth,
  97. TimeoutHandle,
  98. basicauth_from_netrc,
  99. get_env_proxy_for_url,
  100. netrc_from_env,
  101. sentinel,
  102. strip_auth_from_url,
  103. )
  104. from .http import WS_KEY, HttpVersion, WebSocketReader, WebSocketWriter
  105. from .http_websocket import WSHandshakeError, ws_ext_gen, ws_ext_parse
  106. from .tracing import Trace, TraceConfig
  107. from .typedefs import JSONEncoder, LooseCookies, LooseHeaders, Query, StrOrURL
  108. __all__ = (
  109. # client_exceptions
  110. "ClientConnectionError",
  111. "ClientConnectionResetError",
  112. "ClientConnectorCertificateError",
  113. "ClientConnectorDNSError",
  114. "ClientConnectorError",
  115. "ClientConnectorSSLError",
  116. "ClientError",
  117. "ClientHttpProxyError",
  118. "ClientOSError",
  119. "ClientPayloadError",
  120. "ClientProxyConnectionError",
  121. "ClientResponseError",
  122. "ClientSSLError",
  123. "ConnectionTimeoutError",
  124. "ContentTypeError",
  125. "InvalidURL",
  126. "InvalidUrlClientError",
  127. "RedirectClientError",
  128. "NonHttpUrlClientError",
  129. "InvalidUrlRedirectClientError",
  130. "NonHttpUrlRedirectClientError",
  131. "ServerConnectionError",
  132. "ServerDisconnectedError",
  133. "ServerFingerprintMismatch",
  134. "ServerTimeoutError",
  135. "SocketTimeoutError",
  136. "TooManyRedirects",
  137. "WSServerHandshakeError",
  138. # client_reqrep
  139. "ClientRequest",
  140. "ClientResponse",
  141. "Fingerprint",
  142. "RequestInfo",
  143. # connector
  144. "BaseConnector",
  145. "TCPConnector",
  146. "UnixConnector",
  147. "NamedPipeConnector",
  148. # client_ws
  149. "ClientWebSocketResponse",
  150. # client
  151. "ClientSession",
  152. "ClientTimeout",
  153. "ClientWSTimeout",
  154. "request",
  155. "WSMessageTypeError",
  156. )
  157. if TYPE_CHECKING:
  158. from ssl import SSLContext
  159. else:
  160. SSLContext = None
  161. if sys.version_info >= (3, 11) and TYPE_CHECKING:
  162. from typing import Unpack
  163. class _RequestOptions(TypedDict, total=False):
  164. params: Query
  165. data: Any
  166. json: Any
  167. cookies: Union[LooseCookies, None]
  168. headers: Union[LooseHeaders, None]
  169. skip_auto_headers: Union[Iterable[str], None]
  170. auth: Union[BasicAuth, None]
  171. allow_redirects: bool
  172. max_redirects: int
  173. compress: Union[str, bool, None]
  174. chunked: Union[bool, None]
  175. expect100: bool
  176. raise_for_status: Union[None, bool, Callable[[ClientResponse], Awaitable[None]]]
  177. read_until_eof: bool
  178. proxy: Union[StrOrURL, None]
  179. proxy_auth: Union[BasicAuth, None]
  180. timeout: "Union[ClientTimeout, _SENTINEL, None]"
  181. ssl: Union[SSLContext, bool, Fingerprint]
  182. server_hostname: Union[str, None]
  183. proxy_headers: Union[LooseHeaders, None]
  184. trace_request_ctx: Union[Mapping[str, Any], None]
  185. read_bufsize: Union[int, None]
  186. auto_decompress: Union[bool, None]
  187. max_line_size: Union[int, None]
  188. max_field_size: Union[int, None]
  189. middlewares: Optional[Sequence[ClientMiddlewareType]]
  190. @attr.s(auto_attribs=True, frozen=True, slots=True)
  191. class ClientTimeout:
  192. total: Optional[float] = None
  193. connect: Optional[float] = None
  194. sock_read: Optional[float] = None
  195. sock_connect: Optional[float] = None
  196. ceil_threshold: float = 5
  197. # pool_queue_timeout: Optional[float] = None
  198. # dns_resolution_timeout: Optional[float] = None
  199. # socket_connect_timeout: Optional[float] = None
  200. # connection_acquiring_timeout: Optional[float] = None
  201. # new_connection_timeout: Optional[float] = None
  202. # http_header_timeout: Optional[float] = None
  203. # response_body_timeout: Optional[float] = None
  204. # to create a timeout specific for a single request, either
  205. # - create a completely new one to overwrite the default
  206. # - or use http://www.attrs.org/en/stable/api.html#attr.evolve
  207. # to overwrite the defaults
  208. # 5 Minute default read timeout
  209. DEFAULT_TIMEOUT: Final[ClientTimeout] = ClientTimeout(total=5 * 60, sock_connect=30)
  210. # https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2
  211. IDEMPOTENT_METHODS = frozenset({"GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"})
  212. _RetType = TypeVar("_RetType", ClientResponse, ClientWebSocketResponse)
  213. _CharsetResolver = Callable[[ClientResponse, bytes], str]
  214. class ClientSession:
  215. """First-class interface for making HTTP requests."""
  216. ATTRS = frozenset(
  217. [
  218. "_base_url",
  219. "_base_url_origin",
  220. "_source_traceback",
  221. "_connector",
  222. "_loop",
  223. "_cookie_jar",
  224. "_connector_owner",
  225. "_default_auth",
  226. "_version",
  227. "_json_serialize",
  228. "_requote_redirect_url",
  229. "_timeout",
  230. "_raise_for_status",
  231. "_auto_decompress",
  232. "_trust_env",
  233. "_default_headers",
  234. "_skip_auto_headers",
  235. "_request_class",
  236. "_response_class",
  237. "_ws_response_class",
  238. "_trace_configs",
  239. "_read_bufsize",
  240. "_max_line_size",
  241. "_max_field_size",
  242. "_resolve_charset",
  243. "_default_proxy",
  244. "_default_proxy_auth",
  245. "_retry_connection",
  246. "_middlewares",
  247. "requote_redirect_url",
  248. ]
  249. )
  250. _source_traceback: Optional[traceback.StackSummary] = None
  251. _connector: Optional[BaseConnector] = None
  252. def __init__(
  253. self,
  254. base_url: Optional[StrOrURL] = None,
  255. *,
  256. connector: Optional[BaseConnector] = None,
  257. loop: Optional[asyncio.AbstractEventLoop] = None,
  258. cookies: Optional[LooseCookies] = None,
  259. headers: Optional[LooseHeaders] = None,
  260. proxy: Optional[StrOrURL] = None,
  261. proxy_auth: Optional[BasicAuth] = None,
  262. skip_auto_headers: Optional[Iterable[str]] = None,
  263. auth: Optional[BasicAuth] = None,
  264. json_serialize: JSONEncoder = json.dumps,
  265. request_class: Type[ClientRequest] = ClientRequest,
  266. response_class: Type[ClientResponse] = ClientResponse,
  267. ws_response_class: Type[ClientWebSocketResponse] = ClientWebSocketResponse,
  268. version: HttpVersion = http.HttpVersion11,
  269. cookie_jar: Optional[AbstractCookieJar] = None,
  270. connector_owner: bool = True,
  271. raise_for_status: Union[
  272. bool, Callable[[ClientResponse], Awaitable[None]]
  273. ] = False,
  274. read_timeout: Union[float, _SENTINEL] = sentinel,
  275. conn_timeout: Optional[float] = None,
  276. timeout: Union[object, ClientTimeout] = sentinel,
  277. auto_decompress: bool = True,
  278. trust_env: bool = False,
  279. requote_redirect_url: bool = True,
  280. trace_configs: Optional[List[TraceConfig]] = None,
  281. read_bufsize: int = 2**16,
  282. max_line_size: int = 8190,
  283. max_field_size: int = 8190,
  284. fallback_charset_resolver: _CharsetResolver = lambda r, b: "utf-8",
  285. middlewares: Sequence[ClientMiddlewareType] = (),
  286. ssl_shutdown_timeout: Union[_SENTINEL, None, float] = sentinel,
  287. ) -> None:
  288. # We initialise _connector to None immediately, as it's referenced in __del__()
  289. # and could cause issues if an exception occurs during initialisation.
  290. self._connector: Optional[BaseConnector] = None
  291. if loop is None:
  292. if connector is not None:
  293. loop = connector._loop
  294. loop = loop or asyncio.get_running_loop()
  295. if base_url is None or isinstance(base_url, URL):
  296. self._base_url: Optional[URL] = base_url
  297. self._base_url_origin = None if base_url is None else base_url.origin()
  298. else:
  299. self._base_url = URL(base_url)
  300. self._base_url_origin = self._base_url.origin()
  301. assert self._base_url.absolute, "Only absolute URLs are supported"
  302. if self._base_url is not None and not self._base_url.path.endswith("/"):
  303. raise ValueError("base_url must have a trailing '/'")
  304. if timeout is sentinel or timeout is None:
  305. self._timeout = DEFAULT_TIMEOUT
  306. if read_timeout is not sentinel:
  307. warnings.warn(
  308. "read_timeout is deprecated, use timeout argument instead",
  309. DeprecationWarning,
  310. stacklevel=2,
  311. )
  312. self._timeout = attr.evolve(self._timeout, total=read_timeout)
  313. if conn_timeout is not None:
  314. self._timeout = attr.evolve(self._timeout, connect=conn_timeout)
  315. warnings.warn(
  316. "conn_timeout is deprecated, use timeout argument instead",
  317. DeprecationWarning,
  318. stacklevel=2,
  319. )
  320. else:
  321. if not isinstance(timeout, ClientTimeout):
  322. raise ValueError(
  323. f"timeout parameter cannot be of {type(timeout)} type, "
  324. "please use 'timeout=ClientTimeout(...)'",
  325. )
  326. self._timeout = timeout
  327. if read_timeout is not sentinel:
  328. raise ValueError(
  329. "read_timeout and timeout parameters "
  330. "conflict, please setup "
  331. "timeout.read"
  332. )
  333. if conn_timeout is not None:
  334. raise ValueError(
  335. "conn_timeout and timeout parameters "
  336. "conflict, please setup "
  337. "timeout.connect"
  338. )
  339. if ssl_shutdown_timeout is not sentinel:
  340. warnings.warn(
  341. "The ssl_shutdown_timeout parameter is deprecated and will be removed in aiohttp 4.0",
  342. DeprecationWarning,
  343. stacklevel=2,
  344. )
  345. if connector is None:
  346. connector = TCPConnector(
  347. loop=loop, ssl_shutdown_timeout=ssl_shutdown_timeout
  348. )
  349. if connector._loop is not loop:
  350. raise RuntimeError("Session and connector has to use same event loop")
  351. self._loop = loop
  352. if loop.get_debug():
  353. self._source_traceback = traceback.extract_stack(sys._getframe(1))
  354. if cookie_jar is None:
  355. cookie_jar = CookieJar(loop=loop)
  356. self._cookie_jar = cookie_jar
  357. if cookies:
  358. self._cookie_jar.update_cookies(cookies)
  359. self._connector = connector
  360. self._connector_owner = connector_owner
  361. self._default_auth = auth
  362. self._version = version
  363. self._json_serialize = json_serialize
  364. self._raise_for_status = raise_for_status
  365. self._auto_decompress = auto_decompress
  366. self._trust_env = trust_env
  367. self._requote_redirect_url = requote_redirect_url
  368. self._read_bufsize = read_bufsize
  369. self._max_line_size = max_line_size
  370. self._max_field_size = max_field_size
  371. # Convert to list of tuples
  372. if headers:
  373. real_headers: CIMultiDict[str] = CIMultiDict(headers)
  374. else:
  375. real_headers = CIMultiDict()
  376. self._default_headers: CIMultiDict[str] = real_headers
  377. if skip_auto_headers is not None:
  378. self._skip_auto_headers = frozenset(istr(i) for i in skip_auto_headers)
  379. else:
  380. self._skip_auto_headers = frozenset()
  381. self._request_class = request_class
  382. self._response_class = response_class
  383. self._ws_response_class = ws_response_class
  384. self._trace_configs = trace_configs or []
  385. for trace_config in self._trace_configs:
  386. trace_config.freeze()
  387. self._resolve_charset = fallback_charset_resolver
  388. self._default_proxy = proxy
  389. self._default_proxy_auth = proxy_auth
  390. self._retry_connection: bool = True
  391. self._middlewares = middlewares
  392. def __init_subclass__(cls: Type["ClientSession"]) -> None:
  393. warnings.warn(
  394. "Inheritance class {} from ClientSession "
  395. "is discouraged".format(cls.__name__),
  396. DeprecationWarning,
  397. stacklevel=2,
  398. )
  399. if DEBUG:
  400. def __setattr__(self, name: str, val: Any) -> None:
  401. if name not in self.ATTRS:
  402. warnings.warn(
  403. "Setting custom ClientSession.{} attribute "
  404. "is discouraged".format(name),
  405. DeprecationWarning,
  406. stacklevel=2,
  407. )
  408. super().__setattr__(name, val)
  409. def __del__(self, _warnings: Any = warnings) -> None:
  410. if not self.closed:
  411. kwargs = {"source": self}
  412. _warnings.warn(
  413. f"Unclosed client session {self!r}", ResourceWarning, **kwargs
  414. )
  415. context = {"client_session": self, "message": "Unclosed client session"}
  416. if self._source_traceback is not None:
  417. context["source_traceback"] = self._source_traceback
  418. self._loop.call_exception_handler(context)
  419. if sys.version_info >= (3, 11) and TYPE_CHECKING:
  420. def request(
  421. self,
  422. method: str,
  423. url: StrOrURL,
  424. **kwargs: Unpack[_RequestOptions],
  425. ) -> "_RequestContextManager": ...
  426. else:
  427. def request(
  428. self, method: str, url: StrOrURL, **kwargs: Any
  429. ) -> "_RequestContextManager":
  430. """Perform HTTP request."""
  431. return _RequestContextManager(self._request(method, url, **kwargs))
  432. def _build_url(self, str_or_url: StrOrURL) -> URL:
  433. url = URL(str_or_url)
  434. if self._base_url and not url.absolute:
  435. return self._base_url.join(url)
  436. return url
  437. async def _request(
  438. self,
  439. method: str,
  440. str_or_url: StrOrURL,
  441. *,
  442. params: Query = None,
  443. data: Any = None,
  444. json: Any = None,
  445. cookies: Optional[LooseCookies] = None,
  446. headers: Optional[LooseHeaders] = None,
  447. skip_auto_headers: Optional[Iterable[str]] = None,
  448. auth: Optional[BasicAuth] = None,
  449. allow_redirects: bool = True,
  450. max_redirects: int = 10,
  451. compress: Union[str, bool, None] = None,
  452. chunked: Optional[bool] = None,
  453. expect100: bool = False,
  454. raise_for_status: Union[
  455. None, bool, Callable[[ClientResponse], Awaitable[None]]
  456. ] = None,
  457. read_until_eof: bool = True,
  458. proxy: Optional[StrOrURL] = None,
  459. proxy_auth: Optional[BasicAuth] = None,
  460. timeout: Union[ClientTimeout, _SENTINEL] = sentinel,
  461. verify_ssl: Optional[bool] = None,
  462. fingerprint: Optional[bytes] = None,
  463. ssl_context: Optional[SSLContext] = None,
  464. ssl: Union[SSLContext, bool, Fingerprint] = True,
  465. server_hostname: Optional[str] = None,
  466. proxy_headers: Optional[LooseHeaders] = None,
  467. trace_request_ctx: Optional[Mapping[str, Any]] = None,
  468. read_bufsize: Optional[int] = None,
  469. auto_decompress: Optional[bool] = None,
  470. max_line_size: Optional[int] = None,
  471. max_field_size: Optional[int] = None,
  472. middlewares: Optional[Sequence[ClientMiddlewareType]] = None,
  473. ) -> ClientResponse:
  474. # NOTE: timeout clamps existing connect and read timeouts. We cannot
  475. # set the default to None because we need to detect if the user wants
  476. # to use the existing timeouts by setting timeout to None.
  477. if self.closed:
  478. raise RuntimeError("Session is closed")
  479. ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint)
  480. if data is not None and json is not None:
  481. raise ValueError(
  482. "data and json parameters can not be used at the same time"
  483. )
  484. elif json is not None:
  485. data = payload.JsonPayload(json, dumps=self._json_serialize)
  486. if not isinstance(chunked, bool) and chunked is not None:
  487. warnings.warn("Chunk size is deprecated #1615", DeprecationWarning)
  488. redirects = 0
  489. history: List[ClientResponse] = []
  490. version = self._version
  491. params = params or {}
  492. # Merge with default headers and transform to CIMultiDict
  493. headers = self._prepare_headers(headers)
  494. try:
  495. url = self._build_url(str_or_url)
  496. except ValueError as e:
  497. raise InvalidUrlClientError(str_or_url) from e
  498. assert self._connector is not None
  499. if url.scheme not in self._connector.allowed_protocol_schema_set:
  500. raise NonHttpUrlClientError(url)
  501. skip_headers: Optional[Iterable[istr]]
  502. if skip_auto_headers is not None:
  503. skip_headers = {
  504. istr(i) for i in skip_auto_headers
  505. } | self._skip_auto_headers
  506. elif self._skip_auto_headers:
  507. skip_headers = self._skip_auto_headers
  508. else:
  509. skip_headers = None
  510. if proxy is None:
  511. proxy = self._default_proxy
  512. if proxy_auth is None:
  513. proxy_auth = self._default_proxy_auth
  514. if proxy is None:
  515. proxy_headers = None
  516. else:
  517. proxy_headers = self._prepare_headers(proxy_headers)
  518. try:
  519. proxy = URL(proxy)
  520. except ValueError as e:
  521. raise InvalidURL(proxy) from e
  522. if timeout is sentinel:
  523. real_timeout: ClientTimeout = self._timeout
  524. else:
  525. if not isinstance(timeout, ClientTimeout):
  526. real_timeout = ClientTimeout(total=timeout)
  527. else:
  528. real_timeout = timeout
  529. # timeout is cumulative for all request operations
  530. # (request, redirects, responses, data consuming)
  531. tm = TimeoutHandle(
  532. self._loop, real_timeout.total, ceil_threshold=real_timeout.ceil_threshold
  533. )
  534. handle = tm.start()
  535. if read_bufsize is None:
  536. read_bufsize = self._read_bufsize
  537. if auto_decompress is None:
  538. auto_decompress = self._auto_decompress
  539. if max_line_size is None:
  540. max_line_size = self._max_line_size
  541. if max_field_size is None:
  542. max_field_size = self._max_field_size
  543. traces = [
  544. Trace(
  545. self,
  546. trace_config,
  547. trace_config.trace_config_ctx(trace_request_ctx=trace_request_ctx),
  548. )
  549. for trace_config in self._trace_configs
  550. ]
  551. for trace in traces:
  552. await trace.send_request_start(method, url.update_query(params), headers)
  553. timer = tm.timer()
  554. try:
  555. with timer:
  556. # https://www.rfc-editor.org/rfc/rfc9112.html#name-retrying-requests
  557. retry_persistent_connection = (
  558. self._retry_connection and method in IDEMPOTENT_METHODS
  559. )
  560. while True:
  561. url, auth_from_url = strip_auth_from_url(url)
  562. if not url.raw_host:
  563. # NOTE: Bail early, otherwise, causes `InvalidURL` through
  564. # NOTE: `self._request_class()` below.
  565. err_exc_cls = (
  566. InvalidUrlRedirectClientError
  567. if redirects
  568. else InvalidUrlClientError
  569. )
  570. raise err_exc_cls(url)
  571. # If `auth` was passed for an already authenticated URL,
  572. # disallow only if this is the initial URL; this is to avoid issues
  573. # with sketchy redirects that are not the caller's responsibility
  574. if not history and (auth and auth_from_url):
  575. raise ValueError(
  576. "Cannot combine AUTH argument with "
  577. "credentials encoded in URL"
  578. )
  579. # Override the auth with the one from the URL only if we
  580. # have no auth, or if we got an auth from a redirect URL
  581. if auth is None or (history and auth_from_url is not None):
  582. auth = auth_from_url
  583. if (
  584. auth is None
  585. and self._default_auth
  586. and (
  587. not self._base_url or self._base_url_origin == url.origin()
  588. )
  589. ):
  590. auth = self._default_auth
  591. # Try netrc if auth is still None and trust_env is enabled.
  592. if auth is None and self._trust_env and url.host is not None:
  593. auth = await self._loop.run_in_executor(
  594. None, self._get_netrc_auth, url.host
  595. )
  596. # It would be confusing if we support explicit
  597. # Authorization header with auth argument
  598. if (
  599. headers is not None
  600. and auth is not None
  601. and hdrs.AUTHORIZATION in headers
  602. ):
  603. raise ValueError(
  604. "Cannot combine AUTHORIZATION header "
  605. "with AUTH argument or credentials "
  606. "encoded in URL"
  607. )
  608. all_cookies = self._cookie_jar.filter_cookies(url)
  609. if cookies is not None:
  610. tmp_cookie_jar = CookieJar(
  611. quote_cookie=self._cookie_jar.quote_cookie
  612. )
  613. tmp_cookie_jar.update_cookies(cookies)
  614. req_cookies = tmp_cookie_jar.filter_cookies(url)
  615. if req_cookies:
  616. all_cookies.load(req_cookies)
  617. proxy_: Optional[URL] = None
  618. if proxy is not None:
  619. proxy_ = URL(proxy)
  620. elif self._trust_env:
  621. with suppress(LookupError):
  622. proxy_, proxy_auth = await asyncio.to_thread(
  623. get_env_proxy_for_url, url
  624. )
  625. req = self._request_class(
  626. method,
  627. url,
  628. params=params,
  629. headers=headers,
  630. skip_auto_headers=skip_headers,
  631. data=data,
  632. cookies=all_cookies,
  633. auth=auth,
  634. version=version,
  635. compress=compress,
  636. chunked=chunked,
  637. expect100=expect100,
  638. loop=self._loop,
  639. response_class=self._response_class,
  640. proxy=proxy_,
  641. proxy_auth=proxy_auth,
  642. timer=timer,
  643. session=self,
  644. ssl=ssl if ssl is not None else True,
  645. server_hostname=server_hostname,
  646. proxy_headers=proxy_headers,
  647. traces=traces,
  648. trust_env=self.trust_env,
  649. )
  650. async def _connect_and_send_request(
  651. req: ClientRequest,
  652. ) -> ClientResponse:
  653. # connection timeout
  654. assert self._connector is not None
  655. try:
  656. conn = await self._connector.connect(
  657. req, traces=traces, timeout=real_timeout
  658. )
  659. except asyncio.TimeoutError as exc:
  660. raise ConnectionTimeoutError(
  661. f"Connection timeout to host {req.url}"
  662. ) from exc
  663. assert conn.protocol is not None
  664. conn.protocol.set_response_params(
  665. timer=timer,
  666. skip_payload=req.method in EMPTY_BODY_METHODS,
  667. read_until_eof=read_until_eof,
  668. auto_decompress=auto_decompress,
  669. read_timeout=real_timeout.sock_read,
  670. read_bufsize=read_bufsize,
  671. timeout_ceil_threshold=self._connector._timeout_ceil_threshold,
  672. max_line_size=max_line_size,
  673. max_field_size=max_field_size,
  674. )
  675. try:
  676. resp = await req.send(conn)
  677. try:
  678. await resp.start(conn)
  679. except BaseException:
  680. resp.close()
  681. raise
  682. except BaseException:
  683. conn.close()
  684. raise
  685. return resp
  686. # Apply middleware (if any) - per-request middleware overrides session middleware
  687. effective_middlewares = (
  688. self._middlewares if middlewares is None else middlewares
  689. )
  690. if effective_middlewares:
  691. handler = build_client_middlewares(
  692. _connect_and_send_request, effective_middlewares
  693. )
  694. else:
  695. handler = _connect_and_send_request
  696. try:
  697. resp = await handler(req)
  698. # Client connector errors should not be retried
  699. except (
  700. ConnectionTimeoutError,
  701. ClientConnectorError,
  702. ClientConnectorCertificateError,
  703. ClientConnectorSSLError,
  704. ):
  705. raise
  706. except (ClientOSError, ServerDisconnectedError):
  707. if retry_persistent_connection:
  708. retry_persistent_connection = False
  709. continue
  710. raise
  711. except ClientError:
  712. raise
  713. except OSError as exc:
  714. if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
  715. raise
  716. raise ClientOSError(*exc.args) from exc
  717. # Update cookies from raw headers to preserve duplicates
  718. if resp._raw_cookie_headers:
  719. self._cookie_jar.update_cookies_from_headers(
  720. resp._raw_cookie_headers, resp.url
  721. )
  722. # redirects
  723. if resp.status in (301, 302, 303, 307, 308) and allow_redirects:
  724. for trace in traces:
  725. await trace.send_request_redirect(
  726. method, url.update_query(params), headers, resp
  727. )
  728. redirects += 1
  729. history.append(resp)
  730. if max_redirects and redirects >= max_redirects:
  731. if req._body is not None:
  732. await req._body.close()
  733. resp.close()
  734. raise TooManyRedirects(
  735. history[0].request_info, tuple(history)
  736. )
  737. # For 301 and 302, mimic IE, now changed in RFC
  738. # https://github.com/kennethreitz/requests/pull/269
  739. if (resp.status == 303 and resp.method != hdrs.METH_HEAD) or (
  740. resp.status in (301, 302) and resp.method == hdrs.METH_POST
  741. ):
  742. method = hdrs.METH_GET
  743. data = None
  744. if headers.get(hdrs.CONTENT_LENGTH):
  745. headers.pop(hdrs.CONTENT_LENGTH)
  746. else:
  747. # For 307/308, always preserve the request body
  748. # For 301/302 with non-POST methods, preserve the request body
  749. # https://www.rfc-editor.org/rfc/rfc9110#section-15.4.3-3.1
  750. # Use the existing payload to avoid recreating it from a potentially consumed file
  751. data = req._body
  752. r_url = resp.headers.get(hdrs.LOCATION) or resp.headers.get(
  753. hdrs.URI
  754. )
  755. if r_url is None:
  756. # see github.com/aio-libs/aiohttp/issues/2022
  757. break
  758. else:
  759. # reading from correct redirection
  760. # response is forbidden
  761. resp.release()
  762. try:
  763. parsed_redirect_url = URL(
  764. r_url, encoded=not self._requote_redirect_url
  765. )
  766. except ValueError as e:
  767. if req._body is not None:
  768. await req._body.close()
  769. resp.close()
  770. raise InvalidUrlRedirectClientError(
  771. r_url,
  772. "Server attempted redirecting to a location that does not look like a URL",
  773. ) from e
  774. scheme = parsed_redirect_url.scheme
  775. if scheme not in HTTP_AND_EMPTY_SCHEMA_SET:
  776. if req._body is not None:
  777. await req._body.close()
  778. resp.close()
  779. raise NonHttpUrlRedirectClientError(r_url)
  780. elif not scheme:
  781. parsed_redirect_url = url.join(parsed_redirect_url)
  782. try:
  783. redirect_origin = parsed_redirect_url.origin()
  784. except ValueError as origin_val_err:
  785. if req._body is not None:
  786. await req._body.close()
  787. resp.close()
  788. raise InvalidUrlRedirectClientError(
  789. parsed_redirect_url,
  790. "Invalid redirect URL origin",
  791. ) from origin_val_err
  792. if url.origin() != redirect_origin:
  793. auth = None
  794. headers.pop(hdrs.AUTHORIZATION, None)
  795. url = parsed_redirect_url
  796. params = {}
  797. resp.release()
  798. continue
  799. break
  800. if req._body is not None:
  801. await req._body.close()
  802. # check response status
  803. if raise_for_status is None:
  804. raise_for_status = self._raise_for_status
  805. if raise_for_status is None:
  806. pass
  807. elif callable(raise_for_status):
  808. await raise_for_status(resp)
  809. elif raise_for_status:
  810. resp.raise_for_status()
  811. # register connection
  812. if handle is not None:
  813. if resp.connection is not None:
  814. resp.connection.add_callback(handle.cancel)
  815. else:
  816. handle.cancel()
  817. resp._history = tuple(history)
  818. for trace in traces:
  819. await trace.send_request_end(
  820. method, url.update_query(params), headers, resp
  821. )
  822. return resp
  823. except BaseException as e:
  824. # cleanup timer
  825. tm.close()
  826. if handle:
  827. handle.cancel()
  828. handle = None
  829. for trace in traces:
  830. await trace.send_request_exception(
  831. method, url.update_query(params), headers, e
  832. )
  833. raise
  834. def ws_connect(
  835. self,
  836. url: StrOrURL,
  837. *,
  838. method: str = hdrs.METH_GET,
  839. protocols: Iterable[str] = (),
  840. timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel,
  841. receive_timeout: Optional[float] = None,
  842. autoclose: bool = True,
  843. autoping: bool = True,
  844. heartbeat: Optional[float] = None,
  845. auth: Optional[BasicAuth] = None,
  846. origin: Optional[str] = None,
  847. params: Query = None,
  848. headers: Optional[LooseHeaders] = None,
  849. proxy: Optional[StrOrURL] = None,
  850. proxy_auth: Optional[BasicAuth] = None,
  851. ssl: Union[SSLContext, bool, Fingerprint] = True,
  852. verify_ssl: Optional[bool] = None,
  853. fingerprint: Optional[bytes] = None,
  854. ssl_context: Optional[SSLContext] = None,
  855. server_hostname: Optional[str] = None,
  856. proxy_headers: Optional[LooseHeaders] = None,
  857. compress: int = 0,
  858. max_msg_size: int = 4 * 1024 * 1024,
  859. ) -> "_WSRequestContextManager":
  860. """Initiate websocket connection."""
  861. return _WSRequestContextManager(
  862. self._ws_connect(
  863. url,
  864. method=method,
  865. protocols=protocols,
  866. timeout=timeout,
  867. receive_timeout=receive_timeout,
  868. autoclose=autoclose,
  869. autoping=autoping,
  870. heartbeat=heartbeat,
  871. auth=auth,
  872. origin=origin,
  873. params=params,
  874. headers=headers,
  875. proxy=proxy,
  876. proxy_auth=proxy_auth,
  877. ssl=ssl,
  878. verify_ssl=verify_ssl,
  879. fingerprint=fingerprint,
  880. ssl_context=ssl_context,
  881. server_hostname=server_hostname,
  882. proxy_headers=proxy_headers,
  883. compress=compress,
  884. max_msg_size=max_msg_size,
  885. )
  886. )
  887. async def _ws_connect(
  888. self,
  889. url: StrOrURL,
  890. *,
  891. method: str = hdrs.METH_GET,
  892. protocols: Iterable[str] = (),
  893. timeout: Union[ClientWSTimeout, _SENTINEL] = sentinel,
  894. receive_timeout: Optional[float] = None,
  895. autoclose: bool = True,
  896. autoping: bool = True,
  897. heartbeat: Optional[float] = None,
  898. auth: Optional[BasicAuth] = None,
  899. origin: Optional[str] = None,
  900. params: Query = None,
  901. headers: Optional[LooseHeaders] = None,
  902. proxy: Optional[StrOrURL] = None,
  903. proxy_auth: Optional[BasicAuth] = None,
  904. ssl: Union[SSLContext, bool, Fingerprint] = True,
  905. verify_ssl: Optional[bool] = None,
  906. fingerprint: Optional[bytes] = None,
  907. ssl_context: Optional[SSLContext] = None,
  908. server_hostname: Optional[str] = None,
  909. proxy_headers: Optional[LooseHeaders] = None,
  910. compress: int = 0,
  911. max_msg_size: int = 4 * 1024 * 1024,
  912. ) -> ClientWebSocketResponse:
  913. if timeout is not sentinel:
  914. if isinstance(timeout, ClientWSTimeout):
  915. ws_timeout = timeout
  916. else:
  917. warnings.warn(
  918. "parameter 'timeout' of type 'float' "
  919. "is deprecated, please use "
  920. "'timeout=ClientWSTimeout(ws_close=...)'",
  921. DeprecationWarning,
  922. stacklevel=2,
  923. )
  924. ws_timeout = ClientWSTimeout(ws_close=timeout)
  925. else:
  926. ws_timeout = DEFAULT_WS_CLIENT_TIMEOUT
  927. if receive_timeout is not None:
  928. warnings.warn(
  929. "float parameter 'receive_timeout' "
  930. "is deprecated, please use parameter "
  931. "'timeout=ClientWSTimeout(ws_receive=...)'",
  932. DeprecationWarning,
  933. stacklevel=2,
  934. )
  935. ws_timeout = attr.evolve(ws_timeout, ws_receive=receive_timeout)
  936. if headers is None:
  937. real_headers: CIMultiDict[str] = CIMultiDict()
  938. else:
  939. real_headers = CIMultiDict(headers)
  940. default_headers = {
  941. hdrs.UPGRADE: "websocket",
  942. hdrs.CONNECTION: "Upgrade",
  943. hdrs.SEC_WEBSOCKET_VERSION: "13",
  944. }
  945. for key, value in default_headers.items():
  946. real_headers.setdefault(key, value)
  947. sec_key = base64.b64encode(os.urandom(16))
  948. real_headers[hdrs.SEC_WEBSOCKET_KEY] = sec_key.decode()
  949. if protocols:
  950. real_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = ",".join(protocols)
  951. if origin is not None:
  952. real_headers[hdrs.ORIGIN] = origin
  953. if compress:
  954. extstr = ws_ext_gen(compress=compress)
  955. real_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = extstr
  956. # For the sake of backward compatibility, if user passes in None, convert it to True
  957. if ssl is None:
  958. warnings.warn(
  959. "ssl=None is deprecated, please use ssl=True",
  960. DeprecationWarning,
  961. stacklevel=2,
  962. )
  963. ssl = True
  964. ssl = _merge_ssl_params(ssl, verify_ssl, ssl_context, fingerprint)
  965. # send request
  966. resp = await self.request(
  967. method,
  968. url,
  969. params=params,
  970. headers=real_headers,
  971. read_until_eof=False,
  972. auth=auth,
  973. proxy=proxy,
  974. proxy_auth=proxy_auth,
  975. ssl=ssl,
  976. server_hostname=server_hostname,
  977. proxy_headers=proxy_headers,
  978. )
  979. try:
  980. # check handshake
  981. if resp.status != 101:
  982. raise WSServerHandshakeError(
  983. resp.request_info,
  984. resp.history,
  985. message="Invalid response status",
  986. status=resp.status,
  987. headers=resp.headers,
  988. )
  989. if resp.headers.get(hdrs.UPGRADE, "").lower() != "websocket":
  990. raise WSServerHandshakeError(
  991. resp.request_info,
  992. resp.history,
  993. message="Invalid upgrade header",
  994. status=resp.status,
  995. headers=resp.headers,
  996. )
  997. if resp.headers.get(hdrs.CONNECTION, "").lower() != "upgrade":
  998. raise WSServerHandshakeError(
  999. resp.request_info,
  1000. resp.history,
  1001. message="Invalid connection header",
  1002. status=resp.status,
  1003. headers=resp.headers,
  1004. )
  1005. # key calculation
  1006. r_key = resp.headers.get(hdrs.SEC_WEBSOCKET_ACCEPT, "")
  1007. match = base64.b64encode(hashlib.sha1(sec_key + WS_KEY).digest()).decode()
  1008. if r_key != match:
  1009. raise WSServerHandshakeError(
  1010. resp.request_info,
  1011. resp.history,
  1012. message="Invalid challenge response",
  1013. status=resp.status,
  1014. headers=resp.headers,
  1015. )
  1016. # websocket protocol
  1017. protocol = None
  1018. if protocols and hdrs.SEC_WEBSOCKET_PROTOCOL in resp.headers:
  1019. resp_protocols = [
  1020. proto.strip()
  1021. for proto in resp.headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",")
  1022. ]
  1023. for proto in resp_protocols:
  1024. if proto in protocols:
  1025. protocol = proto
  1026. break
  1027. # websocket compress
  1028. notakeover = False
  1029. if compress:
  1030. compress_hdrs = resp.headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)
  1031. if compress_hdrs:
  1032. try:
  1033. compress, notakeover = ws_ext_parse(compress_hdrs)
  1034. except WSHandshakeError as exc:
  1035. raise WSServerHandshakeError(
  1036. resp.request_info,
  1037. resp.history,
  1038. message=exc.args[0],
  1039. status=resp.status,
  1040. headers=resp.headers,
  1041. ) from exc
  1042. else:
  1043. compress = 0
  1044. notakeover = False
  1045. conn = resp.connection
  1046. assert conn is not None
  1047. conn_proto = conn.protocol
  1048. assert conn_proto is not None
  1049. # For WS connection the read_timeout must be either receive_timeout or greater
  1050. # None == no timeout, i.e. infinite timeout, so None is the max timeout possible
  1051. if ws_timeout.ws_receive is None:
  1052. # Reset regardless
  1053. conn_proto.read_timeout = None
  1054. elif conn_proto.read_timeout is not None:
  1055. conn_proto.read_timeout = max(
  1056. ws_timeout.ws_receive, conn_proto.read_timeout
  1057. )
  1058. transport = conn.transport
  1059. assert transport is not None
  1060. reader = WebSocketDataQueue(conn_proto, 2**16, loop=self._loop)
  1061. conn_proto.set_parser(WebSocketReader(reader, max_msg_size), reader)
  1062. writer = WebSocketWriter(
  1063. conn_proto,
  1064. transport,
  1065. use_mask=True,
  1066. compress=compress,
  1067. notakeover=notakeover,
  1068. )
  1069. except BaseException:
  1070. resp.close()
  1071. raise
  1072. else:
  1073. return self._ws_response_class(
  1074. reader,
  1075. writer,
  1076. protocol,
  1077. resp,
  1078. ws_timeout,
  1079. autoclose,
  1080. autoping,
  1081. self._loop,
  1082. heartbeat=heartbeat,
  1083. compress=compress,
  1084. client_notakeover=notakeover,
  1085. )
  1086. def _prepare_headers(self, headers: Optional[LooseHeaders]) -> "CIMultiDict[str]":
  1087. """Add default headers and transform it to CIMultiDict"""
  1088. # Convert headers to MultiDict
  1089. result = CIMultiDict(self._default_headers)
  1090. if headers:
  1091. if not isinstance(headers, (MultiDictProxy, MultiDict)):
  1092. headers = CIMultiDict(headers)
  1093. added_names: Set[str] = set()
  1094. for key, value in headers.items():
  1095. if key in added_names:
  1096. result.add(key, value)
  1097. else:
  1098. result[key] = value
  1099. added_names.add(key)
  1100. return result
  1101. def _get_netrc_auth(self, host: str) -> Optional[BasicAuth]:
  1102. """
  1103. Get auth from netrc for the given host.
  1104. This method is designed to be called in an executor to avoid
  1105. blocking I/O in the event loop.
  1106. """
  1107. netrc_obj = netrc_from_env()
  1108. try:
  1109. return basicauth_from_netrc(netrc_obj, host)
  1110. except LookupError:
  1111. return None
  1112. if sys.version_info >= (3, 11) and TYPE_CHECKING:
  1113. def get(
  1114. self,
  1115. url: StrOrURL,
  1116. **kwargs: Unpack[_RequestOptions],
  1117. ) -> "_RequestContextManager": ...
  1118. def options(
  1119. self,
  1120. url: StrOrURL,
  1121. **kwargs: Unpack[_RequestOptions],
  1122. ) -> "_RequestContextManager": ...
  1123. def head(
  1124. self,
  1125. url: StrOrURL,
  1126. **kwargs: Unpack[_RequestOptions],
  1127. ) -> "_RequestContextManager": ...
  1128. def post(
  1129. self,
  1130. url: StrOrURL,
  1131. **kwargs: Unpack[_RequestOptions],
  1132. ) -> "_RequestContextManager": ...
  1133. def put(
  1134. self,
  1135. url: StrOrURL,
  1136. **kwargs: Unpack[_RequestOptions],
  1137. ) -> "_RequestContextManager": ...
  1138. def patch(
  1139. self,
  1140. url: StrOrURL,
  1141. **kwargs: Unpack[_RequestOptions],
  1142. ) -> "_RequestContextManager": ...
  1143. def delete(
  1144. self,
  1145. url: StrOrURL,
  1146. **kwargs: Unpack[_RequestOptions],
  1147. ) -> "_RequestContextManager": ...
  1148. else:
  1149. def get(
  1150. self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any
  1151. ) -> "_RequestContextManager":
  1152. """Perform HTTP GET request."""
  1153. return _RequestContextManager(
  1154. self._request(
  1155. hdrs.METH_GET, url, allow_redirects=allow_redirects, **kwargs
  1156. )
  1157. )
  1158. def options(
  1159. self, url: StrOrURL, *, allow_redirects: bool = True, **kwargs: Any
  1160. ) -> "_RequestContextManager":
  1161. """Perform HTTP OPTIONS request."""
  1162. return _RequestContextManager(
  1163. self._request(
  1164. hdrs.METH_OPTIONS, url, allow_redirects=allow_redirects, **kwargs
  1165. )
  1166. )
  1167. def head(
  1168. self, url: StrOrURL, *, allow_redirects: bool = False, **kwargs: Any
  1169. ) -> "_RequestContextManager":
  1170. """Perform HTTP HEAD request."""
  1171. return _RequestContextManager(
  1172. self._request(
  1173. hdrs.METH_HEAD, url, allow_redirects=allow_redirects, **kwargs
  1174. )
  1175. )
  1176. def post(
  1177. self, url: StrOrURL, *, data: Any = None, **kwargs: Any
  1178. ) -> "_RequestContextManager":
  1179. """Perform HTTP POST request."""
  1180. return _RequestContextManager(
  1181. self._request(hdrs.METH_POST, url, data=data, **kwargs)
  1182. )
  1183. def put(
  1184. self, url: StrOrURL, *, data: Any = None, **kwargs: Any
  1185. ) -> "_RequestContextManager":
  1186. """Perform HTTP PUT request."""
  1187. return _RequestContextManager(
  1188. self._request(hdrs.METH_PUT, url, data=data, **kwargs)
  1189. )
  1190. def patch(
  1191. self, url: StrOrURL, *, data: Any = None, **kwargs: Any
  1192. ) -> "_RequestContextManager":
  1193. """Perform HTTP PATCH request."""
  1194. return _RequestContextManager(
  1195. self._request(hdrs.METH_PATCH, url, data=data, **kwargs)
  1196. )
  1197. def delete(self, url: StrOrURL, **kwargs: Any) -> "_RequestContextManager":
  1198. """Perform HTTP DELETE request."""
  1199. return _RequestContextManager(
  1200. self._request(hdrs.METH_DELETE, url, **kwargs)
  1201. )
  1202. async def close(self) -> None:
  1203. """Close underlying connector.
  1204. Release all acquired resources.
  1205. """
  1206. if not self.closed:
  1207. if self._connector is not None and self._connector_owner:
  1208. await self._connector.close()
  1209. self._connector = None
  1210. @property
  1211. def closed(self) -> bool:
  1212. """Is client session closed.
  1213. A readonly property.
  1214. """
  1215. return self._connector is None or self._connector.closed
  1216. @property
  1217. def connector(self) -> Optional[BaseConnector]:
  1218. """Connector instance used for the session."""
  1219. return self._connector
  1220. @property
  1221. def cookie_jar(self) -> AbstractCookieJar:
  1222. """The session cookies."""
  1223. return self._cookie_jar
  1224. @property
  1225. def version(self) -> Tuple[int, int]:
  1226. """The session HTTP protocol version."""
  1227. return self._version
  1228. @property
  1229. def requote_redirect_url(self) -> bool:
  1230. """Do URL requoting on redirection handling."""
  1231. return self._requote_redirect_url
  1232. @requote_redirect_url.setter
  1233. def requote_redirect_url(self, val: bool) -> None:
  1234. """Do URL requoting on redirection handling."""
  1235. warnings.warn(
  1236. "session.requote_redirect_url modification is deprecated #2778",
  1237. DeprecationWarning,
  1238. stacklevel=2,
  1239. )
  1240. self._requote_redirect_url = val
  1241. @property
  1242. def loop(self) -> asyncio.AbstractEventLoop:
  1243. """Session's loop."""
  1244. warnings.warn(
  1245. "client.loop property is deprecated", DeprecationWarning, stacklevel=2
  1246. )
  1247. return self._loop
  1248. @property
  1249. def timeout(self) -> ClientTimeout:
  1250. """Timeout for the session."""
  1251. return self._timeout
  1252. @property
  1253. def headers(self) -> "CIMultiDict[str]":
  1254. """The default headers of the client session."""
  1255. return self._default_headers
  1256. @property
  1257. def skip_auto_headers(self) -> FrozenSet[istr]:
  1258. """Headers for which autogeneration should be skipped"""
  1259. return self._skip_auto_headers
  1260. @property
  1261. def auth(self) -> Optional[BasicAuth]:
  1262. """An object that represents HTTP Basic Authorization"""
  1263. return self._default_auth
  1264. @property
  1265. def json_serialize(self) -> JSONEncoder:
  1266. """Json serializer callable"""
  1267. return self._json_serialize
  1268. @property
  1269. def connector_owner(self) -> bool:
  1270. """Should connector be closed on session closing"""
  1271. return self._connector_owner
  1272. @property
  1273. def raise_for_status(
  1274. self,
  1275. ) -> Union[bool, Callable[[ClientResponse], Awaitable[None]]]:
  1276. """Should `ClientResponse.raise_for_status()` be called for each response."""
  1277. return self._raise_for_status
  1278. @property
  1279. def auto_decompress(self) -> bool:
  1280. """Should the body response be automatically decompressed."""
  1281. return self._auto_decompress
  1282. @property
  1283. def trust_env(self) -> bool:
  1284. """
  1285. Should proxies information from environment or netrc be trusted.
  1286. Information is from HTTP_PROXY / HTTPS_PROXY environment variables
  1287. or ~/.netrc file if present.
  1288. """
  1289. return self._trust_env
  1290. @property
  1291. def trace_configs(self) -> List[TraceConfig]:
  1292. """A list of TraceConfig instances used for client tracing"""
  1293. return self._trace_configs
  1294. def detach(self) -> None:
  1295. """Detach connector from session without closing the former.
  1296. Session is switched to closed state anyway.
  1297. """
  1298. self._connector = None
  1299. def __enter__(self) -> None:
  1300. raise TypeError("Use async with instead")
  1301. def __exit__(
  1302. self,
  1303. exc_type: Optional[Type[BaseException]],
  1304. exc_val: Optional[BaseException],
  1305. exc_tb: Optional[TracebackType],
  1306. ) -> None:
  1307. # __exit__ should exist in pair with __enter__ but never executed
  1308. pass # pragma: no cover
  1309. async def __aenter__(self) -> "ClientSession":
  1310. return self
  1311. async def __aexit__(
  1312. self,
  1313. exc_type: Optional[Type[BaseException]],
  1314. exc_val: Optional[BaseException],
  1315. exc_tb: Optional[TracebackType],
  1316. ) -> None:
  1317. await self.close()
  1318. class _BaseRequestContextManager(Coroutine[Any, Any, _RetType], Generic[_RetType]):
  1319. __slots__ = ("_coro", "_resp")
  1320. def __init__(self, coro: Coroutine["asyncio.Future[Any]", None, _RetType]) -> None:
  1321. self._coro: Coroutine["asyncio.Future[Any]", None, _RetType] = coro
  1322. def send(self, arg: None) -> "asyncio.Future[Any]":
  1323. return self._coro.send(arg)
  1324. def throw(self, *args: Any, **kwargs: Any) -> "asyncio.Future[Any]":
  1325. return self._coro.throw(*args, **kwargs)
  1326. def close(self) -> None:
  1327. return self._coro.close()
  1328. def __await__(self) -> Generator[Any, None, _RetType]:
  1329. ret = self._coro.__await__()
  1330. return ret
  1331. def __iter__(self) -> Generator[Any, None, _RetType]:
  1332. return self.__await__()
  1333. async def __aenter__(self) -> _RetType:
  1334. self._resp: _RetType = await self._coro
  1335. return await self._resp.__aenter__()
  1336. async def __aexit__(
  1337. self,
  1338. exc_type: Optional[Type[BaseException]],
  1339. exc: Optional[BaseException],
  1340. tb: Optional[TracebackType],
  1341. ) -> None:
  1342. await self._resp.__aexit__(exc_type, exc, tb)
  1343. _RequestContextManager = _BaseRequestContextManager[ClientResponse]
  1344. _WSRequestContextManager = _BaseRequestContextManager[ClientWebSocketResponse]
  1345. class _SessionRequestContextManager:
  1346. __slots__ = ("_coro", "_resp", "_session")
  1347. def __init__(
  1348. self,
  1349. coro: Coroutine["asyncio.Future[Any]", None, ClientResponse],
  1350. session: ClientSession,
  1351. ) -> None:
  1352. self._coro = coro
  1353. self._resp: Optional[ClientResponse] = None
  1354. self._session = session
  1355. async def __aenter__(self) -> ClientResponse:
  1356. try:
  1357. self._resp = await self._coro
  1358. except BaseException:
  1359. await self._session.close()
  1360. raise
  1361. else:
  1362. return self._resp
  1363. async def __aexit__(
  1364. self,
  1365. exc_type: Optional[Type[BaseException]],
  1366. exc: Optional[BaseException],
  1367. tb: Optional[TracebackType],
  1368. ) -> None:
  1369. assert self._resp is not None
  1370. self._resp.close()
  1371. await self._session.close()
  1372. if sys.version_info >= (3, 11) and TYPE_CHECKING:
  1373. def request(
  1374. method: str,
  1375. url: StrOrURL,
  1376. *,
  1377. version: HttpVersion = http.HttpVersion11,
  1378. connector: Optional[BaseConnector] = None,
  1379. loop: Optional[asyncio.AbstractEventLoop] = None,
  1380. **kwargs: Unpack[_RequestOptions],
  1381. ) -> _SessionRequestContextManager: ...
  1382. else:
  1383. def request(
  1384. method: str,
  1385. url: StrOrURL,
  1386. *,
  1387. version: HttpVersion = http.HttpVersion11,
  1388. connector: Optional[BaseConnector] = None,
  1389. loop: Optional[asyncio.AbstractEventLoop] = None,
  1390. **kwargs: Any,
  1391. ) -> _SessionRequestContextManager:
  1392. """Constructs and sends a request.
  1393. Returns response object.
  1394. method - HTTP method
  1395. url - request url
  1396. params - (optional) Dictionary or bytes to be sent in the query
  1397. string of the new request
  1398. data - (optional) Dictionary, bytes, or file-like object to
  1399. send in the body of the request
  1400. json - (optional) Any json compatible python object
  1401. headers - (optional) Dictionary of HTTP Headers to send with
  1402. the request
  1403. cookies - (optional) Dict object to send with the request
  1404. auth - (optional) BasicAuth named tuple represent HTTP Basic Auth
  1405. auth - aiohttp.helpers.BasicAuth
  1406. allow_redirects - (optional) If set to False, do not follow
  1407. redirects
  1408. version - Request HTTP version.
  1409. compress - Set to True if request has to be compressed
  1410. with deflate encoding.
  1411. chunked - Set to chunk size for chunked transfer encoding.
  1412. expect100 - Expect 100-continue response from server.
  1413. connector - BaseConnector sub-class instance to support
  1414. connection pooling.
  1415. read_until_eof - Read response until eof if response
  1416. does not have Content-Length header.
  1417. loop - Optional event loop.
  1418. timeout - Optional ClientTimeout settings structure, 5min
  1419. total timeout by default.
  1420. Usage::
  1421. >>> import aiohttp
  1422. >>> async with aiohttp.request('GET', 'http://python.org/') as resp:
  1423. ... print(resp)
  1424. ... data = await resp.read()
  1425. <ClientResponse(https://www.python.org/) [200 OK]>
  1426. """
  1427. connector_owner = False
  1428. if connector is None:
  1429. connector_owner = True
  1430. connector = TCPConnector(loop=loop, force_close=True)
  1431. session = ClientSession(
  1432. loop=loop,
  1433. cookies=kwargs.pop("cookies", None),
  1434. version=version,
  1435. timeout=kwargs.pop("timeout", sentinel),
  1436. connector=connector,
  1437. connector_owner=connector_owner,
  1438. )
  1439. return _SessionRequestContextManager(
  1440. session._request(method, url, **kwargs),
  1441. session,
  1442. )