client.py 57 KB

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