client.py 53 KB

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