Path: blob/master/ invest-robot-contest_TinkoffBotTwitch-main/venv/lib/python3.8/site-packages/aiohttp/client_reqrep.py
7757 views
import asyncio1import codecs2import functools3import io4import re5import sys6import traceback7import warnings8from hashlib import md5, sha1, sha2569from http.cookies import CookieError, Morsel, SimpleCookie10from types import MappingProxyType, TracebackType11from typing import (12TYPE_CHECKING,13Any,14Dict,15Iterable,16List,17Mapping,18Optional,19Tuple,20Type,21Union,22cast,23)2425import attr26from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy27from yarl import URL2829from . import hdrs, helpers, http, multipart, payload30from .abc import AbstractStreamWriter31from .client_exceptions import (32ClientConnectionError,33ClientOSError,34ClientResponseError,35ContentTypeError,36InvalidURL,37ServerFingerprintMismatch,38)39from .formdata import FormData40from .helpers import (41PY_36,42BaseTimerContext,43BasicAuth,44HeadersMixin,45TimerNoop,46noop,47reify,48set_result,49)50from .http import SERVER_SOFTWARE, HttpVersion10, HttpVersion11, StreamWriter51from .log import client_logger52from .streams import StreamReader53from .typedefs import (54DEFAULT_JSON_DECODER,55JSONDecoder,56LooseCookies,57LooseHeaders,58RawHeaders,59)6061try:62import ssl63from ssl import SSLContext64except ImportError: # pragma: no cover65ssl = None # type: ignore[assignment]66SSLContext = object # type: ignore[misc,assignment]6768try:69import cchardet as chardet70except ImportError: # pragma: no cover71import charset_normalizer as chardet # type: ignore[no-redef]727374__all__ = ("ClientRequest", "ClientResponse", "RequestInfo", "Fingerprint")757677if TYPE_CHECKING: # pragma: no cover78from .client import ClientSession79from .connector import Connection80from .tracing import Trace818283json_re = re.compile(r"^application/(?:[\w.+-]+?\+)?json")848586@attr.s(auto_attribs=True, frozen=True, slots=True)87class ContentDisposition:88type: Optional[str]89parameters: "MappingProxyType[str, str]"90filename: Optional[str]919293@attr.s(auto_attribs=True, frozen=True, slots=True)94class RequestInfo:95url: URL96method: str97headers: "CIMultiDictProxy[str]"98real_url: URL = attr.ib()99100@real_url.default101def real_url_default(self) -> URL:102return self.url103104105class Fingerprint:106HASHFUNC_BY_DIGESTLEN = {10716: md5,10820: sha1,10932: sha256,110}111112def __init__(self, fingerprint: bytes) -> None:113digestlen = len(fingerprint)114hashfunc = self.HASHFUNC_BY_DIGESTLEN.get(digestlen)115if not hashfunc:116raise ValueError("fingerprint has invalid length")117elif hashfunc is md5 or hashfunc is sha1:118raise ValueError(119"md5 and sha1 are insecure and " "not supported. Use sha256."120)121self._hashfunc = hashfunc122self._fingerprint = fingerprint123124@property125def fingerprint(self) -> bytes:126return self._fingerprint127128def check(self, transport: asyncio.Transport) -> None:129if not transport.get_extra_info("sslcontext"):130return131sslobj = transport.get_extra_info("ssl_object")132cert = sslobj.getpeercert(binary_form=True)133got = self._hashfunc(cert).digest()134if got != self._fingerprint:135host, port, *_ = transport.get_extra_info("peername")136raise ServerFingerprintMismatch(self._fingerprint, got, host, port)137138139if ssl is not None:140SSL_ALLOWED_TYPES = (ssl.SSLContext, bool, Fingerprint, type(None))141else: # pragma: no cover142SSL_ALLOWED_TYPES = type(None)143144145def _merge_ssl_params(146ssl: Union["SSLContext", bool, Fingerprint, None],147verify_ssl: Optional[bool],148ssl_context: Optional["SSLContext"],149fingerprint: Optional[bytes],150) -> Union["SSLContext", bool, Fingerprint, None]:151if verify_ssl is not None and not verify_ssl:152warnings.warn(153"verify_ssl is deprecated, use ssl=False instead",154DeprecationWarning,155stacklevel=3,156)157if ssl is not None:158raise ValueError(159"verify_ssl, ssl_context, fingerprint and ssl "160"parameters are mutually exclusive"161)162else:163ssl = False164if ssl_context is not None:165warnings.warn(166"ssl_context is deprecated, use ssl=context instead",167DeprecationWarning,168stacklevel=3,169)170if ssl is not None:171raise ValueError(172"verify_ssl, ssl_context, fingerprint and ssl "173"parameters are mutually exclusive"174)175else:176ssl = ssl_context177if fingerprint is not None:178warnings.warn(179"fingerprint is deprecated, " "use ssl=Fingerprint(fingerprint) instead",180DeprecationWarning,181stacklevel=3,182)183if ssl is not None:184raise ValueError(185"verify_ssl, ssl_context, fingerprint and ssl "186"parameters are mutually exclusive"187)188else:189ssl = Fingerprint(fingerprint)190if not isinstance(ssl, SSL_ALLOWED_TYPES):191raise TypeError(192"ssl should be SSLContext, bool, Fingerprint or None, "193"got {!r} instead.".format(ssl)194)195return ssl196197198@attr.s(auto_attribs=True, slots=True, frozen=True)199class ConnectionKey:200# the key should contain an information about used proxy / TLS201# to prevent reusing wrong connections from a pool202host: str203port: Optional[int]204is_ssl: bool205ssl: Union[SSLContext, None, bool, Fingerprint]206proxy: Optional[URL]207proxy_auth: Optional[BasicAuth]208proxy_headers_hash: Optional[int] # hash(CIMultiDict)209210211def _is_expected_content_type(212response_content_type: str, expected_content_type: str213) -> bool:214if expected_content_type == "application/json":215return json_re.match(response_content_type) is not None216return expected_content_type in response_content_type217218219class ClientRequest:220GET_METHODS = {221hdrs.METH_GET,222hdrs.METH_HEAD,223hdrs.METH_OPTIONS,224hdrs.METH_TRACE,225}226POST_METHODS = {hdrs.METH_PATCH, hdrs.METH_POST, hdrs.METH_PUT}227ALL_METHODS = GET_METHODS.union(POST_METHODS).union({hdrs.METH_DELETE})228229DEFAULT_HEADERS = {230hdrs.ACCEPT: "*/*",231hdrs.ACCEPT_ENCODING: "gzip, deflate",232}233234body = b""235auth = None236response = None237238_writer = None # async task for streaming data239_continue = None # waiter future for '100 Continue' response240241# N.B.242# Adding __del__ method with self._writer closing doesn't make sense243# because _writer is instance method, thus it keeps a reference to self.244# Until writer has finished finalizer will not be called.245246def __init__(247self,248method: str,249url: URL,250*,251params: Optional[Mapping[str, str]] = None,252headers: Optional[LooseHeaders] = None,253skip_auto_headers: Iterable[str] = frozenset(),254data: Any = None,255cookies: Optional[LooseCookies] = None,256auth: Optional[BasicAuth] = None,257version: http.HttpVersion = http.HttpVersion11,258compress: Optional[str] = None,259chunked: Optional[bool] = None,260expect100: bool = False,261loop: Optional[asyncio.AbstractEventLoop] = None,262response_class: Optional[Type["ClientResponse"]] = None,263proxy: Optional[URL] = None,264proxy_auth: Optional[BasicAuth] = None,265timer: Optional[BaseTimerContext] = None,266session: Optional["ClientSession"] = None,267ssl: Union[SSLContext, bool, Fingerprint, None] = None,268proxy_headers: Optional[LooseHeaders] = None,269traces: Optional[List["Trace"]] = None,270):271272if loop is None:273loop = asyncio.get_event_loop()274275assert isinstance(url, URL), url276assert isinstance(proxy, (URL, type(None))), proxy277# FIXME: session is None in tests only, need to fix tests278# assert session is not None279self._session = cast("ClientSession", session)280if params:281q = MultiDict(url.query)282url2 = url.with_query(params)283q.extend(url2.query)284url = url.with_query(q)285self.original_url = url286self.url = url.with_fragment(None)287self.method = method.upper()288self.chunked = chunked289self.compress = compress290self.loop = loop291self.length = None292if response_class is None:293real_response_class = ClientResponse294else:295real_response_class = response_class296self.response_class = real_response_class # type: Type[ClientResponse]297self._timer = timer if timer is not None else TimerNoop()298self._ssl = ssl299300if loop.get_debug():301self._source_traceback = traceback.extract_stack(sys._getframe(1))302303self.update_version(version)304self.update_host(url)305self.update_headers(headers)306self.update_auto_headers(skip_auto_headers)307self.update_cookies(cookies)308self.update_content_encoding(data)309self.update_auth(auth)310self.update_proxy(proxy, proxy_auth, proxy_headers)311312self.update_body_from_data(data)313if data is not None or self.method not in self.GET_METHODS:314self.update_transfer_encoding()315self.update_expect_continue(expect100)316if traces is None:317traces = []318self._traces = traces319320def is_ssl(self) -> bool:321return self.url.scheme in ("https", "wss")322323@property324def ssl(self) -> Union["SSLContext", None, bool, Fingerprint]:325return self._ssl326327@property328def connection_key(self) -> ConnectionKey:329proxy_headers = self.proxy_headers330if proxy_headers:331h = hash(332tuple((k, v) for k, v in proxy_headers.items())333) # type: Optional[int]334else:335h = None336return ConnectionKey(337self.host,338self.port,339self.is_ssl(),340self.ssl,341self.proxy,342self.proxy_auth,343h,344)345346@property347def host(self) -> str:348ret = self.url.raw_host349assert ret is not None350return ret351352@property353def port(self) -> Optional[int]:354return self.url.port355356@property357def request_info(self) -> RequestInfo:358headers = CIMultiDictProxy(self.headers) # type: CIMultiDictProxy[str]359return RequestInfo(self.url, self.method, headers, self.original_url)360361def update_host(self, url: URL) -> None:362"""Update destination host, port and connection type (ssl)."""363# get host/port364if not url.raw_host:365raise InvalidURL(url)366367# basic auth info368username, password = url.user, url.password369if username:370self.auth = helpers.BasicAuth(username, password or "")371372def update_version(self, version: Union[http.HttpVersion, str]) -> None:373"""Convert request version to two elements tuple.374375parser HTTP version '1.1' => (1, 1)376"""377if isinstance(version, str):378v = [part.strip() for part in version.split(".", 1)]379try:380version = http.HttpVersion(int(v[0]), int(v[1]))381except ValueError:382raise ValueError(383f"Can not parse http version number: {version}"384) from None385self.version = version386387def update_headers(self, headers: Optional[LooseHeaders]) -> None:388"""Update request headers."""389self.headers = CIMultiDict() # type: CIMultiDict[str]390391# add host392netloc = cast(str, self.url.raw_host)393if helpers.is_ipv6_address(netloc):394netloc = f"[{netloc}]"395if self.url.port is not None and not self.url.is_default_port():396netloc += ":" + str(self.url.port)397self.headers[hdrs.HOST] = netloc398399if headers:400if isinstance(headers, (dict, MultiDictProxy, MultiDict)):401headers = headers.items() # type: ignore[assignment]402403for key, value in headers: # type: ignore[misc]404# A special case for Host header405if key.lower() == "host":406self.headers[key] = value407else:408self.headers.add(key, value)409410def update_auto_headers(self, skip_auto_headers: Iterable[str]) -> None:411self.skip_auto_headers = CIMultiDict(412(hdr, None) for hdr in sorted(skip_auto_headers)413)414used_headers = self.headers.copy()415used_headers.extend(self.skip_auto_headers) # type: ignore[arg-type]416417for hdr, val in self.DEFAULT_HEADERS.items():418if hdr not in used_headers:419self.headers.add(hdr, val)420421if hdrs.USER_AGENT not in used_headers:422self.headers[hdrs.USER_AGENT] = SERVER_SOFTWARE423424def update_cookies(self, cookies: Optional[LooseCookies]) -> None:425"""Update request cookies header."""426if not cookies:427return428429c = SimpleCookie() # type: SimpleCookie[str]430if hdrs.COOKIE in self.headers:431c.load(self.headers.get(hdrs.COOKIE, ""))432del self.headers[hdrs.COOKIE]433434if isinstance(cookies, Mapping):435iter_cookies = cookies.items()436else:437iter_cookies = cookies # type: ignore[assignment]438for name, value in iter_cookies:439if isinstance(value, Morsel):440# Preserve coded_value441mrsl_val = value.get(value.key, Morsel())442mrsl_val.set(value.key, value.value, value.coded_value)443c[name] = mrsl_val444else:445c[name] = value # type: ignore[assignment]446447self.headers[hdrs.COOKIE] = c.output(header="", sep=";").strip()448449def update_content_encoding(self, data: Any) -> None:450"""Set request content encoding."""451if data is None:452return453454enc = self.headers.get(hdrs.CONTENT_ENCODING, "").lower()455if enc:456if self.compress:457raise ValueError(458"compress can not be set " "if Content-Encoding header is set"459)460elif self.compress:461if not isinstance(self.compress, str):462self.compress = "deflate"463self.headers[hdrs.CONTENT_ENCODING] = self.compress464self.chunked = True # enable chunked, no need to deal with length465466def update_transfer_encoding(self) -> None:467"""Analyze transfer-encoding header."""468te = self.headers.get(hdrs.TRANSFER_ENCODING, "").lower()469470if "chunked" in te:471if self.chunked:472raise ValueError(473"chunked can not be set "474'if "Transfer-Encoding: chunked" header is set'475)476477elif self.chunked:478if hdrs.CONTENT_LENGTH in self.headers:479raise ValueError(480"chunked can not be set " "if Content-Length header is set"481)482483self.headers[hdrs.TRANSFER_ENCODING] = "chunked"484else:485if hdrs.CONTENT_LENGTH not in self.headers:486self.headers[hdrs.CONTENT_LENGTH] = str(len(self.body))487488def update_auth(self, auth: Optional[BasicAuth]) -> None:489"""Set basic auth."""490if auth is None:491auth = self.auth492if auth is None:493return494495if not isinstance(auth, helpers.BasicAuth):496raise TypeError("BasicAuth() tuple is required instead")497498self.headers[hdrs.AUTHORIZATION] = auth.encode()499500def update_body_from_data(self, body: Any) -> None:501if body is None:502return503504# FormData505if isinstance(body, FormData):506body = body()507508try:509body = payload.PAYLOAD_REGISTRY.get(body, disposition=None)510except payload.LookupError:511body = FormData(body)()512513self.body = body514515# enable chunked encoding if needed516if not self.chunked:517if hdrs.CONTENT_LENGTH not in self.headers:518size = body.size519if size is None:520self.chunked = True521else:522if hdrs.CONTENT_LENGTH not in self.headers:523self.headers[hdrs.CONTENT_LENGTH] = str(size)524525# copy payload headers526assert body.headers527for (key, value) in body.headers.items():528if key in self.headers:529continue530if key in self.skip_auto_headers:531continue532self.headers[key] = value533534def update_expect_continue(self, expect: bool = False) -> None:535if expect:536self.headers[hdrs.EXPECT] = "100-continue"537elif self.headers.get(hdrs.EXPECT, "").lower() == "100-continue":538expect = True539540if expect:541self._continue = self.loop.create_future()542543def update_proxy(544self,545proxy: Optional[URL],546proxy_auth: Optional[BasicAuth],547proxy_headers: Optional[LooseHeaders],548) -> None:549if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth):550raise ValueError("proxy_auth must be None or BasicAuth() tuple")551self.proxy = proxy552self.proxy_auth = proxy_auth553self.proxy_headers = proxy_headers554555def keep_alive(self) -> bool:556if self.version < HttpVersion10:557# keep alive not supported at all558return False559if self.version == HttpVersion10:560if self.headers.get(hdrs.CONNECTION) == "keep-alive":561return True562else: # no headers means we close for Http 1.0563return False564elif self.headers.get(hdrs.CONNECTION) == "close":565return False566567return True568569async def write_bytes(570self, writer: AbstractStreamWriter, conn: "Connection"571) -> None:572"""Support coroutines that yields bytes objects."""573# 100 response574if self._continue is not None:575await writer.drain()576await self._continue577578protocol = conn.protocol579assert protocol is not None580try:581if isinstance(self.body, payload.Payload):582await self.body.write(writer)583else:584if isinstance(self.body, (bytes, bytearray)):585self.body = (self.body,) # type: ignore[assignment]586587for chunk in self.body:588await writer.write(chunk) # type: ignore[arg-type]589590await writer.write_eof()591except OSError as exc:592new_exc = ClientOSError(593exc.errno, "Can not write request body for %s" % self.url594)595new_exc.__context__ = exc596new_exc.__cause__ = exc597protocol.set_exception(new_exc)598except asyncio.CancelledError as exc:599if not conn.closed:600protocol.set_exception(exc)601except Exception as exc:602protocol.set_exception(exc)603finally:604self._writer = None605606async def send(self, conn: "Connection") -> "ClientResponse":607# Specify request target:608# - CONNECT request must send authority form URI609# - not CONNECT proxy must send absolute form URI610# - most common is origin form URI611if self.method == hdrs.METH_CONNECT:612connect_host = self.url.raw_host613assert connect_host is not None614if helpers.is_ipv6_address(connect_host):615connect_host = f"[{connect_host}]"616path = f"{connect_host}:{self.url.port}"617elif self.proxy and not self.is_ssl():618path = str(self.url)619else:620path = self.url.raw_path621if self.url.raw_query_string:622path += "?" + self.url.raw_query_string623624protocol = conn.protocol625assert protocol is not None626writer = StreamWriter(627protocol,628self.loop,629on_chunk_sent=functools.partial(630self._on_chunk_request_sent, self.method, self.url631),632on_headers_sent=functools.partial(633self._on_headers_request_sent, self.method, self.url634),635)636637if self.compress:638writer.enable_compression(self.compress)639640if self.chunked is not None:641writer.enable_chunking()642643# set default content-type644if (645self.method in self.POST_METHODS646and hdrs.CONTENT_TYPE not in self.skip_auto_headers647and hdrs.CONTENT_TYPE not in self.headers648):649self.headers[hdrs.CONTENT_TYPE] = "application/octet-stream"650651# set the connection header652connection = self.headers.get(hdrs.CONNECTION)653if not connection:654if self.keep_alive():655if self.version == HttpVersion10:656connection = "keep-alive"657else:658if self.version == HttpVersion11:659connection = "close"660661if connection is not None:662self.headers[hdrs.CONNECTION] = connection663664# status + headers665status_line = "{0} {1} HTTP/{2[0]}.{2[1]}".format(666self.method, path, self.version667)668await writer.write_headers(status_line, self.headers)669670self._writer = self.loop.create_task(self.write_bytes(writer, conn))671672response_class = self.response_class673assert response_class is not None674self.response = response_class(675self.method,676self.original_url,677writer=self._writer,678continue100=self._continue,679timer=self._timer,680request_info=self.request_info,681traces=self._traces,682loop=self.loop,683session=self._session,684)685return self.response686687async def close(self) -> None:688if self._writer is not None:689try:690await self._writer691finally:692self._writer = None693694def terminate(self) -> None:695if self._writer is not None:696if not self.loop.is_closed():697self._writer.cancel()698self._writer = None699700async def _on_chunk_request_sent(self, method: str, url: URL, chunk: bytes) -> None:701for trace in self._traces:702await trace.send_request_chunk_sent(method, url, chunk)703704async def _on_headers_request_sent(705self, method: str, url: URL, headers: "CIMultiDict[str]"706) -> None:707for trace in self._traces:708await trace.send_request_headers(method, url, headers)709710711class ClientResponse(HeadersMixin):712713# from the Status-Line of the response714version = None # HTTP-Version715status = None # type: int # Status-Code716reason = None # Reason-Phrase717718content = None # type: StreamReader # Payload stream719_headers = None # type: CIMultiDictProxy[str] # Response headers720_raw_headers = None # type: RawHeaders # Response raw headers721722_connection = None # current connection723_source_traceback = None724# setted up by ClientRequest after ClientResponse object creation725# post-init stage allows to not change ctor signature726_closed = True # to allow __del__ for non-initialized properly response727_released = False728729def __init__(730self,731method: str,732url: URL,733*,734writer: "asyncio.Task[None]",735continue100: Optional["asyncio.Future[bool]"],736timer: BaseTimerContext,737request_info: RequestInfo,738traces: List["Trace"],739loop: asyncio.AbstractEventLoop,740session: "ClientSession",741) -> None:742assert isinstance(url, URL)743744self.method = method745self.cookies = SimpleCookie() # type: SimpleCookie[str]746747self._real_url = url748self._url = url.with_fragment(None)749self._body = None # type: Any750self._writer = writer # type: Optional[asyncio.Task[None]]751self._continue = continue100 # None by default752self._closed = True753self._history = () # type: Tuple[ClientResponse, ...]754self._request_info = request_info755self._timer = timer if timer is not None else TimerNoop()756self._cache = {} # type: Dict[str, Any]757self._traces = traces758self._loop = loop759# store a reference to session #1985760self._session = session # type: Optional[ClientSession]761if loop.get_debug():762self._source_traceback = traceback.extract_stack(sys._getframe(1))763764@reify765def url(self) -> URL:766return self._url767768@reify769def url_obj(self) -> URL:770warnings.warn("Deprecated, use .url #1654", DeprecationWarning, stacklevel=2)771return self._url772773@reify774def real_url(self) -> URL:775return self._real_url776777@reify778def host(self) -> str:779assert self._url.host is not None780return self._url.host781782@reify783def headers(self) -> "CIMultiDictProxy[str]":784return self._headers785786@reify787def raw_headers(self) -> RawHeaders:788return self._raw_headers789790@reify791def request_info(self) -> RequestInfo:792return self._request_info793794@reify795def content_disposition(self) -> Optional[ContentDisposition]:796raw = self._headers.get(hdrs.CONTENT_DISPOSITION)797if raw is None:798return None799disposition_type, params_dct = multipart.parse_content_disposition(raw)800params = MappingProxyType(params_dct)801filename = multipart.content_disposition_filename(params)802return ContentDisposition(disposition_type, params, filename)803804def __del__(self, _warnings: Any = warnings) -> None:805if self._closed:806return807808if self._connection is not None:809self._connection.release()810self._cleanup_writer()811812if self._loop.get_debug():813if PY_36:814kwargs = {"source": self}815else:816kwargs = {}817_warnings.warn(f"Unclosed response {self!r}", ResourceWarning, **kwargs)818context = {"client_response": self, "message": "Unclosed response"}819if self._source_traceback:820context["source_traceback"] = self._source_traceback821self._loop.call_exception_handler(context)822823def __repr__(self) -> str:824out = io.StringIO()825ascii_encodable_url = str(self.url)826if self.reason:827ascii_encodable_reason = self.reason.encode(828"ascii", "backslashreplace"829).decode("ascii")830else:831ascii_encodable_reason = self.reason832print(833"<ClientResponse({}) [{} {}]>".format(834ascii_encodable_url, self.status, ascii_encodable_reason835),836file=out,837)838print(self.headers, file=out)839return out.getvalue()840841@property842def connection(self) -> Optional["Connection"]:843return self._connection844845@reify846def history(self) -> Tuple["ClientResponse", ...]:847"""A sequence of of responses, if redirects occurred."""848return self._history849850@reify851def links(self) -> "MultiDictProxy[MultiDictProxy[Union[str, URL]]]":852links_str = ", ".join(self.headers.getall("link", []))853854if not links_str:855return MultiDictProxy(MultiDict())856857links = MultiDict() # type: MultiDict[MultiDictProxy[Union[str, URL]]]858859for val in re.split(r",(?=\s*<)", links_str):860match = re.match(r"\s*<(.*)>(.*)", val)861if match is None: # pragma: no cover862# the check exists to suppress mypy error863continue864url, params_str = match.groups()865params = params_str.split(";")[1:]866867link = MultiDict() # type: MultiDict[Union[str, URL]]868869for param in params:870match = re.match(r"^\s*(\S*)\s*=\s*(['\"]?)(.*?)(\2)\s*$", param, re.M)871if match is None: # pragma: no cover872# the check exists to suppress mypy error873continue874key, _, value, _ = match.groups()875876link.add(key, value)877878key = link.get("rel", url) # type: ignore[assignment]879880link.add("url", self.url.join(URL(url)))881882links.add(key, MultiDictProxy(link))883884return MultiDictProxy(links)885886async def start(self, connection: "Connection") -> "ClientResponse":887"""Start response processing."""888self._closed = False889self._protocol = connection.protocol890self._connection = connection891892with self._timer:893while True:894# read response895try:896protocol = self._protocol897message, payload = await protocol.read() # type: ignore[union-attr]898except http.HttpProcessingError as exc:899raise ClientResponseError(900self.request_info,901self.history,902status=exc.code,903message=exc.message,904headers=exc.headers,905) from exc906907if message.code < 100 or message.code > 199 or message.code == 101:908break909910if self._continue is not None:911set_result(self._continue, True)912self._continue = None913914# payload eof handler915payload.on_eof(self._response_eof)916917# response status918self.version = message.version919self.status = message.code920self.reason = message.reason921922# headers923self._headers = message.headers # type is CIMultiDictProxy924self._raw_headers = message.raw_headers # type is Tuple[bytes, bytes]925926# payload927self.content = payload928929# cookies930for hdr in self.headers.getall(hdrs.SET_COOKIE, ()):931try:932self.cookies.load(hdr)933except CookieError as exc:934client_logger.warning("Can not load response cookies: %s", exc)935return self936937def _response_eof(self) -> None:938if self._closed:939return940941if self._connection is not None:942# websocket, protocol could be None because943# connection could be detached944if (945self._connection.protocol is not None946and self._connection.protocol.upgraded947):948return949950self._connection.release()951self._connection = None952953self._closed = True954self._cleanup_writer()955956@property957def closed(self) -> bool:958return self._closed959960def close(self) -> None:961if not self._released:962self._notify_content()963if self._closed:964return965966self._closed = True967if self._loop is None or self._loop.is_closed():968return969970if self._connection is not None:971self._connection.close()972self._connection = None973self._cleanup_writer()974975def release(self) -> Any:976if not self._released:977self._notify_content()978if self._closed:979return noop()980981self._closed = True982if self._connection is not None:983self._connection.release()984self._connection = None985986self._cleanup_writer()987return noop()988989@property990def ok(self) -> bool:991"""Returns ``True`` if ``status`` is less than ``400``, ``False`` if not.992993This is **not** a check for ``200 OK`` but a check that the response994status is under 400.995"""996return 400 > self.status997998def raise_for_status(self) -> None:999if not self.ok:1000# reason should always be not None for a started response1001assert self.reason is not None1002self.release()1003raise ClientResponseError(1004self.request_info,1005self.history,1006status=self.status,1007message=self.reason,1008headers=self.headers,1009)10101011def _cleanup_writer(self) -> None:1012if self._writer is not None:1013self._writer.cancel()1014self._writer = None1015self._session = None10161017def _notify_content(self) -> None:1018content = self.content1019if content and content.exception() is None:1020content.set_exception(ClientConnectionError("Connection closed"))1021self._released = True10221023async def wait_for_close(self) -> None:1024if self._writer is not None:1025try:1026await self._writer1027finally:1028self._writer = None1029self.release()10301031async def read(self) -> bytes:1032"""Read response payload."""1033if self._body is None:1034try:1035self._body = await self.content.read()1036for trace in self._traces:1037await trace.send_response_chunk_received(1038self.method, self.url, self._body1039)1040except BaseException:1041self.close()1042raise1043elif self._released:1044raise ClientConnectionError("Connection closed")10451046return self._body # type: ignore[no-any-return]10471048def get_encoding(self) -> str:1049ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower()1050mimetype = helpers.parse_mimetype(ctype)10511052encoding = mimetype.parameters.get("charset")1053if encoding:1054try:1055codecs.lookup(encoding)1056except LookupError:1057encoding = None1058if not encoding:1059if mimetype.type == "application" and (1060mimetype.subtype == "json" or mimetype.subtype == "rdap"1061):1062# RFC 7159 states that the default encoding is UTF-8.1063# RFC 7483 defines application/rdap+json1064encoding = "utf-8"1065elif self._body is None:1066raise RuntimeError(1067"Cannot guess the encoding of " "a not yet read body"1068)1069else:1070encoding = chardet.detect(self._body)["encoding"]1071if not encoding:1072encoding = "utf-8"10731074return encoding10751076async def text(self, encoding: Optional[str] = None, errors: str = "strict") -> str:1077"""Read response payload and decode."""1078if self._body is None:1079await self.read()10801081if encoding is None:1082encoding = self.get_encoding()10831084return self._body.decode( # type: ignore[no-any-return,union-attr]1085encoding, errors=errors1086)10871088async def json(1089self,1090*,1091encoding: Optional[str] = None,1092loads: JSONDecoder = DEFAULT_JSON_DECODER,1093content_type: Optional[str] = "application/json",1094) -> Any:1095"""Read and decodes JSON response."""1096if self._body is None:1097await self.read()10981099if content_type:1100ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower()1101if not _is_expected_content_type(ctype, content_type):1102raise ContentTypeError(1103self.request_info,1104self.history,1105message=(1106"Attempt to decode JSON with " "unexpected mimetype: %s" % ctype1107),1108headers=self.headers,1109)11101111stripped = self._body.strip() # type: ignore[union-attr]1112if not stripped:1113return None11141115if encoding is None:1116encoding = self.get_encoding()11171118return loads(stripped.decode(encoding))11191120async def __aenter__(self) -> "ClientResponse":1121return self11221123async def __aexit__(1124self,1125exc_type: Optional[Type[BaseException]],1126exc_val: Optional[BaseException],1127exc_tb: Optional[TracebackType],1128) -> None:1129# similar to _RequestContextManager, we do not need to check1130# for exceptions, response object can close connection1131# if state is broken1132self.release()113311341135