{
    "file_diffs": [
        {
            "old_file_content": "",
            "new_file_content": "Fixed badly encoded charset crashing when getting response text instead of falling back to charset detector.\n",
            "header": {
                "file": {
                    "path": "CHANGES/9160.bugfix"
                },
                "misc_line": "new file mode 100644"
            },
            "index_line": {
                "old_commit_hash": "00000000",
                "new_commit_hash": "253cfd07",
                "mode": ""
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "/dev/null"
            },
            "plus_file": {
                "path": "b/CHANGES/9160.bugfix"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 0,
                            "length": 0
                        },
                        "new_range": {
                            "start": 1,
                            "length": null
                        },
                        "section": ""
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "Fixed badly encoded charset crashing when getting response text instead of falling back to charset detector.",
                                "type": "added"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "- Contributors -\n----------------\nA. Jesse Jiryu Davis\nAdam Bannister\nAdam Cooper\nAdam Horacek\nAdam Mills\nAdrian Krupa\nAdrián Chaves\nAhmed Tahri\nAlan Tse\nAlec Hanefeld\nAlejandro Gómez\nAleksandr Danshyn\nAleksey Kutepov\nAlex Hayes\nAlex Key\nAlex Khomchenko\nAlex Kuzmenko\nAlex Lisovoy\nAlexander Bayandin\nAlexander Karpinsky\nAlexander Koshevoy\nAlexander Malev\nAlexander Mohr\nAlexander Shorin\nAlexander Travov\nAlexandru Mihai\nAlexey Firsov\nAlexey Nikitin\nAlexey Popravka\nAlexey Stepanov\nAmin Etesamian\nAmit Tulshyan\nAmy Boyle\nAnas El Amraoui\nAnders Melchiorsen\nAndrei Ursulenko\nAndrej Antonov\nAndrew Leech\nAndrew Lytvyn\nAndrew Svetlov\nAndrew Zhou\nAndrii Soldatenko\nAnes Abismail\nAntoine Pietri\nAnton Kasyanov\nAnton Zhdan-Pushkin\nArcadiy Ivanov\nArseny Timoniq\nArtem Yushkovskiy\nArthur Darcet\nAustin Scola\nBen Bader\nBen Greiner\nBen Kallus\nBen Timby\nBenedikt Reinartz\nBob Haddleton\nBoris Feld\nBoyi Chen\nBrett Cannon\nBrian Bouterse\nBrian C. Lane\nBrian Muller\nBruce Merry\nBruno Souza Cabral\nBryan Kok\nBryce Drennan\nCarl George\nCecile Tonglet\nChien-Wei Huang\nChih-Yuan Chen\nChris AtLee\nChris Laws\nChris Moore\nChris Shucksmith\nChristopher Schmitt\nClaudiu Popa\nColin Dunklau\nCong Xu\nDamien Nadé\nDan King\nDan Xu\nDaniel García\nDaniel Golding\nDaniel Grossmann-Kavanagh\nDaniel Nelson\nDanny Song\nDavid Bibb\nDavid Michael Brown\nDenilson Amorim\nDenis Matiychuk\nDenis Moshensky\nDennis Kliban\nDima Veselov\nDimitar Dimitrov\nDiogo Dutra da Mata\nDmitriy Safonov\nDmitry Doroshev\nDmitry Erlikh\nDmitry Lukashin\nDmitry Marakasov\nDmitry Shamov\nDmitry Trofimov\nDmytro Bohomiakov\nDmytro Kuznetsov\nDustin J. Mitchell\nEduard Iskandarov\nEli Ribble\nElizabeth Leddy\nEnrique Saez\nEric Sheng\nErich Healy\nErik Peterson\nEugene Chernyshov\nEugene Naydenov\nEugene Nikolaiev\nEugene Tolmachev\nEvan Kepner\nEvert Lammerts\nFelix Yan\nFernanda Guimarães\nFichteFoll\nFlorian Scheffler\nFranek Magiera\nFrederik Gladhorn\nFrederik Peter Aalund\nGabriel Tremblay\nGary Wilson Jr.\nGennady Andreyev\nGeorges Dubus\nGreg Holt\nGregory Haynes\nGus Goulart\nGustavo Carneiro\nGünther Jena\nHans Adema\nHarmon Y.\nHarry Liu\nHiroshi Ogawa\nHrishikesh Paranjape\nHu Bo\nHugh Young\nHugo Herter\nHugo Hromic\nHugo van Kemenade\nHynek Schlawack\nIgor Alexandrov\nIgor Bolshakov\nIgor Davydenko\nIgor Mozharovsky\nIgor Pavlov\nIllia Volochii\nIlya Chichak\nIlya Gruzinov\nIngmar Steen\nIvan Lakovic\nIvan Larin\nJ. Nick Koston\nJacob Champion\nJaesung Lee\nJake Davis\nJakob Ackermann\nJakub Wilk\nJan Buchar\nJan Gosmann\nJarno Elonen\nJashandeep Sohi\nJean-Baptiste Estival\nJens Steinhauser\nJeonghun Lee\nJeongkyu Shin\nJeroen van der Heijden\nJesus Cea\nJian Zeng\nJinkyu Yi\nJoel Watts\nJohn Parton\nJon Nabozny\nJonas Krüger Svensson\nJonas Obrist\nJonathan Ballet\nJonathan Wright\nJonny Tan\nJoongi Kim\nJordan Borean\nJosep Cugat\nJosh Junon\nJoshu Coats\nJulia Tsemusheva\nJulien Duponchelle\nJungkook Park\nJunjie Tao\nJunyeong Jeong\nJustas Trimailovas\nJustin Foo\nJustin Turner Arthur\nKay Zheng\nKevin Samuel\nKimmo Parviainen-Jalanko\nKirill Klenov\nKirill Malovitsa\nKonstantin Valetov\nKrzysztof Blazewicz\nKyrylo Perevozchikov\nKyungmin Lee\nLars P. Søndergaard\nLee LieWhite\nLiu Hua\nLouis-Philippe Huberdeau\nLoïc Lajeanne\nLu Gong\nLubomir Gelo\nLudovic Gasc\nLuis Pedrosa\nLukasz Marcin Dobrzanski\nMakc Belousow\nManuel Miranda\nMarat Sharafutdinov\nMarc Mueller\nMarco Paolini\nMariano Anaya\nMariusz Masztalerczuk\nMarko Kohtala\nMartijn Pieters\nMartin Melka\nMartin Richard\nMartin Sucha\nMathias Fröjdman\nMathieu Dugré\nMatt VanEseltine\nMatthias Marquardt\nMatthieu Hauglustaine\nMatthieu Rigal\nMatvey Tingaev\nMeet Mangukiya\nMichael Ihnatenko\nMichał Górny\nMikhail Burshteyn\nMikhail Kashkin\nMikhail Lukyanchenko\nMikhail Nacharov\nMisha Behersky\nMitchell Ferree\nMorgan Delahaye-Prat\nMoss Collum\nMun Gwan-gyeong\nNavid Sheikhol\nNicolas Braem\nNikolay Kim\nNikolay Novik\nNikolay Tiunov\nNándor Mátravölgyi\nOisin Aylward\nOlaf Conradi\nPahaz Blinov\nPanagiotis Kolokotronis\nPankaj Pandey\nParag Jain\nPau Freixes\nPaul Colomiets\nPaul J. Dorn\nPaulius Šileikis\nPaulus Schoutsen\nPavel Kamaev\nPavel Polyakov\nPavel Sapezhko\nPavol Vargovčík\nPawel Kowalski\nPawel Miech\nPepe Osca\nPhilipp A.\nPieter van Beek\nQiao Han\nRafael Viotti\nRahul Nahata\nRaphael Bialon\nRaúl Cumplido\nRequired Field\nRobert Lu\nRobert Nikolich\nRoman Podoliaka\nRong Zhang\nSamir Akarioh\nSamuel Colvin\nSean Hunt\nSebastian Acuna\nSebastian Hanula\nSebastian Hüther\nSebastien Geffroy\nSeongSoo Cho\nSergey Ninua\nSergey Skripnick\nSerhii Charykov\nSerhii Kostel\nSerhiy Storchaka\nSimon Kennedy\nSin-Woo Bang\nStanislas Plum\nStanislav Prokop\nStefan Tjarks\nStepan Pletnev\nStephan Jaensch\nStephen Cirelli\nStephen Granade\nSteve Repsher\nSteven Seguin\nSunghyun Hwang\nSunit Deshpande\nSviatoslav Bulbakha\nSviatoslav Sydorenko\nTaha Jahangir\nTaras Voinarovskyi\nTerence Honles\nThanos Lefteris\nThijs Vermeir\nThomas Forbes\nThomas Grainger\nTolga Tezel\nTomasz Trebski\nToshiaki Tanaka\nTrinh Hoang Nhu\nTymofii Tsiapa\nVadim Suharnikov\nVaibhav Sagar\nVamsi Krishna Avula\nVasiliy Faronov\nVasyl Baran\nViacheslav Greshilov\nVictor Collod\nVictor Kovtun\nVikas Kawadia\nViktor Danyliuk\nVille Skyttä\nVincent Maillol\nVitalik Verhovodov\nVitaly Haritonsky\nVitaly Magerya\nVladimir Kamarzin\nVladimir Kozlovski\nVladimir Rutsky\nVladimir Shulyak\nVladimir Zakharov\nVladyslav Bohaichuk\nVladyslav Bondar\nW. Trevor King\nWei Lin\nWeiwei Wang\nWill Fatherley\nWill McGugan\nWillem de Groot\nWilliam Grzybowski\nWilliam S.\nWilson Ong\nwouter bolsterlee\nXavier Halloran\nXiang Li\nYang Zhou\nYannick Koechlin\nYannick Péroux\nYe Cao\nYegor Roganov\nYifei Kong\nYoung-Ho Cha\nYuriy Shatrov\nYury Pliner\nYury Selivanov\nYusuke Tsutsumi\nYuval Ofir\nYuvi Panda\nZainab Lawal\nZeal Wierslee\nZlatan Sičanica\nŁukasz Setla\nМарк Коренберг\nСемён Марьясин\n",
            "new_file_content": "- Contributors -\n----------------\nA. Jesse Jiryu Davis\nAdam Bannister\nAdam Cooper\nAdam Horacek\nAdam Mills\nAdrian Krupa\nAdrián Chaves\nAhmed Tahri\nAlan Tse\nAlec Hanefeld\nAlejandro Gómez\nAleksandr Danshyn\nAleksey Kutepov\nAlex Hayes\nAlex Key\nAlex Khomchenko\nAlex Kuzmenko\nAlex Lisovoy\nAlexander Bayandin\nAlexander Karpinsky\nAlexander Koshevoy\nAlexander Malev\nAlexander Mohr\nAlexander Shorin\nAlexander Travov\nAlexandru Mihai\nAlexey Firsov\nAlexey Nikitin\nAlexey Popravka\nAlexey Stepanov\nAmin Etesamian\nAmit Tulshyan\nAmy Boyle\nAnas El Amraoui\nAnders Melchiorsen\nAndrei Ursulenko\nAndrej Antonov\nAndrew Leech\nAndrew Lytvyn\nAndrew Svetlov\nAndrew Zhou\nAndrii Soldatenko\nAnes Abismail\nAntoine Pietri\nAnton Kasyanov\nAnton Zhdan-Pushkin\nArcadiy Ivanov\nArseny Timoniq\nArtem Yushkovskiy\nArthur Darcet\nAustin Scola\nBen Bader\nBen Greiner\nBen Kallus\nBen Timby\nBenedikt Reinartz\nBob Haddleton\nBoris Feld\nBoyi Chen\nBrett Cannon\nBrian Bouterse\nBrian C. Lane\nBrian Muller\nBruce Merry\nBruno Souza Cabral\nBryan Kok\nBryce Drennan\nCarl George\nCecile Tonglet\nChien-Wei Huang\nChih-Yuan Chen\nChris AtLee\nChris Laws\nChris Moore\nChris Shucksmith\nChristopher Schmitt\nClaudiu Popa\nColin Dunklau\nCong Xu\nDamien Nadé\nDan King\nDan Xu\nDaniel García\nDaniel Golding\nDaniel Grossmann-Kavanagh\nDaniel Nelson\nDanny Song\nDavid Bibb\nDavid Michael Brown\nDenilson Amorim\nDenis Matiychuk\nDenis Moshensky\nDennis Kliban\nDima Veselov\nDimitar Dimitrov\nDiogo Dutra da Mata\nDmitriy Safonov\nDmitry Doroshev\nDmitry Erlikh\nDmitry Lukashin\nDmitry Marakasov\nDmitry Shamov\nDmitry Trofimov\nDmytro Bohomiakov\nDmytro Kuznetsov\nDustin J. Mitchell\nEduard Iskandarov\nEli Ribble\nElizabeth Leddy\nEnrique Saez\nEric Sheng\nErich Healy\nErik Peterson\nEugene Chernyshov\nEugene Naydenov\nEugene Nikolaiev\nEugene Tolmachev\nEvan Kepner\nEvert Lammerts\nFelix Yan\nFernanda Guimarães\nFichteFoll\nFlorian Scheffler\nFranek Magiera\nFrederik Gladhorn\nFrederik Peter Aalund\nGabriel Tremblay\nGary Wilson Jr.\nGennady Andreyev\nGeorges Dubus\nGreg Holt\nGregory Haynes\nGus Goulart\nGustavo Carneiro\nGünther Jena\nHans Adema\nHarmon Y.\nHarry Liu\nHiroshi Ogawa\nHrishikesh Paranjape\nHu Bo\nHugh Young\nHugo Herter\nHugo Hromic\nHugo van Kemenade\nHynek Schlawack\nIgor Alexandrov\nIgor Bolshakov\nIgor Davydenko\nIgor Mozharovsky\nIgor Pavlov\nIllia Volochii\nIlya Chichak\nIlya Gruzinov\nIngmar Steen\nIvan Lakovic\nIvan Larin\nJ. Nick Koston\nJacob Champion\nJaesung Lee\nJake Davis\nJakob Ackermann\nJakub Wilk\nJan Buchar\nJan Gosmann\nJarno Elonen\nJashandeep Sohi\nJean-Baptiste Estival\nJens Steinhauser\nJeonghun Lee\nJeongkyu Shin\nJeroen van der Heijden\nJesus Cea\nJian Zeng\nJinkyu Yi\nJoel Watts\nJohn Parton\nJon Nabozny\nJonas Krüger Svensson\nJonas Obrist\nJonathan Ballet\nJonathan Wright\nJonny Tan\nJoongi Kim\nJordan Borean\nJosep Cugat\nJosh Junon\nJoshu Coats\nJulia Tsemusheva\nJulien Duponchelle\nJungkook Park\nJunjie Tao\nJunyeong Jeong\nJustas Trimailovas\nJustin Foo\nJustin Turner Arthur\nKay Zheng\nKevin Samuel\nKimmo Parviainen-Jalanko\nKirill Klenov\nKirill Malovitsa\nKonstantin Valetov\nKrzysztof Blazewicz\nKyrylo Perevozchikov\nKyungmin Lee\nLars P. Søndergaard\nLee LieWhite\nLiu Hua\nLouis-Philippe Huberdeau\nLoïc Lajeanne\nLu Gong\nLubomir Gelo\nLudovic Gasc\nLuis Pedrosa\nLukasz Marcin Dobrzanski\nMakc Belousow\nManuel Miranda\nMarat Sharafutdinov\nMarc Mueller\nMarco Paolini\nMariano Anaya\nMariusz Masztalerczuk\nMarko Kohtala\nMartijn Pieters\nMartin Melka\nMartin Richard\nMartin Sucha\nMathias Fröjdman\nMathieu Dugré\nMatt VanEseltine\nMatthias Marquardt\nMatthieu Hauglustaine\nMatthieu Rigal\nMatvey Tingaev\nMeet Mangukiya\nMichael Ihnatenko\nMichał Górny\nMikhail Burshteyn\nMikhail Kashkin\nMikhail Lukyanchenko\nMikhail Nacharov\nMisha Behersky\nMitchell Ferree\nMorgan Delahaye-Prat\nMoss Collum\nMun Gwan-gyeong\nNavid Sheikhol\nNicolas Braem\nNikolay Kim\nNikolay Novik\nNikolay Tiunov\nNándor Mátravölgyi\nOisin Aylward\nOlaf Conradi\nPahaz Blinov\nPanagiotis Kolokotronis\nPankaj Pandey\nParag Jain\nPau Freixes\nPaul Colomiets\nPaul J. Dorn\nPaulius Šileikis\nPaulus Schoutsen\nPavel Kamaev\nPavel Polyakov\nPavel Sapezhko\nPavol Vargovčík\nPawel Kowalski\nPawel Miech\nPepe Osca\nPhilipp A.\nPierre-Louis Peeters\nPieter van Beek\nQiao Han\nRafael Viotti\nRahul Nahata\nRaphael Bialon\nRaúl Cumplido\nRequired Field\nRobert Lu\nRobert Nikolich\nRoman Podoliaka\nRong Zhang\nSamir Akarioh\nSamuel Colvin\nSean Hunt\nSebastian Acuna\nSebastian Hanula\nSebastian Hüther\nSebastien Geffroy\nSeongSoo Cho\nSergey Ninua\nSergey Skripnick\nSerhii Charykov\nSerhii Kostel\nSerhiy Storchaka\nSimon Kennedy\nSin-Woo Bang\nStanislas Plum\nStanislav Prokop\nStefan Tjarks\nStepan Pletnev\nStephan Jaensch\nStephen Cirelli\nStephen Granade\nSteve Repsher\nSteven Seguin\nSunghyun Hwang\nSunit Deshpande\nSviatoslav Bulbakha\nSviatoslav Sydorenko\nTaha Jahangir\nTaras Voinarovskyi\nTerence Honles\nThanos Lefteris\nThijs Vermeir\nThomas Forbes\nThomas Grainger\nTolga Tezel\nTomasz Trebski\nToshiaki Tanaka\nTrinh Hoang Nhu\nTymofii Tsiapa\nVadim Suharnikov\nVaibhav Sagar\nVamsi Krishna Avula\nVasiliy Faronov\nVasyl Baran\nViacheslav Greshilov\nVictor Collod\nVictor Kovtun\nVikas Kawadia\nViktor Danyliuk\nVille Skyttä\nVincent Maillol\nVitalik Verhovodov\nVitaly Haritonsky\nVitaly Magerya\nVladimir Kamarzin\nVladimir Kozlovski\nVladimir Rutsky\nVladimir Shulyak\nVladimir Zakharov\nVladyslav Bohaichuk\nVladyslav Bondar\nW. Trevor King\nWei Lin\nWeiwei Wang\nWill Fatherley\nWill McGugan\nWillem de Groot\nWilliam Grzybowski\nWilliam S.\nWilson Ong\nwouter bolsterlee\nXavier Halloran\nXiang Li\nYang Zhou\nYannick Koechlin\nYannick Péroux\nYe Cao\nYegor Roganov\nYifei Kong\nYoung-Ho Cha\nYuriy Shatrov\nYury Pliner\nYury Selivanov\nYusuke Tsutsumi\nYuval Ofir\nYuvi Panda\nZainab Lawal\nZeal Wierslee\nZlatan Sičanica\nŁukasz Setla\nМарк Коренберг\nСемён Марьясин\n",
            "header": {
                "file": {
                    "path": "CONTRIBUTORS.txt"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "e7214dfe",
                "new_commit_hash": "c318f7cc",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/CONTRIBUTORS.txt"
            },
            "plus_file": {
                "path": "b/CONTRIBUTORS.txt"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 271,
                            "length": 6
                        },
                        "new_range": {
                            "start": 271,
                            "length": 7
                        },
                        "section": "Pawel Kowalski"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "Pawel Miech",
                                "type": "context"
                            },
                            {
                                "content": "Pepe Osca",
                                "type": "context"
                            },
                            {
                                "content": "Philipp A.",
                                "type": "context"
                            },
                            {
                                "content": "Pierre-Louis Peeters",
                                "type": "added"
                            },
                            {
                                "content": "Pieter van Beek",
                                "type": "context"
                            },
                            {
                                "content": "Qiao Han",
                                "type": "context"
                            },
                            {
                                "content": "Rafael Viotti",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "import asyncio\nimport codecs\nimport contextlib\nimport functools\nimport io\nimport re\nimport sys\nimport traceback\nimport warnings\nfrom hashlib import md5, sha1, sha256\nfrom http.cookies import CookieError, Morsel, SimpleCookie\nfrom types import MappingProxyType, TracebackType\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Callable,\n    Dict,\n    Iterable,\n    List,\n    Mapping,\n    Optional,\n    Tuple,\n    Type,\n    Union,\n    cast,\n)\n\nimport attr\nfrom multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy\nfrom yarl import URL, __version__ as yarl_version\n\nfrom . import hdrs, helpers, http, multipart, payload\nfrom .abc import AbstractStreamWriter\nfrom .client_exceptions import (\n    ClientConnectionError,\n    ClientOSError,\n    ClientResponseError,\n    ContentTypeError,\n    InvalidURL,\n    ServerFingerprintMismatch,\n)\nfrom .compression_utils import HAS_BROTLI\nfrom .formdata import FormData\nfrom .helpers import (\n    BaseTimerContext,\n    BasicAuth,\n    HeadersMixin,\n    TimerNoop,\n    basicauth_from_netrc,\n    netrc_from_env,\n    noop,\n    reify,\n    set_exception,\n    set_result,\n)\nfrom .http import (\n    SERVER_SOFTWARE,\n    HttpVersion,\n    HttpVersion10,\n    HttpVersion11,\n    StreamWriter,\n)\nfrom .log import client_logger\nfrom .streams import StreamReader\nfrom .typedefs import (\n    DEFAULT_JSON_DECODER,\n    JSONDecoder,\n    LooseCookies,\n    LooseHeaders,\n    Query,\n    RawHeaders,\n)\n\ntry:\n    import ssl\n    from ssl import SSLContext\nexcept ImportError:  # pragma: no cover\n    ssl = None  # type: ignore[assignment]\n    SSLContext = object  # type: ignore[misc,assignment]\n\n\n__all__ = (\"ClientRequest\", \"ClientResponse\", \"RequestInfo\", \"Fingerprint\")\n\n\nif TYPE_CHECKING:\n    from .client import ClientSession\n    from .connector import Connection\n    from .tracing import Trace\n\n\n_CONTAINS_CONTROL_CHAR_RE = re.compile(r\"[^-!#$%&'*+.^_`|~0-9a-zA-Z]\")\n_YARL_SUPPORTS_EXTEND_QUERY = tuple(map(int, yarl_version.split(\".\")[:2])) >= (1, 11)\njson_re = re.compile(r\"^application/(?:[\\w.+-]+?\\+)?json\")\n\n\ndef _gen_default_accept_encoding() -> str:\n    return \"gzip, deflate, br\" if HAS_BROTLI else \"gzip, deflate\"\n\n\n@attr.s(auto_attribs=True, frozen=True, slots=True)\nclass ContentDisposition:\n    type: Optional[str]\n    parameters: \"MappingProxyType[str, str]\"\n    filename: Optional[str]\n\n\n@attr.s(auto_attribs=True, frozen=True, slots=True)\nclass RequestInfo:\n    url: URL\n    method: str\n    headers: \"CIMultiDictProxy[str]\"\n    real_url: URL = attr.ib()\n\n    @real_url.default\n    def real_url_default(self) -> URL:\n        return self.url\n\n\nclass Fingerprint:\n    HASHFUNC_BY_DIGESTLEN = {\n        16: md5,\n        20: sha1,\n        32: sha256,\n    }\n\n    def __init__(self, fingerprint: bytes) -> None:\n        digestlen = len(fingerprint)\n        hashfunc = self.HASHFUNC_BY_DIGESTLEN.get(digestlen)\n        if not hashfunc:\n            raise ValueError(\"fingerprint has invalid length\")\n        elif hashfunc is md5 or hashfunc is sha1:\n            raise ValueError(\n                \"md5 and sha1 are insecure and \" \"not supported. Use sha256.\"\n            )\n        self._hashfunc = hashfunc\n        self._fingerprint = fingerprint\n\n    @property\n    def fingerprint(self) -> bytes:\n        return self._fingerprint\n\n    def check(self, transport: asyncio.Transport) -> None:\n        if not transport.get_extra_info(\"sslcontext\"):\n            return\n        sslobj = transport.get_extra_info(\"ssl_object\")\n        cert = sslobj.getpeercert(binary_form=True)\n        got = self._hashfunc(cert).digest()\n        if got != self._fingerprint:\n            host, port, *_ = transport.get_extra_info(\"peername\")\n            raise ServerFingerprintMismatch(self._fingerprint, got, host, port)\n\n\nif ssl is not None:\n    SSL_ALLOWED_TYPES = (ssl.SSLContext, bool, Fingerprint, type(None))\nelse:  # pragma: no cover\n    SSL_ALLOWED_TYPES = (bool, type(None))\n\n\ndef _merge_ssl_params(\n    ssl: Union[\"SSLContext\", bool, Fingerprint],\n    verify_ssl: Optional[bool],\n    ssl_context: Optional[\"SSLContext\"],\n    fingerprint: Optional[bytes],\n) -> Union[\"SSLContext\", bool, Fingerprint]:\n    if ssl is None:\n        ssl = True  # Double check for backwards compatibility\n    if verify_ssl is not None and not verify_ssl:\n        warnings.warn(\n            \"verify_ssl is deprecated, use ssl=False instead\",\n            DeprecationWarning,\n            stacklevel=3,\n        )\n        if ssl is not True:\n            raise ValueError(\n                \"verify_ssl, ssl_context, fingerprint and ssl \"\n                \"parameters are mutually exclusive\"\n            )\n        else:\n            ssl = False\n    if ssl_context is not None:\n        warnings.warn(\n            \"ssl_context is deprecated, use ssl=context instead\",\n            DeprecationWarning,\n            stacklevel=3,\n        )\n        if ssl is not True:\n            raise ValueError(\n                \"verify_ssl, ssl_context, fingerprint and ssl \"\n                \"parameters are mutually exclusive\"\n            )\n        else:\n            ssl = ssl_context\n    if fingerprint is not None:\n        warnings.warn(\n            \"fingerprint is deprecated, \" \"use ssl=Fingerprint(fingerprint) instead\",\n            DeprecationWarning,\n            stacklevel=3,\n        )\n        if ssl is not True:\n            raise ValueError(\n                \"verify_ssl, ssl_context, fingerprint and ssl \"\n                \"parameters are mutually exclusive\"\n            )\n        else:\n            ssl = Fingerprint(fingerprint)\n    if not isinstance(ssl, SSL_ALLOWED_TYPES):\n        raise TypeError(\n            \"ssl should be SSLContext, bool, Fingerprint or None, \"\n            \"got {!r} instead.\".format(ssl)\n        )\n    return ssl\n\n\n@attr.s(auto_attribs=True, slots=True, frozen=True, cache_hash=True)\nclass ConnectionKey:\n    # the key should contain an information about used proxy / TLS\n    # to prevent reusing wrong connections from a pool\n    host: str\n    port: Optional[int]\n    is_ssl: bool\n    ssl: Union[SSLContext, bool, Fingerprint]\n    proxy: Optional[URL]\n    proxy_auth: Optional[BasicAuth]\n    proxy_headers_hash: Optional[int]  # hash(CIMultiDict)\n\n\ndef _is_expected_content_type(\n    response_content_type: str, expected_content_type: str\n) -> bool:\n    if expected_content_type == \"application/json\":\n        return json_re.match(response_content_type) is not None\n    return expected_content_type in response_content_type\n\n\nclass ClientRequest:\n    GET_METHODS = {\n        hdrs.METH_GET,\n        hdrs.METH_HEAD,\n        hdrs.METH_OPTIONS,\n        hdrs.METH_TRACE,\n    }\n    POST_METHODS = {hdrs.METH_PATCH, hdrs.METH_POST, hdrs.METH_PUT}\n    ALL_METHODS = GET_METHODS.union(POST_METHODS).union({hdrs.METH_DELETE})\n\n    DEFAULT_HEADERS = {\n        hdrs.ACCEPT: \"*/*\",\n        hdrs.ACCEPT_ENCODING: _gen_default_accept_encoding(),\n    }\n\n    # Type of body depends on PAYLOAD_REGISTRY, which is dynamic.\n    body: Any = b\"\"\n    auth = None\n    response = None\n\n    __writer = None  # async task for streaming data\n    _continue = None  # waiter future for '100 Continue' response\n\n    # N.B.\n    # Adding __del__ method with self._writer closing doesn't make sense\n    # because _writer is instance method, thus it keeps a reference to self.\n    # Until writer has finished finalizer will not be called.\n\n    def __init__(\n        self,\n        method: str,\n        url: URL,\n        *,\n        params: Query = None,\n        headers: Optional[LooseHeaders] = None,\n        skip_auto_headers: Optional[Iterable[str]] = None,\n        data: Any = None,\n        cookies: Optional[LooseCookies] = None,\n        auth: Optional[BasicAuth] = None,\n        version: http.HttpVersion = http.HttpVersion11,\n        compress: Union[str, bool, None] = None,\n        chunked: Optional[bool] = None,\n        expect100: bool = False,\n        loop: Optional[asyncio.AbstractEventLoop] = None,\n        response_class: Optional[Type[\"ClientResponse\"]] = None,\n        proxy: Optional[URL] = None,\n        proxy_auth: Optional[BasicAuth] = None,\n        timer: Optional[BaseTimerContext] = None,\n        session: Optional[\"ClientSession\"] = None,\n        ssl: Union[SSLContext, bool, Fingerprint] = True,\n        proxy_headers: Optional[LooseHeaders] = None,\n        traces: Optional[List[\"Trace\"]] = None,\n        trust_env: bool = False,\n        server_hostname: Optional[str] = None,\n    ):\n        if loop is None:\n            loop = asyncio.get_event_loop()\n\n        match = _CONTAINS_CONTROL_CHAR_RE.search(method)\n        if match:\n            raise ValueError(\n                f\"Method cannot contain non-token characters {method!r} \"\n                \"(found at least {match.group()!r})\"\n            )\n\n        assert isinstance(url, URL), url\n        assert isinstance(proxy, (URL, type(None))), proxy\n        # FIXME: session is None in tests only, need to fix tests\n        # assert session is not None\n        self._session = cast(\"ClientSession\", session)\n        if params:\n            if _YARL_SUPPORTS_EXTEND_QUERY:\n                url = url.extend_query(params)\n            else:\n                q = MultiDict(url.query)\n                url2 = url.with_query(params)\n                q.extend(url2.query)\n                url = url.with_query(q)\n        self.original_url = url\n        self.url = url.with_fragment(None)\n        self.method = method.upper()\n        self.chunked = chunked\n        self.compress = compress\n        self.loop = loop\n        self.length = None\n        if response_class is None:\n            real_response_class = ClientResponse\n        else:\n            real_response_class = response_class\n        self.response_class: Type[ClientResponse] = real_response_class\n        self._timer = timer if timer is not None else TimerNoop()\n        self._ssl = ssl if ssl is not None else True\n        self.server_hostname = server_hostname\n\n        if loop.get_debug():\n            self._source_traceback = traceback.extract_stack(sys._getframe(1))\n\n        self.update_version(version)\n        self.update_host(url)\n        self.update_headers(headers)\n        self.update_auto_headers(skip_auto_headers)\n        self.update_cookies(cookies)\n        self.update_content_encoding(data)\n        self.update_auth(auth, trust_env)\n        self.update_proxy(proxy, proxy_auth, proxy_headers)\n\n        self.update_body_from_data(data)\n        if data is not None or self.method not in self.GET_METHODS:\n            self.update_transfer_encoding()\n        self.update_expect_continue(expect100)\n        if traces is None:\n            traces = []\n        self._traces = traces\n\n    def __reset_writer(self, _: object = None) -> None:\n        self.__writer = None\n\n    @property\n    def _writer(self) -> Optional[\"asyncio.Task[None]\"]:\n        return self.__writer\n\n    @_writer.setter\n    def _writer(self, writer: Optional[\"asyncio.Task[None]\"]) -> None:\n        if self.__writer is not None:\n            self.__writer.remove_done_callback(self.__reset_writer)\n        self.__writer = writer\n        if writer is None:\n            return\n        if writer.done():\n            # The writer is already done, so we can reset it immediately.\n            self.__reset_writer()\n        else:\n            writer.add_done_callback(self.__reset_writer)\n\n    def is_ssl(self) -> bool:\n        return self.url.scheme in (\"https\", \"wss\")\n\n    @property\n    def ssl(self) -> Union[\"SSLContext\", bool, Fingerprint]:\n        return self._ssl\n\n    @property\n    def connection_key(self) -> ConnectionKey:\n        proxy_headers = self.proxy_headers\n        if proxy_headers:\n            h: Optional[int] = hash(tuple((k, v) for k, v in proxy_headers.items()))\n        else:\n            h = None\n        return ConnectionKey(\n            self.host,\n            self.port,\n            self.is_ssl(),\n            self.ssl,\n            self.proxy,\n            self.proxy_auth,\n            h,\n        )\n\n    @property\n    def host(self) -> str:\n        ret = self.url.raw_host\n        assert ret is not None\n        return ret\n\n    @property\n    def port(self) -> Optional[int]:\n        return self.url.port\n\n    @property\n    def request_info(self) -> RequestInfo:\n        headers: CIMultiDictProxy[str] = CIMultiDictProxy(self.headers)\n        return RequestInfo(self.url, self.method, headers, self.original_url)\n\n    def update_host(self, url: URL) -> None:\n        \"\"\"Update destination host, port and connection type (ssl).\"\"\"\n        # get host/port\n        if not url.raw_host:\n            raise InvalidURL(url)\n\n        # basic auth info\n        username, password = url.user, url.password\n        if username or password:\n            self.auth = helpers.BasicAuth(username or \"\", password or \"\")\n\n    def update_version(self, version: Union[http.HttpVersion, str]) -> None:\n        \"\"\"Convert request version to two elements tuple.\n\n        parser HTTP version '1.1' => (1, 1)\n        \"\"\"\n        if isinstance(version, str):\n            v = [part.strip() for part in version.split(\".\", 1)]\n            try:\n                version = http.HttpVersion(int(v[0]), int(v[1]))\n            except ValueError:\n                raise ValueError(\n                    f\"Can not parse http version number: {version}\"\n                ) from None\n        self.version = version\n\n    def update_headers(self, headers: Optional[LooseHeaders]) -> None:\n        \"\"\"Update request headers.\"\"\"\n        self.headers: CIMultiDict[str] = CIMultiDict()\n\n        # add host\n        netloc = cast(str, self.url.raw_host)\n        if helpers.is_ipv6_address(netloc):\n            netloc = f\"[{netloc}]\"\n        # See https://github.com/aio-libs/aiohttp/issues/3636.\n        netloc = netloc.rstrip(\".\")\n        if self.url.port is not None and not self.url.is_default_port():\n            netloc += \":\" + str(self.url.port)\n        self.headers[hdrs.HOST] = netloc\n\n        if headers:\n            if isinstance(headers, (dict, MultiDictProxy, MultiDict)):\n                headers = headers.items()\n\n            for key, value in headers:  # type: ignore[misc]\n                # A special case for Host header\n                if key.lower() == \"host\":\n                    self.headers[key] = value\n                else:\n                    self.headers.add(key, value)\n\n    def update_auto_headers(self, skip_auto_headers: Optional[Iterable[str]]) -> None:\n        if skip_auto_headers is not None:\n            self.skip_auto_headers = CIMultiDict(\n                (hdr, None) for hdr in sorted(skip_auto_headers)\n            )\n            used_headers = self.headers.copy()\n            used_headers.extend(self.skip_auto_headers)  # type: ignore[arg-type]\n        else:\n            # Fast path when there are no headers to skip\n            # which is the most common case.\n            self.skip_auto_headers = CIMultiDict()\n            used_headers = self.headers\n\n        for hdr, val in self.DEFAULT_HEADERS.items():\n            if hdr not in used_headers:\n                self.headers.add(hdr, val)\n\n        if hdrs.USER_AGENT not in used_headers:\n            self.headers[hdrs.USER_AGENT] = SERVER_SOFTWARE\n\n    def update_cookies(self, cookies: Optional[LooseCookies]) -> None:\n        \"\"\"Update request cookies header.\"\"\"\n        if not cookies:\n            return\n\n        c = SimpleCookie()\n        if hdrs.COOKIE in self.headers:\n            c.load(self.headers.get(hdrs.COOKIE, \"\"))\n            del self.headers[hdrs.COOKIE]\n\n        if isinstance(cookies, Mapping):\n            iter_cookies = cookies.items()\n        else:\n            iter_cookies = cookies  # type: ignore[assignment]\n        for name, value in iter_cookies:\n            if isinstance(value, Morsel):\n                # Preserve coded_value\n                mrsl_val = value.get(value.key, Morsel())\n                mrsl_val.set(value.key, value.value, value.coded_value)\n                c[name] = mrsl_val\n            else:\n                c[name] = value  # type: ignore[assignment]\n\n        self.headers[hdrs.COOKIE] = c.output(header=\"\", sep=\";\").strip()\n\n    def update_content_encoding(self, data: Any) -> None:\n        \"\"\"Set request content encoding.\"\"\"\n        if not data:\n            # Don't compress an empty body.\n            self.compress = None\n            return\n\n        enc = self.headers.get(hdrs.CONTENT_ENCODING, \"\").lower()\n        if enc:\n            if self.compress:\n                raise ValueError(\n                    \"compress can not be set \" \"if Content-Encoding header is set\"\n                )\n        elif self.compress:\n            if not isinstance(self.compress, str):\n                self.compress = \"deflate\"\n            self.headers[hdrs.CONTENT_ENCODING] = self.compress\n            self.chunked = True  # enable chunked, no need to deal with length\n\n    def update_transfer_encoding(self) -> None:\n        \"\"\"Analyze transfer-encoding header.\"\"\"\n        te = self.headers.get(hdrs.TRANSFER_ENCODING, \"\").lower()\n\n        if \"chunked\" in te:\n            if self.chunked:\n                raise ValueError(\n                    \"chunked can not be set \"\n                    'if \"Transfer-Encoding: chunked\" header is set'\n                )\n\n        elif self.chunked:\n            if hdrs.CONTENT_LENGTH in self.headers:\n                raise ValueError(\n                    \"chunked can not be set \" \"if Content-Length header is set\"\n                )\n\n            self.headers[hdrs.TRANSFER_ENCODING] = \"chunked\"\n        else:\n            if hdrs.CONTENT_LENGTH not in self.headers:\n                self.headers[hdrs.CONTENT_LENGTH] = str(len(self.body))\n\n    def update_auth(self, auth: Optional[BasicAuth], trust_env: bool = False) -> None:\n        \"\"\"Set basic auth.\"\"\"\n        if auth is None:\n            auth = self.auth\n        if auth is None and trust_env and self.url.host is not None:\n            netrc_obj = netrc_from_env()\n            with contextlib.suppress(LookupError):\n                auth = basicauth_from_netrc(netrc_obj, self.url.host)\n        if auth is None:\n            return\n\n        if not isinstance(auth, helpers.BasicAuth):\n            raise TypeError(\"BasicAuth() tuple is required instead\")\n\n        self.headers[hdrs.AUTHORIZATION] = auth.encode()\n\n    def update_body_from_data(self, body: Any) -> None:\n        if body is None:\n            return\n\n        # FormData\n        if isinstance(body, FormData):\n            body = body()\n\n        try:\n            body = payload.PAYLOAD_REGISTRY.get(body, disposition=None)\n        except payload.LookupError:\n            body = FormData(body)()\n\n        self.body = body\n\n        # enable chunked encoding if needed\n        if not self.chunked:\n            if hdrs.CONTENT_LENGTH not in self.headers:\n                size = body.size\n                if size is None:\n                    self.chunked = True\n                else:\n                    if hdrs.CONTENT_LENGTH not in self.headers:\n                        self.headers[hdrs.CONTENT_LENGTH] = str(size)\n\n        # copy payload headers\n        assert body.headers\n        for key, value in body.headers.items():\n            if key in self.headers or key in self.skip_auto_headers:\n                continue\n            self.headers[key] = value\n\n    def update_expect_continue(self, expect: bool = False) -> None:\n        if expect:\n            self.headers[hdrs.EXPECT] = \"100-continue\"\n        elif self.headers.get(hdrs.EXPECT, \"\").lower() == \"100-continue\":\n            expect = True\n\n        if expect:\n            self._continue = self.loop.create_future()\n\n    def update_proxy(\n        self,\n        proxy: Optional[URL],\n        proxy_auth: Optional[BasicAuth],\n        proxy_headers: Optional[LooseHeaders],\n    ) -> None:\n        if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth):\n            raise ValueError(\"proxy_auth must be None or BasicAuth() tuple\")\n        self.proxy = proxy\n        self.proxy_auth = proxy_auth\n        if proxy_headers is not None and not isinstance(\n            proxy_headers, (MultiDict, MultiDictProxy)\n        ):\n            proxy_headers = CIMultiDict(proxy_headers)\n        self.proxy_headers = proxy_headers\n\n    def keep_alive(self) -> bool:\n        if self.version < HttpVersion10:\n            # keep alive not supported at all\n            return False\n        if self.version == HttpVersion10:\n            if self.headers.get(hdrs.CONNECTION) == \"keep-alive\":\n                return True\n            else:  # no headers means we close for Http 1.0\n                return False\n        elif self.headers.get(hdrs.CONNECTION) == \"close\":\n            return False\n\n        return True\n\n    async def write_bytes(\n        self, writer: AbstractStreamWriter, conn: \"Connection\"\n    ) -> None:\n        \"\"\"Support coroutines that yields bytes objects.\"\"\"\n        # 100 response\n        if self._continue is not None:\n            try:\n                await writer.drain()\n                await self._continue\n            except asyncio.CancelledError:\n                return\n\n        protocol = conn.protocol\n        assert protocol is not None\n        try:\n            if isinstance(self.body, payload.Payload):\n                await self.body.write(writer)\n            else:\n                if isinstance(self.body, (bytes, bytearray)):\n                    self.body = (self.body,)\n\n                for chunk in self.body:\n                    await writer.write(chunk)\n        except OSError as underlying_exc:\n            reraised_exc = underlying_exc\n\n            exc_is_not_timeout = underlying_exc.errno is not None or not isinstance(\n                underlying_exc, asyncio.TimeoutError\n            )\n            if exc_is_not_timeout:\n                reraised_exc = ClientOSError(\n                    underlying_exc.errno,\n                    f\"Can not write request body for {self.url !s}\",\n                )\n\n            set_exception(protocol, reraised_exc, underlying_exc)\n        except asyncio.CancelledError:\n            # Body hasn't been fully sent, so connection can't be reused.\n            conn.close()\n        except Exception as underlying_exc:\n            set_exception(\n                protocol,\n                ClientConnectionError(\n                    f\"Failed to send bytes into the underlying connection {conn !s}\",\n                ),\n                underlying_exc,\n            )\n        else:\n            await writer.write_eof()\n            protocol.start_timeout()\n\n    async def send(self, conn: \"Connection\") -> \"ClientResponse\":\n        # Specify request target:\n        # - CONNECT request must send authority form URI\n        # - not CONNECT proxy must send absolute form URI\n        # - most common is origin form URI\n        if self.method == hdrs.METH_CONNECT:\n            connect_host = self.url.raw_host\n            assert connect_host is not None\n            if helpers.is_ipv6_address(connect_host):\n                connect_host = f\"[{connect_host}]\"\n            path = f\"{connect_host}:{self.url.port}\"\n        elif self.proxy and not self.is_ssl():\n            path = str(self.url)\n        else:\n            path = self.url.raw_path\n            if self.url.raw_query_string:\n                path += \"?\" + self.url.raw_query_string\n\n        protocol = conn.protocol\n        assert protocol is not None\n        writer = StreamWriter(\n            protocol,\n            self.loop,\n            on_chunk_sent=(\n                functools.partial(self._on_chunk_request_sent, self.method, self.url)\n                if self._traces\n                else None\n            ),\n            on_headers_sent=(\n                functools.partial(self._on_headers_request_sent, self.method, self.url)\n                if self._traces\n                else None\n            ),\n        )\n\n        if self.compress:\n            writer.enable_compression(self.compress)  # type: ignore[arg-type]\n\n        if self.chunked is not None:\n            writer.enable_chunking()\n\n        # set default content-type\n        if (\n            self.method in self.POST_METHODS\n            and hdrs.CONTENT_TYPE not in self.skip_auto_headers\n            and hdrs.CONTENT_TYPE not in self.headers\n        ):\n            self.headers[hdrs.CONTENT_TYPE] = \"application/octet-stream\"\n\n        # set the connection header\n        connection = self.headers.get(hdrs.CONNECTION)\n        if not connection:\n            if self.keep_alive():\n                if self.version == HttpVersion10:\n                    connection = \"keep-alive\"\n            else:\n                if self.version == HttpVersion11:\n                    connection = \"close\"\n\n        if connection is not None:\n            self.headers[hdrs.CONNECTION] = connection\n\n        # status + headers\n        v = self.version\n        status_line = f\"{self.method} {path} HTTP/{v.major}.{v.minor}\"\n        await writer.write_headers(status_line, self.headers)\n        coro = self.write_bytes(writer, conn)\n\n        if sys.version_info >= (3, 12):\n            # Optimization for Python 3.12, try to write\n            # bytes immediately to avoid having to schedule\n            # the task on the event loop.\n            task = asyncio.Task(coro, loop=self.loop, eager_start=True)\n        else:\n            task = self.loop.create_task(coro)\n\n        self._writer = task\n        response_class = self.response_class\n        assert response_class is not None\n        self.response = response_class(\n            self.method,\n            self.original_url,\n            writer=self._writer,\n            continue100=self._continue,\n            timer=self._timer,\n            request_info=self.request_info,\n            traces=self._traces,\n            loop=self.loop,\n            session=self._session,\n        )\n        return self.response\n\n    async def close(self) -> None:\n        if self._writer is not None:\n            with contextlib.suppress(asyncio.CancelledError):\n                await self._writer\n\n    def terminate(self) -> None:\n        if self._writer is not None:\n            if not self.loop.is_closed():\n                self._writer.cancel()\n            self._writer.remove_done_callback(self.__reset_writer)\n            self._writer = None\n\n    async def _on_chunk_request_sent(self, method: str, url: URL, chunk: bytes) -> None:\n        for trace in self._traces:\n            await trace.send_request_chunk_sent(method, url, chunk)\n\n    async def _on_headers_request_sent(\n        self, method: str, url: URL, headers: \"CIMultiDict[str]\"\n    ) -> None:\n        for trace in self._traces:\n            await trace.send_request_headers(method, url, headers)\n\n\nclass ClientResponse(HeadersMixin):\n\n    # Some of these attributes are None when created,\n    # but will be set by the start() method.\n    # As the end user will likely never see the None values, we cheat the types below.\n    # from the Status-Line of the response\n    version: Optional[HttpVersion] = None  # HTTP-Version\n    status: int = None  # type: ignore[assignment] # Status-Code\n    reason: Optional[str] = None  # Reason-Phrase\n\n    content: StreamReader = None  # type: ignore[assignment]  # Payload stream\n    _headers: CIMultiDictProxy[str] = None  # type: ignore[assignment]\n    _raw_headers: RawHeaders = None  # type: ignore[assignment]\n\n    _connection = None  # current connection\n    _source_traceback: Optional[traceback.StackSummary] = None\n    # set up by ClientRequest after ClientResponse object creation\n    # post-init stage allows to not change ctor signature\n    _closed = True  # to allow __del__ for non-initialized properly response\n    _released = False\n    __writer = None\n\n    def __init__(\n        self,\n        method: str,\n        url: URL,\n        *,\n        writer: \"asyncio.Task[None]\",\n        continue100: Optional[\"asyncio.Future[bool]\"],\n        timer: BaseTimerContext,\n        request_info: RequestInfo,\n        traces: List[\"Trace\"],\n        loop: asyncio.AbstractEventLoop,\n        session: \"ClientSession\",\n    ) -> None:\n        assert isinstance(url, URL)\n\n        self.method = method\n        self.cookies = SimpleCookie()\n\n        self._real_url = url\n        self._url = url.with_fragment(None)\n        self._body: Any = None\n        self._writer: Optional[asyncio.Task[None]] = writer\n        self._continue = continue100  # None by default\n        self._closed = True\n        self._history: Tuple[ClientResponse, ...] = ()\n        self._request_info = request_info\n        self._timer = timer if timer is not None else TimerNoop()\n        self._cache: Dict[str, Any] = {}\n        self._traces = traces\n        self._loop = loop\n        # store a reference to session #1985\n        self._session: Optional[ClientSession] = session\n        # Save reference to _resolve_charset, so that get_encoding() will still\n        # work after the response has finished reading the body.\n        if session is None:\n            # TODO: Fix session=None in tests (see ClientRequest.__init__).\n            self._resolve_charset: Callable[[\"ClientResponse\", bytes], str] = (\n                lambda *_: \"utf-8\"\n            )\n        else:\n            self._resolve_charset = session._resolve_charset\n        if loop.get_debug():\n            self._source_traceback = traceback.extract_stack(sys._getframe(1))\n\n    def __reset_writer(self, _: object = None) -> None:\n        self.__writer = None\n\n    @property\n    def _writer(self) -> Optional[\"asyncio.Task[None]\"]:\n        return self.__writer\n\n    @_writer.setter\n    def _writer(self, writer: Optional[\"asyncio.Task[None]\"]) -> None:\n        if self.__writer is not None:\n            self.__writer.remove_done_callback(self.__reset_writer)\n        self.__writer = writer\n        if writer is None:\n            return\n        if writer.done():\n            # The writer is already done, so we can reset it immediately.\n            self.__reset_writer()\n        else:\n            writer.add_done_callback(self.__reset_writer)\n\n    @reify\n    def url(self) -> URL:\n        return self._url\n\n    @reify\n    def url_obj(self) -> URL:\n        warnings.warn(\"Deprecated, use .url #1654\", DeprecationWarning, stacklevel=2)\n        return self._url\n\n    @reify\n    def real_url(self) -> URL:\n        return self._real_url\n\n    @reify\n    def host(self) -> str:\n        assert self._url.host is not None\n        return self._url.host\n\n    @reify\n    def headers(self) -> \"CIMultiDictProxy[str]\":\n        return self._headers\n\n    @reify\n    def raw_headers(self) -> RawHeaders:\n        return self._raw_headers\n\n    @reify\n    def request_info(self) -> RequestInfo:\n        return self._request_info\n\n    @reify\n    def content_disposition(self) -> Optional[ContentDisposition]:\n        raw = self._headers.get(hdrs.CONTENT_DISPOSITION)\n        if raw is None:\n            return None\n        disposition_type, params_dct = multipart.parse_content_disposition(raw)\n        params = MappingProxyType(params_dct)\n        filename = multipart.content_disposition_filename(params)\n        return ContentDisposition(disposition_type, params, filename)\n\n    def __del__(self, _warnings: Any = warnings) -> None:\n        if self._closed:\n            return\n\n        if self._connection is not None:\n            self._connection.release()\n            self._cleanup_writer()\n\n            if self._loop.get_debug():\n                kwargs = {\"source\": self}\n                _warnings.warn(f\"Unclosed response {self!r}\", ResourceWarning, **kwargs)\n                context = {\"client_response\": self, \"message\": \"Unclosed response\"}\n                if self._source_traceback:\n                    context[\"source_traceback\"] = self._source_traceback\n                self._loop.call_exception_handler(context)\n\n    def __repr__(self) -> str:\n        out = io.StringIO()\n        ascii_encodable_url = str(self.url)\n        if self.reason:\n            ascii_encodable_reason = self.reason.encode(\n                \"ascii\", \"backslashreplace\"\n            ).decode(\"ascii\")\n        else:\n            ascii_encodable_reason = \"None\"\n        print(\n            \"<ClientResponse({}) [{} {}]>\".format(\n                ascii_encodable_url, self.status, ascii_encodable_reason\n            ),\n            file=out,\n        )\n        print(self.headers, file=out)\n        return out.getvalue()\n\n    @property\n    def connection(self) -> Optional[\"Connection\"]:\n        return self._connection\n\n    @reify\n    def history(self) -> Tuple[\"ClientResponse\", ...]:\n        \"\"\"A sequence of of responses, if redirects occurred.\"\"\"\n        return self._history\n\n    @reify\n    def links(self) -> \"MultiDictProxy[MultiDictProxy[Union[str, URL]]]\":\n        links_str = \", \".join(self.headers.getall(\"link\", []))\n\n        if not links_str:\n            return MultiDictProxy(MultiDict())\n\n        links: MultiDict[MultiDictProxy[Union[str, URL]]] = MultiDict()\n\n        for val in re.split(r\",(?=\\s*<)\", links_str):\n            match = re.match(r\"\\s*<(.*)>(.*)\", val)\n            if match is None:  # pragma: no cover\n                # the check exists to suppress mypy error\n                continue\n            url, params_str = match.groups()\n            params = params_str.split(\";\")[1:]\n\n            link: MultiDict[Union[str, URL]] = MultiDict()\n\n            for param in params:\n                match = re.match(r\"^\\s*(\\S*)\\s*=\\s*(['\\\"]?)(.*?)(\\2)\\s*$\", param, re.M)\n                if match is None:  # pragma: no cover\n                    # the check exists to suppress mypy error\n                    continue\n                key, _, value, _ = match.groups()\n\n                link.add(key, value)\n\n            key = link.get(\"rel\", url)\n\n            link.add(\"url\", self.url.join(URL(url)))\n\n            links.add(str(key), MultiDictProxy(link))\n\n        return MultiDictProxy(links)\n\n    async def start(self, connection: \"Connection\") -> \"ClientResponse\":\n        \"\"\"Start response processing.\"\"\"\n        self._closed = False\n        self._protocol = connection.protocol\n        self._connection = connection\n\n        with self._timer:\n            while True:\n                # read response\n                try:\n                    protocol = self._protocol\n                    message, payload = await protocol.read()  # type: ignore[union-attr]\n                except http.HttpProcessingError as exc:\n                    raise ClientResponseError(\n                        self.request_info,\n                        self.history,\n                        status=exc.code,\n                        message=exc.message,\n                        headers=exc.headers,\n                    ) from exc\n\n                if message.code < 100 or message.code > 199 or message.code == 101:\n                    break\n\n                if self._continue is not None:\n                    set_result(self._continue, True)\n                    self._continue = None\n\n        # payload eof handler\n        payload.on_eof(self._response_eof)\n\n        # response status\n        self.version = message.version\n        self.status = message.code\n        self.reason = message.reason\n\n        # headers\n        self._headers = message.headers  # type is CIMultiDictProxy\n        self._raw_headers = message.raw_headers  # type is Tuple[bytes, bytes]\n\n        # payload\n        self.content = payload\n\n        # cookies\n        for hdr in self.headers.getall(hdrs.SET_COOKIE, ()):\n            try:\n                self.cookies.load(hdr)\n            except CookieError as exc:\n                client_logger.warning(\"Can not load response cookies: %s\", exc)\n        return self\n\n    def _response_eof(self) -> None:\n        if self._closed:\n            return\n\n        # protocol could be None because connection could be detached\n        protocol = self._connection and self._connection.protocol\n        if protocol is not None and protocol.upgraded:\n            return\n\n        self._closed = True\n        self._cleanup_writer()\n        self._release_connection()\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    def close(self) -> None:\n        if not self._released:\n            self._notify_content()\n\n        self._closed = True\n        if self._loop is None or self._loop.is_closed():\n            return\n\n        self._cleanup_writer()\n        if self._connection is not None:\n            self._connection.close()\n            self._connection = None\n\n    def release(self) -> Any:\n        if not self._released:\n            self._notify_content()\n\n        self._closed = True\n\n        self._cleanup_writer()\n        self._release_connection()\n        return noop()\n\n    @property\n    def ok(self) -> bool:\n        \"\"\"Returns ``True`` if ``status`` is less than ``400``, ``False`` if not.\n\n        This is **not** a check for ``200 OK`` but a check that the response\n        status is under 400.\n        \"\"\"\n        return 400 > self.status\n\n    def raise_for_status(self) -> None:\n        if not self.ok:\n            # reason should always be not None for a started response\n            assert self.reason is not None\n            self.release()\n            raise ClientResponseError(\n                self.request_info,\n                self.history,\n                status=self.status,\n                message=self.reason,\n                headers=self.headers,\n            )\n\n    def _release_connection(self) -> None:\n        if self._connection is not None:\n            if self._writer is None:\n                self._connection.release()\n                self._connection = None\n            else:\n                self._writer.add_done_callback(lambda f: self._release_connection())\n\n    async def _wait_released(self) -> None:\n        if self._writer is not None:\n            await self._writer\n        self._release_connection()\n\n    def _cleanup_writer(self) -> None:\n        if self._writer is not None:\n            self._writer.cancel()\n        self._session = None\n\n    def _notify_content(self) -> None:\n        content = self.content\n        if content and content.exception() is None:\n            set_exception(content, ClientConnectionError(\"Connection closed\"))\n        self._released = True\n\n    async def wait_for_close(self) -> None:\n        if self._writer is not None:\n            await self._writer\n        self.release()\n\n    async def read(self) -> bytes:\n        \"\"\"Read response payload.\"\"\"\n        if self._body is None:\n            try:\n                self._body = await self.content.read()\n                for trace in self._traces:\n                    await trace.send_response_chunk_received(\n                        self.method, self.url, self._body\n                    )\n            except BaseException:\n                self.close()\n                raise\n        elif self._released:  # Response explicitly released\n            raise ClientConnectionError(\"Connection closed\")\n\n        protocol = self._connection and self._connection.protocol\n        if protocol is None or not protocol.upgraded:\n            await self._wait_released()  # Underlying connection released\n        return self._body  # type: ignore[no-any-return]\n\n    def get_encoding(self) -> str:\n        ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n        mimetype = helpers.parse_mimetype(ctype)\n\n        encoding = mimetype.parameters.get(\"charset\")\n        if encoding:\n            with contextlib.suppress(LookupError):\n                return codecs.lookup(encoding).name\n\n        if mimetype.type == \"application\" and (\n            mimetype.subtype == \"json\" or mimetype.subtype == \"rdap\"\n        ):\n            # RFC 7159 states that the default encoding is UTF-8.\n            # RFC 7483 defines application/rdap+json\n            return \"utf-8\"\n\n        if self._body is None:\n            raise RuntimeError(\n                \"Cannot compute fallback encoding of a not yet read body\"\n            )\n\n        return self._resolve_charset(self, self._body)\n\n    async def text(self, encoding: Optional[str] = None, errors: str = \"strict\") -> str:\n        \"\"\"Read response payload and decode.\"\"\"\n        if self._body is None:\n            await self.read()\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return self._body.decode(  # type: ignore[no-any-return,union-attr]\n            encoding, errors=errors\n        )\n\n    async def json(\n        self,\n        *,\n        encoding: Optional[str] = None,\n        loads: JSONDecoder = DEFAULT_JSON_DECODER,\n        content_type: Optional[str] = \"application/json\",\n    ) -> Any:\n        \"\"\"Read and decodes JSON response.\"\"\"\n        if self._body is None:\n            await self.read()\n\n        if content_type:\n            ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n            if not _is_expected_content_type(ctype, content_type):\n                raise ContentTypeError(\n                    self.request_info,\n                    self.history,\n                    status=self.status,\n                    message=(\n                        \"Attempt to decode JSON with \" \"unexpected mimetype: %s\" % ctype\n                    ),\n                    headers=self.headers,\n                )\n\n        stripped = self._body.strip()  # type: ignore[union-attr]\n        if not stripped:\n            return None\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return loads(stripped.decode(encoding))\n\n    async def __aenter__(self) -> \"ClientResponse\":\n        return self\n\n    async def __aexit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        # similar to _RequestContextManager, we do not need to check\n        # for exceptions, response object can close connection\n        # if state is broken\n        self.release()\n        await self.wait_for_close()\n",
            "new_file_content": "import asyncio\nimport codecs\nimport contextlib\nimport functools\nimport io\nimport re\nimport sys\nimport traceback\nimport warnings\nfrom hashlib import md5, sha1, sha256\nfrom http.cookies import CookieError, Morsel, SimpleCookie\nfrom types import MappingProxyType, TracebackType\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Callable,\n    Dict,\n    Iterable,\n    List,\n    Mapping,\n    Optional,\n    Tuple,\n    Type,\n    Union,\n    cast,\n)\n\nimport attr\nfrom multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy\nfrom yarl import URL, __version__ as yarl_version\n\nfrom . import hdrs, helpers, http, multipart, payload\nfrom .abc import AbstractStreamWriter\nfrom .client_exceptions import (\n    ClientConnectionError,\n    ClientOSError,\n    ClientResponseError,\n    ContentTypeError,\n    InvalidURL,\n    ServerFingerprintMismatch,\n)\nfrom .compression_utils import HAS_BROTLI\nfrom .formdata import FormData\nfrom .helpers import (\n    BaseTimerContext,\n    BasicAuth,\n    HeadersMixin,\n    TimerNoop,\n    basicauth_from_netrc,\n    netrc_from_env,\n    noop,\n    reify,\n    set_exception,\n    set_result,\n)\nfrom .http import (\n    SERVER_SOFTWARE,\n    HttpVersion,\n    HttpVersion10,\n    HttpVersion11,\n    StreamWriter,\n)\nfrom .log import client_logger\nfrom .streams import StreamReader\nfrom .typedefs import (\n    DEFAULT_JSON_DECODER,\n    JSONDecoder,\n    LooseCookies,\n    LooseHeaders,\n    Query,\n    RawHeaders,\n)\n\ntry:\n    import ssl\n    from ssl import SSLContext\nexcept ImportError:  # pragma: no cover\n    ssl = None  # type: ignore[assignment]\n    SSLContext = object  # type: ignore[misc,assignment]\n\n\n__all__ = (\"ClientRequest\", \"ClientResponse\", \"RequestInfo\", \"Fingerprint\")\n\n\nif TYPE_CHECKING:\n    from .client import ClientSession\n    from .connector import Connection\n    from .tracing import Trace\n\n\n_CONTAINS_CONTROL_CHAR_RE = re.compile(r\"[^-!#$%&'*+.^_`|~0-9a-zA-Z]\")\n_YARL_SUPPORTS_EXTEND_QUERY = tuple(map(int, yarl_version.split(\".\")[:2])) >= (1, 11)\njson_re = re.compile(r\"^application/(?:[\\w.+-]+?\\+)?json\")\n\n\ndef _gen_default_accept_encoding() -> str:\n    return \"gzip, deflate, br\" if HAS_BROTLI else \"gzip, deflate\"\n\n\n@attr.s(auto_attribs=True, frozen=True, slots=True)\nclass ContentDisposition:\n    type: Optional[str]\n    parameters: \"MappingProxyType[str, str]\"\n    filename: Optional[str]\n\n\n@attr.s(auto_attribs=True, frozen=True, slots=True)\nclass RequestInfo:\n    url: URL\n    method: str\n    headers: \"CIMultiDictProxy[str]\"\n    real_url: URL = attr.ib()\n\n    @real_url.default\n    def real_url_default(self) -> URL:\n        return self.url\n\n\nclass Fingerprint:\n    HASHFUNC_BY_DIGESTLEN = {\n        16: md5,\n        20: sha1,\n        32: sha256,\n    }\n\n    def __init__(self, fingerprint: bytes) -> None:\n        digestlen = len(fingerprint)\n        hashfunc = self.HASHFUNC_BY_DIGESTLEN.get(digestlen)\n        if not hashfunc:\n            raise ValueError(\"fingerprint has invalid length\")\n        elif hashfunc is md5 or hashfunc is sha1:\n            raise ValueError(\n                \"md5 and sha1 are insecure and \" \"not supported. Use sha256.\"\n            )\n        self._hashfunc = hashfunc\n        self._fingerprint = fingerprint\n\n    @property\n    def fingerprint(self) -> bytes:\n        return self._fingerprint\n\n    def check(self, transport: asyncio.Transport) -> None:\n        if not transport.get_extra_info(\"sslcontext\"):\n            return\n        sslobj = transport.get_extra_info(\"ssl_object\")\n        cert = sslobj.getpeercert(binary_form=True)\n        got = self._hashfunc(cert).digest()\n        if got != self._fingerprint:\n            host, port, *_ = transport.get_extra_info(\"peername\")\n            raise ServerFingerprintMismatch(self._fingerprint, got, host, port)\n\n\nif ssl is not None:\n    SSL_ALLOWED_TYPES = (ssl.SSLContext, bool, Fingerprint, type(None))\nelse:  # pragma: no cover\n    SSL_ALLOWED_TYPES = (bool, type(None))\n\n\ndef _merge_ssl_params(\n    ssl: Union[\"SSLContext\", bool, Fingerprint],\n    verify_ssl: Optional[bool],\n    ssl_context: Optional[\"SSLContext\"],\n    fingerprint: Optional[bytes],\n) -> Union[\"SSLContext\", bool, Fingerprint]:\n    if ssl is None:\n        ssl = True  # Double check for backwards compatibility\n    if verify_ssl is not None and not verify_ssl:\n        warnings.warn(\n            \"verify_ssl is deprecated, use ssl=False instead\",\n            DeprecationWarning,\n            stacklevel=3,\n        )\n        if ssl is not True:\n            raise ValueError(\n                \"verify_ssl, ssl_context, fingerprint and ssl \"\n                \"parameters are mutually exclusive\"\n            )\n        else:\n            ssl = False\n    if ssl_context is not None:\n        warnings.warn(\n            \"ssl_context is deprecated, use ssl=context instead\",\n            DeprecationWarning,\n            stacklevel=3,\n        )\n        if ssl is not True:\n            raise ValueError(\n                \"verify_ssl, ssl_context, fingerprint and ssl \"\n                \"parameters are mutually exclusive\"\n            )\n        else:\n            ssl = ssl_context\n    if fingerprint is not None:\n        warnings.warn(\n            \"fingerprint is deprecated, \" \"use ssl=Fingerprint(fingerprint) instead\",\n            DeprecationWarning,\n            stacklevel=3,\n        )\n        if ssl is not True:\n            raise ValueError(\n                \"verify_ssl, ssl_context, fingerprint and ssl \"\n                \"parameters are mutually exclusive\"\n            )\n        else:\n            ssl = Fingerprint(fingerprint)\n    if not isinstance(ssl, SSL_ALLOWED_TYPES):\n        raise TypeError(\n            \"ssl should be SSLContext, bool, Fingerprint or None, \"\n            \"got {!r} instead.\".format(ssl)\n        )\n    return ssl\n\n\n@attr.s(auto_attribs=True, slots=True, frozen=True, cache_hash=True)\nclass ConnectionKey:\n    # the key should contain an information about used proxy / TLS\n    # to prevent reusing wrong connections from a pool\n    host: str\n    port: Optional[int]\n    is_ssl: bool\n    ssl: Union[SSLContext, bool, Fingerprint]\n    proxy: Optional[URL]\n    proxy_auth: Optional[BasicAuth]\n    proxy_headers_hash: Optional[int]  # hash(CIMultiDict)\n\n\ndef _is_expected_content_type(\n    response_content_type: str, expected_content_type: str\n) -> bool:\n    if expected_content_type == \"application/json\":\n        return json_re.match(response_content_type) is not None\n    return expected_content_type in response_content_type\n\n\nclass ClientRequest:\n    GET_METHODS = {\n        hdrs.METH_GET,\n        hdrs.METH_HEAD,\n        hdrs.METH_OPTIONS,\n        hdrs.METH_TRACE,\n    }\n    POST_METHODS = {hdrs.METH_PATCH, hdrs.METH_POST, hdrs.METH_PUT}\n    ALL_METHODS = GET_METHODS.union(POST_METHODS).union({hdrs.METH_DELETE})\n\n    DEFAULT_HEADERS = {\n        hdrs.ACCEPT: \"*/*\",\n        hdrs.ACCEPT_ENCODING: _gen_default_accept_encoding(),\n    }\n\n    # Type of body depends on PAYLOAD_REGISTRY, which is dynamic.\n    body: Any = b\"\"\n    auth = None\n    response = None\n\n    __writer = None  # async task for streaming data\n    _continue = None  # waiter future for '100 Continue' response\n\n    # N.B.\n    # Adding __del__ method with self._writer closing doesn't make sense\n    # because _writer is instance method, thus it keeps a reference to self.\n    # Until writer has finished finalizer will not be called.\n\n    def __init__(\n        self,\n        method: str,\n        url: URL,\n        *,\n        params: Query = None,\n        headers: Optional[LooseHeaders] = None,\n        skip_auto_headers: Optional[Iterable[str]] = None,\n        data: Any = None,\n        cookies: Optional[LooseCookies] = None,\n        auth: Optional[BasicAuth] = None,\n        version: http.HttpVersion = http.HttpVersion11,\n        compress: Union[str, bool, None] = None,\n        chunked: Optional[bool] = None,\n        expect100: bool = False,\n        loop: Optional[asyncio.AbstractEventLoop] = None,\n        response_class: Optional[Type[\"ClientResponse\"]] = None,\n        proxy: Optional[URL] = None,\n        proxy_auth: Optional[BasicAuth] = None,\n        timer: Optional[BaseTimerContext] = None,\n        session: Optional[\"ClientSession\"] = None,\n        ssl: Union[SSLContext, bool, Fingerprint] = True,\n        proxy_headers: Optional[LooseHeaders] = None,\n        traces: Optional[List[\"Trace\"]] = None,\n        trust_env: bool = False,\n        server_hostname: Optional[str] = None,\n    ):\n        if loop is None:\n            loop = asyncio.get_event_loop()\n\n        match = _CONTAINS_CONTROL_CHAR_RE.search(method)\n        if match:\n            raise ValueError(\n                f\"Method cannot contain non-token characters {method!r} \"\n                \"(found at least {match.group()!r})\"\n            )\n\n        assert isinstance(url, URL), url\n        assert isinstance(proxy, (URL, type(None))), proxy\n        # FIXME: session is None in tests only, need to fix tests\n        # assert session is not None\n        self._session = cast(\"ClientSession\", session)\n        if params:\n            if _YARL_SUPPORTS_EXTEND_QUERY:\n                url = url.extend_query(params)\n            else:\n                q = MultiDict(url.query)\n                url2 = url.with_query(params)\n                q.extend(url2.query)\n                url = url.with_query(q)\n        self.original_url = url\n        self.url = url.with_fragment(None)\n        self.method = method.upper()\n        self.chunked = chunked\n        self.compress = compress\n        self.loop = loop\n        self.length = None\n        if response_class is None:\n            real_response_class = ClientResponse\n        else:\n            real_response_class = response_class\n        self.response_class: Type[ClientResponse] = real_response_class\n        self._timer = timer if timer is not None else TimerNoop()\n        self._ssl = ssl if ssl is not None else True\n        self.server_hostname = server_hostname\n\n        if loop.get_debug():\n            self._source_traceback = traceback.extract_stack(sys._getframe(1))\n\n        self.update_version(version)\n        self.update_host(url)\n        self.update_headers(headers)\n        self.update_auto_headers(skip_auto_headers)\n        self.update_cookies(cookies)\n        self.update_content_encoding(data)\n        self.update_auth(auth, trust_env)\n        self.update_proxy(proxy, proxy_auth, proxy_headers)\n\n        self.update_body_from_data(data)\n        if data is not None or self.method not in self.GET_METHODS:\n            self.update_transfer_encoding()\n        self.update_expect_continue(expect100)\n        if traces is None:\n            traces = []\n        self._traces = traces\n\n    def __reset_writer(self, _: object = None) -> None:\n        self.__writer = None\n\n    @property\n    def _writer(self) -> Optional[\"asyncio.Task[None]\"]:\n        return self.__writer\n\n    @_writer.setter\n    def _writer(self, writer: Optional[\"asyncio.Task[None]\"]) -> None:\n        if self.__writer is not None:\n            self.__writer.remove_done_callback(self.__reset_writer)\n        self.__writer = writer\n        if writer is None:\n            return\n        if writer.done():\n            # The writer is already done, so we can reset it immediately.\n            self.__reset_writer()\n        else:\n            writer.add_done_callback(self.__reset_writer)\n\n    def is_ssl(self) -> bool:\n        return self.url.scheme in (\"https\", \"wss\")\n\n    @property\n    def ssl(self) -> Union[\"SSLContext\", bool, Fingerprint]:\n        return self._ssl\n\n    @property\n    def connection_key(self) -> ConnectionKey:\n        proxy_headers = self.proxy_headers\n        if proxy_headers:\n            h: Optional[int] = hash(tuple((k, v) for k, v in proxy_headers.items()))\n        else:\n            h = None\n        return ConnectionKey(\n            self.host,\n            self.port,\n            self.is_ssl(),\n            self.ssl,\n            self.proxy,\n            self.proxy_auth,\n            h,\n        )\n\n    @property\n    def host(self) -> str:\n        ret = self.url.raw_host\n        assert ret is not None\n        return ret\n\n    @property\n    def port(self) -> Optional[int]:\n        return self.url.port\n\n    @property\n    def request_info(self) -> RequestInfo:\n        headers: CIMultiDictProxy[str] = CIMultiDictProxy(self.headers)\n        return RequestInfo(self.url, self.method, headers, self.original_url)\n\n    def update_host(self, url: URL) -> None:\n        \"\"\"Update destination host, port and connection type (ssl).\"\"\"\n        # get host/port\n        if not url.raw_host:\n            raise InvalidURL(url)\n\n        # basic auth info\n        username, password = url.user, url.password\n        if username or password:\n            self.auth = helpers.BasicAuth(username or \"\", password or \"\")\n\n    def update_version(self, version: Union[http.HttpVersion, str]) -> None:\n        \"\"\"Convert request version to two elements tuple.\n\n        parser HTTP version '1.1' => (1, 1)\n        \"\"\"\n        if isinstance(version, str):\n            v = [part.strip() for part in version.split(\".\", 1)]\n            try:\n                version = http.HttpVersion(int(v[0]), int(v[1]))\n            except ValueError:\n                raise ValueError(\n                    f\"Can not parse http version number: {version}\"\n                ) from None\n        self.version = version\n\n    def update_headers(self, headers: Optional[LooseHeaders]) -> None:\n        \"\"\"Update request headers.\"\"\"\n        self.headers: CIMultiDict[str] = CIMultiDict()\n\n        # add host\n        netloc = cast(str, self.url.raw_host)\n        if helpers.is_ipv6_address(netloc):\n            netloc = f\"[{netloc}]\"\n        # See https://github.com/aio-libs/aiohttp/issues/3636.\n        netloc = netloc.rstrip(\".\")\n        if self.url.port is not None and not self.url.is_default_port():\n            netloc += \":\" + str(self.url.port)\n        self.headers[hdrs.HOST] = netloc\n\n        if headers:\n            if isinstance(headers, (dict, MultiDictProxy, MultiDict)):\n                headers = headers.items()\n\n            for key, value in headers:  # type: ignore[misc]\n                # A special case for Host header\n                if key.lower() == \"host\":\n                    self.headers[key] = value\n                else:\n                    self.headers.add(key, value)\n\n    def update_auto_headers(self, skip_auto_headers: Optional[Iterable[str]]) -> None:\n        if skip_auto_headers is not None:\n            self.skip_auto_headers = CIMultiDict(\n                (hdr, None) for hdr in sorted(skip_auto_headers)\n            )\n            used_headers = self.headers.copy()\n            used_headers.extend(self.skip_auto_headers)  # type: ignore[arg-type]\n        else:\n            # Fast path when there are no headers to skip\n            # which is the most common case.\n            self.skip_auto_headers = CIMultiDict()\n            used_headers = self.headers\n\n        for hdr, val in self.DEFAULT_HEADERS.items():\n            if hdr not in used_headers:\n                self.headers.add(hdr, val)\n\n        if hdrs.USER_AGENT not in used_headers:\n            self.headers[hdrs.USER_AGENT] = SERVER_SOFTWARE\n\n    def update_cookies(self, cookies: Optional[LooseCookies]) -> None:\n        \"\"\"Update request cookies header.\"\"\"\n        if not cookies:\n            return\n\n        c = SimpleCookie()\n        if hdrs.COOKIE in self.headers:\n            c.load(self.headers.get(hdrs.COOKIE, \"\"))\n            del self.headers[hdrs.COOKIE]\n\n        if isinstance(cookies, Mapping):\n            iter_cookies = cookies.items()\n        else:\n            iter_cookies = cookies  # type: ignore[assignment]\n        for name, value in iter_cookies:\n            if isinstance(value, Morsel):\n                # Preserve coded_value\n                mrsl_val = value.get(value.key, Morsel())\n                mrsl_val.set(value.key, value.value, value.coded_value)\n                c[name] = mrsl_val\n            else:\n                c[name] = value  # type: ignore[assignment]\n\n        self.headers[hdrs.COOKIE] = c.output(header=\"\", sep=\";\").strip()\n\n    def update_content_encoding(self, data: Any) -> None:\n        \"\"\"Set request content encoding.\"\"\"\n        if not data:\n            # Don't compress an empty body.\n            self.compress = None\n            return\n\n        enc = self.headers.get(hdrs.CONTENT_ENCODING, \"\").lower()\n        if enc:\n            if self.compress:\n                raise ValueError(\n                    \"compress can not be set \" \"if Content-Encoding header is set\"\n                )\n        elif self.compress:\n            if not isinstance(self.compress, str):\n                self.compress = \"deflate\"\n            self.headers[hdrs.CONTENT_ENCODING] = self.compress\n            self.chunked = True  # enable chunked, no need to deal with length\n\n    def update_transfer_encoding(self) -> None:\n        \"\"\"Analyze transfer-encoding header.\"\"\"\n        te = self.headers.get(hdrs.TRANSFER_ENCODING, \"\").lower()\n\n        if \"chunked\" in te:\n            if self.chunked:\n                raise ValueError(\n                    \"chunked can not be set \"\n                    'if \"Transfer-Encoding: chunked\" header is set'\n                )\n\n        elif self.chunked:\n            if hdrs.CONTENT_LENGTH in self.headers:\n                raise ValueError(\n                    \"chunked can not be set \" \"if Content-Length header is set\"\n                )\n\n            self.headers[hdrs.TRANSFER_ENCODING] = \"chunked\"\n        else:\n            if hdrs.CONTENT_LENGTH not in self.headers:\n                self.headers[hdrs.CONTENT_LENGTH] = str(len(self.body))\n\n    def update_auth(self, auth: Optional[BasicAuth], trust_env: bool = False) -> None:\n        \"\"\"Set basic auth.\"\"\"\n        if auth is None:\n            auth = self.auth\n        if auth is None and trust_env and self.url.host is not None:\n            netrc_obj = netrc_from_env()\n            with contextlib.suppress(LookupError):\n                auth = basicauth_from_netrc(netrc_obj, self.url.host)\n        if auth is None:\n            return\n\n        if not isinstance(auth, helpers.BasicAuth):\n            raise TypeError(\"BasicAuth() tuple is required instead\")\n\n        self.headers[hdrs.AUTHORIZATION] = auth.encode()\n\n    def update_body_from_data(self, body: Any) -> None:\n        if body is None:\n            return\n\n        # FormData\n        if isinstance(body, FormData):\n            body = body()\n\n        try:\n            body = payload.PAYLOAD_REGISTRY.get(body, disposition=None)\n        except payload.LookupError:\n            body = FormData(body)()\n\n        self.body = body\n\n        # enable chunked encoding if needed\n        if not self.chunked:\n            if hdrs.CONTENT_LENGTH not in self.headers:\n                size = body.size\n                if size is None:\n                    self.chunked = True\n                else:\n                    if hdrs.CONTENT_LENGTH not in self.headers:\n                        self.headers[hdrs.CONTENT_LENGTH] = str(size)\n\n        # copy payload headers\n        assert body.headers\n        for key, value in body.headers.items():\n            if key in self.headers or key in self.skip_auto_headers:\n                continue\n            self.headers[key] = value\n\n    def update_expect_continue(self, expect: bool = False) -> None:\n        if expect:\n            self.headers[hdrs.EXPECT] = \"100-continue\"\n        elif self.headers.get(hdrs.EXPECT, \"\").lower() == \"100-continue\":\n            expect = True\n\n        if expect:\n            self._continue = self.loop.create_future()\n\n    def update_proxy(\n        self,\n        proxy: Optional[URL],\n        proxy_auth: Optional[BasicAuth],\n        proxy_headers: Optional[LooseHeaders],\n    ) -> None:\n        if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth):\n            raise ValueError(\"proxy_auth must be None or BasicAuth() tuple\")\n        self.proxy = proxy\n        self.proxy_auth = proxy_auth\n        if proxy_headers is not None and not isinstance(\n            proxy_headers, (MultiDict, MultiDictProxy)\n        ):\n            proxy_headers = CIMultiDict(proxy_headers)\n        self.proxy_headers = proxy_headers\n\n    def keep_alive(self) -> bool:\n        if self.version < HttpVersion10:\n            # keep alive not supported at all\n            return False\n        if self.version == HttpVersion10:\n            if self.headers.get(hdrs.CONNECTION) == \"keep-alive\":\n                return True\n            else:  # no headers means we close for Http 1.0\n                return False\n        elif self.headers.get(hdrs.CONNECTION) == \"close\":\n            return False\n\n        return True\n\n    async def write_bytes(\n        self, writer: AbstractStreamWriter, conn: \"Connection\"\n    ) -> None:\n        \"\"\"Support coroutines that yields bytes objects.\"\"\"\n        # 100 response\n        if self._continue is not None:\n            try:\n                await writer.drain()\n                await self._continue\n            except asyncio.CancelledError:\n                return\n\n        protocol = conn.protocol\n        assert protocol is not None\n        try:\n            if isinstance(self.body, payload.Payload):\n                await self.body.write(writer)\n            else:\n                if isinstance(self.body, (bytes, bytearray)):\n                    self.body = (self.body,)\n\n                for chunk in self.body:\n                    await writer.write(chunk)\n        except OSError as underlying_exc:\n            reraised_exc = underlying_exc\n\n            exc_is_not_timeout = underlying_exc.errno is not None or not isinstance(\n                underlying_exc, asyncio.TimeoutError\n            )\n            if exc_is_not_timeout:\n                reraised_exc = ClientOSError(\n                    underlying_exc.errno,\n                    f\"Can not write request body for {self.url !s}\",\n                )\n\n            set_exception(protocol, reraised_exc, underlying_exc)\n        except asyncio.CancelledError:\n            # Body hasn't been fully sent, so connection can't be reused.\n            conn.close()\n        except Exception as underlying_exc:\n            set_exception(\n                protocol,\n                ClientConnectionError(\n                    f\"Failed to send bytes into the underlying connection {conn !s}\",\n                ),\n                underlying_exc,\n            )\n        else:\n            await writer.write_eof()\n            protocol.start_timeout()\n\n    async def send(self, conn: \"Connection\") -> \"ClientResponse\":\n        # Specify request target:\n        # - CONNECT request must send authority form URI\n        # - not CONNECT proxy must send absolute form URI\n        # - most common is origin form URI\n        if self.method == hdrs.METH_CONNECT:\n            connect_host = self.url.raw_host\n            assert connect_host is not None\n            if helpers.is_ipv6_address(connect_host):\n                connect_host = f\"[{connect_host}]\"\n            path = f\"{connect_host}:{self.url.port}\"\n        elif self.proxy and not self.is_ssl():\n            path = str(self.url)\n        else:\n            path = self.url.raw_path\n            if self.url.raw_query_string:\n                path += \"?\" + self.url.raw_query_string\n\n        protocol = conn.protocol\n        assert protocol is not None\n        writer = StreamWriter(\n            protocol,\n            self.loop,\n            on_chunk_sent=(\n                functools.partial(self._on_chunk_request_sent, self.method, self.url)\n                if self._traces\n                else None\n            ),\n            on_headers_sent=(\n                functools.partial(self._on_headers_request_sent, self.method, self.url)\n                if self._traces\n                else None\n            ),\n        )\n\n        if self.compress:\n            writer.enable_compression(self.compress)  # type: ignore[arg-type]\n\n        if self.chunked is not None:\n            writer.enable_chunking()\n\n        # set default content-type\n        if (\n            self.method in self.POST_METHODS\n            and hdrs.CONTENT_TYPE not in self.skip_auto_headers\n            and hdrs.CONTENT_TYPE not in self.headers\n        ):\n            self.headers[hdrs.CONTENT_TYPE] = \"application/octet-stream\"\n\n        # set the connection header\n        connection = self.headers.get(hdrs.CONNECTION)\n        if not connection:\n            if self.keep_alive():\n                if self.version == HttpVersion10:\n                    connection = \"keep-alive\"\n            else:\n                if self.version == HttpVersion11:\n                    connection = \"close\"\n\n        if connection is not None:\n            self.headers[hdrs.CONNECTION] = connection\n\n        # status + headers\n        v = self.version\n        status_line = f\"{self.method} {path} HTTP/{v.major}.{v.minor}\"\n        await writer.write_headers(status_line, self.headers)\n        coro = self.write_bytes(writer, conn)\n\n        if sys.version_info >= (3, 12):\n            # Optimization for Python 3.12, try to write\n            # bytes immediately to avoid having to schedule\n            # the task on the event loop.\n            task = asyncio.Task(coro, loop=self.loop, eager_start=True)\n        else:\n            task = self.loop.create_task(coro)\n\n        self._writer = task\n        response_class = self.response_class\n        assert response_class is not None\n        self.response = response_class(\n            self.method,\n            self.original_url,\n            writer=self._writer,\n            continue100=self._continue,\n            timer=self._timer,\n            request_info=self.request_info,\n            traces=self._traces,\n            loop=self.loop,\n            session=self._session,\n        )\n        return self.response\n\n    async def close(self) -> None:\n        if self._writer is not None:\n            with contextlib.suppress(asyncio.CancelledError):\n                await self._writer\n\n    def terminate(self) -> None:\n        if self._writer is not None:\n            if not self.loop.is_closed():\n                self._writer.cancel()\n            self._writer.remove_done_callback(self.__reset_writer)\n            self._writer = None\n\n    async def _on_chunk_request_sent(self, method: str, url: URL, chunk: bytes) -> None:\n        for trace in self._traces:\n            await trace.send_request_chunk_sent(method, url, chunk)\n\n    async def _on_headers_request_sent(\n        self, method: str, url: URL, headers: \"CIMultiDict[str]\"\n    ) -> None:\n        for trace in self._traces:\n            await trace.send_request_headers(method, url, headers)\n\n\nclass ClientResponse(HeadersMixin):\n\n    # Some of these attributes are None when created,\n    # but will be set by the start() method.\n    # As the end user will likely never see the None values, we cheat the types below.\n    # from the Status-Line of the response\n    version: Optional[HttpVersion] = None  # HTTP-Version\n    status: int = None  # type: ignore[assignment] # Status-Code\n    reason: Optional[str] = None  # Reason-Phrase\n\n    content: StreamReader = None  # type: ignore[assignment]  # Payload stream\n    _headers: CIMultiDictProxy[str] = None  # type: ignore[assignment]\n    _raw_headers: RawHeaders = None  # type: ignore[assignment]\n\n    _connection = None  # current connection\n    _source_traceback: Optional[traceback.StackSummary] = None\n    # set up by ClientRequest after ClientResponse object creation\n    # post-init stage allows to not change ctor signature\n    _closed = True  # to allow __del__ for non-initialized properly response\n    _released = False\n    __writer = None\n\n    def __init__(\n        self,\n        method: str,\n        url: URL,\n        *,\n        writer: \"asyncio.Task[None]\",\n        continue100: Optional[\"asyncio.Future[bool]\"],\n        timer: BaseTimerContext,\n        request_info: RequestInfo,\n        traces: List[\"Trace\"],\n        loop: asyncio.AbstractEventLoop,\n        session: \"ClientSession\",\n    ) -> None:\n        assert isinstance(url, URL)\n\n        self.method = method\n        self.cookies = SimpleCookie()\n\n        self._real_url = url\n        self._url = url.with_fragment(None)\n        self._body: Any = None\n        self._writer: Optional[asyncio.Task[None]] = writer\n        self._continue = continue100  # None by default\n        self._closed = True\n        self._history: Tuple[ClientResponse, ...] = ()\n        self._request_info = request_info\n        self._timer = timer if timer is not None else TimerNoop()\n        self._cache: Dict[str, Any] = {}\n        self._traces = traces\n        self._loop = loop\n        # store a reference to session #1985\n        self._session: Optional[ClientSession] = session\n        # Save reference to _resolve_charset, so that get_encoding() will still\n        # work after the response has finished reading the body.\n        if session is None:\n            # TODO: Fix session=None in tests (see ClientRequest.__init__).\n            self._resolve_charset: Callable[[\"ClientResponse\", bytes], str] = (\n                lambda *_: \"utf-8\"\n            )\n        else:\n            self._resolve_charset = session._resolve_charset\n        if loop.get_debug():\n            self._source_traceback = traceback.extract_stack(sys._getframe(1))\n\n    def __reset_writer(self, _: object = None) -> None:\n        self.__writer = None\n\n    @property\n    def _writer(self) -> Optional[\"asyncio.Task[None]\"]:\n        return self.__writer\n\n    @_writer.setter\n    def _writer(self, writer: Optional[\"asyncio.Task[None]\"]) -> None:\n        if self.__writer is not None:\n            self.__writer.remove_done_callback(self.__reset_writer)\n        self.__writer = writer\n        if writer is None:\n            return\n        if writer.done():\n            # The writer is already done, so we can reset it immediately.\n            self.__reset_writer()\n        else:\n            writer.add_done_callback(self.__reset_writer)\n\n    @reify\n    def url(self) -> URL:\n        return self._url\n\n    @reify\n    def url_obj(self) -> URL:\n        warnings.warn(\"Deprecated, use .url #1654\", DeprecationWarning, stacklevel=2)\n        return self._url\n\n    @reify\n    def real_url(self) -> URL:\n        return self._real_url\n\n    @reify\n    def host(self) -> str:\n        assert self._url.host is not None\n        return self._url.host\n\n    @reify\n    def headers(self) -> \"CIMultiDictProxy[str]\":\n        return self._headers\n\n    @reify\n    def raw_headers(self) -> RawHeaders:\n        return self._raw_headers\n\n    @reify\n    def request_info(self) -> RequestInfo:\n        return self._request_info\n\n    @reify\n    def content_disposition(self) -> Optional[ContentDisposition]:\n        raw = self._headers.get(hdrs.CONTENT_DISPOSITION)\n        if raw is None:\n            return None\n        disposition_type, params_dct = multipart.parse_content_disposition(raw)\n        params = MappingProxyType(params_dct)\n        filename = multipart.content_disposition_filename(params)\n        return ContentDisposition(disposition_type, params, filename)\n\n    def __del__(self, _warnings: Any = warnings) -> None:\n        if self._closed:\n            return\n\n        if self._connection is not None:\n            self._connection.release()\n            self._cleanup_writer()\n\n            if self._loop.get_debug():\n                kwargs = {\"source\": self}\n                _warnings.warn(f\"Unclosed response {self!r}\", ResourceWarning, **kwargs)\n                context = {\"client_response\": self, \"message\": \"Unclosed response\"}\n                if self._source_traceback:\n                    context[\"source_traceback\"] = self._source_traceback\n                self._loop.call_exception_handler(context)\n\n    def __repr__(self) -> str:\n        out = io.StringIO()\n        ascii_encodable_url = str(self.url)\n        if self.reason:\n            ascii_encodable_reason = self.reason.encode(\n                \"ascii\", \"backslashreplace\"\n            ).decode(\"ascii\")\n        else:\n            ascii_encodable_reason = \"None\"\n        print(\n            \"<ClientResponse({}) [{} {}]>\".format(\n                ascii_encodable_url, self.status, ascii_encodable_reason\n            ),\n            file=out,\n        )\n        print(self.headers, file=out)\n        return out.getvalue()\n\n    @property\n    def connection(self) -> Optional[\"Connection\"]:\n        return self._connection\n\n    @reify\n    def history(self) -> Tuple[\"ClientResponse\", ...]:\n        \"\"\"A sequence of of responses, if redirects occurred.\"\"\"\n        return self._history\n\n    @reify\n    def links(self) -> \"MultiDictProxy[MultiDictProxy[Union[str, URL]]]\":\n        links_str = \", \".join(self.headers.getall(\"link\", []))\n\n        if not links_str:\n            return MultiDictProxy(MultiDict())\n\n        links: MultiDict[MultiDictProxy[Union[str, URL]]] = MultiDict()\n\n        for val in re.split(r\",(?=\\s*<)\", links_str):\n            match = re.match(r\"\\s*<(.*)>(.*)\", val)\n            if match is None:  # pragma: no cover\n                # the check exists to suppress mypy error\n                continue\n            url, params_str = match.groups()\n            params = params_str.split(\";\")[1:]\n\n            link: MultiDict[Union[str, URL]] = MultiDict()\n\n            for param in params:\n                match = re.match(r\"^\\s*(\\S*)\\s*=\\s*(['\\\"]?)(.*?)(\\2)\\s*$\", param, re.M)\n                if match is None:  # pragma: no cover\n                    # the check exists to suppress mypy error\n                    continue\n                key, _, value, _ = match.groups()\n\n                link.add(key, value)\n\n            key = link.get(\"rel\", url)\n\n            link.add(\"url\", self.url.join(URL(url)))\n\n            links.add(str(key), MultiDictProxy(link))\n\n        return MultiDictProxy(links)\n\n    async def start(self, connection: \"Connection\") -> \"ClientResponse\":\n        \"\"\"Start response processing.\"\"\"\n        self._closed = False\n        self._protocol = connection.protocol\n        self._connection = connection\n\n        with self._timer:\n            while True:\n                # read response\n                try:\n                    protocol = self._protocol\n                    message, payload = await protocol.read()  # type: ignore[union-attr]\n                except http.HttpProcessingError as exc:\n                    raise ClientResponseError(\n                        self.request_info,\n                        self.history,\n                        status=exc.code,\n                        message=exc.message,\n                        headers=exc.headers,\n                    ) from exc\n\n                if message.code < 100 or message.code > 199 or message.code == 101:\n                    break\n\n                if self._continue is not None:\n                    set_result(self._continue, True)\n                    self._continue = None\n\n        # payload eof handler\n        payload.on_eof(self._response_eof)\n\n        # response status\n        self.version = message.version\n        self.status = message.code\n        self.reason = message.reason\n\n        # headers\n        self._headers = message.headers  # type is CIMultiDictProxy\n        self._raw_headers = message.raw_headers  # type is Tuple[bytes, bytes]\n\n        # payload\n        self.content = payload\n\n        # cookies\n        for hdr in self.headers.getall(hdrs.SET_COOKIE, ()):\n            try:\n                self.cookies.load(hdr)\n            except CookieError as exc:\n                client_logger.warning(\"Can not load response cookies: %s\", exc)\n        return self\n\n    def _response_eof(self) -> None:\n        if self._closed:\n            return\n\n        # protocol could be None because connection could be detached\n        protocol = self._connection and self._connection.protocol\n        if protocol is not None and protocol.upgraded:\n            return\n\n        self._closed = True\n        self._cleanup_writer()\n        self._release_connection()\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    def close(self) -> None:\n        if not self._released:\n            self._notify_content()\n\n        self._closed = True\n        if self._loop is None or self._loop.is_closed():\n            return\n\n        self._cleanup_writer()\n        if self._connection is not None:\n            self._connection.close()\n            self._connection = None\n\n    def release(self) -> Any:\n        if not self._released:\n            self._notify_content()\n\n        self._closed = True\n\n        self._cleanup_writer()\n        self._release_connection()\n        return noop()\n\n    @property\n    def ok(self) -> bool:\n        \"\"\"Returns ``True`` if ``status`` is less than ``400``, ``False`` if not.\n\n        This is **not** a check for ``200 OK`` but a check that the response\n        status is under 400.\n        \"\"\"\n        return 400 > self.status\n\n    def raise_for_status(self) -> None:\n        if not self.ok:\n            # reason should always be not None for a started response\n            assert self.reason is not None\n            self.release()\n            raise ClientResponseError(\n                self.request_info,\n                self.history,\n                status=self.status,\n                message=self.reason,\n                headers=self.headers,\n            )\n\n    def _release_connection(self) -> None:\n        if self._connection is not None:\n            if self._writer is None:\n                self._connection.release()\n                self._connection = None\n            else:\n                self._writer.add_done_callback(lambda f: self._release_connection())\n\n    async def _wait_released(self) -> None:\n        if self._writer is not None:\n            await self._writer\n        self._release_connection()\n\n    def _cleanup_writer(self) -> None:\n        if self._writer is not None:\n            self._writer.cancel()\n        self._session = None\n\n    def _notify_content(self) -> None:\n        content = self.content\n        if content and content.exception() is None:\n            set_exception(content, ClientConnectionError(\"Connection closed\"))\n        self._released = True\n\n    async def wait_for_close(self) -> None:\n        if self._writer is not None:\n            await self._writer\n        self.release()\n\n    async def read(self) -> bytes:\n        \"\"\"Read response payload.\"\"\"\n        if self._body is None:\n            try:\n                self._body = await self.content.read()\n                for trace in self._traces:\n                    await trace.send_response_chunk_received(\n                        self.method, self.url, self._body\n                    )\n            except BaseException:\n                self.close()\n                raise\n        elif self._released:  # Response explicitly released\n            raise ClientConnectionError(\"Connection closed\")\n\n        protocol = self._connection and self._connection.protocol\n        if protocol is None or not protocol.upgraded:\n            await self._wait_released()  # Underlying connection released\n        return self._body  # type: ignore[no-any-return]\n\n    def get_encoding(self) -> str:\n        ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n        mimetype = helpers.parse_mimetype(ctype)\n\n        encoding = mimetype.parameters.get(\"charset\")\n        if encoding:\n            with contextlib.suppress(LookupError, ValueError):\n                return codecs.lookup(encoding).name\n\n        if mimetype.type == \"application\" and (\n            mimetype.subtype == \"json\" or mimetype.subtype == \"rdap\"\n        ):\n            # RFC 7159 states that the default encoding is UTF-8.\n            # RFC 7483 defines application/rdap+json\n            return \"utf-8\"\n\n        if self._body is None:\n            raise RuntimeError(\n                \"Cannot compute fallback encoding of a not yet read body\"\n            )\n\n        return self._resolve_charset(self, self._body)\n\n    async def text(self, encoding: Optional[str] = None, errors: str = \"strict\") -> str:\n        \"\"\"Read response payload and decode.\"\"\"\n        if self._body is None:\n            await self.read()\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return self._body.decode(  # type: ignore[no-any-return,union-attr]\n            encoding, errors=errors\n        )\n\n    async def json(\n        self,\n        *,\n        encoding: Optional[str] = None,\n        loads: JSONDecoder = DEFAULT_JSON_DECODER,\n        content_type: Optional[str] = \"application/json\",\n    ) -> Any:\n        \"\"\"Read and decodes JSON response.\"\"\"\n        if self._body is None:\n            await self.read()\n\n        if content_type:\n            ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n            if not _is_expected_content_type(ctype, content_type):\n                raise ContentTypeError(\n                    self.request_info,\n                    self.history,\n                    status=self.status,\n                    message=(\n                        \"Attempt to decode JSON with \" \"unexpected mimetype: %s\" % ctype\n                    ),\n                    headers=self.headers,\n                )\n\n        stripped = self._body.strip()  # type: ignore[union-attr]\n        if not stripped:\n            return None\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return loads(stripped.decode(encoding))\n\n    async def __aenter__(self) -> \"ClientResponse\":\n        return self\n\n    async def __aexit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        # similar to _RequestContextManager, we do not need to check\n        # for exceptions, response object can close connection\n        # if state is broken\n        self.release()\n        await self.wait_for_close()\n",
            "header": {
                "file": {
                    "path": "aiohttp/client_reqrep.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "93e7b59a",
                "new_commit_hash": "57f3323a",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/aiohttp/client_reqrep.py"
            },
            "plus_file": {
                "path": "b/aiohttp/client_reqrep.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 1168,
                            "length": 7
                        },
                        "new_range": {
                            "start": 1168,
                            "length": 7
                        },
                        "section": "class ClientResponse(HeadersMixin):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        encoding = mimetype.parameters.get(\"charset\")",
                                "type": "context"
                            },
                            {
                                "content": "        if encoding:",
                                "type": "context"
                            },
                            {
                                "content": "            with contextlib.suppress(LookupError):",
                                "type": "deleted"
                            },
                            {
                                "content": "            with contextlib.suppress(LookupError, ValueError):",
                                "type": "added"
                            },
                            {
                                "content": "                return codecs.lookup(encoding).name",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        if mimetype.type == \"application\" and (",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "aiohttp/client_reqrep.py",
                            "type": "class",
                            "name": "ClientResponse",
                            "content": "class ClientResponse(HeadersMixin):\n\n    # Some of these attributes are None when created,\n    # but will be set by the start() method.\n    # As the end user will likely never see the None values, we cheat the types below.\n    # from the Status-Line of the response\n    version: Optional[HttpVersion] = None  # HTTP-Version\n    status: int = None  # type: ignore[assignment] # Status-Code\n    reason: Optional[str] = None  # Reason-Phrase\n\n    content: StreamReader = None  # type: ignore[assignment]  # Payload stream\n    _headers: CIMultiDictProxy[str] = None  # type: ignore[assignment]\n    _raw_headers: RawHeaders = None  # type: ignore[assignment]\n\n    _connection = None  # current connection\n    _source_traceback: Optional[traceback.StackSummary] = None\n    # set up by ClientRequest after ClientResponse object creation\n    # post-init stage allows to not change ctor signature\n    _closed = True  # to allow __del__ for non-initialized properly response\n    _released = False\n    __writer = None\n\n    def __init__(\n        self,\n        method: str,\n        url: URL,\n        *,\n        writer: \"asyncio.Task[None]\",\n        continue100: Optional[\"asyncio.Future[bool]\"],\n        timer: BaseTimerContext,\n        request_info: RequestInfo,\n        traces: List[\"Trace\"],\n        loop: asyncio.AbstractEventLoop,\n        session: \"ClientSession\",\n    ) -> None:\n        assert isinstance(url, URL)\n\n        self.method = method\n        self.cookies = SimpleCookie()\n\n        self._real_url = url\n        self._url = url.with_fragment(None)\n        self._body: Any = None\n        self._writer: Optional[asyncio.Task[None]] = writer\n        self._continue = continue100  # None by default\n        self._closed = True\n        self._history: Tuple[ClientResponse, ...] = ()\n        self._request_info = request_info\n        self._timer = timer if timer is not None else TimerNoop()\n        self._cache: Dict[str, Any] = {}\n        self._traces = traces\n        self._loop = loop\n        # store a reference to session #1985\n        self._session: Optional[ClientSession] = session\n        # Save reference to _resolve_charset, so that get_encoding() will still\n        # work after the response has finished reading the body.\n        if session is None:\n            # TODO: Fix session=None in tests (see ClientRequest.__init__).\n            self._resolve_charset: Callable[[\"ClientResponse\", bytes], str] = (\n                lambda *_: \"utf-8\"\n            )\n        else:\n            self._resolve_charset = session._resolve_charset\n        if loop.get_debug():\n            self._source_traceback = traceback.extract_stack(sys._getframe(1))\n\n    def __reset_writer(self, _: object = None) -> None:\n        self.__writer = None\n\n    @property\n    def _writer(self) -> Optional[\"asyncio.Task[None]\"]:\n        return self.__writer\n\n    @_writer.setter\n    def _writer(self, writer: Optional[\"asyncio.Task[None]\"]) -> None:\n        if self.__writer is not None:\n            self.__writer.remove_done_callback(self.__reset_writer)\n        self.__writer = writer\n        if writer is None:\n            return\n        if writer.done():\n            # The writer is already done, so we can reset it immediately.\n            self.__reset_writer()\n        else:\n            writer.add_done_callback(self.__reset_writer)\n\n    @reify\n    def url(self) -> URL:\n        return self._url\n\n    @reify\n    def url_obj(self) -> URL:\n        warnings.warn(\"Deprecated, use .url #1654\", DeprecationWarning, stacklevel=2)\n        return self._url\n\n    @reify\n    def real_url(self) -> URL:\n        return self._real_url\n\n    @reify\n    def host(self) -> str:\n        assert self._url.host is not None\n        return self._url.host\n\n    @reify\n    def headers(self) -> \"CIMultiDictProxy[str]\":\n        return self._headers\n\n    @reify\n    def raw_headers(self) -> RawHeaders:\n        return self._raw_headers\n\n    @reify\n    def request_info(self) -> RequestInfo:\n        return self._request_info\n\n    @reify\n    def content_disposition(self) -> Optional[ContentDisposition]:\n        raw = self._headers.get(hdrs.CONTENT_DISPOSITION)\n        if raw is None:\n            return None\n        disposition_type, params_dct = multipart.parse_content_disposition(raw)\n        params = MappingProxyType(params_dct)\n        filename = multipart.content_disposition_filename(params)\n        return ContentDisposition(disposition_type, params, filename)\n\n    def __del__(self, _warnings: Any = warnings) -> None:\n        if self._closed:\n            return\n\n        if self._connection is not None:\n            self._connection.release()\n            self._cleanup_writer()\n\n            if self._loop.get_debug():\n                kwargs = {\"source\": self}\n                _warnings.warn(f\"Unclosed response {self!r}\", ResourceWarning, **kwargs)\n                context = {\"client_response\": self, \"message\": \"Unclosed response\"}\n                if self._source_traceback:\n                    context[\"source_traceback\"] = self._source_traceback\n                self._loop.call_exception_handler(context)\n\n    def __repr__(self) -> str:\n        out = io.StringIO()\n        ascii_encodable_url = str(self.url)\n        if self.reason:\n            ascii_encodable_reason = self.reason.encode(\n                \"ascii\", \"backslashreplace\"\n            ).decode(\"ascii\")\n        else:\n            ascii_encodable_reason = \"None\"\n        print(\n            \"<ClientResponse({}) [{} {}]>\".format(\n                ascii_encodable_url, self.status, ascii_encodable_reason\n            ),\n            file=out,\n        )\n        print(self.headers, file=out)\n        return out.getvalue()\n\n    @property\n    def connection(self) -> Optional[\"Connection\"]:\n        return self._connection\n\n    @reify\n    def history(self) -> Tuple[\"ClientResponse\", ...]:\n        \"\"\"A sequence of of responses, if redirects occurred.\"\"\"\n        return self._history\n\n    @reify\n    def links(self) -> \"MultiDictProxy[MultiDictProxy[Union[str, URL]]]\":\n        links_str = \", \".join(self.headers.getall(\"link\", []))\n\n        if not links_str:\n            return MultiDictProxy(MultiDict())\n\n        links: MultiDict[MultiDictProxy[Union[str, URL]]] = MultiDict()\n\n        for val in re.split(r\",(?=\\s*<)\", links_str):\n            match = re.match(r\"\\s*<(.*)>(.*)\", val)\n            if match is None:  # pragma: no cover\n                # the check exists to suppress mypy error\n                continue\n            url, params_str = match.groups()\n            params = params_str.split(\";\")[1:]\n\n            link: MultiDict[Union[str, URL]] = MultiDict()\n\n            for param in params:\n                match = re.match(r\"^\\s*(\\S*)\\s*=\\s*(['\\\"]?)(.*?)(\\2)\\s*$\", param, re.M)\n                if match is None:  # pragma: no cover\n                    # the check exists to suppress mypy error\n                    continue\n                key, _, value, _ = match.groups()\n\n                link.add(key, value)\n\n            key = link.get(\"rel\", url)\n\n            link.add(\"url\", self.url.join(URL(url)))\n\n            links.add(str(key), MultiDictProxy(link))\n\n        return MultiDictProxy(links)\n\n    async def start(self, connection: \"Connection\") -> \"ClientResponse\":\n        \"\"\"Start response processing.\"\"\"\n        self._closed = False\n        self._protocol = connection.protocol\n        self._connection = connection\n\n        with self._timer:\n            while True:\n                # read response\n                try:\n                    protocol = self._protocol\n                    message, payload = await protocol.read()  # type: ignore[union-attr]\n                except http.HttpProcessingError as exc:\n                    raise ClientResponseError(\n                        self.request_info,\n                        self.history,\n                        status=exc.code,\n                        message=exc.message,\n                        headers=exc.headers,\n                    ) from exc\n\n                if message.code < 100 or message.code > 199 or message.code == 101:\n                    break\n\n                if self._continue is not None:\n                    set_result(self._continue, True)\n                    self._continue = None\n\n        # payload eof handler\n        payload.on_eof(self._response_eof)\n\n        # response status\n        self.version = message.version\n        self.status = message.code\n        self.reason = message.reason\n\n        # headers\n        self._headers = message.headers  # type is CIMultiDictProxy\n        self._raw_headers = message.raw_headers  # type is Tuple[bytes, bytes]\n\n        # payload\n        self.content = payload\n\n        # cookies\n        for hdr in self.headers.getall(hdrs.SET_COOKIE, ()):\n            try:\n                self.cookies.load(hdr)\n            except CookieError as exc:\n                client_logger.warning(\"Can not load response cookies: %s\", exc)\n        return self\n\n    def _response_eof(self) -> None:\n        if self._closed:\n            return\n\n        # protocol could be None because connection could be detached\n        protocol = self._connection and self._connection.protocol\n        if protocol is not None and protocol.upgraded:\n            return\n\n        self._closed = True\n        self._cleanup_writer()\n        self._release_connection()\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    def close(self) -> None:\n        if not self._released:\n            self._notify_content()\n\n        self._closed = True\n        if self._loop is None or self._loop.is_closed():\n            return\n\n        self._cleanup_writer()\n        if self._connection is not None:\n            self._connection.close()\n            self._connection = None\n\n    def release(self) -> Any:\n        if not self._released:\n            self._notify_content()\n\n        self._closed = True\n\n        self._cleanup_writer()\n        self._release_connection()\n        return noop()\n\n    @property\n    def ok(self) -> bool:\n        \"\"\"Returns ``True`` if ``status`` is less than ``400``, ``False`` if not.\n\n        This is **not** a check for ``200 OK`` but a check that the response\n        status is under 400.\n        \"\"\"\n        return 400 > self.status\n\n    def raise_for_status(self) -> None:\n        if not self.ok:\n            # reason should always be not None for a started response\n            assert self.reason is not None\n            self.release()\n            raise ClientResponseError(\n                self.request_info,\n                self.history,\n                status=self.status,\n                message=self.reason,\n                headers=self.headers,\n            )\n\n    def _release_connection(self) -> None:\n        if self._connection is not None:\n            if self._writer is None:\n                self._connection.release()\n                self._connection = None\n            else:\n                self._writer.add_done_callback(lambda f: self._release_connection())\n\n    async def _wait_released(self) -> None:\n        if self._writer is not None:\n            await self._writer\n        self._release_connection()\n\n    def _cleanup_writer(self) -> None:\n        if self._writer is not None:\n            self._writer.cancel()\n        self._session = None\n\n    def _notify_content(self) -> None:\n        content = self.content\n        if content and content.exception() is None:\n            set_exception(content, ClientConnectionError(\"Connection closed\"))\n        self._released = True\n\n    async def wait_for_close(self) -> None:\n        if self._writer is not None:\n            await self._writer\n        self.release()\n\n    async def read(self) -> bytes:\n        \"\"\"Read response payload.\"\"\"\n        if self._body is None:\n            try:\n                self._body = await self.content.read()\n                for trace in self._traces:\n                    await trace.send_response_chunk_received(\n                        self.method, self.url, self._body\n                    )\n            except BaseException:\n                self.close()\n                raise\n        elif self._released:  # Response explicitly released\n            raise ClientConnectionError(\"Connection closed\")\n\n        protocol = self._connection and self._connection.protocol\n        if protocol is None or not protocol.upgraded:\n            await self._wait_released()  # Underlying connection released\n        return self._body  # type: ignore[no-any-return]\n\n    def get_encoding(self) -> str:\n        ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n        mimetype = helpers.parse_mimetype(ctype)\n\n        encoding = mimetype.parameters.get(\"charset\")\n        if encoding:\n            with contextlib.suppress(LookupError):\n                return codecs.lookup(encoding).name\n\n        if mimetype.type == \"application\" and (\n            mimetype.subtype == \"json\" or mimetype.subtype == \"rdap\"\n        ):\n            # RFC 7159 states that the default encoding is UTF-8.\n            # RFC 7483 defines application/rdap+json\n            return \"utf-8\"\n\n        if self._body is None:\n            raise RuntimeError(\n                \"Cannot compute fallback encoding of a not yet read body\"\n            )\n\n        return self._resolve_charset(self, self._body)\n\n    async def text(self, encoding: Optional[str] = None, errors: str = \"strict\") -> str:\n        \"\"\"Read response payload and decode.\"\"\"\n        if self._body is None:\n            await self.read()\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return self._body.decode(  # type: ignore[no-any-return,union-attr]\n            encoding, errors=errors\n        )\n\n    async def json(\n        self,\n        *,\n        encoding: Optional[str] = None,\n        loads: JSONDecoder = DEFAULT_JSON_DECODER,\n        content_type: Optional[str] = \"application/json\",\n    ) -> Any:\n        \"\"\"Read and decodes JSON response.\"\"\"\n        if self._body is None:\n            await self.read()\n\n        if content_type:\n            ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n            if not _is_expected_content_type(ctype, content_type):\n                raise ContentTypeError(\n                    self.request_info,\n                    self.history,\n                    status=self.status,\n                    message=(\n                        \"Attempt to decode JSON with \" \"unexpected mimetype: %s\" % ctype\n                    ),\n                    headers=self.headers,\n                )\n\n        stripped = self._body.strip()  # type: ignore[union-attr]\n        if not stripped:\n            return None\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return loads(stripped.decode(encoding))\n\n    async def __aenter__(self) -> \"ClientResponse\":\n        return self\n\n    async def __aexit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        # similar to _RequestContextManager, we do not need to check\n        # for exceptions, response object can close connection\n        # if state is broken\n        self.release()\n        await self.wait_for_close()",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 798,
                            "end_lineno": 1246,
                            "parent": null
                        },
                        {
                            "file_name": "aiohttp/client_reqrep.py",
                            "type": "method",
                            "name": "ClientResponse.get_encoding",
                            "content": "class ClientResponse(HeadersMixin):\n\n    # Some of these attributes are None when created,\n    # but will be set by the start() method.\n    # As the end user will likely never see the None values, we cheat the types below.\n    # from the Status-Line of the response\n    version: Optional[HttpVersion] = None  # HTTP-Version\n    status: int = None  # type: ignore[assignment] # Status-Code\n    reason: Optional[str] = None  # Reason-Phrase\n\n    content: StreamReader = None  # type: ignore[assignment]  # Payload stream\n    _headers: CIMultiDictProxy[str] = None  # type: ignore[assignment]\n    _raw_headers: RawHeaders = None  # type: ignore[assignment]\n\n    _connection = None  # current connection\n    _source_traceback: Optional[traceback.StackSummary] = None\n    # set up by ClientRequest after ClientResponse object creation\n    # post-init stage allows to not change ctor signature\n    _closed = True  # to allow __del__ for non-initialized properly response\n    _released = False\n    __writer = None\n\n    def __init__(\n        self,\n        method: str,\n        url: URL,\n        *,\n        writer: \"asyncio.Task[None]\",\n        continue100: Optional[\"asyncio.Future[bool]\"],\n        timer: BaseTimerContext,\n        request_info: RequestInfo,\n        traces: List[\"Trace\"],\n        loop: asyncio.AbstractEventLoop,\n        session: \"ClientSession\",\n    ) -> None:\n        assert isinstance(url, URL)\n\n        self.method = method\n        self.cookies = SimpleCookie()\n\n        self._real_url = url\n        self._url = url.with_fragment(None)\n        self._body: Any = None\n        self._writer: Optional[asyncio.Task[None]] = writer\n        self._continue = continue100  # None by default\n        self._closed = True\n        self._history: Tuple[ClientResponse, ...] = ()\n        self._request_info = request_info\n        self._timer = timer if timer is not None else TimerNoop()\n        self._cache: Dict[str, Any] = {}\n        self._traces = traces\n        self._loop = loop\n        # store a reference to session #1985\n        self._session: Optional[ClientSession] = session\n        # Save reference to _resolve_charset, so that get_encoding() will still\n        # work after the response has finished reading the body.\n        if session is None:\n            # TODO: Fix session=None in tests (see ClientRequest.__init__).\n            self._resolve_charset: Callable[[\"ClientResponse\", bytes], str] = (\n                lambda *_: \"utf-8\"\n            )\n        else:\n            self._resolve_charset = session._resolve_charset\n        if loop.get_debug():\n            self._source_traceback = traceback.extract_stack(sys._getframe(1))\n\n    def __reset_writer(self, _: object = None) -> None:\n        self.__writer = None\n\n    @property\n    def _writer(self) -> Optional[\"asyncio.Task[None]\"]:\n        return self.__writer\n\n    @_writer.setter\n    def _writer(self, writer: Optional[\"asyncio.Task[None]\"]) -> None:\n        if self.__writer is not None:\n            self.__writer.remove_done_callback(self.__reset_writer)\n        self.__writer = writer\n        if writer is None:\n            return\n        if writer.done():\n            # The writer is already done, so we can reset it immediately.\n            self.__reset_writer()\n        else:\n            writer.add_done_callback(self.__reset_writer)\n\n    @reify\n    def url(self) -> URL:\n        return self._url\n\n    @reify\n    def url_obj(self) -> URL:\n        warnings.warn(\"Deprecated, use .url #1654\", DeprecationWarning, stacklevel=2)\n        return self._url\n\n    @reify\n    def real_url(self) -> URL:\n        return self._real_url\n\n    @reify\n    def host(self) -> str:\n        assert self._url.host is not None\n        return self._url.host\n\n    @reify\n    def headers(self) -> \"CIMultiDictProxy[str]\":\n        return self._headers\n\n    @reify\n    def raw_headers(self) -> RawHeaders:\n        return self._raw_headers\n\n    @reify\n    def request_info(self) -> RequestInfo:\n        return self._request_info\n\n    @reify\n    def content_disposition(self) -> Optional[ContentDisposition]:\n        raw = self._headers.get(hdrs.CONTENT_DISPOSITION)\n        if raw is None:\n            return None\n        disposition_type, params_dct = multipart.parse_content_disposition(raw)\n        params = MappingProxyType(params_dct)\n        filename = multipart.content_disposition_filename(params)\n        return ContentDisposition(disposition_type, params, filename)\n\n    def __del__(self, _warnings: Any = warnings) -> None:\n        if self._closed:\n            return\n\n        if self._connection is not None:\n            self._connection.release()\n            self._cleanup_writer()\n\n            if self._loop.get_debug():\n                kwargs = {\"source\": self}\n                _warnings.warn(f\"Unclosed response {self!r}\", ResourceWarning, **kwargs)\n                context = {\"client_response\": self, \"message\": \"Unclosed response\"}\n                if self._source_traceback:\n                    context[\"source_traceback\"] = self._source_traceback\n                self._loop.call_exception_handler(context)\n\n    def __repr__(self) -> str:\n        out = io.StringIO()\n        ascii_encodable_url = str(self.url)\n        if self.reason:\n            ascii_encodable_reason = self.reason.encode(\n                \"ascii\", \"backslashreplace\"\n            ).decode(\"ascii\")\n        else:\n            ascii_encodable_reason = \"None\"\n        print(\n            \"<ClientResponse({}) [{} {}]>\".format(\n                ascii_encodable_url, self.status, ascii_encodable_reason\n            ),\n            file=out,\n        )\n        print(self.headers, file=out)\n        return out.getvalue()\n\n    @property\n    def connection(self) -> Optional[\"Connection\"]:\n        return self._connection\n\n    @reify\n    def history(self) -> Tuple[\"ClientResponse\", ...]:\n        \"\"\"A sequence of of responses, if redirects occurred.\"\"\"\n        return self._history\n\n    @reify\n    def links(self) -> \"MultiDictProxy[MultiDictProxy[Union[str, URL]]]\":\n        links_str = \", \".join(self.headers.getall(\"link\", []))\n\n        if not links_str:\n            return MultiDictProxy(MultiDict())\n\n        links: MultiDict[MultiDictProxy[Union[str, URL]]] = MultiDict()\n\n        for val in re.split(r\",(?=\\s*<)\", links_str):\n            match = re.match(r\"\\s*<(.*)>(.*)\", val)\n            if match is None:  # pragma: no cover\n                # the check exists to suppress mypy error\n                continue\n            url, params_str = match.groups()\n            params = params_str.split(\";\")[1:]\n\n            link: MultiDict[Union[str, URL]] = MultiDict()\n\n            for param in params:\n                match = re.match(r\"^\\s*(\\S*)\\s*=\\s*(['\\\"]?)(.*?)(\\2)\\s*$\", param, re.M)\n                if match is None:  # pragma: no cover\n                    # the check exists to suppress mypy error\n                    continue\n                key, _, value, _ = match.groups()\n\n                link.add(key, value)\n\n            key = link.get(\"rel\", url)\n\n            link.add(\"url\", self.url.join(URL(url)))\n\n            links.add(str(key), MultiDictProxy(link))\n\n        return MultiDictProxy(links)\n\n    async def start(self, connection: \"Connection\") -> \"ClientResponse\":\n        \"\"\"Start response processing.\"\"\"\n        self._closed = False\n        self._protocol = connection.protocol\n        self._connection = connection\n\n        with self._timer:\n            while True:\n                # read response\n                try:\n                    protocol = self._protocol\n                    message, payload = await protocol.read()  # type: ignore[union-attr]\n                except http.HttpProcessingError as exc:\n                    raise ClientResponseError(\n                        self.request_info,\n                        self.history,\n                        status=exc.code,\n                        message=exc.message,\n                        headers=exc.headers,\n                    ) from exc\n\n                if message.code < 100 or message.code > 199 or message.code == 101:\n                    break\n\n                if self._continue is not None:\n                    set_result(self._continue, True)\n                    self._continue = None\n\n        # payload eof handler\n        payload.on_eof(self._response_eof)\n\n        # response status\n        self.version = message.version\n        self.status = message.code\n        self.reason = message.reason\n\n        # headers\n        self._headers = message.headers  # type is CIMultiDictProxy\n        self._raw_headers = message.raw_headers  # type is Tuple[bytes, bytes]\n\n        # payload\n        self.content = payload\n\n        # cookies\n        for hdr in self.headers.getall(hdrs.SET_COOKIE, ()):\n            try:\n                self.cookies.load(hdr)\n            except CookieError as exc:\n                client_logger.warning(\"Can not load response cookies: %s\", exc)\n        return self\n\n    def _response_eof(self) -> None:\n        if self._closed:\n            return\n\n        # protocol could be None because connection could be detached\n        protocol = self._connection and self._connection.protocol\n        if protocol is not None and protocol.upgraded:\n            return\n\n        self._closed = True\n        self._cleanup_writer()\n        self._release_connection()\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    def close(self) -> None:\n        if not self._released:\n            self._notify_content()\n\n        self._closed = True\n        if self._loop is None or self._loop.is_closed():\n            return\n\n        self._cleanup_writer()\n        if self._connection is not None:\n            self._connection.close()\n            self._connection = None\n\n    def release(self) -> Any:\n        if not self._released:\n            self._notify_content()\n\n        self._closed = True\n\n        self._cleanup_writer()\n        self._release_connection()\n        return noop()\n\n    @property\n    def ok(self) -> bool:\n        \"\"\"Returns ``True`` if ``status`` is less than ``400``, ``False`` if not.\n\n        This is **not** a check for ``200 OK`` but a check that the response\n        status is under 400.\n        \"\"\"\n        return 400 > self.status\n\n    def raise_for_status(self) -> None:\n        if not self.ok:\n            # reason should always be not None for a started response\n            assert self.reason is not None\n            self.release()\n            raise ClientResponseError(\n                self.request_info,\n                self.history,\n                status=self.status,\n                message=self.reason,\n                headers=self.headers,\n            )\n\n    def _release_connection(self) -> None:\n        if self._connection is not None:\n            if self._writer is None:\n                self._connection.release()\n                self._connection = None\n            else:\n                self._writer.add_done_callback(lambda f: self._release_connection())\n\n    async def _wait_released(self) -> None:\n        if self._writer is not None:\n            await self._writer\n        self._release_connection()\n\n    def _cleanup_writer(self) -> None:\n        if self._writer is not None:\n            self._writer.cancel()\n        self._session = None\n\n    def _notify_content(self) -> None:\n        content = self.content\n        if content and content.exception() is None:\n            set_exception(content, ClientConnectionError(\"Connection closed\"))\n        self._released = True\n\n    async def wait_for_close(self) -> None:\n        if self._writer is not None:\n            await self._writer\n        self.release()\n\n    async def read(self) -> bytes:\n        \"\"\"Read response payload.\"\"\"\n        if self._body is None:\n            try:\n                self._body = await self.content.read()\n                for trace in self._traces:\n                    await trace.send_response_chunk_received(\n                        self.method, self.url, self._body\n                    )\n            except BaseException:\n                self.close()\n                raise\n        elif self._released:  # Response explicitly released\n            raise ClientConnectionError(\"Connection closed\")\n\n        protocol = self._connection and self._connection.protocol\n        if protocol is None or not protocol.upgraded:\n            await self._wait_released()  # Underlying connection released\n        return self._body  # type: ignore[no-any-return]\n\n    def get_encoding(self) -> str:\n        ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n        mimetype = helpers.parse_mimetype(ctype)\n\n        encoding = mimetype.parameters.get(\"charset\")\n        if encoding:\n            with contextlib.suppress(LookupError):\n                return codecs.lookup(encoding).name\n\n        if mimetype.type == \"application\" and (\n            mimetype.subtype == \"json\" or mimetype.subtype == \"rdap\"\n        ):\n            # RFC 7159 states that the default encoding is UTF-8.\n            # RFC 7483 defines application/rdap+json\n            return \"utf-8\"\n\n        if self._body is None:\n            raise RuntimeError(\n                \"Cannot compute fallback encoding of a not yet read body\"\n            )\n\n        return self._resolve_charset(self, self._body)\n\n    async def text(self, encoding: Optional[str] = None, errors: str = \"strict\") -> str:\n        \"\"\"Read response payload and decode.\"\"\"\n        if self._body is None:\n            await self.read()\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return self._body.decode(  # type: ignore[no-any-return,union-attr]\n            encoding, errors=errors\n        )\n\n    async def json(\n        self,\n        *,\n        encoding: Optional[str] = None,\n        loads: JSONDecoder = DEFAULT_JSON_DECODER,\n        content_type: Optional[str] = \"application/json\",\n    ) -> Any:\n        \"\"\"Read and decodes JSON response.\"\"\"\n        if self._body is None:\n            await self.read()\n\n        if content_type:\n            ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n            if not _is_expected_content_type(ctype, content_type):\n                raise ContentTypeError(\n                    self.request_info,\n                    self.history,\n                    status=self.status,\n                    message=(\n                        \"Attempt to decode JSON with \" \"unexpected mimetype: %s\" % ctype\n                    ),\n                    headers=self.headers,\n                )\n\n        stripped = self._body.strip()  # type: ignore[union-attr]\n        if not stripped:\n            return None\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return loads(stripped.decode(encoding))\n\n    async def __aenter__(self) -> \"ClientResponse\":\n        return self\n\n    async def __aexit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        # similar to _RequestContextManager, we do not need to check\n        # for exceptions, response object can close connection\n        # if state is broken\n        self.release()\n        await self.wait_for_close()",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 1165,
                            "end_lineno": 1186,
                            "parent": {
                                "file_name": "aiohttp/client_reqrep.py",
                                "type": "class",
                                "name": "ClientResponse",
                                "content": "class ClientResponse(HeadersMixin):\n\n    # Some of these attributes are None when created,\n    # but will be set by the start() method.\n    # As the end user will likely never see the None values, we cheat the types below.\n    # from the Status-Line of the response\n    version: Optional[HttpVersion] = None  # HTTP-Version\n    status: int = None  # type: ignore[assignment] # Status-Code\n    reason: Optional[str] = None  # Reason-Phrase\n\n    content: StreamReader = None  # type: ignore[assignment]  # Payload stream\n    _headers: CIMultiDictProxy[str] = None  # type: ignore[assignment]\n    _raw_headers: RawHeaders = None  # type: ignore[assignment]\n\n    _connection = None  # current connection\n    _source_traceback: Optional[traceback.StackSummary] = None\n    # set up by ClientRequest after ClientResponse object creation\n    # post-init stage allows to not change ctor signature\n    _closed = True  # to allow __del__ for non-initialized properly response\n    _released = False\n    __writer = None\n\n    def __init__(\n        self,\n        method: str,\n        url: URL,\n        *,\n        writer: \"asyncio.Task[None]\",\n        continue100: Optional[\"asyncio.Future[bool]\"],\n        timer: BaseTimerContext,\n        request_info: RequestInfo,\n        traces: List[\"Trace\"],\n        loop: asyncio.AbstractEventLoop,\n        session: \"ClientSession\",\n    ) -> None:\n        assert isinstance(url, URL)\n\n        self.method = method\n        self.cookies = SimpleCookie()\n\n        self._real_url = url\n        self._url = url.with_fragment(None)\n        self._body: Any = None\n        self._writer: Optional[asyncio.Task[None]] = writer\n        self._continue = continue100  # None by default\n        self._closed = True\n        self._history: Tuple[ClientResponse, ...] = ()\n        self._request_info = request_info\n        self._timer = timer if timer is not None else TimerNoop()\n        self._cache: Dict[str, Any] = {}\n        self._traces = traces\n        self._loop = loop\n        # store a reference to session #1985\n        self._session: Optional[ClientSession] = session\n        # Save reference to _resolve_charset, so that get_encoding() will still\n        # work after the response has finished reading the body.\n        if session is None:\n            # TODO: Fix session=None in tests (see ClientRequest.__init__).\n            self._resolve_charset: Callable[[\"ClientResponse\", bytes], str] = (\n                lambda *_: \"utf-8\"\n            )\n        else:\n            self._resolve_charset = session._resolve_charset\n        if loop.get_debug():\n            self._source_traceback = traceback.extract_stack(sys._getframe(1))\n\n    def __reset_writer(self, _: object = None) -> None:\n        self.__writer = None\n\n    @property\n    def _writer(self) -> Optional[\"asyncio.Task[None]\"]:\n        return self.__writer\n\n    @_writer.setter\n    def _writer(self, writer: Optional[\"asyncio.Task[None]\"]) -> None:\n        if self.__writer is not None:\n            self.__writer.remove_done_callback(self.__reset_writer)\n        self.__writer = writer\n        if writer is None:\n            return\n        if writer.done():\n            # The writer is already done, so we can reset it immediately.\n            self.__reset_writer()\n        else:\n            writer.add_done_callback(self.__reset_writer)\n\n    @reify\n    def url(self) -> URL:\n        return self._url\n\n    @reify\n    def url_obj(self) -> URL:\n        warnings.warn(\"Deprecated, use .url #1654\", DeprecationWarning, stacklevel=2)\n        return self._url\n\n    @reify\n    def real_url(self) -> URL:\n        return self._real_url\n\n    @reify\n    def host(self) -> str:\n        assert self._url.host is not None\n        return self._url.host\n\n    @reify\n    def headers(self) -> \"CIMultiDictProxy[str]\":\n        return self._headers\n\n    @reify\n    def raw_headers(self) -> RawHeaders:\n        return self._raw_headers\n\n    @reify\n    def request_info(self) -> RequestInfo:\n        return self._request_info\n\n    @reify\n    def content_disposition(self) -> Optional[ContentDisposition]:\n        raw = self._headers.get(hdrs.CONTENT_DISPOSITION)\n        if raw is None:\n            return None\n        disposition_type, params_dct = multipart.parse_content_disposition(raw)\n        params = MappingProxyType(params_dct)\n        filename = multipart.content_disposition_filename(params)\n        return ContentDisposition(disposition_type, params, filename)\n\n    def __del__(self, _warnings: Any = warnings) -> None:\n        if self._closed:\n            return\n\n        if self._connection is not None:\n            self._connection.release()\n            self._cleanup_writer()\n\n            if self._loop.get_debug():\n                kwargs = {\"source\": self}\n                _warnings.warn(f\"Unclosed response {self!r}\", ResourceWarning, **kwargs)\n                context = {\"client_response\": self, \"message\": \"Unclosed response\"}\n                if self._source_traceback:\n                    context[\"source_traceback\"] = self._source_traceback\n                self._loop.call_exception_handler(context)\n\n    def __repr__(self) -> str:\n        out = io.StringIO()\n        ascii_encodable_url = str(self.url)\n        if self.reason:\n            ascii_encodable_reason = self.reason.encode(\n                \"ascii\", \"backslashreplace\"\n            ).decode(\"ascii\")\n        else:\n            ascii_encodable_reason = \"None\"\n        print(\n            \"<ClientResponse({}) [{} {}]>\".format(\n                ascii_encodable_url, self.status, ascii_encodable_reason\n            ),\n            file=out,\n        )\n        print(self.headers, file=out)\n        return out.getvalue()\n\n    @property\n    def connection(self) -> Optional[\"Connection\"]:\n        return self._connection\n\n    @reify\n    def history(self) -> Tuple[\"ClientResponse\", ...]:\n        \"\"\"A sequence of of responses, if redirects occurred.\"\"\"\n        return self._history\n\n    @reify\n    def links(self) -> \"MultiDictProxy[MultiDictProxy[Union[str, URL]]]\":\n        links_str = \", \".join(self.headers.getall(\"link\", []))\n\n        if not links_str:\n            return MultiDictProxy(MultiDict())\n\n        links: MultiDict[MultiDictProxy[Union[str, URL]]] = MultiDict()\n\n        for val in re.split(r\",(?=\\s*<)\", links_str):\n            match = re.match(r\"\\s*<(.*)>(.*)\", val)\n            if match is None:  # pragma: no cover\n                # the check exists to suppress mypy error\n                continue\n            url, params_str = match.groups()\n            params = params_str.split(\";\")[1:]\n\n            link: MultiDict[Union[str, URL]] = MultiDict()\n\n            for param in params:\n                match = re.match(r\"^\\s*(\\S*)\\s*=\\s*(['\\\"]?)(.*?)(\\2)\\s*$\", param, re.M)\n                if match is None:  # pragma: no cover\n                    # the check exists to suppress mypy error\n                    continue\n                key, _, value, _ = match.groups()\n\n                link.add(key, value)\n\n            key = link.get(\"rel\", url)\n\n            link.add(\"url\", self.url.join(URL(url)))\n\n            links.add(str(key), MultiDictProxy(link))\n\n        return MultiDictProxy(links)\n\n    async def start(self, connection: \"Connection\") -> \"ClientResponse\":\n        \"\"\"Start response processing.\"\"\"\n        self._closed = False\n        self._protocol = connection.protocol\n        self._connection = connection\n\n        with self._timer:\n            while True:\n                # read response\n                try:\n                    protocol = self._protocol\n                    message, payload = await protocol.read()  # type: ignore[union-attr]\n                except http.HttpProcessingError as exc:\n                    raise ClientResponseError(\n                        self.request_info,\n                        self.history,\n                        status=exc.code,\n                        message=exc.message,\n                        headers=exc.headers,\n                    ) from exc\n\n                if message.code < 100 or message.code > 199 or message.code == 101:\n                    break\n\n                if self._continue is not None:\n                    set_result(self._continue, True)\n                    self._continue = None\n\n        # payload eof handler\n        payload.on_eof(self._response_eof)\n\n        # response status\n        self.version = message.version\n        self.status = message.code\n        self.reason = message.reason\n\n        # headers\n        self._headers = message.headers  # type is CIMultiDictProxy\n        self._raw_headers = message.raw_headers  # type is Tuple[bytes, bytes]\n\n        # payload\n        self.content = payload\n\n        # cookies\n        for hdr in self.headers.getall(hdrs.SET_COOKIE, ()):\n            try:\n                self.cookies.load(hdr)\n            except CookieError as exc:\n                client_logger.warning(\"Can not load response cookies: %s\", exc)\n        return self\n\n    def _response_eof(self) -> None:\n        if self._closed:\n            return\n\n        # protocol could be None because connection could be detached\n        protocol = self._connection and self._connection.protocol\n        if protocol is not None and protocol.upgraded:\n            return\n\n        self._closed = True\n        self._cleanup_writer()\n        self._release_connection()\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    def close(self) -> None:\n        if not self._released:\n            self._notify_content()\n\n        self._closed = True\n        if self._loop is None or self._loop.is_closed():\n            return\n\n        self._cleanup_writer()\n        if self._connection is not None:\n            self._connection.close()\n            self._connection = None\n\n    def release(self) -> Any:\n        if not self._released:\n            self._notify_content()\n\n        self._closed = True\n\n        self._cleanup_writer()\n        self._release_connection()\n        return noop()\n\n    @property\n    def ok(self) -> bool:\n        \"\"\"Returns ``True`` if ``status`` is less than ``400``, ``False`` if not.\n\n        This is **not** a check for ``200 OK`` but a check that the response\n        status is under 400.\n        \"\"\"\n        return 400 > self.status\n\n    def raise_for_status(self) -> None:\n        if not self.ok:\n            # reason should always be not None for a started response\n            assert self.reason is not None\n            self.release()\n            raise ClientResponseError(\n                self.request_info,\n                self.history,\n                status=self.status,\n                message=self.reason,\n                headers=self.headers,\n            )\n\n    def _release_connection(self) -> None:\n        if self._connection is not None:\n            if self._writer is None:\n                self._connection.release()\n                self._connection = None\n            else:\n                self._writer.add_done_callback(lambda f: self._release_connection())\n\n    async def _wait_released(self) -> None:\n        if self._writer is not None:\n            await self._writer\n        self._release_connection()\n\n    def _cleanup_writer(self) -> None:\n        if self._writer is not None:\n            self._writer.cancel()\n        self._session = None\n\n    def _notify_content(self) -> None:\n        content = self.content\n        if content and content.exception() is None:\n            set_exception(content, ClientConnectionError(\"Connection closed\"))\n        self._released = True\n\n    async def wait_for_close(self) -> None:\n        if self._writer is not None:\n            await self._writer\n        self.release()\n\n    async def read(self) -> bytes:\n        \"\"\"Read response payload.\"\"\"\n        if self._body is None:\n            try:\n                self._body = await self.content.read()\n                for trace in self._traces:\n                    await trace.send_response_chunk_received(\n                        self.method, self.url, self._body\n                    )\n            except BaseException:\n                self.close()\n                raise\n        elif self._released:  # Response explicitly released\n            raise ClientConnectionError(\"Connection closed\")\n\n        protocol = self._connection and self._connection.protocol\n        if protocol is None or not protocol.upgraded:\n            await self._wait_released()  # Underlying connection released\n        return self._body  # type: ignore[no-any-return]\n\n    def get_encoding(self) -> str:\n        ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n        mimetype = helpers.parse_mimetype(ctype)\n\n        encoding = mimetype.parameters.get(\"charset\")\n        if encoding:\n            with contextlib.suppress(LookupError):\n                return codecs.lookup(encoding).name\n\n        if mimetype.type == \"application\" and (\n            mimetype.subtype == \"json\" or mimetype.subtype == \"rdap\"\n        ):\n            # RFC 7159 states that the default encoding is UTF-8.\n            # RFC 7483 defines application/rdap+json\n            return \"utf-8\"\n\n        if self._body is None:\n            raise RuntimeError(\n                \"Cannot compute fallback encoding of a not yet read body\"\n            )\n\n        return self._resolve_charset(self, self._body)\n\n    async def text(self, encoding: Optional[str] = None, errors: str = \"strict\") -> str:\n        \"\"\"Read response payload and decode.\"\"\"\n        if self._body is None:\n            await self.read()\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return self._body.decode(  # type: ignore[no-any-return,union-attr]\n            encoding, errors=errors\n        )\n\n    async def json(\n        self,\n        *,\n        encoding: Optional[str] = None,\n        loads: JSONDecoder = DEFAULT_JSON_DECODER,\n        content_type: Optional[str] = \"application/json\",\n    ) -> Any:\n        \"\"\"Read and decodes JSON response.\"\"\"\n        if self._body is None:\n            await self.read()\n\n        if content_type:\n            ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n            if not _is_expected_content_type(ctype, content_type):\n                raise ContentTypeError(\n                    self.request_info,\n                    self.history,\n                    status=self.status,\n                    message=(\n                        \"Attempt to decode JSON with \" \"unexpected mimetype: %s\" % ctype\n                    ),\n                    headers=self.headers,\n                )\n\n        stripped = self._body.strip()  # type: ignore[union-attr]\n        if not stripped:\n            return None\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return loads(stripped.decode(encoding))\n\n    async def __aenter__(self) -> \"ClientResponse\":\n        return self\n\n    async def __aexit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        # similar to _RequestContextManager, we do not need to check\n        # for exceptions, response object can close connection\n        # if state is broken\n        self.release()\n        await self.wait_for_close()",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 798,
                                "end_lineno": 1246,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "# Tests for aiohttp/client.py\n\nimport gc\nimport sys\nfrom typing import Callable\nfrom unittest import mock\n\nimport pytest\nfrom multidict import CIMultiDict\nfrom yarl import URL\n\nimport aiohttp\nfrom aiohttp import http\nfrom aiohttp.client_reqrep import ClientResponse, RequestInfo\nfrom aiohttp.helpers import TimerNoop\nfrom aiohttp.test_utils import make_mocked_coro\n\n\nclass WriterMock(mock.AsyncMock):\n    def __await__(self) -> None:\n        return self().__await__()\n\n    def add_done_callback(self, cb: Callable[[], None]) -> None:\n        cb()\n\n    def done(self) -> bool:\n        return True\n\n\n@pytest.fixture\ndef session():\n    return mock.Mock()\n\n\nasync def test_http_processing_error(session) -> None:\n    loop = mock.Mock()\n    request_info = mock.Mock()\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://del-cl-resp.org\"),\n        request_info=request_info,\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    loop.get_debug = mock.Mock()\n    loop.get_debug.return_value = True\n\n    connection = mock.Mock()\n    connection.protocol = aiohttp.DataQueue(loop)\n    connection.protocol.set_response_params = mock.Mock()\n    connection.protocol.set_exception(http.HttpProcessingError())\n\n    with pytest.raises(aiohttp.ClientResponseError) as info:\n        await response.start(connection)\n\n    assert info.value.request_info is request_info\n    response.close()\n\n\ndef test_del(session) -> None:\n    loop = mock.Mock()\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://del-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    loop.get_debug = mock.Mock()\n    loop.get_debug.return_value = True\n\n    connection = mock.Mock()\n    response._closed = False\n    response._connection = connection\n    loop.set_exception_handler(lambda loop, ctx: None)\n\n    with pytest.warns(ResourceWarning):\n        del response\n        gc.collect()\n\n    connection.release.assert_called_with()\n\n\ndef test_close(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._closed = False\n    response._connection = mock.Mock()\n    response.close()\n    assert response.connection is None\n    response.close()\n    response.close()\n\n\ndef test_wait_for_100_1(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://python.org\"),\n        continue100=object(),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    assert response._continue is not None\n    response.close()\n\n\ndef test_wait_for_100_2(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://python.org\"),\n        request_info=mock.Mock(),\n        continue100=None,\n        writer=WriterMock(),\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    assert response._continue is None\n    response.close()\n\n\ndef test_repr(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response.status = 200\n    response.reason = \"Ok\"\n    assert \"<ClientResponse(http://def-cl-resp.org) [200 Ok]>\" in repr(response)\n\n\ndef test_repr_non_ascii_url() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://fake-host.org/\\u03bb\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    assert \"<ClientResponse(http://fake-host.org/%CE%BB) [None None]>\" in repr(response)\n\n\ndef test_repr_non_ascii_reason() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://fake-host.org/path\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.reason = \"\\u03bb\"\n    assert \"<ClientResponse(http://fake-host.org/path) [None \\\\u03bb]>\" in repr(\n        response\n    )\n\n\ndef test_url_obj_deprecated() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://fake-host.org/\"),\n        request_info=mock.Mock(),\n        writer=mock.Mock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    with pytest.warns(DeprecationWarning):\n        response.url_obj\n\n\nasync def test_read_and_release_connection(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result(b\"payload\")\n        return fut\n\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.read()\n    assert res == b\"payload\"\n    assert response._connection is None\n\n\nasync def test_read_and_release_connection_with_error(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    content = response.content = mock.Mock()\n    content.read.return_value = loop.create_future()\n    content.read.return_value.set_exception(ValueError)\n\n    with pytest.raises(ValueError):\n        await response.read()\n    assert response._closed\n\n\nasync def test_release(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    fut = loop.create_future()\n    fut.set_result(b\"\")\n    content = response.content = mock.Mock()\n    content.readany.return_value = fut\n\n    response.release()\n    assert response._connection is None\n\n\n@pytest.mark.skipif(\n    sys.implementation.name != \"cpython\",\n    reason=\"Other implementations has different GC strategies\",\n)\nasync def test_release_on_del(loop, session) -> None:\n    connection = mock.Mock()\n    connection.protocol.upgraded = False\n\n    def run(conn):\n        response = ClientResponse(\n            \"get\",\n            URL(\"http://def-cl-resp.org\"),\n            request_info=mock.Mock(),\n            writer=WriterMock(),\n            continue100=None,\n            timer=TimerNoop(),\n            traces=[],\n            loop=loop,\n            session=session,\n        )\n        response._closed = False\n        response._connection = conn\n\n    run(connection)\n\n    assert connection.release.called\n\n\nasync def test_response_eof(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=None,\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._closed = False\n    conn = response._connection = mock.Mock()\n    conn.protocol.upgraded = False\n\n    response._response_eof()\n    assert conn.release.called\n    assert response._connection is None\n\n\nasync def test_response_eof_upgraded(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    conn = response._connection = mock.Mock()\n    conn.protocol.upgraded = True\n\n    response._response_eof()\n    assert not conn.release.called\n    assert response._connection is conn\n\n\nasync def test_response_eof_after_connection_detach(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=None,\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._closed = False\n    conn = response._connection = mock.Mock()\n    conn.protocol = None\n\n    response._response_eof()\n    assert conn.release.called\n    assert response._connection is None\n\n\nasync def test_text(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.text()\n    assert res == '{\"тест\": \"пройден\"}'\n    assert response._connection is None\n\n\nasync def test_text_bad_encoding(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тестkey\": \"пройденvalue\"}'.encode(\"cp1251\"))\n        return fut\n\n    # lie about the encoding\n    response._headers = {\"Content-Type\": \"application/json;charset=utf-8\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n    with pytest.raises(UnicodeDecodeError):\n        await response.text()\n    # only the valid utf-8 characters will be returned\n    res = await response.text(errors=\"ignore\")\n    assert res == '{\"key\": \"value\"}'\n    assert response._connection is None\n\n\nasync def test_text_custom_encoding(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n    response.get_encoding = mock.Mock()\n\n    res = await response.text(encoding=\"cp1251\")\n    assert res == '{\"тест\": \"пройден\"}'\n    assert response._connection is None\n    assert not response.get_encoding.called\n\n\n@pytest.mark.parametrize(\"content_type\", (\"text/plain\", \"text/plain;charset=invalid\"))\nasync def test_text_charset_resolver(content_type: str, loop, session) -> None:\n    session._resolve_charset = lambda r, b: \"cp1251\"\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": content_type}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    await response.read()\n    res = await response.text()\n    assert res == '{\"тест\": \"пройден\"}'\n    assert response._connection is None\n    assert response.get_encoding() == \"cp1251\"\n\n\nasync def test_get_encoding_body_none(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"encoding\": \"test\"}')\n        return fut\n\n    response._headers = {\"Content-Type\": \"text/html\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    with pytest.raises(\n        RuntimeError,\n        match=\"^Cannot compute fallback encoding of a not yet read body$\",\n    ):\n        response.get_encoding()\n    assert response.closed\n\n\nasync def test_text_after_read(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.text()\n    assert res == '{\"тест\": \"пройден\"}'\n    assert response._connection is None\n\n\nasync def test_json(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.json()\n    assert res == {\"тест\": \"пройден\"}\n    assert response._connection is None\n\n\nasync def test_json_extended_content_type(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\n        \"Content-Type\": \"application/this.is-1_content+subtype+json;charset=cp1251\"\n    }\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.json()\n    assert res == {\"тест\": \"пройден\"}\n    assert response._connection is None\n\n\nasync def test_json_custom_content_type(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"custom/type;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.json(content_type=\"custom/type\")\n    assert res == {\"тест\": \"пройден\"}\n    assert response._connection is None\n\n\nasync def test_json_custom_loader(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response._body = b\"data\"\n\n    def custom(content):\n        return content + \"-custom\"\n\n    res = await response.json(loads=custom)\n    assert res == \"data-custom\"\n\n\nasync def test_json_invalid_content_type(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = {\"Content-Type\": \"data/octet-stream\"}\n    response._body = b\"\"\n    response.status = 500\n\n    with pytest.raises(aiohttp.ContentTypeError) as info:\n        await response.json()\n\n    assert info.value.request_info == response.request_info\n    assert info.value.status == 500\n\n\nasync def test_json_no_content(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = {\"Content-Type\": \"data/octet-stream\"}\n    response._body = b\"\"\n\n    res = await response.json(content_type=None)\n    assert res is None\n\n\nasync def test_json_override_encoding(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=utf8\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n    response.get_encoding = mock.Mock()\n\n    res = await response.json(encoding=\"cp1251\")\n    assert res == {\"тест\": \"пройден\"}\n    assert response._connection is None\n    assert not response.get_encoding.called\n\n\ndef test_get_encoding_unknown(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    response._headers = {\"Content-Type\": \"application/json\"}\n    assert response.get_encoding() == \"utf-8\"\n\n\ndef test_raise_for_status_2xx() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 200\n    response.reason = \"OK\"\n    response.raise_for_status()  # should not raise\n\n\ndef test_raise_for_status_4xx() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 409\n    response.reason = \"CONFLICT\"\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert str(cm.value.status) == \"409\"\n    assert str(cm.value.message) == \"CONFLICT\"\n    assert response.closed\n\n\ndef test_raise_for_status_4xx_without_reason() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 404\n    response.reason = \"\"\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert str(cm.value.status) == \"404\"\n    assert str(cm.value.message) == \"\"\n    assert response.closed\n\n\ndef test_resp_host() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://del-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    assert \"del-cl-resp.org\" == response.host\n\n\ndef test_content_type() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n\n    assert \"application/json\" == response.content_type\n\n\ndef test_content_type_no_header() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {}\n\n    assert \"application/octet-stream\" == response.content_type\n\n\ndef test_charset() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n\n    assert \"cp1251\" == response.charset\n\n\ndef test_charset_no_header() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {}\n\n    assert response.charset is None\n\n\ndef test_charset_no_charset() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\"Content-Type\": \"application/json\"}\n\n    assert response.charset is None\n\n\ndef test_content_disposition_full() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\n        \"Content-Disposition\": 'attachment; filename=\"archive.tar.gz\"; foo=bar'\n    }\n\n    assert \"attachment\" == response.content_disposition.type\n    assert \"bar\" == response.content_disposition.parameters[\"foo\"]\n    assert \"archive.tar.gz\" == response.content_disposition.filename\n    with pytest.raises(TypeError):\n        response.content_disposition.parameters[\"foo\"] = \"baz\"\n\n\ndef test_content_disposition_no_parameters() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\"Content-Disposition\": \"attachment\"}\n\n    assert \"attachment\" == response.content_disposition.type\n    assert response.content_disposition.filename is None\n    assert {} == response.content_disposition.parameters\n\n\ndef test_content_disposition_no_header() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {}\n\n    assert response.content_disposition is None\n\n\ndef test_response_request_info() -> None:\n    url = \"http://def-cl-resp.org\"\n    headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response = ClientResponse(\n        \"get\",\n        URL(url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    assert url == response.request_info.url\n    assert \"get\" == response.request_info.method\n    assert headers == response.request_info.headers\n\n\ndef test_request_info_in_exception() -> None:\n    url = \"http://def-cl-resp.org\"\n    headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response = ClientResponse(\n        \"get\",\n        URL(url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 409\n    response.reason = \"CONFLICT\"\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert cm.value.request_info == response.request_info\n\n\ndef test_no_redirect_history_in_exception() -> None:\n    url = \"http://def-cl-resp.org\"\n    headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response = ClientResponse(\n        \"get\",\n        URL(url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 409\n    response.reason = \"CONFLICT\"\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert () == cm.value.history\n\n\ndef test_redirect_history_in_exception() -> None:\n    hist_url = \"http://def-cl-resp.org\"\n    url = \"http://def-cl-resp.org/index.htm\"\n    hist_headers = {\"Content-Type\": \"application/json;charset=cp1251\", \"Location\": url}\n    headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response = ClientResponse(\n        \"get\",\n        URL(url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 409\n    response.reason = \"CONFLICT\"\n\n    hist_response = ClientResponse(\n        \"get\",\n        URL(hist_url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n\n    hist_response._headers = hist_headers\n    hist_response.status = 301\n    hist_response.reason = \"REDIRECT\"\n\n    response._history = [hist_response]\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert [hist_response] == cm.value.history\n\n\nasync def test_response_read_triggers_callback(loop, session) -> None:\n    trace = mock.Mock()\n    trace.send_response_chunk_received = make_mocked_coro()\n    response_method = \"get\"\n    response_url = URL(\"http://def-cl-resp.org\")\n    response_body = b\"This is response\"\n\n    response = ClientResponse(\n        response_method,\n        response_url,\n        request_info=mock.Mock,\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        loop=loop,\n        session=session,\n        traces=[trace],\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result(response_body)\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.read()\n    assert res == response_body\n    assert response._connection is None\n\n    assert trace.send_response_chunk_received.called\n    assert trace.send_response_chunk_received.call_args == mock.call(\n        response_method, response_url, response_body\n    )\n\n\ndef test_response_real_url(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/#urlfragment\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    assert response.url == url.with_fragment(None)\n    assert response.real_url == url\n\n\ndef test_response_links_comma_separated(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict(\n        [\n            (\n                \"Link\",\n                (\n                    \"<http://example.com/page/1.html>; rel=next, \"\n                    \"<http://example.com/>; rel=home\"\n                ),\n            )\n        ]\n    )\n    assert response.links == {\n        \"next\": {\"url\": URL(\"http://example.com/page/1.html\"), \"rel\": \"next\"},\n        \"home\": {\"url\": URL(\"http://example.com/\"), \"rel\": \"home\"},\n    }\n\n\ndef test_response_links_multiple_headers(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict(\n        [\n            (\"Link\", \"<http://example.com/page/1.html>; rel=next\"),\n            (\"Link\", \"<http://example.com/>; rel=home\"),\n        ]\n    )\n    assert response.links == {\n        \"next\": {\"url\": URL(\"http://example.com/page/1.html\"), \"rel\": \"next\"},\n        \"home\": {\"url\": URL(\"http://example.com/\"), \"rel\": \"home\"},\n    }\n\n\ndef test_response_links_no_rel(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict([(\"Link\", \"<http://example.com/>\")])\n    assert response.links == {\n        \"http://example.com/\": {\"url\": URL(\"http://example.com/\")}\n    }\n\n\ndef test_response_links_quoted(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict(\n        [\n            (\"Link\", '<http://example.com/>; rel=\"home-page\"'),\n        ]\n    )\n    assert response.links == {\n        \"home-page\": {\"url\": URL(\"http://example.com/\"), \"rel\": \"home-page\"}\n    }\n\n\ndef test_response_links_relative(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict(\n        [\n            (\"Link\", \"</relative/path>; rel=rel\"),\n        ]\n    )\n    assert response.links == {\n        \"rel\": {\"url\": URL(\"http://def-cl-resp.org/relative/path\"), \"rel\": \"rel\"}\n    }\n\n\ndef test_response_links_empty(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict()\n    assert response.links == {}\n\n\ndef test_response_not_closed_after_get_ok(mocker) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://del-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 400\n    response.reason = \"Bad Request\"\n    response._closed = False\n    spy = mocker.spy(response, \"raise_for_status\")\n    assert not response.ok\n    assert not response.closed\n    assert spy.call_count == 0\n",
            "new_file_content": "# Tests for aiohttp/client.py\n\nimport gc\nimport sys\nfrom typing import Callable\nfrom unittest import mock\n\nimport pytest\nfrom multidict import CIMultiDict, CIMultiDictProxy\nfrom yarl import URL\n\nimport aiohttp\nfrom aiohttp import http\nfrom aiohttp.client_reqrep import ClientResponse, RequestInfo\nfrom aiohttp.helpers import TimerNoop\nfrom aiohttp.test_utils import make_mocked_coro\n\n\nclass WriterMock(mock.AsyncMock):\n    def __await__(self) -> None:\n        return self().__await__()\n\n    def add_done_callback(self, cb: Callable[[], None]) -> None:\n        cb()\n\n    def done(self) -> bool:\n        return True\n\n\n@pytest.fixture\ndef session():\n    return mock.Mock()\n\n\nasync def test_http_processing_error(session) -> None:\n    loop = mock.Mock()\n    request_info = mock.Mock()\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://del-cl-resp.org\"),\n        request_info=request_info,\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    loop.get_debug = mock.Mock()\n    loop.get_debug.return_value = True\n\n    connection = mock.Mock()\n    connection.protocol = aiohttp.DataQueue(loop)\n    connection.protocol.set_response_params = mock.Mock()\n    connection.protocol.set_exception(http.HttpProcessingError())\n\n    with pytest.raises(aiohttp.ClientResponseError) as info:\n        await response.start(connection)\n\n    assert info.value.request_info is request_info\n    response.close()\n\n\ndef test_del(session) -> None:\n    loop = mock.Mock()\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://del-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    loop.get_debug = mock.Mock()\n    loop.get_debug.return_value = True\n\n    connection = mock.Mock()\n    response._closed = False\n    response._connection = connection\n    loop.set_exception_handler(lambda loop, ctx: None)\n\n    with pytest.warns(ResourceWarning):\n        del response\n        gc.collect()\n\n    connection.release.assert_called_with()\n\n\ndef test_close(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._closed = False\n    response._connection = mock.Mock()\n    response.close()\n    assert response.connection is None\n    response.close()\n    response.close()\n\n\ndef test_wait_for_100_1(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://python.org\"),\n        continue100=object(),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    assert response._continue is not None\n    response.close()\n\n\ndef test_wait_for_100_2(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://python.org\"),\n        request_info=mock.Mock(),\n        continue100=None,\n        writer=WriterMock(),\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    assert response._continue is None\n    response.close()\n\n\ndef test_repr(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response.status = 200\n    response.reason = \"Ok\"\n    assert \"<ClientResponse(http://def-cl-resp.org) [200 Ok]>\" in repr(response)\n\n\ndef test_repr_non_ascii_url() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://fake-host.org/\\u03bb\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    assert \"<ClientResponse(http://fake-host.org/%CE%BB) [None None]>\" in repr(response)\n\n\ndef test_repr_non_ascii_reason() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://fake-host.org/path\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.reason = \"\\u03bb\"\n    assert \"<ClientResponse(http://fake-host.org/path) [None \\\\u03bb]>\" in repr(\n        response\n    )\n\n\ndef test_url_obj_deprecated() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://fake-host.org/\"),\n        request_info=mock.Mock(),\n        writer=mock.Mock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    with pytest.warns(DeprecationWarning):\n        response.url_obj\n\n\nasync def test_read_and_release_connection(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result(b\"payload\")\n        return fut\n\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.read()\n    assert res == b\"payload\"\n    assert response._connection is None\n\n\nasync def test_read_and_release_connection_with_error(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    content = response.content = mock.Mock()\n    content.read.return_value = loop.create_future()\n    content.read.return_value.set_exception(ValueError)\n\n    with pytest.raises(ValueError):\n        await response.read()\n    assert response._closed\n\n\nasync def test_release(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    fut = loop.create_future()\n    fut.set_result(b\"\")\n    content = response.content = mock.Mock()\n    content.readany.return_value = fut\n\n    response.release()\n    assert response._connection is None\n\n\n@pytest.mark.skipif(\n    sys.implementation.name != \"cpython\",\n    reason=\"Other implementations has different GC strategies\",\n)\nasync def test_release_on_del(loop, session) -> None:\n    connection = mock.Mock()\n    connection.protocol.upgraded = False\n\n    def run(conn):\n        response = ClientResponse(\n            \"get\",\n            URL(\"http://def-cl-resp.org\"),\n            request_info=mock.Mock(),\n            writer=WriterMock(),\n            continue100=None,\n            timer=TimerNoop(),\n            traces=[],\n            loop=loop,\n            session=session,\n        )\n        response._closed = False\n        response._connection = conn\n\n    run(connection)\n\n    assert connection.release.called\n\n\nasync def test_response_eof(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=None,\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._closed = False\n    conn = response._connection = mock.Mock()\n    conn.protocol.upgraded = False\n\n    response._response_eof()\n    assert conn.release.called\n    assert response._connection is None\n\n\nasync def test_response_eof_upgraded(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    conn = response._connection = mock.Mock()\n    conn.protocol.upgraded = True\n\n    response._response_eof()\n    assert not conn.release.called\n    assert response._connection is conn\n\n\nasync def test_response_eof_after_connection_detach(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=None,\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._closed = False\n    conn = response._connection = mock.Mock()\n    conn.protocol = None\n\n    response._response_eof()\n    assert conn.release.called\n    assert response._connection is None\n\n\nasync def test_text(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.text()\n    assert res == '{\"тест\": \"пройден\"}'\n    assert response._connection is None\n\n\nasync def test_text_bad_encoding(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тестkey\": \"пройденvalue\"}'.encode(\"cp1251\"))\n        return fut\n\n    # lie about the encoding\n    response._headers = {\"Content-Type\": \"application/json;charset=utf-8\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n    with pytest.raises(UnicodeDecodeError):\n        await response.text()\n    # only the valid utf-8 characters will be returned\n    res = await response.text(errors=\"ignore\")\n    assert res == '{\"key\": \"value\"}'\n    assert response._connection is None\n\n\nasync def test_text_badly_encoded_encoding_header(loop, session) -> None:\n    session._resolve_charset = lambda *_: \"utf-8\"\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args: object, **kwargs: object):\n        fut = loop.create_future()\n        fut.set_result(b\"foo\")\n        return fut\n\n    h = {\"Content-Type\": \"text/html; charset=\\udc81gutf-8\\udc81\\udc8d\"}\n    response._headers = CIMultiDictProxy(CIMultiDict(h))\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    await response.read()\n    encoding = response.get_encoding()\n\n    assert encoding == \"utf-8\"\n\n\nasync def test_text_custom_encoding(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n    response.get_encoding = mock.Mock()\n\n    res = await response.text(encoding=\"cp1251\")\n    assert res == '{\"тест\": \"пройден\"}'\n    assert response._connection is None\n    assert not response.get_encoding.called\n\n\n@pytest.mark.parametrize(\"content_type\", (\"text/plain\", \"text/plain;charset=invalid\"))\nasync def test_text_charset_resolver(content_type: str, loop, session) -> None:\n    session._resolve_charset = lambda r, b: \"cp1251\"\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": content_type}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    await response.read()\n    res = await response.text()\n    assert res == '{\"тест\": \"пройден\"}'\n    assert response._connection is None\n    assert response.get_encoding() == \"cp1251\"\n\n\nasync def test_get_encoding_body_none(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"encoding\": \"test\"}')\n        return fut\n\n    response._headers = {\"Content-Type\": \"text/html\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    with pytest.raises(\n        RuntimeError,\n        match=\"^Cannot compute fallback encoding of a not yet read body$\",\n    ):\n        response.get_encoding()\n    assert response.closed\n\n\nasync def test_text_after_read(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.text()\n    assert res == '{\"тест\": \"пройден\"}'\n    assert response._connection is None\n\n\nasync def test_json(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.json()\n    assert res == {\"тест\": \"пройден\"}\n    assert response._connection is None\n\n\nasync def test_json_extended_content_type(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\n        \"Content-Type\": \"application/this.is-1_content+subtype+json;charset=cp1251\"\n    }\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.json()\n    assert res == {\"тест\": \"пройден\"}\n    assert response._connection is None\n\n\nasync def test_json_custom_content_type(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"custom/type;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.json(content_type=\"custom/type\")\n    assert res == {\"тест\": \"пройден\"}\n    assert response._connection is None\n\n\nasync def test_json_custom_loader(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response._body = b\"data\"\n\n    def custom(content):\n        return content + \"-custom\"\n\n    res = await response.json(loads=custom)\n    assert res == \"data-custom\"\n\n\nasync def test_json_invalid_content_type(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = {\"Content-Type\": \"data/octet-stream\"}\n    response._body = b\"\"\n    response.status = 500\n\n    with pytest.raises(aiohttp.ContentTypeError) as info:\n        await response.json()\n\n    assert info.value.request_info == response.request_info\n    assert info.value.status == 500\n\n\nasync def test_json_no_content(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = {\"Content-Type\": \"data/octet-stream\"}\n    response._body = b\"\"\n\n    res = await response.json(content_type=None)\n    assert res is None\n\n\nasync def test_json_override_encoding(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=utf8\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n    response.get_encoding = mock.Mock()\n\n    res = await response.json(encoding=\"cp1251\")\n    assert res == {\"тест\": \"пройден\"}\n    assert response._connection is None\n    assert not response.get_encoding.called\n\n\ndef test_get_encoding_unknown(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    response._headers = {\"Content-Type\": \"application/json\"}\n    assert response.get_encoding() == \"utf-8\"\n\n\ndef test_raise_for_status_2xx() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 200\n    response.reason = \"OK\"\n    response.raise_for_status()  # should not raise\n\n\ndef test_raise_for_status_4xx() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 409\n    response.reason = \"CONFLICT\"\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert str(cm.value.status) == \"409\"\n    assert str(cm.value.message) == \"CONFLICT\"\n    assert response.closed\n\n\ndef test_raise_for_status_4xx_without_reason() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 404\n    response.reason = \"\"\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert str(cm.value.status) == \"404\"\n    assert str(cm.value.message) == \"\"\n    assert response.closed\n\n\ndef test_resp_host() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://del-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    assert \"del-cl-resp.org\" == response.host\n\n\ndef test_content_type() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n\n    assert \"application/json\" == response.content_type\n\n\ndef test_content_type_no_header() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {}\n\n    assert \"application/octet-stream\" == response.content_type\n\n\ndef test_charset() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n\n    assert \"cp1251\" == response.charset\n\n\ndef test_charset_no_header() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {}\n\n    assert response.charset is None\n\n\ndef test_charset_no_charset() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\"Content-Type\": \"application/json\"}\n\n    assert response.charset is None\n\n\ndef test_content_disposition_full() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\n        \"Content-Disposition\": 'attachment; filename=\"archive.tar.gz\"; foo=bar'\n    }\n\n    assert \"attachment\" == response.content_disposition.type\n    assert \"bar\" == response.content_disposition.parameters[\"foo\"]\n    assert \"archive.tar.gz\" == response.content_disposition.filename\n    with pytest.raises(TypeError):\n        response.content_disposition.parameters[\"foo\"] = \"baz\"\n\n\ndef test_content_disposition_no_parameters() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\"Content-Disposition\": \"attachment\"}\n\n    assert \"attachment\" == response.content_disposition.type\n    assert response.content_disposition.filename is None\n    assert {} == response.content_disposition.parameters\n\n\ndef test_content_disposition_no_header() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {}\n\n    assert response.content_disposition is None\n\n\ndef test_response_request_info() -> None:\n    url = \"http://def-cl-resp.org\"\n    headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response = ClientResponse(\n        \"get\",\n        URL(url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    assert url == response.request_info.url\n    assert \"get\" == response.request_info.method\n    assert headers == response.request_info.headers\n\n\ndef test_request_info_in_exception() -> None:\n    url = \"http://def-cl-resp.org\"\n    headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response = ClientResponse(\n        \"get\",\n        URL(url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 409\n    response.reason = \"CONFLICT\"\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert cm.value.request_info == response.request_info\n\n\ndef test_no_redirect_history_in_exception() -> None:\n    url = \"http://def-cl-resp.org\"\n    headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response = ClientResponse(\n        \"get\",\n        URL(url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 409\n    response.reason = \"CONFLICT\"\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert () == cm.value.history\n\n\ndef test_redirect_history_in_exception() -> None:\n    hist_url = \"http://def-cl-resp.org\"\n    url = \"http://def-cl-resp.org/index.htm\"\n    hist_headers = {\"Content-Type\": \"application/json;charset=cp1251\", \"Location\": url}\n    headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response = ClientResponse(\n        \"get\",\n        URL(url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 409\n    response.reason = \"CONFLICT\"\n\n    hist_response = ClientResponse(\n        \"get\",\n        URL(hist_url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n\n    hist_response._headers = hist_headers\n    hist_response.status = 301\n    hist_response.reason = \"REDIRECT\"\n\n    response._history = [hist_response]\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert [hist_response] == cm.value.history\n\n\nasync def test_response_read_triggers_callback(loop, session) -> None:\n    trace = mock.Mock()\n    trace.send_response_chunk_received = make_mocked_coro()\n    response_method = \"get\"\n    response_url = URL(\"http://def-cl-resp.org\")\n    response_body = b\"This is response\"\n\n    response = ClientResponse(\n        response_method,\n        response_url,\n        request_info=mock.Mock,\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        loop=loop,\n        session=session,\n        traces=[trace],\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result(response_body)\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.read()\n    assert res == response_body\n    assert response._connection is None\n\n    assert trace.send_response_chunk_received.called\n    assert trace.send_response_chunk_received.call_args == mock.call(\n        response_method, response_url, response_body\n    )\n\n\ndef test_response_real_url(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/#urlfragment\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    assert response.url == url.with_fragment(None)\n    assert response.real_url == url\n\n\ndef test_response_links_comma_separated(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict(\n        [\n            (\n                \"Link\",\n                (\n                    \"<http://example.com/page/1.html>; rel=next, \"\n                    \"<http://example.com/>; rel=home\"\n                ),\n            )\n        ]\n    )\n    assert response.links == {\n        \"next\": {\"url\": URL(\"http://example.com/page/1.html\"), \"rel\": \"next\"},\n        \"home\": {\"url\": URL(\"http://example.com/\"), \"rel\": \"home\"},\n    }\n\n\ndef test_response_links_multiple_headers(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict(\n        [\n            (\"Link\", \"<http://example.com/page/1.html>; rel=next\"),\n            (\"Link\", \"<http://example.com/>; rel=home\"),\n        ]\n    )\n    assert response.links == {\n        \"next\": {\"url\": URL(\"http://example.com/page/1.html\"), \"rel\": \"next\"},\n        \"home\": {\"url\": URL(\"http://example.com/\"), \"rel\": \"home\"},\n    }\n\n\ndef test_response_links_no_rel(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict([(\"Link\", \"<http://example.com/>\")])\n    assert response.links == {\n        \"http://example.com/\": {\"url\": URL(\"http://example.com/\")}\n    }\n\n\ndef test_response_links_quoted(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict(\n        [\n            (\"Link\", '<http://example.com/>; rel=\"home-page\"'),\n        ]\n    )\n    assert response.links == {\n        \"home-page\": {\"url\": URL(\"http://example.com/\"), \"rel\": \"home-page\"}\n    }\n\n\ndef test_response_links_relative(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict(\n        [\n            (\"Link\", \"</relative/path>; rel=rel\"),\n        ]\n    )\n    assert response.links == {\n        \"rel\": {\"url\": URL(\"http://def-cl-resp.org/relative/path\"), \"rel\": \"rel\"}\n    }\n\n\ndef test_response_links_empty(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict()\n    assert response.links == {}\n\n\ndef test_response_not_closed_after_get_ok(mocker) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://del-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 400\n    response.reason = \"Bad Request\"\n    response._closed = False\n    spy = mocker.spy(response, \"raise_for_status\")\n    assert not response.ok\n    assert not response.closed\n    assert spy.call_count == 0\n",
            "header": {
                "file": {
                    "path": "tests/test_client_response.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "628e3d71",
                "new_commit_hash": "ede3950a",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tests/test_client_response.py"
            },
            "plus_file": {
                "path": "b/tests/test_client_response.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 6,
                            "length": 7
                        },
                        "new_range": {
                            "start": 6,
                            "length": 7
                        },
                        "section": "from typing import Callable"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "from unittest import mock",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "import pytest",
                                "type": "context"
                            },
                            {
                                "content": "from multidict import CIMultiDict",
                                "type": "deleted"
                            },
                            {
                                "content": "from multidict import CIMultiDict, CIMultiDictProxy",
                                "type": "added"
                            },
                            {
                                "content": "from yarl import URL",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "import aiohttp",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "tests/test_client_response.py",
                            "type": "statement",
                            "name": "",
                            "content": "from multidict import CIMultiDict, CIMultiDictProxy",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 9,
                            "end_lineno": 9,
                            "parent": null
                        }
                    ],
                    "deleted_entities": [
                        {
                            "file_name": "tests/test_client_response.py",
                            "type": "statement",
                            "name": "",
                            "content": "from multidict import CIMultiDict",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 9,
                            "end_lineno": 9,
                            "parent": null
                        }
                    ]
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 423,
                            "length": 6
                        },
                        "new_range": {
                            "start": 423,
                            "length": 36
                        },
                        "section": "async def test_text_bad_encoding(loop, session) -> None:"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    assert response._connection is None",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "async def test_text_badly_encoded_encoding_header(loop, session) -> None:",
                                "type": "added"
                            },
                            {
                                "content": "    session._resolve_charset = lambda *_: \"utf-8\"",
                                "type": "added"
                            },
                            {
                                "content": "    response = ClientResponse(",
                                "type": "added"
                            },
                            {
                                "content": "        \"get\",",
                                "type": "added"
                            },
                            {
                                "content": "        URL(\"http://def-cl-resp.org\"),",
                                "type": "added"
                            },
                            {
                                "content": "        request_info=mock.Mock(),",
                                "type": "added"
                            },
                            {
                                "content": "        writer=WriterMock(),",
                                "type": "added"
                            },
                            {
                                "content": "        continue100=None,",
                                "type": "added"
                            },
                            {
                                "content": "        timer=TimerNoop(),",
                                "type": "added"
                            },
                            {
                                "content": "        traces=[],",
                                "type": "added"
                            },
                            {
                                "content": "        loop=loop,",
                                "type": "added"
                            },
                            {
                                "content": "        session=session,",
                                "type": "added"
                            },
                            {
                                "content": "    )",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    def side_effect(*args: object, **kwargs: object):",
                                "type": "added"
                            },
                            {
                                "content": "        fut = loop.create_future()",
                                "type": "added"
                            },
                            {
                                "content": "        fut.set_result(b\"foo\")",
                                "type": "added"
                            },
                            {
                                "content": "        return fut",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    h = {\"Content-Type\": \"text/html; charset=\\udc81gutf-8\\udc81\\udc8d\"}",
                                "type": "added"
                            },
                            {
                                "content": "    response._headers = CIMultiDictProxy(CIMultiDict(h))",
                                "type": "added"
                            },
                            {
                                "content": "    content = response.content = mock.Mock()",
                                "type": "added"
                            },
                            {
                                "content": "    content.read.side_effect = side_effect",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    await response.read()",
                                "type": "added"
                            },
                            {
                                "content": "    encoding = response.get_encoding()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    assert encoding == \"utf-8\"",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "async def test_text_custom_encoding(loop, session) -> None:",
                                "type": "context"
                            },
                            {
                                "content": "    response = ClientResponse(",
                                "type": "context"
                            },
                            {
                                "content": "        \"get\",",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "tests/test_client_response.py",
                            "type": "function",
                            "name": "test_text_badly_encoded_encoding_header",
                            "content": "async def test_text_badly_encoded_encoding_header(loop, session) -> None:\n    session._resolve_charset = lambda *_: \"utf-8\"\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args: object, **kwargs: object):\n        fut = loop.create_future()\n        fut.set_result(b\"foo\")\n        return fut\n\n    h = {\"Content-Type\": \"text/html; charset=\\udc81gutf-8\\udc81\\udc8d\"}\n    response._headers = CIMultiDictProxy(CIMultiDict(h))\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    await response.read()\n    encoding = response.get_encoding()\n\n    assert encoding == \"utf-8\"",
                            "ast_type_str": "AsyncFunctionDef",
                            "start_lineno": 426,
                            "end_lineno": 453,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "b15c89558f1a52eb22ca400ff8624def096123d0^",
    "new_commit_hash": "b15c89558f1a52eb22ca400ff8624def096123d0",
    "commit_message": "[PR #9160 backport][3.10] Fix badly encoded charset crashing instead of falling back to detector (#9181)\n\nBackport of #9160 to 3.10",
    "commit_date": "2024-09-18T13:23:09+01:00",
    "metadata": {}
}
{
    "repo_name": "aiohttp",
    "new_commit_hash": "b15c89558f1a52eb22ca400ff8624def096123d0",
    "test_file_codes": [
        "# Tests for aiohttp/client.py\n\nimport gc\nimport sys\nfrom typing import Callable\nfrom unittest import mock\n\nimport pytest\nfrom multidict import CIMultiDict, CIMultiDictProxy\nfrom yarl import URL\n\nimport aiohttp\nfrom aiohttp import http\nfrom aiohttp.client_reqrep import ClientResponse, RequestInfo\nfrom aiohttp.helpers import TimerNoop\nfrom aiohttp.test_utils import make_mocked_coro\n\n\nclass WriterMock(mock.AsyncMock):\n    def __await__(self) -> None:\n        return self().__await__()\n\n    def add_done_callback(self, cb: Callable[[], None]) -> None:\n        cb()\n\n    def done(self) -> bool:\n        return True\n\n\n@pytest.fixture\ndef session():\n    return mock.Mock()\n\n\nasync def test_http_processing_error(session) -> None:\n    loop = mock.Mock()\n    request_info = mock.Mock()\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://del-cl-resp.org\"),\n        request_info=request_info,\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    loop.get_debug = mock.Mock()\n    loop.get_debug.return_value = True\n\n    connection = mock.Mock()\n    connection.protocol = aiohttp.DataQueue(loop)\n    connection.protocol.set_response_params = mock.Mock()\n    connection.protocol.set_exception(http.HttpProcessingError())\n\n    with pytest.raises(aiohttp.ClientResponseError) as info:\n        await response.start(connection)\n\n    assert info.value.request_info is request_info\n    response.close()\n\n\ndef test_del(session) -> None:\n    loop = mock.Mock()\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://del-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    loop.get_debug = mock.Mock()\n    loop.get_debug.return_value = True\n\n    connection = mock.Mock()\n    response._closed = False\n    response._connection = connection\n    loop.set_exception_handler(lambda loop, ctx: None)\n\n    with pytest.warns(ResourceWarning):\n        del response\n        gc.collect()\n\n    connection.release.assert_called_with()\n\n\ndef test_close(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._closed = False\n    response._connection = mock.Mock()\n    response.close()\n    assert response.connection is None\n    response.close()\n    response.close()\n\n\ndef test_wait_for_100_1(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://python.org\"),\n        continue100=object(),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    assert response._continue is not None\n    response.close()\n\n\ndef test_wait_for_100_2(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://python.org\"),\n        request_info=mock.Mock(),\n        continue100=None,\n        writer=WriterMock(),\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    assert response._continue is None\n    response.close()\n\n\ndef test_repr(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response.status = 200\n    response.reason = \"Ok\"\n    assert \"<ClientResponse(http://def-cl-resp.org) [200 Ok]>\" in repr(response)\n\n\ndef test_repr_non_ascii_url() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://fake-host.org/\\u03bb\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    assert \"<ClientResponse(http://fake-host.org/%CE%BB) [None None]>\" in repr(response)\n\n\ndef test_repr_non_ascii_reason() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://fake-host.org/path\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.reason = \"\\u03bb\"\n    assert \"<ClientResponse(http://fake-host.org/path) [None \\\\u03bb]>\" in repr(\n        response\n    )\n\n\ndef test_url_obj_deprecated() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://fake-host.org/\"),\n        request_info=mock.Mock(),\n        writer=mock.Mock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    with pytest.warns(DeprecationWarning):\n        response.url_obj\n\n\nasync def test_read_and_release_connection(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result(b\"payload\")\n        return fut\n\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.read()\n    assert res == b\"payload\"\n    assert response._connection is None\n\n\nasync def test_read_and_release_connection_with_error(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    content = response.content = mock.Mock()\n    content.read.return_value = loop.create_future()\n    content.read.return_value.set_exception(ValueError)\n\n    with pytest.raises(ValueError):\n        await response.read()\n    assert response._closed\n\n\nasync def test_release(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    fut = loop.create_future()\n    fut.set_result(b\"\")\n    content = response.content = mock.Mock()\n    content.readany.return_value = fut\n\n    response.release()\n    assert response._connection is None\n\n\n@pytest.mark.skipif(\n    sys.implementation.name != \"cpython\",\n    reason=\"Other implementations has different GC strategies\",\n)\nasync def test_release_on_del(loop, session) -> None:\n    connection = mock.Mock()\n    connection.protocol.upgraded = False\n\n    def run(conn):\n        response = ClientResponse(\n            \"get\",\n            URL(\"http://def-cl-resp.org\"),\n            request_info=mock.Mock(),\n            writer=WriterMock(),\n            continue100=None,\n            timer=TimerNoop(),\n            traces=[],\n            loop=loop,\n            session=session,\n        )\n        response._closed = False\n        response._connection = conn\n\n    run(connection)\n\n    assert connection.release.called\n\n\nasync def test_response_eof(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=None,\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._closed = False\n    conn = response._connection = mock.Mock()\n    conn.protocol.upgraded = False\n\n    response._response_eof()\n    assert conn.release.called\n    assert response._connection is None\n\n\nasync def test_response_eof_upgraded(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    conn = response._connection = mock.Mock()\n    conn.protocol.upgraded = True\n\n    response._response_eof()\n    assert not conn.release.called\n    assert response._connection is conn\n\n\nasync def test_response_eof_after_connection_detach(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=None,\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._closed = False\n    conn = response._connection = mock.Mock()\n    conn.protocol = None\n\n    response._response_eof()\n    assert conn.release.called\n    assert response._connection is None\n\n\nasync def test_text(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.text()\n    assert res == '{\"тест\": \"пройден\"}'\n    assert response._connection is None\n\n\nasync def test_text_bad_encoding(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тестkey\": \"пройденvalue\"}'.encode(\"cp1251\"))\n        return fut\n\n    # lie about the encoding\n    response._headers = {\"Content-Type\": \"application/json;charset=utf-8\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n    with pytest.raises(UnicodeDecodeError):\n        await response.text()\n    # only the valid utf-8 characters will be returned\n    res = await response.text(errors=\"ignore\")\n    assert res == '{\"key\": \"value\"}'\n    assert response._connection is None\n\n\nasync def test_text_badly_encoded_encoding_header(loop, session) -> None:\n    session._resolve_charset = lambda *_: \"utf-8\"\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args: object, **kwargs: object):\n        fut = loop.create_future()\n        fut.set_result(b\"foo\")\n        return fut\n\n    h = {\"Content-Type\": \"text/html; charset=\\udc81gutf-8\\udc81\\udc8d\"}\n    response._headers = CIMultiDictProxy(CIMultiDict(h))\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    await response.read()\n    encoding = response.get_encoding()\n\n    assert encoding == \"utf-8\"\n\n\nasync def test_text_custom_encoding(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n    response.get_encoding = mock.Mock()\n\n    res = await response.text(encoding=\"cp1251\")\n    assert res == '{\"тест\": \"пройден\"}'\n    assert response._connection is None\n    assert not response.get_encoding.called\n\n\n@pytest.mark.parametrize(\"content_type\", (\"text/plain\", \"text/plain;charset=invalid\"))\nasync def test_text_charset_resolver(content_type: str, loop, session) -> None:\n    session._resolve_charset = lambda r, b: \"cp1251\"\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": content_type}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    await response.read()\n    res = await response.text()\n    assert res == '{\"тест\": \"пройден\"}'\n    assert response._connection is None\n    assert response.get_encoding() == \"cp1251\"\n\n\nasync def test_get_encoding_body_none(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"encoding\": \"test\"}')\n        return fut\n\n    response._headers = {\"Content-Type\": \"text/html\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    with pytest.raises(\n        RuntimeError,\n        match=\"^Cannot compute fallback encoding of a not yet read body$\",\n    ):\n        response.get_encoding()\n    assert response.closed\n\n\nasync def test_text_after_read(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.text()\n    assert res == '{\"тест\": \"пройден\"}'\n    assert response._connection is None\n\n\nasync def test_json(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.json()\n    assert res == {\"тест\": \"пройден\"}\n    assert response._connection is None\n\n\nasync def test_json_extended_content_type(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\n        \"Content-Type\": \"application/this.is-1_content+subtype+json;charset=cp1251\"\n    }\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.json()\n    assert res == {\"тест\": \"пройден\"}\n    assert response._connection is None\n\n\nasync def test_json_custom_content_type(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"custom/type;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.json(content_type=\"custom/type\")\n    assert res == {\"тест\": \"пройден\"}\n    assert response._connection is None\n\n\nasync def test_json_custom_loader(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response._body = b\"data\"\n\n    def custom(content):\n        return content + \"-custom\"\n\n    res = await response.json(loads=custom)\n    assert res == \"data-custom\"\n\n\nasync def test_json_invalid_content_type(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = {\"Content-Type\": \"data/octet-stream\"}\n    response._body = b\"\"\n    response.status = 500\n\n    with pytest.raises(aiohttp.ContentTypeError) as info:\n        await response.json()\n\n    assert info.value.request_info == response.request_info\n    assert info.value.status == 500\n\n\nasync def test_json_no_content(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = {\"Content-Type\": \"data/octet-stream\"}\n    response._body = b\"\"\n\n    res = await response.json(content_type=None)\n    assert res is None\n\n\nasync def test_json_override_encoding(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result('{\"тест\": \"пройден\"}'.encode(\"cp1251\"))\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=utf8\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n    response.get_encoding = mock.Mock()\n\n    res = await response.json(encoding=\"cp1251\")\n    assert res == {\"тест\": \"пройден\"}\n    assert response._connection is None\n    assert not response.get_encoding.called\n\n\ndef test_get_encoding_unknown(loop, session) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n\n    response._headers = {\"Content-Type\": \"application/json\"}\n    assert response.get_encoding() == \"utf-8\"\n\n\ndef test_raise_for_status_2xx() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 200\n    response.reason = \"OK\"\n    response.raise_for_status()  # should not raise\n\n\ndef test_raise_for_status_4xx() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 409\n    response.reason = \"CONFLICT\"\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert str(cm.value.status) == \"409\"\n    assert str(cm.value.message) == \"CONFLICT\"\n    assert response.closed\n\n\ndef test_raise_for_status_4xx_without_reason() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 404\n    response.reason = \"\"\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert str(cm.value.status) == \"404\"\n    assert str(cm.value.message) == \"\"\n    assert response.closed\n\n\ndef test_resp_host() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://del-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    assert \"del-cl-resp.org\" == response.host\n\n\ndef test_content_type() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n\n    assert \"application/json\" == response.content_type\n\n\ndef test_content_type_no_header() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {}\n\n    assert \"application/octet-stream\" == response.content_type\n\n\ndef test_charset() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n\n    assert \"cp1251\" == response.charset\n\n\ndef test_charset_no_header() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {}\n\n    assert response.charset is None\n\n\ndef test_charset_no_charset() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\"Content-Type\": \"application/json\"}\n\n    assert response.charset is None\n\n\ndef test_content_disposition_full() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\n        \"Content-Disposition\": 'attachment; filename=\"archive.tar.gz\"; foo=bar'\n    }\n\n    assert \"attachment\" == response.content_disposition.type\n    assert \"bar\" == response.content_disposition.parameters[\"foo\"]\n    assert \"archive.tar.gz\" == response.content_disposition.filename\n    with pytest.raises(TypeError):\n        response.content_disposition.parameters[\"foo\"] = \"baz\"\n\n\ndef test_content_disposition_no_parameters() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {\"Content-Disposition\": \"attachment\"}\n\n    assert \"attachment\" == response.content_disposition.type\n    assert response.content_disposition.filename is None\n    assert {} == response.content_disposition.parameters\n\n\ndef test_content_disposition_no_header() -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://def-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response._headers = {}\n\n    assert response.content_disposition is None\n\n\ndef test_response_request_info() -> None:\n    url = \"http://def-cl-resp.org\"\n    headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response = ClientResponse(\n        \"get\",\n        URL(url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    assert url == response.request_info.url\n    assert \"get\" == response.request_info.method\n    assert headers == response.request_info.headers\n\n\ndef test_request_info_in_exception() -> None:\n    url = \"http://def-cl-resp.org\"\n    headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response = ClientResponse(\n        \"get\",\n        URL(url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 409\n    response.reason = \"CONFLICT\"\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert cm.value.request_info == response.request_info\n\n\ndef test_no_redirect_history_in_exception() -> None:\n    url = \"http://def-cl-resp.org\"\n    headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response = ClientResponse(\n        \"get\",\n        URL(url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 409\n    response.reason = \"CONFLICT\"\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert () == cm.value.history\n\n\ndef test_redirect_history_in_exception() -> None:\n    hist_url = \"http://def-cl-resp.org\"\n    url = \"http://def-cl-resp.org/index.htm\"\n    hist_headers = {\"Content-Type\": \"application/json;charset=cp1251\", \"Location\": url}\n    headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    response = ClientResponse(\n        \"get\",\n        URL(url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 409\n    response.reason = \"CONFLICT\"\n\n    hist_response = ClientResponse(\n        \"get\",\n        URL(hist_url),\n        request_info=RequestInfo(url, \"get\", headers),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n\n    hist_response._headers = hist_headers\n    hist_response.status = 301\n    hist_response.reason = \"REDIRECT\"\n\n    response._history = [hist_response]\n    with pytest.raises(aiohttp.ClientResponseError) as cm:\n        response.raise_for_status()\n    assert [hist_response] == cm.value.history\n\n\nasync def test_response_read_triggers_callback(loop, session) -> None:\n    trace = mock.Mock()\n    trace.send_response_chunk_received = make_mocked_coro()\n    response_method = \"get\"\n    response_url = URL(\"http://def-cl-resp.org\")\n    response_body = b\"This is response\"\n\n    response = ClientResponse(\n        response_method,\n        response_url,\n        request_info=mock.Mock,\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        loop=loop,\n        session=session,\n        traces=[trace],\n    )\n\n    def side_effect(*args, **kwargs):\n        fut = loop.create_future()\n        fut.set_result(response_body)\n        return fut\n\n    response._headers = {\"Content-Type\": \"application/json;charset=cp1251\"}\n    content = response.content = mock.Mock()\n    content.read.side_effect = side_effect\n\n    res = await response.read()\n    assert res == response_body\n    assert response._connection is None\n\n    assert trace.send_response_chunk_received.called\n    assert trace.send_response_chunk_received.call_args == mock.call(\n        response_method, response_url, response_body\n    )\n\n\ndef test_response_real_url(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/#urlfragment\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    assert response.url == url.with_fragment(None)\n    assert response.real_url == url\n\n\ndef test_response_links_comma_separated(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict(\n        [\n            (\n                \"Link\",\n                (\n                    \"<http://example.com/page/1.html>; rel=next, \"\n                    \"<http://example.com/>; rel=home\"\n                ),\n            )\n        ]\n    )\n    assert response.links == {\n        \"next\": {\"url\": URL(\"http://example.com/page/1.html\"), \"rel\": \"next\"},\n        \"home\": {\"url\": URL(\"http://example.com/\"), \"rel\": \"home\"},\n    }\n\n\ndef test_response_links_multiple_headers(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict(\n        [\n            (\"Link\", \"<http://example.com/page/1.html>; rel=next\"),\n            (\"Link\", \"<http://example.com/>; rel=home\"),\n        ]\n    )\n    assert response.links == {\n        \"next\": {\"url\": URL(\"http://example.com/page/1.html\"), \"rel\": \"next\"},\n        \"home\": {\"url\": URL(\"http://example.com/\"), \"rel\": \"home\"},\n    }\n\n\ndef test_response_links_no_rel(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict([(\"Link\", \"<http://example.com/>\")])\n    assert response.links == {\n        \"http://example.com/\": {\"url\": URL(\"http://example.com/\")}\n    }\n\n\ndef test_response_links_quoted(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict(\n        [\n            (\"Link\", '<http://example.com/>; rel=\"home-page\"'),\n        ]\n    )\n    assert response.links == {\n        \"home-page\": {\"url\": URL(\"http://example.com/\"), \"rel\": \"home-page\"}\n    }\n\n\ndef test_response_links_relative(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict(\n        [\n            (\"Link\", \"</relative/path>; rel=rel\"),\n        ]\n    )\n    assert response.links == {\n        \"rel\": {\"url\": URL(\"http://def-cl-resp.org/relative/path\"), \"rel\": \"rel\"}\n    }\n\n\ndef test_response_links_empty(loop, session) -> None:\n    url = URL(\"http://def-cl-resp.org/\")\n    response = ClientResponse(\n        \"get\",\n        url,\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=loop,\n        session=session,\n    )\n    response._headers = CIMultiDict()\n    assert response.links == {}\n\n\ndef test_response_not_closed_after_get_ok(mocker) -> None:\n    response = ClientResponse(\n        \"get\",\n        URL(\"http://del-cl-resp.org\"),\n        request_info=mock.Mock(),\n        writer=WriterMock(),\n        continue100=None,\n        timer=TimerNoop(),\n        traces=[],\n        loop=mock.Mock(),\n        session=mock.Mock(),\n    )\n    response.status = 400\n    response.reason = \"Bad Request\"\n    response._closed = False\n    spy = mocker.spy(response, \"raise_for_status\")\n    assert not response.ok\n    assert not response.closed\n    assert spy.call_count == 0\n",
        "import asyncio\nimport base64\nimport os\nimport socket\nimport ssl\nimport sys\nfrom hashlib import md5, sha1, sha256\nfrom pathlib import Path\nfrom tempfile import TemporaryDirectory\nfrom typing import Any\nfrom unittest import mock\nfrom uuid import uuid4\n\nimport pytest\n\nfrom aiohttp.client_proto import ResponseHandler\nfrom aiohttp.http import WS_KEY\nfrom aiohttp.test_utils import loop_context\n\ntry:\n    import trustme\n\n    # Check if the CA is available in runtime, MacOS on Py3.10 fails somehow\n    trustme.CA()\n\n    TRUSTME: bool = True\nexcept ImportError:\n    TRUSTME = False\n\npytest_plugins = [\"aiohttp.pytest_plugin\", \"pytester\"]\n\nIS_HPUX = sys.platform.startswith(\"hp-ux\")\nIS_LINUX = sys.platform.startswith(\"linux\")\n\n\n@pytest.fixture\ndef tls_certificate_authority():\n    if not TRUSTME:\n        pytest.xfail(\"trustme is not supported\")\n    return trustme.CA()\n\n\n@pytest.fixture\ndef tls_certificate(tls_certificate_authority):\n    return tls_certificate_authority.issue_cert(\n        \"localhost\",\n        \"xn--prklad-4va.localhost\",\n        \"127.0.0.1\",\n        \"::1\",\n    )\n\n\n@pytest.fixture\ndef ssl_ctx(tls_certificate):\n    ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)\n    tls_certificate.configure_cert(ssl_ctx)\n    return ssl_ctx\n\n\n@pytest.fixture\ndef client_ssl_ctx(tls_certificate_authority):\n    ssl_ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)\n    tls_certificate_authority.configure_trust(ssl_ctx)\n    return ssl_ctx\n\n\n@pytest.fixture\ndef tls_ca_certificate_pem_path(tls_certificate_authority):\n    with tls_certificate_authority.cert_pem.tempfile() as ca_cert_pem:\n        yield ca_cert_pem\n\n\n@pytest.fixture\ndef tls_certificate_pem_path(tls_certificate):\n    with tls_certificate.private_key_and_cert_chain_pem.tempfile() as cert_pem:\n        yield cert_pem\n\n\n@pytest.fixture\ndef tls_certificate_pem_bytes(tls_certificate):\n    return tls_certificate.cert_chain_pems[0].bytes()\n\n\n@pytest.fixture\ndef tls_certificate_fingerprint_sha256(tls_certificate_pem_bytes):\n    tls_cert_der = ssl.PEM_cert_to_DER_cert(tls_certificate_pem_bytes.decode())\n    return sha256(tls_cert_der).digest()\n\n\n@pytest.fixture\ndef unix_sockname(tmp_path, tmp_path_factory):\n    \"\"\"Generate an fs path to the UNIX domain socket for testing.\n\n    N.B. Different OS kernels have different fs path length limitations\n    for it. For Linux, it's 108, for HP-UX it's 92 (or higher) depending\n    on its version. For for most of the BSDs (Open, Free, macOS) it's\n    mostly 104 but sometimes it can be down to 100.\n\n    Ref: https://github.com/aio-libs/aiohttp/issues/3572\n    \"\"\"\n    if not hasattr(socket, \"AF_UNIX\"):\n        pytest.skip(\"requires UNIX sockets\")\n\n    max_sock_len = 92 if IS_HPUX else 108 if IS_LINUX else 100\n    \"\"\"Amount of bytes allocated for the UNIX socket path by OS kernel.\n\n    Ref: https://unix.stackexchange.com/a/367012/27133\n    \"\"\"\n\n    sock_file_name = \"unix.sock\"\n    unique_prefix = f\"{uuid4()!s}-\"\n    unique_prefix_len = len(unique_prefix.encode())\n\n    root_tmp_dir = Path(\"/tmp\").resolve()\n    os_tmp_dir = Path(os.getenv(\"TMPDIR\", \"/tmp\")).resolve()\n    original_base_tmp_path = Path(\n        str(tmp_path_factory.getbasetemp()),\n    ).resolve()\n\n    original_base_tmp_path_hash = md5(\n        str(original_base_tmp_path).encode(),\n    ).hexdigest()\n\n    def make_tmp_dir(base_tmp_dir):\n        return TemporaryDirectory(\n            dir=str(base_tmp_dir),\n            prefix=\"pt-\",\n            suffix=f\"-{original_base_tmp_path_hash!s}\",\n        )\n\n    def assert_sock_fits(sock_path):\n        sock_path_len = len(sock_path.encode())\n        # exit-check to verify that it's correct and simplify debugging\n        # in the future\n        assert sock_path_len <= max_sock_len, (\n            \"Suggested UNIX socket ({sock_path}) is {sock_path_len} bytes \"\n            \"long but the current kernel only has {max_sock_len} bytes \"\n            \"allocated to hold it so it must be shorter. \"\n            \"See https://github.com/aio-libs/aiohttp/issues/3572 \"\n            \"for more info.\"\n        ).format_map(locals())\n\n    paths = original_base_tmp_path, os_tmp_dir, root_tmp_dir\n    unique_paths = [p for n, p in enumerate(paths) if p not in paths[:n]]\n    paths_num = len(unique_paths)\n\n    for num, tmp_dir_path in enumerate(paths, 1):\n        with make_tmp_dir(tmp_dir_path) as tmpd:\n            tmpd = Path(tmpd).resolve()\n            sock_path = str(tmpd / sock_file_name)\n            sock_path_len = len(sock_path.encode())\n\n            if num >= paths_num:\n                # exit-check to verify that it's correct and simplify\n                # debugging in the future\n                assert_sock_fits(sock_path)\n\n            if sock_path_len <= max_sock_len:\n                if max_sock_len - sock_path_len >= unique_prefix_len:\n                    # If we're lucky to have extra space in the path,\n                    # let's also make it more unique\n                    sock_path = str(tmpd / \"\".join((unique_prefix, sock_file_name)))\n                    # Double-checking it:\n                    assert_sock_fits(sock_path)\n                yield sock_path\n                return\n\n\n@pytest.fixture\ndef pipe_name():\n    name = rf\"\\\\.\\pipe\\{uuid4().hex}\"\n    return name\n\n\n@pytest.fixture\ndef create_mocked_conn(loop: Any):\n    def _proto_factory(conn_closing_result=None, **kwargs):\n        proto = mock.create_autospec(ResponseHandler, **kwargs)\n        proto.closed = loop.create_future()\n        proto.closed.set_result(conn_closing_result)\n        return proto\n\n    yield _proto_factory\n\n\n@pytest.fixture\ndef selector_loop():\n    policy = asyncio.WindowsSelectorEventLoopPolicy()\n    asyncio.set_event_loop_policy(policy)\n\n    with loop_context(policy.new_event_loop) as _loop:\n        asyncio.set_event_loop(_loop)\n        yield _loop\n\n\n@pytest.fixture\ndef netrc_contents(\n    tmp_path: Path,\n    monkeypatch: pytest.MonkeyPatch,\n    request: pytest.FixtureRequest,\n):\n    \"\"\"\n    Prepare :file:`.netrc` with given contents.\n\n    Monkey-patches :envvar:`NETRC` to point to created file.\n    \"\"\"\n    netrc_contents = getattr(request, \"param\", None)\n\n    netrc_file_path = tmp_path / \".netrc\"\n    if netrc_contents is not None:\n        netrc_file_path.write_text(netrc_contents)\n\n    monkeypatch.setenv(\"NETRC\", str(netrc_file_path))\n\n    return netrc_file_path\n\n\n@pytest.fixture\ndef start_connection():\n    with mock.patch(\n        \"aiohttp.connector.aiohappyeyeballs.start_connection\",\n        autospec=True,\n        spec_set=True,\n    ) as start_connection_mock:\n        yield start_connection_mock\n\n\n@pytest.fixture\ndef key_data():\n    return os.urandom(16)\n\n\n@pytest.fixture\ndef key(key_data: Any):\n    return base64.b64encode(key_data)\n\n\n@pytest.fixture\ndef ws_key(key: Any):\n    return base64.b64encode(sha1(key + WS_KEY).digest()).decode()\n"
    ],
    "test_file_names": [
        "test_1.py",
        "conftest.py"
    ],
    "setup_res_code": 0,
    "setup_res_stdout": "re-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/requirements/cython.txt\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/tools/gen.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/hdrs.py\n./tools/gen.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/_helpers.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/_http_parser.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/_http_writer.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/_websocket.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/_helpers.pyi\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/_cparser.pxd\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/_find_header.pxd\ncython -3 -o aiohttp/_helpers.c aiohttp/_helpers.pyx -I aiohttp -Werror\ncython -3 -o aiohttp/_http_parser.c aiohttp/_http_parser.pyx -I aiohttp -Werror\ncython -3 -o aiohttp/_http_writer.c aiohttp/_http_writer.pyx -I aiohttp -Werror\ncython -3 -o aiohttp/_websocket.c aiohttp/_websocket.pyx -I aiohttp -Werror\ncd vendor/llhttp; npm ci\n\n> llhttp@9.2.1 prepare\n> npm run clean && npm run build-ts\n\n\n> llhttp@9.2.1 clean\n> rm -rf lib && rm -rf test/tmp\n\n\n> llhttp@9.2.1 build-ts\n> tsc\n\n\nadded 213 packages, and audited 214 packages in 5s\n\n41 packages are looking for funding\n  run `npm fund` for details\n\n5 vulnerabilities (3 moderate, 2 high)\n\nTo address issues that do not require attention, run:\n  npm audit fix\n\nSome issues need review, and may require choosing\na different dependency.\n\nRun `npm audit` for details.\nmake -C vendor/llhttp generate\nmake[1]: Entering directory '/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/vendor/llhttp'\nnpx ts-node bin/generate.ts\nmake[1]: Leaving directory '/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/vendor/llhttp'\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/__init__.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/abc.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/base_protocol.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/client.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/client_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/client_proto.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/client_reqrep.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/client_ws.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/compression_utils.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/connector.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/cookiejar.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/formdata.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/helpers.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/http.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/http_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/http_parser.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/http_websocket.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/http_writer.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/locks.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/log.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/multipart.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/payload.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/payload_streamer.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/pytest_plugin.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/resolver.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/streams.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/tcp_helpers.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/test_utils.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/tracing.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/typedefs.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_app.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_fileresponse.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_log.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_middlewares.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_protocol.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_request.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_response.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_routedef.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_runner.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_server.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_urldispatcher.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/web_ws.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/aiohttp/worker.py\nuv pip install -e . -c requirements/runtime-deps.txt\n",
    "setup_res_stderr": "Using CPython 3.9.21\nCreating virtual environment at: .venv\nActivate with: source .venv/bin/activate\nResolved 1 package in 41ms\nPrepared 1 package in 0.29ms\nInstalled 1 package in 16ms\n + pip==24.3.1\nResolved 2 packages in 8ms\nInstalled 2 packages in 15ms\n + cython==3.0.10\n + multidict==6.0.5\nResolved 96 packages in 39ms\nUninstalled 1 package in 39ms\nInstalled 95 packages in 126ms\n + aiodns==3.2.0\n + aiohappyeyeballs==2.3.4\n + aiohttp-theme==0.1.6\n + aioredis==2.0.1\n + aiosignal==1.3.1\n + alabaster==0.7.13\n + annotated-types==0.5.0\n + async-timeout==4.0.3\n + attrs==23.2.0\n + babel==2.12.1\n + blockdiag==3.0.0\n + brotli==1.1.0\n + build==1.0.3\n + certifi==2023.7.22\n + cffi==1.17.0\n + cfgv==3.3.1\n + charset-normalizer==3.2.0\n + cherry-picker==2.2.0\n + click==8.1.6\n + coverage==7.6.0\n + cryptography==41.0.3\n + distlib==0.3.7\n + docutils==0.20.1\n + exceptiongroup==1.1.2\n + filelock==3.12.2\n + freezegun==1.5.1\n + frozenlist==1.4.1\n + funcparserlib==1.0.1\n + gidgethub==5.3.0\n + gunicorn==22.0.0\n + identify==2.5.26\n + idna==3.4\n + imagesize==1.4.1\n + importlib-metadata==7.0.0\n + importlib-resources==6.1.1\n + incremental==22.10.0\n + iniconfig==2.0.0\n + jinja2==3.1.2\n + markupsafe==2.1.3\n + mypy==1.11.1\n + mypy-extensions==1.0.0\n + nodeenv==1.8.0\n + packaging==23.1\n + pillow==9.5.0\n - pip==24.3.1\n + pip==23.2.1\n + pip-tools==7.4.1\n + platformdirs==3.10.0\n + pluggy==1.5.0\n + pre-commit==3.5.0\n + proxy-py==2.4.4\n + pycares==4.3.0\n + pycparser==2.21\n + pydantic==2.2.0\n + pydantic-core==2.6.0\n + pygments==2.15.1\n + pyjwt==2.8.0\n + pyproject-hooks==1.0.0\n + pytest==8.3.2\n + pytest-cov==5.0.0\n + pytest-mock==3.14.0\n + python-dateutil==2.8.2\n + python-on-whales==0.72.0\n + pyyaml==6.0.1\n + re-assert==1.1.0\n + regex==2023.6.3\n + requests==2.31.0\n + setuptools==68.0.0\n + setuptools-git==1.2\n + six==1.16.0\n + slotscheck==0.19.0\n + snowballstemmer==2.2.0\n + sphinx==7.1.2\n + sphinxcontrib-applehelp==1.0.4\n + sphinxcontrib-blockdiag==3.0.0\n + sphinxcontrib-devhelp==1.0.2\n + sphinxcontrib-htmlhelp==2.0.1\n + sphinxcontrib-jsmath==1.0.1\n + sphinxcontrib-qthelp==1.0.3\n + sphinxcontrib-serializinghtml==1.1.5\n + sphinxcontrib-towncrier==0.4.0a0\n + tomli==2.0.1\n + towncrier==23.11.0\n + tqdm==4.65.0\n + trustme==1.1.0\n + typer==0.9.0\n + typing-extensions==4.11.0\n + uritemplate==4.1.1\n + urllib3==2.0.4\n + uvloop==0.19.0\n + virtualenv==20.24.2\n + wait-for-it==2.2.2\n + webcolors==1.13\n + wheel==0.41.0\n + yarl==1.11.0\n + zipp==3.17.0\nResolved 9 packages in 1.64s\nPrepared 1 package in 16.17s\nInstalled 1 package in 0.62ms\n + aiohttp==3.10.6.dev0 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0)\nResolved 24 packages in 13ms\nInstalled 5 packages in 4ms\n + async-generator==1.10\n + brotlipy==0.7.0\n + chardet==5.2.0\n + multdict==0.0.5\n + pytest-asyncio==0.25.0\n",
    "new_commit_res_code": 0,
    "new_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-8.3.2, pluggy-1.5.0 -- /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/.venv/bin/python\ncachedir: .pytest_cache\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0\nconfigfile: setup.cfg\nplugins: cov-5.0.0, asyncio-0.25.0, mock-3.14.0\nasyncio: mode=strict, asyncio_default_fixture_loop_scope=None\ncollecting ... collected 57 items\n\nr2e_tests/test_1.py::test_http_processing_error PASSED                   [  1%]\nr2e_tests/test_1.py::test_del PASSED                                     [  3%]\nr2e_tests/test_1.py::test_close[pyloop] PASSED                           [  5%]\nr2e_tests/test_1.py::test_wait_for_100_1[pyloop] PASSED                  [  7%]\nr2e_tests/test_1.py::test_wait_for_100_2[pyloop] PASSED                  [  8%]\nr2e_tests/test_1.py::test_repr[pyloop] PASSED                            [ 10%]\nr2e_tests/test_1.py::test_repr_non_ascii_url PASSED                      [ 12%]\nr2e_tests/test_1.py::test_repr_non_ascii_reason PASSED                   [ 14%]\nr2e_tests/test_1.py::test_url_obj_deprecated PASSED                      [ 15%]\nr2e_tests/test_1.py::test_read_and_release_connection[pyloop] PASSED     [ 17%]\nr2e_tests/test_1.py::test_read_and_release_connection_with_error[pyloop] PASSED [ 19%]\nr2e_tests/test_1.py::test_release[pyloop] PASSED                         [ 21%]\nr2e_tests/test_1.py::test_release_on_del[pyloop] PASSED                  [ 22%]\nr2e_tests/test_1.py::test_response_eof[pyloop] PASSED                    [ 24%]\nr2e_tests/test_1.py::test_response_eof_upgraded[pyloop] PASSED           [ 26%]\nr2e_tests/test_1.py::test_response_eof_after_connection_detach[pyloop] PASSED [ 28%]\nr2e_tests/test_1.py::test_text[pyloop] PASSED                            [ 29%]\nr2e_tests/test_1.py::test_text_bad_encoding[pyloop] PASSED               [ 31%]\nr2e_tests/test_1.py::test_text_badly_encoded_encoding_header[pyloop] PASSED [ 33%]\nr2e_tests/test_1.py::test_text_custom_encoding[pyloop] PASSED            [ 35%]\nr2e_tests/test_1.py::test_text_charset_resolver[pyloop-text/plain] PASSED [ 36%]\nr2e_tests/test_1.py::test_text_charset_resolver[pyloop-text/plain;charset=invalid] PASSED [ 38%]\nr2e_tests/test_1.py::test_get_encoding_body_none[pyloop] PASSED          [ 40%]\nr2e_tests/test_1.py::test_text_after_read[pyloop] PASSED                 [ 42%]\nr2e_tests/test_1.py::test_json[pyloop] PASSED                            [ 43%]\nr2e_tests/test_1.py::test_json_extended_content_type[pyloop] PASSED      [ 45%]\nr2e_tests/test_1.py::test_json_custom_content_type[pyloop] PASSED        [ 47%]\nr2e_tests/test_1.py::test_json_custom_loader[pyloop] PASSED              [ 49%]\nr2e_tests/test_1.py::test_json_invalid_content_type[pyloop] PASSED       [ 50%]\nr2e_tests/test_1.py::test_json_no_content[pyloop] PASSED                 [ 52%]\nr2e_tests/test_1.py::test_json_override_encoding[pyloop] PASSED          [ 54%]\nr2e_tests/test_1.py::test_get_encoding_unknown[pyloop] PASSED            [ 56%]\nr2e_tests/test_1.py::test_raise_for_status_2xx PASSED                    [ 57%]\nr2e_tests/test_1.py::test_raise_for_status_4xx PASSED                    [ 59%]\nr2e_tests/test_1.py::test_raise_for_status_4xx_without_reason PASSED     [ 61%]\nr2e_tests/test_1.py::test_resp_host PASSED                               [ 63%]\nr2e_tests/test_1.py::test_content_type PASSED                            [ 64%]\nr2e_tests/test_1.py::test_content_type_no_header PASSED                  [ 66%]\nr2e_tests/test_1.py::test_charset PASSED                                 [ 68%]\nr2e_tests/test_1.py::test_charset_no_header PASSED                       [ 70%]\nr2e_tests/test_1.py::test_charset_no_charset PASSED                      [ 71%]\nr2e_tests/test_1.py::test_content_disposition_full PASSED                [ 73%]\nr2e_tests/test_1.py::test_content_disposition_no_parameters PASSED       [ 75%]\nr2e_tests/test_1.py::test_content_disposition_no_header PASSED           [ 77%]\nr2e_tests/test_1.py::test_response_request_info PASSED                   [ 78%]\nr2e_tests/test_1.py::test_request_info_in_exception PASSED               [ 80%]\nr2e_tests/test_1.py::test_no_redirect_history_in_exception PASSED        [ 82%]\nr2e_tests/test_1.py::test_redirect_history_in_exception PASSED           [ 84%]\nr2e_tests/test_1.py::test_response_read_triggers_callback[pyloop] PASSED [ 85%]\nr2e_tests/test_1.py::test_response_real_url[pyloop] PASSED               [ 87%]\nr2e_tests/test_1.py::test_response_links_comma_separated[pyloop] PASSED  [ 89%]\nr2e_tests/test_1.py::test_response_links_multiple_headers[pyloop] PASSED [ 91%]\nr2e_tests/test_1.py::test_response_links_no_rel[pyloop] PASSED           [ 92%]\nr2e_tests/test_1.py::test_response_links_quoted[pyloop] PASSED           [ 94%]\nr2e_tests/test_1.py::test_response_links_relative[pyloop] PASSED         [ 96%]\nr2e_tests/test_1.py::test_response_links_empty[pyloop] PASSED            [ 98%]\nr2e_tests/test_1.py::test_response_not_closed_after_get_ok PASSED        [100%]\n\n==================================== PASSES ====================================\n\n---------- coverage: platform linux, python 3.9.21-final-0 -----------\nName                                     Stmts   Miss Branch BrPart  Cover\n--------------------------------------------------------------------------\naiohttp/__init__.py                         27      8      2      1    69%\naiohttp/abc.py                              97      8      0      0    92%\naiohttp/base_protocol.py                    67     51     22      0    18%\naiohttp/client.py                          545    394    196      3    21%\naiohttp/client_exceptions.py               154     61     20      2    55%\naiohttp/client_proto.py                    165    131     56      0    15%\naiohttp/client_reqrep.py                   699    350    302     13    42%\naiohttp/client_ws.py                       259    212     80      1    14%\naiohttp/compression_utils.py                70     36     14      0    40%\naiohttp/connector.py                       762    613    323      1    14%\naiohttp/cookiejar.py                       255    200    118      0    15%\naiohttp/formdata.py                         97     79     46      0    13%\naiohttp/hdrs.py                             90      0      0      0   100%\naiohttp/helpers.py                         501    300    178      7    33%\naiohttp/http.py                             11      0      2      0   100%\naiohttp/http_exceptions.py                  50     17      4      1    63%\naiohttp/http_parser.py                     504    404    208      1    14%\naiohttp/http_websocket.py                  393    287    152      0    20%\naiohttp/http_writer.py                     118     81     50      1    23%\naiohttp/locks.py                            24     16      4      0    29%\naiohttp/log.py                               7      0      0      0   100%\naiohttp/multipart.py                       623    466    266     13    20%\naiohttp/payload.py                         237    113     58      1    44%\naiohttp/payload_streamer.py                 31     11      4      0    69%\naiohttp/pytest_plugin.py                   162     77     42      4    50%\naiohttp/resolver.py                         81     58     26      0    21%\naiohttp/streams.py                         398    301    146      4    19%\naiohttp/tcp_helpers.py                      19     11      6      0    32%\naiohttp/test_utils.py                      318    192     52      5    35%\naiohttp/tracing.py                         191     69     32      0    69%\naiohttp/typedefs.py                         26      0      0      0   100%\naiohttp/web.py                             122     84     52      0    23%\naiohttp/web_app.py                         294    198     82      0    26%\naiohttp/web_exceptions.py                  157     25      8      0    80%\naiohttp/web_fileresponse.py                163    127     54      1    19%\naiohttp/web_log.py                         102     58     20      0    36%\naiohttp/web_middlewares.py                  55     42     20      0    17%\naiohttp/web_protocol.py                    354    294    155      0    12%\naiohttp/web_request.py                     445    294    138      1    27%\naiohttp/web_response.py                    479    365    220      0    16%\naiohttp/web_routedef.py                    100     44     10      0    55%\naiohttp/web_runner.py                      213    130     36      0    33%\naiohttp/web_server.py                       42     24     12      0    33%\naiohttp/web_urldispatcher.py               743    476    161      0    30%\naiohttp/web_ws.py                          382    310    132      1    15%\naiohttp/worker.py                          126    126     30      0     0%\ntests/conftest.py                          131    131     16      0     0%\ntests/test_base_protocol.py                198    198      2      0     0%\ntests/test_circular_imports.py              29     29     13      0     0%\ntests/test_classbasedview.py                39     39      0      0     0%\ntests/test_client_connection.py             94     94      2      0     0%\ntests/test_client_exceptions.py            199    199     14      0     0%\ntests/test_client_fingerprint.py            59     59      0      0     0%\ntests/test_client_functional.py           2505   2505     86      0     0%\ntests/test_client_proto.py                  99     99      0      0     0%\ntests/test_client_request.py               808    808     18      0     0%\ntests/test_client_response.py              480    480      6      0     0%\ntests/test_client_session.py               566    566     29      0     0%\ntests/test_client_ws.py                    486    486      2      0     0%\ntests/test_client_ws_functional.py         750    750     32      0     0%\ntests/test_connector.py                   1702   1702     88      0     0%\ntests/test_cookiejar.py                    350    350     24      0     0%\ntests/test_flowcontrol_streams.py          100    100      0      0     0%\ntests/test_formdata.py                      81     81      6      0     0%\ntests/test_helpers.py                      470    470     24      0     0%\ntests/test_http_exceptions.py              109    109     10      0     0%\ntests/test_http_parser.py                 1088   1088     66      0     0%\ntests/test_http_writer.py                  182    182     10      0     0%\ntests/test_imports.py                       34     34      6      0     0%\ntests/test_locks.py                         39     39      0      0     0%\ntests/test_loop.py                          36     36      0      0     0%\ntests/test_multipart.py                    826    826     32      0     0%\ntests/test_multipart_helpers.py            446    446      0      0     0%\ntests/test_payload.py                       95     95      0      0     0%\ntests/test_proxy.py                        302    302      0      0     0%\ntests/test_proxy_functional.py             459    459     18      0     0%\ntests/test_pytest_plugin.py                 27     27      0      0     0%\ntests/test_resolver.py                     199    199     20      0     0%\ntests/test_route_def.py                    210    210      0      0     0%\ntests/test_run_app.py                      563    563     30      0     0%\ntests/test_streams.py                     1077   1077     38      0     0%\ntests/test_tcp_helpers.py                   52     52      2      0     0%\ntests/test_test_utils.py                   251    251      6      0     0%\ntests/test_tracing.py                       48     48      0      0     0%\ntests/test_urldispatch.py                  851    851     24      0     0%\ntests/test_web_app.py                      454    454      6      0     0%\ntests/test_web_cli.py                       75     75      0      0     0%\ntests/test_web_exceptions.py               176    176     22      0     0%\ntests/test_web_functional.py              1580   1580     36      0     0%\ntests/test_web_log.py                      116    116      2      0     0%\ntests/test_web_middleware.py               268    268     16      0     0%\ntests/test_web_request.py                  507    507      8      0     0%\ntests/test_web_request_handler.py           42     42      0      0     0%\ntests/test_web_response.py                 885    885      6      0     0%\ntests/test_web_runner.py                   151    151      4      0     0%\ntests/test_web_sendfile.py                  84     84      0      0     0%\ntests/test_web_sendfile_functional.py      655    655     18      0     0%\ntests/test_web_server.py                   162    162      0      0     0%\ntests/test_web_urldispatcher.py            517    517     24      0     0%\ntests/test_web_websocket.py                401    401      8      0     0%\ntests/test_web_websocket_functional.py     799    799     28      0     0%\ntests/test_websocket_handshake.py          152    152     12      0     0%\ntests/test_websocket_parser.py             300    300     12      0     0%\ntests/test_websocket_writer.py              95     95      6      0     0%\ntests/test_worker.py                       191    191      2      0     0%\n--------------------------------------------------------------------------\nTOTAL                                    34408  30793   4373     61    10%\n\n============================= slowest 10 durations =============================\n0.02s call     r2e_tests/test_1.py::test_http_processing_error\n0.02s teardown r2e_tests/test_1.py::test_response_read_triggers_callback[pyloop]\n0.01s teardown r2e_tests/test_1.py::test_get_encoding_unknown[pyloop]\n0.01s call     r2e_tests/test_1.py::test_del\n0.01s teardown r2e_tests/test_1.py::test_response_links_comma_separated[pyloop]\n0.01s teardown r2e_tests/test_1.py::test_response_links_no_rel[pyloop]\n0.01s teardown r2e_tests/test_1.py::test_response_links_multiple_headers[pyloop]\n0.01s teardown r2e_tests/test_1.py::test_response_real_url[pyloop]\n0.01s teardown r2e_tests/test_1.py::test_json_override_encoding[pyloop]\n0.01s teardown r2e_tests/test_1.py::test_text_after_read[pyloop]\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_http_processing_error\nPASSED r2e_tests/test_1.py::test_del\nPASSED r2e_tests/test_1.py::test_close[pyloop]\nPASSED r2e_tests/test_1.py::test_wait_for_100_1[pyloop]\nPASSED r2e_tests/test_1.py::test_wait_for_100_2[pyloop]\nPASSED r2e_tests/test_1.py::test_repr[pyloop]\nPASSED r2e_tests/test_1.py::test_repr_non_ascii_url\nPASSED r2e_tests/test_1.py::test_repr_non_ascii_reason\nPASSED r2e_tests/test_1.py::test_url_obj_deprecated\nPASSED r2e_tests/test_1.py::test_read_and_release_connection[pyloop]\nPASSED r2e_tests/test_1.py::test_read_and_release_connection_with_error[pyloop]\nPASSED r2e_tests/test_1.py::test_release[pyloop]\nPASSED r2e_tests/test_1.py::test_release_on_del[pyloop]\nPASSED r2e_tests/test_1.py::test_response_eof[pyloop]\nPASSED r2e_tests/test_1.py::test_response_eof_upgraded[pyloop]\nPASSED r2e_tests/test_1.py::test_response_eof_after_connection_detach[pyloop]\nPASSED r2e_tests/test_1.py::test_text[pyloop]\nPASSED r2e_tests/test_1.py::test_text_bad_encoding[pyloop]\nPASSED r2e_tests/test_1.py::test_text_badly_encoded_encoding_header[pyloop]\nPASSED r2e_tests/test_1.py::test_text_custom_encoding[pyloop]\nPASSED r2e_tests/test_1.py::test_text_charset_resolver[pyloop-text/plain]\nPASSED r2e_tests/test_1.py::test_text_charset_resolver[pyloop-text/plain;charset=invalid]\nPASSED r2e_tests/test_1.py::test_get_encoding_body_none[pyloop]\nPASSED r2e_tests/test_1.py::test_text_after_read[pyloop]\nPASSED r2e_tests/test_1.py::test_json[pyloop]\nPASSED r2e_tests/test_1.py::test_json_extended_content_type[pyloop]\nPASSED r2e_tests/test_1.py::test_json_custom_content_type[pyloop]\nPASSED r2e_tests/test_1.py::test_json_custom_loader[pyloop]\nPASSED r2e_tests/test_1.py::test_json_invalid_content_type[pyloop]\nPASSED r2e_tests/test_1.py::test_json_no_content[pyloop]\nPASSED r2e_tests/test_1.py::test_json_override_encoding[pyloop]\nPASSED r2e_tests/test_1.py::test_get_encoding_unknown[pyloop]\nPASSED r2e_tests/test_1.py::test_raise_for_status_2xx\nPASSED r2e_tests/test_1.py::test_raise_for_status_4xx\nPASSED r2e_tests/test_1.py::test_raise_for_status_4xx_without_reason\nPASSED r2e_tests/test_1.py::test_resp_host\nPASSED r2e_tests/test_1.py::test_content_type\nPASSED r2e_tests/test_1.py::test_content_type_no_header\nPASSED r2e_tests/test_1.py::test_charset\nPASSED r2e_tests/test_1.py::test_charset_no_header\nPASSED r2e_tests/test_1.py::test_charset_no_charset\nPASSED r2e_tests/test_1.py::test_content_disposition_full\nPASSED r2e_tests/test_1.py::test_content_disposition_no_parameters\nPASSED r2e_tests/test_1.py::test_content_disposition_no_header\nPASSED r2e_tests/test_1.py::test_response_request_info\nPASSED r2e_tests/test_1.py::test_request_info_in_exception\nPASSED r2e_tests/test_1.py::test_no_redirect_history_in_exception\nPASSED r2e_tests/test_1.py::test_redirect_history_in_exception\nPASSED r2e_tests/test_1.py::test_response_read_triggers_callback[pyloop]\nPASSED r2e_tests/test_1.py::test_response_real_url[pyloop]\nPASSED r2e_tests/test_1.py::test_response_links_comma_separated[pyloop]\nPASSED r2e_tests/test_1.py::test_response_links_multiple_headers[pyloop]\nPASSED r2e_tests/test_1.py::test_response_links_no_rel[pyloop]\nPASSED r2e_tests/test_1.py::test_response_links_quoted[pyloop]\nPASSED r2e_tests/test_1.py::test_response_links_relative[pyloop]\nPASSED r2e_tests/test_1.py::test_response_links_empty[pyloop]\nPASSED r2e_tests/test_1.py::test_response_not_closed_after_get_ok\n============================== 57 passed in 4.49s ==============================\n",
    "new_commit_res_stderr": "/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/.venv/lib/python3.9/site-packages/pytest_asyncio/plugin.py:207: PytestDeprecationWarning: The configuration option \"asyncio_default_fixture_loop_scope\" is unset.\nThe event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: \"function\", \"class\", \"module\", \"package\", \"session\"\n\n  warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))\n",
    "old_commit_res_code": 1,
    "old_commit_res_stdout": "============================= test session starts ==============================\nplatform linux -- Python 3.9.21, pytest-8.3.2, pluggy-1.5.0 -- /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/.venv/bin/python\ncachedir: .pytest_cache\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0\nconfigfile: setup.cfg\nplugins: cov-5.0.0, asyncio-0.25.0, mock-3.14.0\nasyncio: mode=strict, asyncio_default_fixture_loop_scope=None\ncollecting ... collected 57 items\n\nr2e_tests/test_1.py::test_http_processing_error PASSED                   [  1%]\nr2e_tests/test_1.py::test_del PASSED                                     [  3%]\nr2e_tests/test_1.py::test_close[pyloop] PASSED                           [  5%]\nr2e_tests/test_1.py::test_wait_for_100_1[pyloop] PASSED                  [  7%]\nr2e_tests/test_1.py::test_wait_for_100_2[pyloop] PASSED                  [  8%]\nr2e_tests/test_1.py::test_repr[pyloop] PASSED                            [ 10%]\nr2e_tests/test_1.py::test_repr_non_ascii_url PASSED                      [ 12%]\nr2e_tests/test_1.py::test_repr_non_ascii_reason PASSED                   [ 14%]\nr2e_tests/test_1.py::test_url_obj_deprecated PASSED                      [ 15%]\nr2e_tests/test_1.py::test_read_and_release_connection[pyloop] PASSED     [ 17%]\nr2e_tests/test_1.py::test_read_and_release_connection_with_error[pyloop] PASSED [ 19%]\nr2e_tests/test_1.py::test_release[pyloop] PASSED                         [ 21%]\nr2e_tests/test_1.py::test_release_on_del[pyloop] PASSED                  [ 22%]\nr2e_tests/test_1.py::test_response_eof[pyloop] PASSED                    [ 24%]\nr2e_tests/test_1.py::test_response_eof_upgraded[pyloop] PASSED           [ 26%]\nr2e_tests/test_1.py::test_response_eof_after_connection_detach[pyloop] PASSED [ 28%]\nr2e_tests/test_1.py::test_text[pyloop] PASSED                            [ 29%]\nr2e_tests/test_1.py::test_text_bad_encoding[pyloop] PASSED               [ 31%]\nr2e_tests/test_1.py::test_text_badly_encoded_encoding_header[pyloop] FAILED [ 33%]\nr2e_tests/test_1.py::test_text_custom_encoding[pyloop] PASSED            [ 35%]\nr2e_tests/test_1.py::test_text_charset_resolver[pyloop-text/plain] PASSED [ 36%]\nr2e_tests/test_1.py::test_text_charset_resolver[pyloop-text/plain;charset=invalid] PASSED [ 38%]\nr2e_tests/test_1.py::test_get_encoding_body_none[pyloop] PASSED          [ 40%]\nr2e_tests/test_1.py::test_text_after_read[pyloop] PASSED                 [ 42%]\nr2e_tests/test_1.py::test_json[pyloop] PASSED                            [ 43%]\nr2e_tests/test_1.py::test_json_extended_content_type[pyloop] PASSED      [ 45%]\nr2e_tests/test_1.py::test_json_custom_content_type[pyloop] PASSED        [ 47%]\nr2e_tests/test_1.py::test_json_custom_loader[pyloop] PASSED              [ 49%]\nr2e_tests/test_1.py::test_json_invalid_content_type[pyloop] PASSED       [ 50%]\nr2e_tests/test_1.py::test_json_no_content[pyloop] PASSED                 [ 52%]\nr2e_tests/test_1.py::test_json_override_encoding[pyloop] PASSED          [ 54%]\nr2e_tests/test_1.py::test_get_encoding_unknown[pyloop] PASSED            [ 56%]\nr2e_tests/test_1.py::test_raise_for_status_2xx PASSED                    [ 57%]\nr2e_tests/test_1.py::test_raise_for_status_4xx PASSED                    [ 59%]\nr2e_tests/test_1.py::test_raise_for_status_4xx_without_reason PASSED     [ 61%]\nr2e_tests/test_1.py::test_resp_host PASSED                               [ 63%]\nr2e_tests/test_1.py::test_content_type PASSED                            [ 64%]\nr2e_tests/test_1.py::test_content_type_no_header PASSED                  [ 66%]\nr2e_tests/test_1.py::test_charset PASSED                                 [ 68%]\nr2e_tests/test_1.py::test_charset_no_header PASSED                       [ 70%]\nr2e_tests/test_1.py::test_charset_no_charset PASSED                      [ 71%]\nr2e_tests/test_1.py::test_content_disposition_full PASSED                [ 73%]\nr2e_tests/test_1.py::test_content_disposition_no_parameters PASSED       [ 75%]\nr2e_tests/test_1.py::test_content_disposition_no_header PASSED           [ 77%]\nr2e_tests/test_1.py::test_response_request_info PASSED                   [ 78%]\nr2e_tests/test_1.py::test_request_info_in_exception PASSED               [ 80%]\nr2e_tests/test_1.py::test_no_redirect_history_in_exception PASSED        [ 82%]\nr2e_tests/test_1.py::test_redirect_history_in_exception PASSED           [ 84%]\nr2e_tests/test_1.py::test_response_read_triggers_callback[pyloop] PASSED [ 85%]\nr2e_tests/test_1.py::test_response_real_url[pyloop] PASSED               [ 87%]\nr2e_tests/test_1.py::test_response_links_comma_separated[pyloop] PASSED  [ 89%]\nr2e_tests/test_1.py::test_response_links_multiple_headers[pyloop] PASSED [ 91%]\nr2e_tests/test_1.py::test_response_links_no_rel[pyloop] PASSED           [ 92%]\nr2e_tests/test_1.py::test_response_links_quoted[pyloop] PASSED           [ 94%]\nr2e_tests/test_1.py::test_response_links_relative[pyloop] PASSED         [ 96%]\nr2e_tests/test_1.py::test_response_links_empty[pyloop] PASSED            [ 98%]\nr2e_tests/test_1.py::test_response_not_closed_after_get_ok PASSED        [100%]\n\n=================================== FAILURES ===================================\n_______________ test_text_badly_encoded_encoding_header[pyloop] ________________\n\nloop = <_UnixSelectorEventLoop running=False closed=False debug=False>\nsession = <Mock id='124207852673824'>\n\n    async def test_text_badly_encoded_encoding_header(loop, session) -> None:\n        session._resolve_charset = lambda *_: \"utf-8\"\n        response = ClientResponse(\n            \"get\",\n            URL(\"http://def-cl-resp.org\"),\n            request_info=mock.Mock(),\n            writer=WriterMock(),\n            continue100=None,\n            timer=TimerNoop(),\n            traces=[],\n            loop=loop,\n            session=session,\n        )\n    \n        def side_effect(*args: object, **kwargs: object):\n            fut = loop.create_future()\n            fut.set_result(b\"foo\")\n            return fut\n    \n        h = {\"Content-Type\": \"text/html; charset=\\udc81gutf-8\\udc81\\udc8d\"}\n        response._headers = CIMultiDictProxy(CIMultiDict(h))\n        content = response.content = mock.Mock()\n        content.read.side_effect = side_effect\n    \n        await response.read()\n>       encoding = response.get_encoding()\n\ncontent    = <Mock id='124207853896224'>\nh          = {'Content-Type': 'text/html; charset=\\udc81gutf-8\\udc81\\udc8d'}\nloop       = <_UnixSelectorEventLoop running=False closed=False debug=False>\nresponse   = <ClientResponse(http://def-cl-resp.org) [None None]>\n<CIMultiDictProxy('Content-Type': 'text/html; charset=\\udc81gutf-8\\udc81\\udc8d')>\n\nsession    = <Mock id='124207852673824'>\nside_effect = <function test_text_badly_encoded_encoding_header.<locals>.side_effect at 0x70f764f69ee0>\n\nr2e_tests/test_1.py:451: \n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \n\nself = <ClientResponse(http://def-cl-resp.org) [None None]>\n<CIMultiDictProxy('Content-Type': 'text/html; charset=\\udc81gutf-8\\udc81\\udc8d')>\n\n\n    def get_encoding(self) -> str:\n        ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n        mimetype = helpers.parse_mimetype(ctype)\n    \n        encoding = mimetype.parameters.get(\"charset\")\n        if encoding:\n            with contextlib.suppress(LookupError):\n>               return codecs.lookup(encoding).name\nE               UnicodeEncodeError: 'utf-8' codec can't encode character '\\udc81' in position 0: surrogates not allowed\n\nctype      = 'text/html; charset=\\udc81gutf-8\\udc81\\udc8d'\nencoding   = '\\udc81gutf-8\\udc81\\udc8d'\nmimetype   = MimeType(type='text', subtype='html', suffix='', parameters=<MultiDictProxy('charset': '\\udc81gutf-8\\udc81\\udc8d')>)\nself       = <ClientResponse(http://def-cl-resp.org) [None None]>\n<CIMultiDictProxy('Content-Type': 'text/html; charset=\\udc81gutf-8\\udc81\\udc8d')>\n\n\naiohttp/client_reqrep.py:1172: UnicodeEncodeError\n==================================== PASSES ====================================\n\n---------- coverage: platform linux, python 3.9.21-final-0 -----------\nName                                     Stmts   Miss Branch BrPart  Cover\n--------------------------------------------------------------------------\naiohttp/__init__.py                         27      8      2      1    69%\naiohttp/abc.py                              97      8      0      0    92%\naiohttp/base_protocol.py                    67     51     22      0    18%\naiohttp/client.py                          545    394    196      3    21%\naiohttp/client_exceptions.py               154     61     20      2    55%\naiohttp/client_proto.py                    165    131     56      0    15%\naiohttp/client_reqrep.py                   699    350    302     13    42%\naiohttp/client_ws.py                       259    212     80      1    14%\naiohttp/compression_utils.py                70     36     14      0    40%\naiohttp/connector.py                       762    613    323      1    14%\naiohttp/cookiejar.py                       255    200    118      0    15%\naiohttp/formdata.py                         97     79     46      0    13%\naiohttp/hdrs.py                             90      0      0      0   100%\naiohttp/helpers.py                         501    300    178      7    33%\naiohttp/http.py                             11      0      2      0   100%\naiohttp/http_exceptions.py                  50     17      4      1    63%\naiohttp/http_parser.py                     504    404    208      1    14%\naiohttp/http_websocket.py                  393    287    152      0    20%\naiohttp/http_writer.py                     118     81     50      1    23%\naiohttp/locks.py                            24     16      4      0    29%\naiohttp/log.py                               7      0      0      0   100%\naiohttp/multipart.py                       623    466    266     13    20%\naiohttp/payload.py                         237    113     58      1    44%\naiohttp/payload_streamer.py                 31     11      4      0    69%\naiohttp/pytest_plugin.py                   162     76     42      4    51%\naiohttp/resolver.py                         81     58     26      0    21%\naiohttp/streams.py                         398    301    146      4    19%\naiohttp/tcp_helpers.py                      19     11      6      0    32%\naiohttp/test_utils.py                      318    192     52      5    35%\naiohttp/tracing.py                         191     69     32      0    69%\naiohttp/typedefs.py                         26      0      0      0   100%\naiohttp/web.py                             122     84     52      0    23%\naiohttp/web_app.py                         294    198     82      0    26%\naiohttp/web_exceptions.py                  157     25      8      0    80%\naiohttp/web_fileresponse.py                163    127     54      1    19%\naiohttp/web_log.py                         102     58     20      0    36%\naiohttp/web_middlewares.py                  55     42     20      0    17%\naiohttp/web_protocol.py                    354    294    155      0    12%\naiohttp/web_request.py                     445    294    138      1    27%\naiohttp/web_response.py                    479    365    220      0    16%\naiohttp/web_routedef.py                    100     44     10      0    55%\naiohttp/web_runner.py                      213    130     36      0    33%\naiohttp/web_server.py                       42     24     12      0    33%\naiohttp/web_urldispatcher.py               743    476    161      0    30%\naiohttp/web_ws.py                          382    310    132      1    15%\naiohttp/worker.py                          126    126     30      0     0%\ntests/conftest.py                          131    131     16      0     0%\ntests/test_base_protocol.py                198    198      2      0     0%\ntests/test_circular_imports.py              29     29     13      0     0%\ntests/test_classbasedview.py                39     39      0      0     0%\ntests/test_client_connection.py             94     94      2      0     0%\ntests/test_client_exceptions.py            199    199     14      0     0%\ntests/test_client_fingerprint.py            59     59      0      0     0%\ntests/test_client_functional.py           2505   2505     86      0     0%\ntests/test_client_proto.py                  99     99      0      0     0%\ntests/test_client_request.py               808    808     18      0     0%\ntests/test_client_response.py              466    466      4      0     0%\ntests/test_client_session.py               566    566     29      0     0%\ntests/test_client_ws.py                    486    486      2      0     0%\ntests/test_client_ws_functional.py         750    750     32      0     0%\ntests/test_connector.py                   1702   1702     88      0     0%\ntests/test_cookiejar.py                    350    350     24      0     0%\ntests/test_flowcontrol_streams.py          100    100      0      0     0%\ntests/test_formdata.py                      81     81      6      0     0%\ntests/test_helpers.py                      470    470     24      0     0%\ntests/test_http_exceptions.py              109    109     10      0     0%\ntests/test_http_parser.py                 1088   1088     66      0     0%\ntests/test_http_writer.py                  182    182     10      0     0%\ntests/test_imports.py                       34     34      6      0     0%\ntests/test_locks.py                         39     39      0      0     0%\ntests/test_loop.py                          36     36      0      0     0%\ntests/test_multipart.py                    826    826     32      0     0%\ntests/test_multipart_helpers.py            446    446      0      0     0%\ntests/test_payload.py                       95     95      0      0     0%\ntests/test_proxy.py                        302    302      0      0     0%\ntests/test_proxy_functional.py             459    459     18      0     0%\ntests/test_pytest_plugin.py                 27     27      0      0     0%\ntests/test_resolver.py                     199    199     20      0     0%\ntests/test_route_def.py                    210    210      0      0     0%\ntests/test_run_app.py                      563    563     30      0     0%\ntests/test_streams.py                     1077   1077     38      0     0%\ntests/test_tcp_helpers.py                   52     52      2      0     0%\ntests/test_test_utils.py                   251    251      6      0     0%\ntests/test_tracing.py                       48     48      0      0     0%\ntests/test_urldispatch.py                  851    851     24      0     0%\ntests/test_web_app.py                      454    454      6      0     0%\ntests/test_web_cli.py                       75     75      0      0     0%\ntests/test_web_exceptions.py               176    176     22      0     0%\ntests/test_web_functional.py              1580   1580     36      0     0%\ntests/test_web_log.py                      116    116      2      0     0%\ntests/test_web_middleware.py               268    268     16      0     0%\ntests/test_web_request.py                  507    507      8      0     0%\ntests/test_web_request_handler.py           42     42      0      0     0%\ntests/test_web_response.py                 885    885      6      0     0%\ntests/test_web_runner.py                   151    151      4      0     0%\ntests/test_web_sendfile.py                  84     84      0      0     0%\ntests/test_web_sendfile_functional.py      655    655     18      0     0%\ntests/test_web_server.py                   162    162      0      0     0%\ntests/test_web_urldispatcher.py            517    517     24      0     0%\ntests/test_web_websocket.py                401    401      8      0     0%\ntests/test_web_websocket_functional.py     799    799     28      0     0%\ntests/test_websocket_handshake.py          152    152     12      0     0%\ntests/test_websocket_parser.py             300    300     12      0     0%\ntests/test_websocket_writer.py              95     95      6      0     0%\ntests/test_worker.py                       191    191      2      0     0%\n--------------------------------------------------------------------------\nTOTAL                                    34394  30778   4371     61    10%\n\n============================= slowest 10 durations =============================\n0.04s call     r2e_tests/test_1.py::test_http_processing_error\n0.02s teardown r2e_tests/test_1.py::test_json_custom_loader[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_read_and_release_connection_with_error[pyloop]\n0.02s call     r2e_tests/test_1.py::test_del\n0.02s teardown r2e_tests/test_1.py::test_json_no_content[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_text_custom_encoding[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_read_and_release_connection[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_text_charset_resolver[pyloop-text/plain]\n0.02s teardown r2e_tests/test_1.py::test_get_encoding_body_none[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_response_read_triggers_callback[pyloop]\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_http_processing_error\nPASSED r2e_tests/test_1.py::test_del\nPASSED r2e_tests/test_1.py::test_close[pyloop]\nPASSED r2e_tests/test_1.py::test_wait_for_100_1[pyloop]\nPASSED r2e_tests/test_1.py::test_wait_for_100_2[pyloop]\nPASSED r2e_tests/test_1.py::test_repr[pyloop]\nPASSED r2e_tests/test_1.py::test_repr_non_ascii_url\nPASSED r2e_tests/test_1.py::test_repr_non_ascii_reason\nPASSED r2e_tests/test_1.py::test_url_obj_deprecated\nPASSED r2e_tests/test_1.py::test_read_and_release_connection[pyloop]\nPASSED r2e_tests/test_1.py::test_read_and_release_connection_with_error[pyloop]\nPASSED r2e_tests/test_1.py::test_release[pyloop]\nPASSED r2e_tests/test_1.py::test_release_on_del[pyloop]\nPASSED r2e_tests/test_1.py::test_response_eof[pyloop]\nPASSED r2e_tests/test_1.py::test_response_eof_upgraded[pyloop]\nPASSED r2e_tests/test_1.py::test_response_eof_after_connection_detach[pyloop]\nPASSED r2e_tests/test_1.py::test_text[pyloop]\nPASSED r2e_tests/test_1.py::test_text_bad_encoding[pyloop]\nPASSED r2e_tests/test_1.py::test_text_custom_encoding[pyloop]\nPASSED r2e_tests/test_1.py::test_text_charset_resolver[pyloop-text/plain]\nPASSED r2e_tests/test_1.py::test_text_charset_resolver[pyloop-text/plain;charset=invalid]\nPASSED r2e_tests/test_1.py::test_get_encoding_body_none[pyloop]\nPASSED r2e_tests/test_1.py::test_text_after_read[pyloop]\nPASSED r2e_tests/test_1.py::test_json[pyloop]\nPASSED r2e_tests/test_1.py::test_json_extended_content_type[pyloop]\nPASSED r2e_tests/test_1.py::test_json_custom_content_type[pyloop]\nPASSED r2e_tests/test_1.py::test_json_custom_loader[pyloop]\nPASSED r2e_tests/test_1.py::test_json_invalid_content_type[pyloop]\nPASSED r2e_tests/test_1.py::test_json_no_content[pyloop]\nPASSED r2e_tests/test_1.py::test_json_override_encoding[pyloop]\nPASSED r2e_tests/test_1.py::test_get_encoding_unknown[pyloop]\nPASSED r2e_tests/test_1.py::test_raise_for_status_2xx\nPASSED r2e_tests/test_1.py::test_raise_for_status_4xx\nPASSED r2e_tests/test_1.py::test_raise_for_status_4xx_without_reason\nPASSED r2e_tests/test_1.py::test_resp_host\nPASSED r2e_tests/test_1.py::test_content_type\nPASSED r2e_tests/test_1.py::test_content_type_no_header\nPASSED r2e_tests/test_1.py::test_charset\nPASSED r2e_tests/test_1.py::test_charset_no_header\nPASSED r2e_tests/test_1.py::test_charset_no_charset\nPASSED r2e_tests/test_1.py::test_content_disposition_full\nPASSED r2e_tests/test_1.py::test_content_disposition_no_parameters\nPASSED r2e_tests/test_1.py::test_content_disposition_no_header\nPASSED r2e_tests/test_1.py::test_response_request_info\nPASSED r2e_tests/test_1.py::test_request_info_in_exception\nPASSED r2e_tests/test_1.py::test_no_redirect_history_in_exception\nPASSED r2e_tests/test_1.py::test_redirect_history_in_exception\nPASSED r2e_tests/test_1.py::test_response_read_triggers_callback[pyloop]\nPASSED r2e_tests/test_1.py::test_response_real_url[pyloop]\nPASSED r2e_tests/test_1.py::test_response_links_comma_separated[pyloop]\nPASSED r2e_tests/test_1.py::test_response_links_multiple_headers[pyloop]\nPASSED r2e_tests/test_1.py::test_response_links_no_rel[pyloop]\nPASSED r2e_tests/test_1.py::test_response_links_quoted[pyloop]\nPASSED r2e_tests/test_1.py::test_response_links_relative[pyloop]\nPASSED r2e_tests/test_1.py::test_response_links_empty[pyloop]\nPASSED r2e_tests/test_1.py::test_response_not_closed_after_get_ok\nFAILED r2e_tests/test_1.py::test_text_badly_encoded_encoding_header[pyloop]\n========================= 1 failed, 56 passed in 6.17s =========================\n",
    "old_commit_res_stderr": "/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_b15c89558f1a52eb22ca400ff8624def096123d0/.venv/lib/python3.9/site-packages/pytest_asyncio/plugin.py:207: PytestDeprecationWarning: The configuration option \"asyncio_default_fixture_loop_scope\" is unset.\nThe event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: \"function\", \"class\", \"module\", \"package\", \"session\"\n\n  warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET))\n"
}
CHANGES/9160.bugfix CONTRIBUTORS.txt aiohttp/client_reqrep.py tests/test_client_response.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Malformed charset in Content-Type header causes UnicodeEncodeError

**Description:**
When a response includes a `Content-Type` header with an improperly encoded `charset` parameter, invoking the method to retrieve the encoding results in a crash instead of handling the error gracefully.

**Example Code:**
```python
from aiohttp import ClientResponse
from multidict import CIMultiDict, CIMultiDictProxy
from yarl import URL
import asyncio
from unittest import mock

async def example():
    loop = asyncio.get_event_loop()
    session = mock.Mock()
    session._resolve_charset = lambda *_: "utf-8"
    response = ClientResponse(
        "get",
        URL("http://example.com"),
        request_info=mock.Mock(),
        writer=mock.Mock(),
        continue100=None,
        timer=mock.Mock(),
        traces=[],
        loop=loop,
        session=session,
    )
    
    # Malformed charset
    headers = {"Content-Type": "text/html; charset=\udc81gutf-8\udc81\udc8d"}
    response._headers = CIMultiDictProxy(CIMultiDict(headers))
    response.content = mock.Mock()
    response.content.read = mock.AsyncMock(return_value=b"foo")
    
    await response.read()
    encoding = response.get_encoding()
    print(encoding)
```

**Expected Behavior:**
The `get_encoding()` method should handle the malformed `charset` parameter gracefully, defaulting to a safe encoding like `"utf-8"` without raising an error.

**Actual Behavior:**
Invoking `get_encoding()` raises a `UnicodeEncodeError` due to the invalid characters in the `charset` parameter.

**Error Message:**
```
UnicodeEncodeError: 'utf-8' codec can't encode character '\udc81' in position 0: surrogates not allowed
```
[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### aiohttp/client_reqrep.py
import asyncio
import codecs
import contextlib
import functools
import io
import re
import sys
import traceback
import warnings
from hashlib import md5, sha1, sha256
from http.cookies import CookieError, Morsel, SimpleCookie
from types import MappingProxyType, TracebackType
from typing import (
    TYPE_CHECKING,
    Any,
    Callable,
    Dict,
    Iterable,
    List,
    Mapping,
    Optional,
    Tuple,
    Type,
    Union,
    cast,
)

import attr
from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy
from yarl import URL, __version__ as yarl_version

from . import hdrs, helpers, http, multipart, payload
from .abc import AbstractStreamWriter
from .client_exceptions import (
    ClientConnectionError,
    ClientOSError,
    ClientResponseError,
    ContentTypeError,
    InvalidURL,
    ServerFingerprintMismatch,
)
from .compression_utils import HAS_BROTLI
from .formdata import FormData
from .helpers import (
    BaseTimerContext,
    BasicAuth,
    HeadersMixin,
    TimerNoop,
    basicauth_from_netrc,
    netrc_from_env,
    noop,
    reify,
    set_exception,
    set_result,
)
from .http import (
    SERVER_SOFTWARE,
    HttpVersion,
    HttpVersion10,
    HttpVersion11,
    StreamWriter,
)
from .log import client_logger
from .streams import StreamReader
from .typedefs import (
    DEFAULT_JSON_DECODER,
    JSONDecoder,
    LooseCookies,
    LooseHeaders,
    Query,
    RawHeaders,
)

try:
    import ssl
    from ssl import SSLContext
except ImportError:  # pragma: no cover
    ssl = None  # type: ignore[assignment]
    SSLContext = object  # type: ignore[misc,assignment]


__all__ = ("ClientRequest", "ClientResponse", "RequestInfo", "Fingerprint")


if TYPE_CHECKING:
    from .client import ClientSession
    from .connector import Connection
    from .tracing import Trace


_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]")
_YARL_SUPPORTS_EXTEND_QUERY = tuple(map(int, yarl_version.split(".")[:2])) >= (1, 11)
json_re = re.compile(r"^application/(?:[\w.+-]+?\+)?json")


def _gen_default_accept_encoding() -> str:
    return "gzip, deflate, br" if HAS_BROTLI else "gzip, deflate"


@attr.s(auto_attribs=True, frozen=True, slots=True)
class ContentDisposition:
    type: Optional[str]
    parameters: "MappingProxyType[str, str]"
    filename: Optional[str]


@attr.s(auto_attribs=True, frozen=True, slots=True)
class RequestInfo:
    url: URL
    method: str
    headers: "CIMultiDictProxy[str]"
    real_url: URL = attr.ib()

    @real_url.default
    def real_url_default(self) -> URL:
        return self.url


class Fingerprint:
    HASHFUNC_BY_DIGESTLEN = {
        16: md5,
        20: sha1,
        32: sha256,
    }

    def __init__(self, fingerprint: bytes) -> None:
        digestlen = len(fingerprint)
        hashfunc = self.HASHFUNC_BY_DIGESTLEN.get(digestlen)
        if not hashfunc:
            raise ValueError("fingerprint has invalid length")
        elif hashfunc is md5 or hashfunc is sha1:
            raise ValueError(
                "md5 and sha1 are insecure and " "not supported. Use sha256."
            )
        self._hashfunc = hashfunc
        self._fingerprint = fingerprint

    @property
    def fingerprint(self) -> bytes:
        return self._fingerprint

    def check(self, transport: asyncio.Transport) -> None:
        if not transport.get_extra_info("sslcontext"):
            return
        sslobj = transport.get_extra_info("ssl_object")
        cert = sslobj.getpeercert(binary_form=True)
        got = self._hashfunc(cert).digest()
        if got != self._fingerprint:
            host, port, *_ = transport.get_extra_info("peername")
            raise ServerFingerprintMismatch(self._fingerprint, got, host, port)


if ssl is not None:
    SSL_ALLOWED_TYPES = (ssl.SSLContext, bool, Fingerprint, type(None))
else:  # pragma: no cover
    SSL_ALLOWED_TYPES = (bool, type(None))


def _merge_ssl_params(
    ssl: Union["SSLContext", bool, Fingerprint],
    verify_ssl: Optional[bool],
    ssl_context: Optional["SSLContext"],
    fingerprint: Optional[bytes],
) -> Union["SSLContext", bool, Fingerprint]:
    if ssl is None:
        ssl = True  # Double check for backwards compatibility
    if verify_ssl is not None and not verify_ssl:
        warnings.warn(
            "verify_ssl is deprecated, use ssl=False instead",
            DeprecationWarning,
            stacklevel=3,
        )
        if ssl is not True:
            raise ValueError(
                "verify_ssl, ssl_context, fingerprint and ssl "
                "parameters are mutually exclusive"
            )
        else:
            ssl = False
    if ssl_context is not None:
        warnings.warn(
            "ssl_context is deprecated, use ssl=context instead",
            DeprecationWarning,
            stacklevel=3,
        )
        if ssl is not True:
            raise ValueError(
                "verify_ssl, ssl_context, fingerprint and ssl "
                "parameters are mutually exclusive"
            )
        else:
            ssl = ssl_context
    if fingerprint is not None:
        warnings.warn(
            "fingerprint is deprecated, " "use ssl=Fingerprint(fingerprint) instead",
            DeprecationWarning,
            stacklevel=3,
        )
        if ssl is not True:
            raise ValueError(
                "verify_ssl, ssl_context, fingerprint and ssl "
                "parameters are mutually exclusive"
            )
        else:
            ssl = Fingerprint(fingerprint)
    if not isinstance(ssl, SSL_ALLOWED_TYPES):
        raise TypeError(
            "ssl should be SSLContext, bool, Fingerprint or None, "
            "got {!r} instead.".format(ssl)
        )
    return ssl


@attr.s(auto_attribs=True, slots=True, frozen=True, cache_hash=True)
class ConnectionKey:
    # the key should contain an information about used proxy / TLS
    # to prevent reusing wrong connections from a pool
    host: str
    port: Optional[int]
    is_ssl: bool
    ssl: Union[SSLContext, bool, Fingerprint]
    proxy: Optional[URL]
    proxy_auth: Optional[BasicAuth]
    proxy_headers_hash: Optional[int]  # hash(CIMultiDict)


def _is_expected_content_type(
    response_content_type: str, expected_content_type: str
) -> bool:
    if expected_content_type == "application/json":
        return json_re.match(response_content_type) is not None
    return expected_content_type in response_content_type


class ClientRequest:
    GET_METHODS = {
        hdrs.METH_GET,
        hdrs.METH_HEAD,
        hdrs.METH_OPTIONS,
        hdrs.METH_TRACE,
    }
    POST_METHODS = {hdrs.METH_PATCH, hdrs.METH_POST, hdrs.METH_PUT}
    ALL_METHODS = GET_METHODS.union(POST_METHODS).union({hdrs.METH_DELETE})

    DEFAULT_HEADERS = {
        hdrs.ACCEPT: "*/*",
        hdrs.ACCEPT_ENCODING: _gen_default_accept_encoding(),
    }

    # Type of body depends on PAYLOAD_REGISTRY, which is dynamic.
    body: Any = b""
    auth = None
    response = None

    __writer = None  # async task for streaming data
    _continue = None  # waiter future for '100 Continue' response

    # N.B.
    # Adding __del__ method with self._writer closing doesn't make sense
    # because _writer is instance method, thus it keeps a reference to self.
    # Until writer has finished finalizer will not be called.

    def __init__(
        self,
        method: str,
        url: URL,
        *,
        params: Query = None,
        headers: Optional[LooseHeaders] = None,
        skip_auto_headers: Optional[Iterable[str]] = None,
        data: Any = None,
        cookies: Optional[LooseCookies] = None,
        auth: Optional[BasicAuth] = None,
        version: http.HttpVersion = http.HttpVersion11,
        compress: Union[str, bool, None] = None,
        chunked: Optional[bool] = None,
        expect100: bool = False,
        loop: Optional[asyncio.AbstractEventLoop] = None,
        response_class: Optional[Type["ClientResponse"]] = None,
        proxy: Optional[URL] = None,
        proxy_auth: Optional[BasicAuth] = None,
        timer: Optional[BaseTimerContext] = None,
        session: Optional["ClientSession"] = None,
        ssl: Union[SSLContext, bool, Fingerprint] = True,
        proxy_headers: Optional[LooseHeaders] = None,
        traces: Optional[List["Trace"]] = None,
        trust_env: bool = False,
        server_hostname: Optional[str] = None,
    ):
        if loop is None:
            loop = asyncio.get_event_loop()

        match = _CONTAINS_CONTROL_CHAR_RE.search(method)
        if match:
            raise ValueError(
                f"Method cannot contain non-token characters {method!r} "
                "(found at least {match.group()!r})"
            )

        assert isinstance(url, URL), url
        assert isinstance(proxy, (URL, type(None))), proxy
        # FIXME: session is None in tests only, need to fix tests
        # assert session is not None
        self._session = cast("ClientSession", session)
        if params:
            if _YARL_SUPPORTS_EXTEND_QUERY:
                url = url.extend_query(params)
            else:
                q = MultiDict(url.query)
                url2 = url.with_query(params)
                q.extend(url2.query)
                url = url.with_query(q)
        self.original_url = url
        self.url = url.with_fragment(None)
        self.method = method.upper()
        self.chunked = chunked
        self.compress = compress
        self.loop = loop
        self.length = None
        if response_class is None:
            real_response_class = ClientResponse
        else:
            real_response_class = response_class
        self.response_class: Type[ClientResponse] = real_response_class
        self._timer = timer if timer is not None else TimerNoop()
        self._ssl = ssl if ssl is not None else True
        self.server_hostname = server_hostname

        if loop.get_debug():
            self._source_traceback = traceback.extract_stack(sys._getframe(1))

        self.update_version(version)
        self.update_host(url)
        self.update_headers(headers)
        self.update_auto_headers(skip_auto_headers)
        self.update_cookies(cookies)
        self.update_content_encoding(data)
        self.update_auth(auth, trust_env)
        self.update_proxy(proxy, proxy_auth, proxy_headers)

        self.update_body_from_data(data)
        if data is not None or self.method not in self.GET_METHODS:
            self.update_transfer_encoding()
        self.update_expect_continue(expect100)
        if traces is None:
            traces = []
        self._traces = traces

    def __reset_writer(self, _: object = None) -> None:
        self.__writer = None

    @property
    def _writer(self) -> Optional["asyncio.Task[None]"]:
        return self.__writer

    @_writer.setter
    def _writer(self, writer: Optional["asyncio.Task[None]"]) -> None:
        if self.__writer is not None:
            self.__writer.remove_done_callback(self.__reset_writer)
        self.__writer = writer
        if writer is None:
            return
        if writer.done():
            # The writer is already done, so we can reset it immediately.
            self.__reset_writer()
        else:
            writer.add_done_callback(self.__reset_writer)

    def is_ssl(self) -> bool:
        return self.url.scheme in ("https", "wss")

    @property
    def ssl(self) -> Union["SSLContext", bool, Fingerprint]:
        return self._ssl

    @property
    def connection_key(self) -> ConnectionKey:
        proxy_headers = self.proxy_headers
        if proxy_headers:
            h: Optional[int] = hash(tuple((k, v) for k, v in proxy_headers.items()))
        else:
            h = None
        return ConnectionKey(
            self.host,
            self.port,
            self.is_ssl(),
            self.ssl,
            self.proxy,
            self.proxy_auth,
            h,
        )

    @property
    def host(self) -> str:
        ret = self.url.raw_host
        assert ret is not None
        return ret

    @property
    def port(self) -> Optional[int]:
        return self.url.port

    @property
    def request_info(self) -> RequestInfo:
        headers: CIMultiDictProxy[str] = CIMultiDictProxy(self.headers)
        return RequestInfo(self.url, self.method, headers, self.original_url)

    def update_host(self, url: URL) -> None:
        """Update destination host, port and connection type (ssl)."""
        # get host/port
        if not url.raw_host:
            raise InvalidURL(url)

        # basic auth info
        username, password = url.user, url.password
        if username or password:
            self.auth = helpers.BasicAuth(username or "", password or "")

    def update_version(self, version: Union[http.HttpVersion, str]) -> None:
        """Convert request version to two elements tuple.

        parser HTTP version '1.1' => (1, 1)
        """
        if isinstance(version, str):
            v = [part.strip() for part in version.split(".", 1)]
            try:
                version = http.HttpVersion(int(v[0]), int(v[1]))
            except ValueError:
                raise ValueError(
                    f"Can not parse http version number: {version}"
                ) from None
        self.version = version

    def update_headers(self, headers: Optional[LooseHeaders]) -> None:
        """Update request headers."""
        self.headers: CIMultiDict[str] = CIMultiDict()

        # add host
        netloc = cast(str, self.url.raw_host)
        if helpers.is_ipv6_address(netloc):
            netloc = f"[{netloc}]"
        # See https://github.com/aio-libs/aiohttp/issues/3636.
        netloc = netloc.rstrip(".")
        if self.url.port is not None and not self.url.is_default_port():
            netloc += ":" + str(self.url.port)
        self.headers[hdrs.HOST] = netloc

        if headers:
            if isinstance(headers, (dict, MultiDictProxy, MultiDict)):
                headers = headers.items()

            for key, value in headers:  # type: ignore[misc]
                # A special case for Host header
                if key.lower() == "host":
                    self.headers[key] = value
                else:
                    self.headers.add(key, value)

    def update_auto_headers(self, skip_auto_headers: Optional[Iterable[str]]) -> None:
        if skip_auto_headers is not None:
            self.skip_auto_headers = CIMultiDict(
                (hdr, None) for hdr in sorted(skip_auto_headers)
            )
            used_headers = self.headers.copy()
            used_headers.extend(self.skip_auto_headers)  # type: ignore[arg-type]
        else:
            # Fast path when there are no headers to skip
            # which is the most common case.
            self.skip_auto_headers = CIMultiDict()
            used_headers = self.headers

        for hdr, val in self.DEFAULT_HEADERS.items():
            if hdr not in used_headers:
                self.headers.add(hdr, val)

        if hdrs.USER_AGENT not in used_headers:
            self.headers[hdrs.USER_AGENT] = SERVER_SOFTWARE

    def update_cookies(self, cookies: Optional[LooseCookies]) -> None:
        """Update request cookies header."""
        if not cookies:
            return

        c = SimpleCookie()
        if hdrs.COOKIE in self.headers:
            c.load(self.headers.get(hdrs.COOKIE, ""))
            del self.headers[hdrs.COOKIE]

        if isinstance(cookies, Mapping):
            iter_cookies = cookies.items()
        else:
            iter_cookies = cookies  # type: ignore[assignment]
        for name, value in iter_cookies:
            if isinstance(value, Morsel):
                # Preserve coded_value
                mrsl_val = value.get(value.key, Morsel())
                mrsl_val.set(value.key, value.value, value.coded_value)
                c[name] = mrsl_val
            else:
                c[name] = value  # type: ignore[assignment]

        self.headers[hdrs.COOKIE] = c.output(header="", sep=";").strip()

    def update_content_encoding(self, data: Any) -> None:
        """Set request content encoding."""
        if not data:
            # Don't compress an empty body.
            self.compress = None
            return

        enc = self.headers.get(hdrs.CONTENT_ENCODING, "").lower()
        if enc:
            if self.compress:
                raise ValueError(
                    "compress can not be set " "if Content-Encoding header is set"
                )
        elif self.compress:
            if not isinstance(self.compress, str):
                self.compress = "deflate"
            self.headers[hdrs.CONTENT_ENCODING] = self.compress
            self.chunked = True  # enable chunked, no need to deal with length

    def update_transfer_encoding(self) -> None:
        """Analyze transfer-encoding header."""
        te = self.headers.get(hdrs.TRANSFER_ENCODING, "").lower()

        if "chunked" in te:
            if self.chunked:
                raise ValueError(
                    "chunked can not be set "
                    'if "Transfer-Encoding: chunked" header is set'
                )

        elif self.chunked:
            if hdrs.CONTENT_LENGTH in self.headers:
                raise ValueError(
                    "chunked can not be set " "if Content-Length header is set"
                )

            self.headers[hdrs.TRANSFER_ENCODING] = "chunked"
        else:
            if hdrs.CONTENT_LENGTH not in self.headers:
                self.headers[hdrs.CONTENT_LENGTH] = str(len(self.body))

    def update_auth(self, auth: Optional[BasicAuth], trust_env: bool = False) -> None:
        """Set basic auth."""
        if auth is None:
            auth = self.auth
        if auth is None and trust_env and self.url.host is not None:
            netrc_obj = netrc_from_env()
            with contextlib.suppress(LookupError):
                auth = basicauth_from_netrc(netrc_obj, self.url.host)
        if auth is None:
            return

        if not isinstance(auth, helpers.BasicAuth):
            raise TypeError("BasicAuth() tuple is required instead")

        self.headers[hdrs.AUTHORIZATION] = auth.encode()

    def update_body_from_data(self, body: Any) -> None:
        if body is None:
            return

        # FormData
        if isinstance(body, FormData):
            body = body()

        try:
            body = payload.PAYLOAD_REGISTRY.get(body, disposition=None)
        except payload.LookupError:
            body = FormData(body)()

        self.body = body

        # enable chunked encoding if needed
        if not self.chunked:
            if hdrs.CONTENT_LENGTH not in self.headers:
                size = body.size
                if size is None:
                    self.chunked = True
                else:
                    if hdrs.CONTENT_LENGTH not in self.headers:
                        self.headers[hdrs.CONTENT_LENGTH] = str(size)

        # copy payload headers
        assert body.headers
        for key, value in body.headers.items():
            if key in self.headers or key in self.skip_auto_headers:
                continue
            self.headers[key] = value

    def update_expect_continue(self, expect: bool = False) -> None:
        if expect:
            self.headers[hdrs.EXPECT] = "100-continue"
        elif self.headers.get(hdrs.EXPECT, "").lower() == "100-continue":
            expect = True

        if expect:
            self._continue = self.loop.create_future()

    def update_proxy(
        self,
        proxy: Optional[URL],
        proxy_auth: Optional[BasicAuth],
        proxy_headers: Optional[LooseHeaders],
    ) -> None:
        if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth):
            raise ValueError("proxy_auth must be None or BasicAuth() tuple")
        self.proxy = proxy
        self.proxy_auth = proxy_auth
        if proxy_headers is not None and not isinstance(
            proxy_headers, (MultiDict, MultiDictProxy)
        ):
            proxy_headers = CIMultiDict(proxy_headers)
        self.proxy_headers = proxy_headers

    def keep_alive(self) -> bool:
        if self.version < HttpVersion10:
            # keep alive not supported at all
            return False
        if self.version == HttpVersion10:
            if self.headers.get(hdrs.CONNECTION) == "keep-alive":
                return True
            else:  # no headers means we close for Http 1.0
                return False
        elif self.headers.get(hdrs.CONNECTION) == "close":
            return False

        return True

    async def write_bytes(
        self, writer: AbstractStreamWriter, conn: "Connection"
    ) -> None:
        """Support coroutines that yields bytes objects."""
        # 100 response
        if self._continue is not None:
            try:
                await writer.drain()
                await self._continue
            except asyncio.CancelledError:
                return

        protocol = conn.protocol
        assert protocol is not None
        try:
            if isinstance(self.body, payload.Payload):
                await self.body.write(writer)
            else:
                if isinstance(self.body, (bytes, bytearray)):
                    self.body = (self.body,)

                for chunk in self.body:
                    await writer.write(chunk)
        except OSError as underlying_exc:
            reraised_exc = underlying_exc

            exc_is_not_timeout = underlying_exc.errno is not None or not isinstance(
                underlying_exc, asyncio.TimeoutError
            )
            if exc_is_not_timeout:
                reraised_exc = ClientOSError(
                    underlying_exc.errno,
                    f"Can not write request body for {self.url !s}",
                )

            set_exception(protocol, reraised_exc, underlying_exc)
        except asyncio.CancelledError:
            # Body hasn't been fully sent, so connection can't be reused.
            conn.close()
        except Exception as underlying_exc:
            set_exception(
                protocol,
                ClientConnectionError(
                    f"Failed to send bytes into the underlying connection {conn !s}",
                ),
                underlying_exc,
            )
        else:
            await writer.write_eof()
            protocol.start_timeout()

    async def send(self, conn: "Connection") -> "ClientResponse":
        # Specify request target:
        # - CONNECT request must send authority form URI
        # - not CONNECT proxy must send absolute form URI
        # - most common is origin form URI
        if self.method == hdrs.METH_CONNECT:
            connect_host = self.url.raw_host
            assert connect_host is not None
            if helpers.is_ipv6_address(connect_host):
                connect_host = f"[{connect_host}]"
            path = f"{connect_host}:{self.url.port}"
        elif self.proxy and not self.is_ssl():
            path = str(self.url)
        else:
            path = self.url.raw_path
            if self.url.raw_query_string:
                path += "?" + self.url.raw_query_string

        protocol = conn.protocol
        assert protocol is not None
        writer = StreamWriter(
            protocol,
            self.loop,
            on_chunk_sent=(
                functools.partial(self._on_chunk_request_sent, self.method, self.url)
                if self._traces
                else None
            ),
            on_headers_sent=(
                functools.partial(self._on_headers_request_sent, self.method, self.url)
                if self._traces
                else None
            ),
        )

        if self.compress:
            writer.enable_compression(self.compress)  # type: ignore[arg-type]

        if self.chunked is not None:
            writer.enable_chunking()

        # set default content-type
        if (
            self.method in self.POST_METHODS
            and hdrs.CONTENT_TYPE not in self.skip_auto_headers
            and hdrs.CONTENT_TYPE not in self.headers
        ):
            self.headers[hdrs.CONTENT_TYPE] = "application/octet-stream"

        # set the connection header
        connection = self.headers.get(hdrs.CONNECTION)
        if not connection:
            if self.keep_alive():
                if self.version == HttpVersion10:
                    connection = "keep-alive"
            else:
                if self.version == HttpVersion11:
                    connection = "close"

        if connection is not None:
            self.headers[hdrs.CONNECTION] = connection

        # status + headers
        v = self.version
        status_line = f"{self.method} {path} HTTP/{v.major}.{v.minor}"
        await writer.write_headers(status_line, self.headers)
        coro = self.write_bytes(writer, conn)

        if sys.version_info >= (3, 12):
            # Optimization for Python 3.12, try to write
            # bytes immediately to avoid having to schedule
            # the task on the event loop.
            task = asyncio.Task(coro, loop=self.loop, eager_start=True)
        else:
            task = self.loop.create_task(coro)

        self._writer = task
        response_class = self.response_class
        assert response_class is not None
        self.response = response_class(
            self.method,
            self.original_url,
            writer=self._writer,
            continue100=self._continue,
            timer=self._timer,
            request_info=self.request_info,
            traces=self._traces,
            loop=self.loop,
            session=self._session,
        )
        return self.response

    async def close(self) -> None:
        if self._writer is not None:
            with contextlib.suppress(asyncio.CancelledError):
                await self._writer

    def terminate(self) -> None:
        if self._writer is not None:
            if not self.loop.is_closed():
                self._writer.cancel()
            self._writer.remove_done_callback(self.__reset_writer)
            self._writer = None

    async def _on_chunk_request_sent(self, method: str, url: URL, chunk: bytes) -> None:
        for trace in self._traces:
            await trace.send_request_chunk_sent(method, url, chunk)

    async def _on_headers_request_sent(
        self, method: str, url: URL, headers: "CIMultiDict[str]"
    ) -> None:
        for trace in self._traces:
            await trace.send_request_headers(method, url, headers)


class ClientResponse(HeadersMixin):

    # Some of these attributes are None when created,
    # but will be set by the start() method.
    # As the end user will likely never see the None values, we cheat the types below.
    # from the Status-Line of the response
    version: Optional[HttpVersion] = None  # HTTP-Version
    status: int = None  # type: ignore[assignment] # Status-Code
    reason: Optional[str] = None  # Reason-Phrase

    content: StreamReader = None  # type: ignore[assignment]  # Payload stream
    _headers: CIMultiDictProxy[str] = None  # type: ignore[assignment]
    _raw_headers: RawHeaders = None  # type: ignore[assignment]

    _connection = None  # current connection
    _source_traceback: Optional[traceback.StackSummary] = None
    # set up by ClientRequest after ClientResponse object creation
    # post-init stage allows to not change ctor signature
    _closed = True  # to allow __del__ for non-initialized properly response
    _released = False
    __writer = None

    def __init__(
        self,
        method: str,
        url: URL,
        *,
        writer: "asyncio.Task[None]",
        continue100: Optional["asyncio.Future[bool]"],
        timer: BaseTimerContext,
        request_info: RequestInfo,
        traces: List["Trace"],
        loop: asyncio.AbstractEventLoop,
        session: "ClientSession",
    ) -> None:
        assert isinstance(url, URL)

        self.method = method
        self.cookies = SimpleCookie()

        self._real_url = url
        self._url = url.with_fragment(None)
        self._body: Any = None
        self._writer: Optional[asyncio.Task[None]] = writer
        self._continue = continue100  # None by default
        self._closed = True
        self._history: Tuple[ClientResponse, ...] = ()
        self._request_info = request_info
        self._timer = timer if timer is not None else TimerNoop()
        self._cache: Dict[str, Any] = {}
        self._traces = traces
        self._loop = loop
        # store a reference to session #1985
        self._session: Optional[ClientSession] = session
        # Save reference to _resolve_charset, so that get_encoding() will still
        # work after the response has finished reading the body.
        if session is None:
            # TODO: Fix session=None in tests (see ClientRequest.__init__).
            self._resolve_charset: Callable[["ClientResponse", bytes], str] = (
                lambda *_: "utf-8"
            )
        else:
            self._resolve_charset = session._resolve_charset
        if loop.get_debug():
            self._source_traceback = traceback.extract_stack(sys._getframe(1))

    def __reset_writer(self, _: object = None) -> None:
        self.__writer = None

    @property
    def _writer(self) -> Optional["asyncio.Task[None]"]:
        return self.__writer

    @_writer.setter
    def _writer(self, writer: Optional["asyncio.Task[None]"]) -> None:
        if self.__writer is not None:
            self.__writer.remove_done_callback(self.__reset_writer)
        self.__writer = writer
        if writer is None:
            return
        if writer.done():
            # The writer is already done, so we can reset it immediately.
            self.__reset_writer()
        else:
            writer.add_done_callback(self.__reset_writer)

    @reify
    def url(self) -> URL:
        return self._url

    @reify
    def url_obj(self) -> URL:
        warnings.warn("Deprecated, use .url #1654", DeprecationWarning, stacklevel=2)
        return self._url

    @reify
    def real_url(self) -> URL:
        return self._real_url

    @reify
    def host(self) -> str:
        assert self._url.host is not None
        return self._url.host

    @reify
    def headers(self) -> "CIMultiDictProxy[str]":
        return self._headers

    @reify
    def raw_headers(self) -> RawHeaders:
        return self._raw_headers

    @reify
    def request_info(self) -> RequestInfo:
        return self._request_info

    @reify
    def content_disposition(self) -> Optional[ContentDisposition]:
        raw = self._headers.get(hdrs.CONTENT_DISPOSITION)
        if raw is None:
            return None
        disposition_type, params_dct = multipart.parse_content_disposition(raw)
        params = MappingProxyType(params_dct)
        filename = multipart.content_disposition_filename(params)
        return ContentDisposition(disposition_type, params, filename)

    def __del__(self, _warnings: Any = warnings) -> None:
        if self._closed:
            return

        if self._connection is not None:
            self._connection.release()
            self._cleanup_writer()

            if self._loop.get_debug():
                kwargs = {"source": self}
                _warnings.warn(f"Unclosed response {self!r}", ResourceWarning, **kwargs)
                context = {"client_response": self, "message": "Unclosed response"}
                if self._source_traceback:
                    context["source_traceback"] = self._source_traceback
                self._loop.call_exception_handler(context)

    def __repr__(self) -> str:
        out = io.StringIO()
        ascii_encodable_url = str(self.url)
        if self.reason:
            ascii_encodable_reason = self.reason.encode(
                "ascii", "backslashreplace"
            ).decode("ascii")
        else:
            ascii_encodable_reason = "None"
        print(
            "<ClientResponse({}) [{} {}]>".format(
                ascii_encodable_url, self.status, ascii_encodable_reason
            ),
            file=out,
        )
        print(self.headers, file=out)
        return out.getvalue()

    @property
    def connection(self) -> Optional["Connection"]:
        return self._connection

    @reify
    def history(self) -> Tuple["ClientResponse", ...]:
        """A sequence of of responses, if redirects occurred."""
        return self._history

    @reify
    def links(self) -> "MultiDictProxy[MultiDictProxy[Union[str, URL]]]":
        links_str = ", ".join(self.headers.getall("link", []))

        if not links_str:
            return MultiDictProxy(MultiDict())

        links: MultiDict[MultiDictProxy[Union[str, URL]]] = MultiDict()

        for val in re.split(r",(?=\s*<)", links_str):
            match = re.match(r"\s*<(.*)>(.*)", val)
            if match is None:  # pragma: no cover
                # the check exists to suppress mypy error
                continue
            url, params_str = match.groups()
            params = params_str.split(";")[1:]

            link: MultiDict[Union[str, URL]] = MultiDict()

            for param in params:
                match = re.match(r"^\s*(\S*)\s*=\s*(['\"]?)(.*?)(\2)\s*$", param, re.M)
                if match is None:  # pragma: no cover
                    # the check exists to suppress mypy error
                    continue
                key, _, value, _ = match.groups()

                link.add(key, value)

            key = link.get("rel", url)

            link.add("url", self.url.join(URL(url)))

            links.add(str(key), MultiDictProxy(link))

        return MultiDictProxy(links)

    async def start(self, connection: "Connection") -> "ClientResponse":
        """Start response processing."""
        self._closed = False
        self._protocol = connection.protocol
        self._connection = connection

        with self._timer:
            while True:
                # read response
                try:
                    protocol = self._protocol
                    message, payload = await protocol.read()  # type: ignore[union-attr]
                except http.HttpProcessingError as exc:
                    raise ClientResponseError(
                        self.request_info,
                        self.history,
                        status=exc.code,
                        message=exc.message,
                        headers=exc.headers,
                    ) from exc

                if message.code < 100 or message.code > 199 or message.code == 101:
                    break

                if self._continue is not None:
                    set_result(self._continue, True)
                    self._continue = None

        # payload eof handler
        payload.on_eof(self._response_eof)

        # response status
        self.version = message.version
        self.status = message.code
        self.reason = message.reason

        # headers
        self._headers = message.headers  # type is CIMultiDictProxy
        self._raw_headers = message.raw_headers  # type is Tuple[bytes, bytes]

        # payload
        self.content = payload

        # cookies
        for hdr in self.headers.getall(hdrs.SET_COOKIE, ()):
            try:
                self.cookies.load(hdr)
            except CookieError as exc:
                client_logger.warning("Can not load response cookies: %s", exc)
        return self

    def _response_eof(self) -> None:
        if self._closed:
            return

        # protocol could be None because connection could be detached
        protocol = self._connection and self._connection.protocol
        if protocol is not None and protocol.upgraded:
            return

        self._closed = True
        self._cleanup_writer()
        self._release_connection()

    @property
    def closed(self) -> bool:
        return self._closed

    def close(self) -> None:
        if not self._released:
            self._notify_content()

        self._closed = True
        if self._loop is None or self._loop.is_closed():
            return

        self._cleanup_writer()
        if self._connection is not None:
            self._connection.close()
            self._connection = None

    def release(self) -> Any:
        if not self._released:
            self._notify_content()

        self._closed = True

        self._cleanup_writer()
        self._release_connection()
        return noop()

    @property
    def ok(self) -> bool:
        """Returns ``True`` if ``status`` is less than ``400``, ``False`` if not.

        This is **not** a check for ``200 OK`` but a check that the response
        status is under 400.
        """
        return 400 > self.status

    def raise_for_status(self) -> None:
        if not self.ok:
            # reason should always be not None for a started response
            assert self.reason is not None
            self.release()
            raise ClientResponseError(
                self.request_info,
                self.history,
                status=self.status,
                message=self.reason,
                headers=self.headers,
            )

    def _release_connection(self) -> None:
        if self._connection is not None:
            if self._writer is None:
                self._connection.release()
                self._connection = None
            else:
                self._writer.add_done_callback(lambda f: self._release_connection())

    async def _wait_released(self) -> None:
        if self._writer is not None:
            await self._writer
        self._release_connection()

    def _cleanup_writer(self) -> None:
        if self._writer is not None:
            self._writer.cancel()
        self._session = None

    def _notify_content(self) -> None:
        content = self.content
        if content and content.exception() is None:
            set_exception(content, ClientConnectionError("Connection closed"))
        self._released = True

    async def wait_for_close(self) -> None:
        if self._writer is not None:
            await self._writer
        self.release()

    async def read(self) -> bytes:
        """Read response payload."""
        if self._body is None:
            try:
                self._body = await self.content.read()
                for trace in self._traces:
                    await trace.send_response_chunk_received(
                        self.method, self.url, self._body
                    )
            except BaseException:
                self.close()
                raise
        elif self._released:  # Response explicitly released
            raise ClientConnectionError("Connection closed")

        protocol = self._connection and self._connection.protocol
        if protocol is None or not protocol.upgraded:
            await self._wait_released()  # Underlying connection released
        return self._body  # type: ignore[no-any-return]

    def get_encoding(self) -> str:
        ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower()
        mimetype = helpers.parse_mimetype(ctype)

        encoding = mimetype.parameters.get("charset")
        if encoding:
            with contextlib.suppress(LookupError):
                return codecs.lookup(encoding).name

        if mimetype.type == "application" and (
            mimetype.subtype == "json" or mimetype.subtype == "rdap"
        ):
            # RFC 7159 states that the default encoding is UTF-8.
            # RFC 7483 defines application/rdap+json
            return "utf-8"

        if self._body is None:
            raise RuntimeError(
                "Cannot compute fallback encoding of a not yet read body"
            )

        return self._resolve_charset(self, self._body)

    async def text(self, encoding: Optional[str] = None, errors: str = "strict") -> str:
        """Read response payload and decode."""
        if self._body is None:
            await self.read()

        if encoding is None:
            encoding = self.get_encoding()

        return self._body.decode(  # type: ignore[no-any-return,union-attr]
            encoding, errors=errors
        )

    async def json(
        self,
        *,
        encoding: Optional[str] = None,
        loads: JSONDecoder = DEFAULT_JSON_DECODER,
        content_type: Optional[str] = "application/json",
    ) -> Any:
        """Read and decodes JSON response."""
        if self._body is None:
            await self.read()

        if content_type:
            ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower()
            if not _is_expected_content_type(ctype, content_type):
                raise ContentTypeError(
                    self.request_info,
                    self.history,
                    status=self.status,
                    message=(
                        "Attempt to decode JSON with " "unexpected mimetype: %s" % ctype
                    ),
                    headers=self.headers,
                )

        stripped = self._body.strip()  # type: ignore[union-attr]
        if not stripped:
            return None

        if encoding is None:
            encoding = self.get_encoding()

        return loads(stripped.decode(encoding))

    async def __aenter__(self) -> "ClientResponse":
        return self

    async def __aexit__(
        self,
        exc_type: Optional[Type[BaseException]],
        exc_val: Optional[BaseException],
        exc_tb: Optional[TracebackType],
    ) -> None:
        # similar to _RequestContextManager, we do not need to check
        # for exceptions, response object can close connection
        # if state is broken
        self.release()
        await self.wait_for_close()

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
[ISSUE]
**Title:** Malformed charset in Content-Type header causes UnicodeEncodeError

**Description:**
When a response includes a `Content-Type` header with an improperly encoded `charset` parameter, invoking the method to retrieve the encoding results in a crash instead of handling the error gracefully.

**Example Code:**
```python
from aiohttp import ClientResponse
from multidict import CIMultiDict, CIMultiDictProxy
from yarl import URL
import asyncio
from unittest import mock

async def example():
    loop = asyncio.get_event_loop()
    session = mock.Mock()
    session._resolve_charset = lambda *_: "utf-8"
    response = ClientResponse(
        "get",
        URL("http://example.com"),
        request_info=mock.Mock(),
        writer=mock.Mock(),
        continue100=None,
        timer=mock.Mock(),
        traces=[],
        loop=loop,
        session=session,
    )
    
    # Malformed charset
    headers = {"Content-Type": "text/html; charset=\udc81gutf-8\udc81\udc8d"}
    response._headers = CIMultiDictProxy(CIMultiDict(headers))
    response.content = mock.Mock()
    response.content.read = mock.AsyncMock(return_value=b"foo")
    
    await response.read()
    encoding = response.get_encoding()
    print(encoding)
```

**Expected Behavior:**
The `get_encoding()` method should handle the malformed `charset` parameter gracefully, defaulting to a safe encoding like `"utf-8"` without raising an error.

**Actual Behavior:**
Invoking `get_encoding()` raises a `UnicodeEncodeError` due to the invalid characters in the `charset` parameter.

**Error Message:**
```
UnicodeEncodeError: 'utf-8' codec can't encode character '\udc81' in position 0: surrogates not allowed
```
[/ISSUE]
{
    "test_http_processing_error": "PASSED",
    "test_del": "PASSED",
    "test_close[pyloop]": "PASSED",
    "test_wait_for_100_1[pyloop]": "PASSED",
    "test_wait_for_100_2[pyloop]": "PASSED",
    "test_repr[pyloop]": "PASSED",
    "test_repr_non_ascii_url": "PASSED",
    "test_repr_non_ascii_reason": "PASSED",
    "test_url_obj_deprecated": "PASSED",
    "test_read_and_release_connection[pyloop]": "PASSED",
    "test_read_and_release_connection_with_error[pyloop]": "PASSED",
    "test_release[pyloop]": "PASSED",
    "test_release_on_del[pyloop]": "PASSED",
    "test_response_eof[pyloop]": "PASSED",
    "test_response_eof_upgraded[pyloop]": "PASSED",
    "test_response_eof_after_connection_detach[pyloop]": "PASSED",
    "test_text[pyloop]": "PASSED",
    "test_text_bad_encoding[pyloop]": "PASSED",
    "test_text_badly_encoded_encoding_header[pyloop]": "PASSED",
    "test_text_custom_encoding[pyloop]": "PASSED",
    "test_text_charset_resolver[pyloop-text/plain]": "PASSED",
    "test_text_charset_resolver[pyloop-text/plain;charset=invalid]": "PASSED",
    "test_get_encoding_body_none[pyloop]": "PASSED",
    "test_text_after_read[pyloop]": "PASSED",
    "test_json[pyloop]": "PASSED",
    "test_json_extended_content_type[pyloop]": "PASSED",
    "test_json_custom_content_type[pyloop]": "PASSED",
    "test_json_custom_loader[pyloop]": "PASSED",
    "test_json_invalid_content_type[pyloop]": "PASSED",
    "test_json_no_content[pyloop]": "PASSED",
    "test_json_override_encoding[pyloop]": "PASSED",
    "test_get_encoding_unknown[pyloop]": "PASSED",
    "test_raise_for_status_2xx": "PASSED",
    "test_raise_for_status_4xx": "PASSED",
    "test_raise_for_status_4xx_without_reason": "PASSED",
    "test_resp_host": "PASSED",
    "test_content_type": "PASSED",
    "test_content_type_no_header": "PASSED",
    "test_charset": "PASSED",
    "test_charset_no_header": "PASSED",
    "test_charset_no_charset": "PASSED",
    "test_content_disposition_full": "PASSED",
    "test_content_disposition_no_parameters": "PASSED",
    "test_content_disposition_no_header": "PASSED",
    "test_response_request_info": "PASSED",
    "test_request_info_in_exception": "PASSED",
    "test_no_redirect_history_in_exception": "PASSED",
    "test_redirect_history_in_exception": "PASSED",
    "test_response_read_triggers_callback[pyloop]": "PASSED",
    "test_response_real_url[pyloop]": "PASSED",
    "test_response_links_comma_separated[pyloop]": "PASSED",
    "test_response_links_multiple_headers[pyloop]": "PASSED",
    "test_response_links_no_rel[pyloop]": "PASSED",
    "test_response_links_quoted[pyloop]": "PASSED",
    "test_response_links_relative[pyloop]": "PASSED",
    "test_response_links_empty[pyloop]": "PASSED",
    "test_response_not_closed_after_get_ok": "PASSED"
}
diff --git a/aiohttp/client_reqrep.py b/aiohttp/client_reqrep.py
index 93e7b59a..57f3323a 100644
--- a/aiohttp/client_reqrep.py
+++ b/aiohttp/client_reqrep.py
@@ -1168,7 +1168,7 @@ class ClientResponse(HeadersMixin):
 
         encoding = mimetype.parameters.get("charset")
         if encoding:
-            with contextlib.suppress(LookupError):
+            with contextlib.suppress(LookupError, ValueError):
                 return codecs.lookup(encoding).name
 
         if mimetype.type == "application" and (

{"aiohttp/client_reqrep.py": "import asyncio\nimport codecs\nimport contextlib\nimport functools\nimport io\nimport re\nimport sys\nimport traceback\nimport warnings\nfrom hashlib import md5, sha1, sha256\nfrom http.cookies import CookieError, Morsel, SimpleCookie\nfrom types import MappingProxyType, TracebackType\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Callable,\n    Dict,\n    Iterable,\n    List,\n    Mapping,\n    Optional,\n    Tuple,\n    Type,\n    Union,\n    cast,\n)\n\nimport attr\nfrom multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy\nfrom yarl import URL, __version__ as yarl_version\n\nfrom . import hdrs, helpers, http, multipart, payload\nfrom .abc import AbstractStreamWriter\nfrom .client_exceptions import (\n    ClientConnectionError,\n    ClientOSError,\n    ClientResponseError,\n    ContentTypeError,\n    InvalidURL,\n    ServerFingerprintMismatch,\n)\nfrom .compression_utils import HAS_BROTLI\nfrom .formdata import FormData\nfrom .helpers import (\n    BaseTimerContext,\n    BasicAuth,\n    HeadersMixin,\n    TimerNoop,\n    basicauth_from_netrc,\n    netrc_from_env,\n    noop,\n    reify,\n    set_exception,\n    set_result,\n)\nfrom .http import (\n    SERVER_SOFTWARE,\n    HttpVersion,\n    HttpVersion10,\n    HttpVersion11,\n    StreamWriter,\n)\nfrom .log import client_logger\nfrom .streams import StreamReader\nfrom .typedefs import (\n    DEFAULT_JSON_DECODER,\n    JSONDecoder,\n    LooseCookies,\n    LooseHeaders,\n    Query,\n    RawHeaders,\n)\n\ntry:\n    import ssl\n    from ssl import SSLContext\nexcept ImportError:  # pragma: no cover\n    ssl = None  # type: ignore[assignment]\n    SSLContext = object  # type: ignore[misc,assignment]\n\n\n__all__ = (\"ClientRequest\", \"ClientResponse\", \"RequestInfo\", \"Fingerprint\")\n\n\nif TYPE_CHECKING:\n    from .client import ClientSession\n    from .connector import Connection\n    from .tracing import Trace\n\n\n_CONTAINS_CONTROL_CHAR_RE = re.compile(r\"[^-!#$%&'*+.^_`|~0-9a-zA-Z]\")\n_YARL_SUPPORTS_EXTEND_QUERY = tuple(map(int, yarl_version.split(\".\")[:2])) >= (1, 11)\njson_re = re.compile(r\"^application/(?:[\\w.+-]+?\\+)?json\")\n\n\ndef _gen_default_accept_encoding() -> str:\n    return \"gzip, deflate, br\" if HAS_BROTLI else \"gzip, deflate\"\n\n\n@attr.s(auto_attribs=True, frozen=True, slots=True)\nclass ContentDisposition:\n    type: Optional[str]\n    parameters: \"MappingProxyType[str, str]\"\n    filename: Optional[str]\n\n\n@attr.s(auto_attribs=True, frozen=True, slots=True)\nclass RequestInfo:\n    url: URL\n    method: str\n    headers: \"CIMultiDictProxy[str]\"\n    real_url: URL = attr.ib()\n\n    @real_url.default\n    def real_url_default(self) -> URL:\n        return self.url\n\n\nclass Fingerprint:\n    HASHFUNC_BY_DIGESTLEN = {\n        16: md5,\n        20: sha1,\n        32: sha256,\n    }\n\n    def __init__(self, fingerprint: bytes) -> None:\n        digestlen = len(fingerprint)\n        hashfunc = self.HASHFUNC_BY_DIGESTLEN.get(digestlen)\n        if not hashfunc:\n            raise ValueError(\"fingerprint has invalid length\")\n        elif hashfunc is md5 or hashfunc is sha1:\n            raise ValueError(\n                \"md5 and sha1 are insecure and \" \"not supported. Use sha256.\"\n            )\n        self._hashfunc = hashfunc\n        self._fingerprint = fingerprint\n\n    @property\n    def fingerprint(self) -> bytes:\n        return self._fingerprint\n\n    def check(self, transport: asyncio.Transport) -> None:\n        if not transport.get_extra_info(\"sslcontext\"):\n            return\n        sslobj = transport.get_extra_info(\"ssl_object\")\n        cert = sslobj.getpeercert(binary_form=True)\n        got = self._hashfunc(cert).digest()\n        if got != self._fingerprint:\n            host, port, *_ = transport.get_extra_info(\"peername\")\n            raise ServerFingerprintMismatch(self._fingerprint, got, host, port)\n\n\nif ssl is not None:\n    SSL_ALLOWED_TYPES = (ssl.SSLContext, bool, Fingerprint, type(None))\nelse:  # pragma: no cover\n    SSL_ALLOWED_TYPES = (bool, type(None))\n\n\ndef _merge_ssl_params(\n    ssl: Union[\"SSLContext\", bool, Fingerprint],\n    verify_ssl: Optional[bool],\n    ssl_context: Optional[\"SSLContext\"],\n    fingerprint: Optional[bytes],\n) -> Union[\"SSLContext\", bool, Fingerprint]:\n    if ssl is None:\n        ssl = True  # Double check for backwards compatibility\n    if verify_ssl is not None and not verify_ssl:\n        warnings.warn(\n            \"verify_ssl is deprecated, use ssl=False instead\",\n            DeprecationWarning,\n            stacklevel=3,\n        )\n        if ssl is not True:\n            raise ValueError(\n                \"verify_ssl, ssl_context, fingerprint and ssl \"\n                \"parameters are mutually exclusive\"\n            )\n        else:\n            ssl = False\n    if ssl_context is not None:\n        warnings.warn(\n            \"ssl_context is deprecated, use ssl=context instead\",\n            DeprecationWarning,\n            stacklevel=3,\n        )\n        if ssl is not True:\n            raise ValueError(\n                \"verify_ssl, ssl_context, fingerprint and ssl \"\n                \"parameters are mutually exclusive\"\n            )\n        else:\n            ssl = ssl_context\n    if fingerprint is not None:\n        warnings.warn(\n            \"fingerprint is deprecated, \" \"use ssl=Fingerprint(fingerprint) instead\",\n            DeprecationWarning,\n            stacklevel=3,\n        )\n        if ssl is not True:\n            raise ValueError(\n                \"verify_ssl, ssl_context, fingerprint and ssl \"\n                \"parameters are mutually exclusive\"\n            )\n        else:\n            ssl = Fingerprint(fingerprint)\n    if not isinstance(ssl, SSL_ALLOWED_TYPES):\n        raise TypeError(\n            \"ssl should be SSLContext, bool, Fingerprint or None, \"\n            \"got {!r} instead.\".format(ssl)\n        )\n    return ssl\n\n\n@attr.s(auto_attribs=True, slots=True, frozen=True, cache_hash=True)\nclass ConnectionKey:\n    # the key should contain an information about used proxy / TLS\n    # to prevent reusing wrong connections from a pool\n    host: str\n    port: Optional[int]\n    is_ssl: bool\n    ssl: Union[SSLContext, bool, Fingerprint]\n    proxy: Optional[URL]\n    proxy_auth: Optional[BasicAuth]\n    proxy_headers_hash: Optional[int]  # hash(CIMultiDict)\n\n\ndef _is_expected_content_type(\n    response_content_type: str, expected_content_type: str\n) -> bool:\n    if expected_content_type == \"application/json\":\n        return json_re.match(response_content_type) is not None\n    return expected_content_type in response_content_type\n\n\nclass ClientRequest:\n    GET_METHODS = {\n        hdrs.METH_GET,\n        hdrs.METH_HEAD,\n        hdrs.METH_OPTIONS,\n        hdrs.METH_TRACE,\n    }\n    POST_METHODS = {hdrs.METH_PATCH, hdrs.METH_POST, hdrs.METH_PUT}\n    ALL_METHODS = GET_METHODS.union(POST_METHODS).union({hdrs.METH_DELETE})\n\n    DEFAULT_HEADERS = {\n        hdrs.ACCEPT: \"*/*\",\n        hdrs.ACCEPT_ENCODING: _gen_default_accept_encoding(),\n    }\n\n    # Type of body depends on PAYLOAD_REGISTRY, which is dynamic.\n    body: Any = b\"\"\n    auth = None\n    response = None\n\n    __writer = None  # async task for streaming data\n    _continue = None  # waiter future for '100 Continue' response\n\n    # N.B.\n    # Adding __del__ method with self._writer closing doesn't make sense\n    # because _writer is instance method, thus it keeps a reference to self.\n    # Until writer has finished finalizer will not be called.\n\n    def __init__(\n        self,\n        method: str,\n        url: URL,\n        *,\n        params: Query = None,\n        headers: Optional[LooseHeaders] = None,\n        skip_auto_headers: Optional[Iterable[str]] = None,\n        data: Any = None,\n        cookies: Optional[LooseCookies] = None,\n        auth: Optional[BasicAuth] = None,\n        version: http.HttpVersion = http.HttpVersion11,\n        compress: Union[str, bool, None] = None,\n        chunked: Optional[bool] = None,\n        expect100: bool = False,\n        loop: Optional[asyncio.AbstractEventLoop] = None,\n        response_class: Optional[Type[\"ClientResponse\"]] = None,\n        proxy: Optional[URL] = None,\n        proxy_auth: Optional[BasicAuth] = None,\n        timer: Optional[BaseTimerContext] = None,\n        session: Optional[\"ClientSession\"] = None,\n        ssl: Union[SSLContext, bool, Fingerprint] = True,\n        proxy_headers: Optional[LooseHeaders] = None,\n        traces: Optional[List[\"Trace\"]] = None,\n        trust_env: bool = False,\n        server_hostname: Optional[str] = None,\n    ):\n        if loop is None:\n            loop = asyncio.get_event_loop()\n\n        match = _CONTAINS_CONTROL_CHAR_RE.search(method)\n        if match:\n            raise ValueError(\n                f\"Method cannot contain non-token characters {method!r} \"\n                \"(found at least {match.group()!r})\"\n            )\n\n        assert isinstance(url, URL), url\n        assert isinstance(proxy, (URL, type(None))), proxy\n        # FIXME: session is None in tests only, need to fix tests\n        # assert session is not None\n        self._session = cast(\"ClientSession\", session)\n        if params:\n            if _YARL_SUPPORTS_EXTEND_QUERY:\n                url = url.extend_query(params)\n            else:\n                q = MultiDict(url.query)\n                url2 = url.with_query(params)\n                q.extend(url2.query)\n                url = url.with_query(q)\n        self.original_url = url\n        self.url = url.with_fragment(None)\n        self.method = method.upper()\n        self.chunked = chunked\n        self.compress = compress\n        self.loop = loop\n        self.length = None\n        if response_class is None:\n            real_response_class = ClientResponse\n        else:\n            real_response_class = response_class\n        self.response_class: Type[ClientResponse] = real_response_class\n        self._timer = timer if timer is not None else TimerNoop()\n        self._ssl = ssl if ssl is not None else True\n        self.server_hostname = server_hostname\n\n        if loop.get_debug():\n            self._source_traceback = traceback.extract_stack(sys._getframe(1))\n\n        self.update_version(version)\n        self.update_host(url)\n        self.update_headers(headers)\n        self.update_auto_headers(skip_auto_headers)\n        self.update_cookies(cookies)\n        self.update_content_encoding(data)\n        self.update_auth(auth, trust_env)\n        self.update_proxy(proxy, proxy_auth, proxy_headers)\n\n        self.update_body_from_data(data)\n        if data is not None or self.method not in self.GET_METHODS:\n            self.update_transfer_encoding()\n        self.update_expect_continue(expect100)\n        if traces is None:\n            traces = []\n        self._traces = traces\n\n    def __reset_writer(self, _: object = None) -> None:\n        self.__writer = None\n\n    @property\n    def _writer(self) -> Optional[\"asyncio.Task[None]\"]:\n        return self.__writer\n\n    @_writer.setter\n    def _writer(self, writer: Optional[\"asyncio.Task[None]\"]) -> None:\n        if self.__writer is not None:\n            self.__writer.remove_done_callback(self.__reset_writer)\n        self.__writer = writer\n        if writer is None:\n            return\n        if writer.done():\n            # The writer is already done, so we can reset it immediately.\n            self.__reset_writer()\n        else:\n            writer.add_done_callback(self.__reset_writer)\n\n    def is_ssl(self) -> bool:\n        return self.url.scheme in (\"https\", \"wss\")\n\n    @property\n    def ssl(self) -> Union[\"SSLContext\", bool, Fingerprint]:\n        return self._ssl\n\n    @property\n    def connection_key(self) -> ConnectionKey:\n        proxy_headers = self.proxy_headers\n        if proxy_headers:\n            h: Optional[int] = hash(tuple((k, v) for k, v in proxy_headers.items()))\n        else:\n            h = None\n        return ConnectionKey(\n            self.host,\n            self.port,\n            self.is_ssl(),\n            self.ssl,\n            self.proxy,\n            self.proxy_auth,\n            h,\n        )\n\n    @property\n    def host(self) -> str:\n        ret = self.url.raw_host\n        assert ret is not None\n        return ret\n\n    @property\n    def port(self) -> Optional[int]:\n        return self.url.port\n\n    @property\n    def request_info(self) -> RequestInfo:\n        headers: CIMultiDictProxy[str] = CIMultiDictProxy(self.headers)\n        return RequestInfo(self.url, self.method, headers, self.original_url)\n\n    def update_host(self, url: URL) -> None:\n        \"\"\"Update destination host, port and connection type (ssl).\"\"\"\n        # get host/port\n        if not url.raw_host:\n            raise InvalidURL(url)\n\n        # basic auth info\n        username, password = url.user, url.password\n        if username or password:\n            self.auth = helpers.BasicAuth(username or \"\", password or \"\")\n\n    def update_version(self, version: Union[http.HttpVersion, str]) -> None:\n        \"\"\"Convert request version to two elements tuple.\n\n        parser HTTP version '1.1' => (1, 1)\n        \"\"\"\n        if isinstance(version, str):\n            v = [part.strip() for part in version.split(\".\", 1)]\n            try:\n                version = http.HttpVersion(int(v[0]), int(v[1]))\n            except ValueError:\n                raise ValueError(\n                    f\"Can not parse http version number: {version}\"\n                ) from None\n        self.version = version\n\n    def update_headers(self, headers: Optional[LooseHeaders]) -> None:\n        \"\"\"Update request headers.\"\"\"\n        self.headers: CIMultiDict[str] = CIMultiDict()\n\n        # add host\n        netloc = cast(str, self.url.raw_host)\n        if helpers.is_ipv6_address(netloc):\n            netloc = f\"[{netloc}]\"\n        # See https://github.com/aio-libs/aiohttp/issues/3636.\n        netloc = netloc.rstrip(\".\")\n        if self.url.port is not None and not self.url.is_default_port():\n            netloc += \":\" + str(self.url.port)\n        self.headers[hdrs.HOST] = netloc\n\n        if headers:\n            if isinstance(headers, (dict, MultiDictProxy, MultiDict)):\n                headers = headers.items()\n\n            for key, value in headers:  # type: ignore[misc]\n                # A special case for Host header\n                if key.lower() == \"host\":\n                    self.headers[key] = value\n                else:\n                    self.headers.add(key, value)\n\n    def update_auto_headers(self, skip_auto_headers: Optional[Iterable[str]]) -> None:\n        if skip_auto_headers is not None:\n            self.skip_auto_headers = CIMultiDict(\n                (hdr, None) for hdr in sorted(skip_auto_headers)\n            )\n            used_headers = self.headers.copy()\n            used_headers.extend(self.skip_auto_headers)  # type: ignore[arg-type]\n        else:\n            # Fast path when there are no headers to skip\n            # which is the most common case.\n            self.skip_auto_headers = CIMultiDict()\n            used_headers = self.headers\n\n        for hdr, val in self.DEFAULT_HEADERS.items():\n            if hdr not in used_headers:\n                self.headers.add(hdr, val)\n\n        if hdrs.USER_AGENT not in used_headers:\n            self.headers[hdrs.USER_AGENT] = SERVER_SOFTWARE\n\n    def update_cookies(self, cookies: Optional[LooseCookies]) -> None:\n        \"\"\"Update request cookies header.\"\"\"\n        if not cookies:\n            return\n\n        c = SimpleCookie()\n        if hdrs.COOKIE in self.headers:\n            c.load(self.headers.get(hdrs.COOKIE, \"\"))\n            del self.headers[hdrs.COOKIE]\n\n        if isinstance(cookies, Mapping):\n            iter_cookies = cookies.items()\n        else:\n            iter_cookies = cookies  # type: ignore[assignment]\n        for name, value in iter_cookies:\n            if isinstance(value, Morsel):\n                # Preserve coded_value\n                mrsl_val = value.get(value.key, Morsel())\n                mrsl_val.set(value.key, value.value, value.coded_value)\n                c[name] = mrsl_val\n            else:\n                c[name] = value  # type: ignore[assignment]\n\n        self.headers[hdrs.COOKIE] = c.output(header=\"\", sep=\";\").strip()\n\n    def update_content_encoding(self, data: Any) -> None:\n        \"\"\"Set request content encoding.\"\"\"\n        if not data:\n            # Don't compress an empty body.\n            self.compress = None\n            return\n\n        enc = self.headers.get(hdrs.CONTENT_ENCODING, \"\").lower()\n        if enc:\n            if self.compress:\n                raise ValueError(\n                    \"compress can not be set \" \"if Content-Encoding header is set\"\n                )\n        elif self.compress:\n            if not isinstance(self.compress, str):\n                self.compress = \"deflate\"\n            self.headers[hdrs.CONTENT_ENCODING] = self.compress\n            self.chunked = True  # enable chunked, no need to deal with length\n\n    def update_transfer_encoding(self) -> None:\n        \"\"\"Analyze transfer-encoding header.\"\"\"\n        te = self.headers.get(hdrs.TRANSFER_ENCODING, \"\").lower()\n\n        if \"chunked\" in te:\n            if self.chunked:\n                raise ValueError(\n                    \"chunked can not be set \"\n                    'if \"Transfer-Encoding: chunked\" header is set'\n                )\n\n        elif self.chunked:\n            if hdrs.CONTENT_LENGTH in self.headers:\n                raise ValueError(\n                    \"chunked can not be set \" \"if Content-Length header is set\"\n                )\n\n            self.headers[hdrs.TRANSFER_ENCODING] = \"chunked\"\n        else:\n            if hdrs.CONTENT_LENGTH not in self.headers:\n                self.headers[hdrs.CONTENT_LENGTH] = str(len(self.body))\n\n    def update_auth(self, auth: Optional[BasicAuth], trust_env: bool = False) -> None:\n        \"\"\"Set basic auth.\"\"\"\n        if auth is None:\n            auth = self.auth\n        if auth is None and trust_env and self.url.host is not None:\n            netrc_obj = netrc_from_env()\n            with contextlib.suppress(LookupError):\n                auth = basicauth_from_netrc(netrc_obj, self.url.host)\n        if auth is None:\n            return\n\n        if not isinstance(auth, helpers.BasicAuth):\n            raise TypeError(\"BasicAuth() tuple is required instead\")\n\n        self.headers[hdrs.AUTHORIZATION] = auth.encode()\n\n    def update_body_from_data(self, body: Any) -> None:\n        if body is None:\n            return\n\n        # FormData\n        if isinstance(body, FormData):\n            body = body()\n\n        try:\n            body = payload.PAYLOAD_REGISTRY.get(body, disposition=None)\n        except payload.LookupError:\n            body = FormData(body)()\n\n        self.body = body\n\n        # enable chunked encoding if needed\n        if not self.chunked:\n            if hdrs.CONTENT_LENGTH not in self.headers:\n                size = body.size\n                if size is None:\n                    self.chunked = True\n                else:\n                    if hdrs.CONTENT_LENGTH not in self.headers:\n                        self.headers[hdrs.CONTENT_LENGTH] = str(size)\n\n        # copy payload headers\n        assert body.headers\n        for key, value in body.headers.items():\n            if key in self.headers or key in self.skip_auto_headers:\n                continue\n            self.headers[key] = value\n\n    def update_expect_continue(self, expect: bool = False) -> None:\n        if expect:\n            self.headers[hdrs.EXPECT] = \"100-continue\"\n        elif self.headers.get(hdrs.EXPECT, \"\").lower() == \"100-continue\":\n            expect = True\n\n        if expect:\n            self._continue = self.loop.create_future()\n\n    def update_proxy(\n        self,\n        proxy: Optional[URL],\n        proxy_auth: Optional[BasicAuth],\n        proxy_headers: Optional[LooseHeaders],\n    ) -> None:\n        if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth):\n            raise ValueError(\"proxy_auth must be None or BasicAuth() tuple\")\n        self.proxy = proxy\n        self.proxy_auth = proxy_auth\n        if proxy_headers is not None and not isinstance(\n            proxy_headers, (MultiDict, MultiDictProxy)\n        ):\n            proxy_headers = CIMultiDict(proxy_headers)\n        self.proxy_headers = proxy_headers\n\n    def keep_alive(self) -> bool:\n        if self.version < HttpVersion10:\n            # keep alive not supported at all\n            return False\n        if self.version == HttpVersion10:\n            if self.headers.get(hdrs.CONNECTION) == \"keep-alive\":\n                return True\n            else:  # no headers means we close for Http 1.0\n                return False\n        elif self.headers.get(hdrs.CONNECTION) == \"close\":\n            return False\n\n        return True\n\n    async def write_bytes(\n        self, writer: AbstractStreamWriter, conn: \"Connection\"\n    ) -> None:\n        \"\"\"Support coroutines that yields bytes objects.\"\"\"\n        # 100 response\n        if self._continue is not None:\n            try:\n                await writer.drain()\n                await self._continue\n            except asyncio.CancelledError:\n                return\n\n        protocol = conn.protocol\n        assert protocol is not None\n        try:\n            if isinstance(self.body, payload.Payload):\n                await self.body.write(writer)\n            else:\n                if isinstance(self.body, (bytes, bytearray)):\n                    self.body = (self.body,)\n\n                for chunk in self.body:\n                    await writer.write(chunk)\n        except OSError as underlying_exc:\n            reraised_exc = underlying_exc\n\n            exc_is_not_timeout = underlying_exc.errno is not None or not isinstance(\n                underlying_exc, asyncio.TimeoutError\n            )\n            if exc_is_not_timeout:\n                reraised_exc = ClientOSError(\n                    underlying_exc.errno,\n                    f\"Can not write request body for {self.url !s}\",\n                )\n\n            set_exception(protocol, reraised_exc, underlying_exc)\n        except asyncio.CancelledError:\n            # Body hasn't been fully sent, so connection can't be reused.\n            conn.close()\n        except Exception as underlying_exc:\n            set_exception(\n                protocol,\n                ClientConnectionError(\n                    f\"Failed to send bytes into the underlying connection {conn !s}\",\n                ),\n                underlying_exc,\n            )\n        else:\n            await writer.write_eof()\n            protocol.start_timeout()\n\n    async def send(self, conn: \"Connection\") -> \"ClientResponse\":\n        # Specify request target:\n        # - CONNECT request must send authority form URI\n        # - not CONNECT proxy must send absolute form URI\n        # - most common is origin form URI\n        if self.method == hdrs.METH_CONNECT:\n            connect_host = self.url.raw_host\n            assert connect_host is not None\n            if helpers.is_ipv6_address(connect_host):\n                connect_host = f\"[{connect_host}]\"\n            path = f\"{connect_host}:{self.url.port}\"\n        elif self.proxy and not self.is_ssl():\n            path = str(self.url)\n        else:\n            path = self.url.raw_path\n            if self.url.raw_query_string:\n                path += \"?\" + self.url.raw_query_string\n\n        protocol = conn.protocol\n        assert protocol is not None\n        writer = StreamWriter(\n            protocol,\n            self.loop,\n            on_chunk_sent=(\n                functools.partial(self._on_chunk_request_sent, self.method, self.url)\n                if self._traces\n                else None\n            ),\n            on_headers_sent=(\n                functools.partial(self._on_headers_request_sent, self.method, self.url)\n                if self._traces\n                else None\n            ),\n        )\n\n        if self.compress:\n            writer.enable_compression(self.compress)  # type: ignore[arg-type]\n\n        if self.chunked is not None:\n            writer.enable_chunking()\n\n        # set default content-type\n        if (\n            self.method in self.POST_METHODS\n            and hdrs.CONTENT_TYPE not in self.skip_auto_headers\n            and hdrs.CONTENT_TYPE not in self.headers\n        ):\n            self.headers[hdrs.CONTENT_TYPE] = \"application/octet-stream\"\n\n        # set the connection header\n        connection = self.headers.get(hdrs.CONNECTION)\n        if not connection:\n            if self.keep_alive():\n                if self.version == HttpVersion10:\n                    connection = \"keep-alive\"\n            else:\n                if self.version == HttpVersion11:\n                    connection = \"close\"\n\n        if connection is not None:\n            self.headers[hdrs.CONNECTION] = connection\n\n        # status + headers\n        v = self.version\n        status_line = f\"{self.method} {path} HTTP/{v.major}.{v.minor}\"\n        await writer.write_headers(status_line, self.headers)\n        coro = self.write_bytes(writer, conn)\n\n        if sys.version_info >= (3, 12):\n            # Optimization for Python 3.12, try to write\n            # bytes immediately to avoid having to schedule\n            # the task on the event loop.\n            task = asyncio.Task(coro, loop=self.loop, eager_start=True)\n        else:\n            task = self.loop.create_task(coro)\n\n        self._writer = task\n        response_class = self.response_class\n        assert response_class is not None\n        self.response = response_class(\n            self.method,\n            self.original_url,\n            writer=self._writer,\n            continue100=self._continue,\n            timer=self._timer,\n            request_info=self.request_info,\n            traces=self._traces,\n            loop=self.loop,\n            session=self._session,\n        )\n        return self.response\n\n    async def close(self) -> None:\n        if self._writer is not None:\n            with contextlib.suppress(asyncio.CancelledError):\n                await self._writer\n\n    def terminate(self) -> None:\n        if self._writer is not None:\n            if not self.loop.is_closed():\n                self._writer.cancel()\n            self._writer.remove_done_callback(self.__reset_writer)\n            self._writer = None\n\n    async def _on_chunk_request_sent(self, method: str, url: URL, chunk: bytes) -> None:\n        for trace in self._traces:\n            await trace.send_request_chunk_sent(method, url, chunk)\n\n    async def _on_headers_request_sent(\n        self, method: str, url: URL, headers: \"CIMultiDict[str]\"\n    ) -> None:\n        for trace in self._traces:\n            await trace.send_request_headers(method, url, headers)\n\n\nclass ClientResponse(HeadersMixin):\n\n    # Some of these attributes are None when created,\n    # but will be set by the start() method.\n    # As the end user will likely never see the None values, we cheat the types below.\n    # from the Status-Line of the response\n    version: Optional[HttpVersion] = None  # HTTP-Version\n    status: int = None  # type: ignore[assignment] # Status-Code\n    reason: Optional[str] = None  # Reason-Phrase\n\n    content: StreamReader = None  # type: ignore[assignment]  # Payload stream\n    _headers: CIMultiDictProxy[str] = None  # type: ignore[assignment]\n    _raw_headers: RawHeaders = None  # type: ignore[assignment]\n\n    _connection = None  # current connection\n    _source_traceback: Optional[traceback.StackSummary] = None\n    # set up by ClientRequest after ClientResponse object creation\n    # post-init stage allows to not change ctor signature\n    _closed = True  # to allow __del__ for non-initialized properly response\n    _released = False\n    __writer = None\n\n    def __init__(\n        self,\n        method: str,\n        url: URL,\n        *,\n        writer: \"asyncio.Task[None]\",\n        continue100: Optional[\"asyncio.Future[bool]\"],\n        timer: BaseTimerContext,\n        request_info: RequestInfo,\n        traces: List[\"Trace\"],\n        loop: asyncio.AbstractEventLoop,\n        session: \"ClientSession\",\n    ) -> None:\n        assert isinstance(url, URL)\n\n        self.method = method\n        self.cookies = SimpleCookie()\n\n        self._real_url = url\n        self._url = url.with_fragment(None)\n        self._body: Any = None\n        self._writer: Optional[asyncio.Task[None]] = writer\n        self._continue = continue100  # None by default\n        self._closed = True\n        self._history: Tuple[ClientResponse, ...] = ()\n        self._request_info = request_info\n        self._timer = timer if timer is not None else TimerNoop()\n        self._cache: Dict[str, Any] = {}\n        self._traces = traces\n        self._loop = loop\n        # store a reference to session #1985\n        self._session: Optional[ClientSession] = session\n        # Save reference to _resolve_charset, so that get_encoding() will still\n        # work after the response has finished reading the body.\n        if session is None:\n            # TODO: Fix session=None in tests (see ClientRequest.__init__).\n            self._resolve_charset: Callable[[\"ClientResponse\", bytes], str] = (\n                lambda *_: \"utf-8\"\n            )\n        else:\n            self._resolve_charset = session._resolve_charset\n        if loop.get_debug():\n            self._source_traceback = traceback.extract_stack(sys._getframe(1))\n\n    def __reset_writer(self, _: object = None) -> None:\n        self.__writer = None\n\n    @property\n    def _writer(self) -> Optional[\"asyncio.Task[None]\"]:\n        return self.__writer\n\n    @_writer.setter\n    def _writer(self, writer: Optional[\"asyncio.Task[None]\"]) -> None:\n        if self.__writer is not None:\n            self.__writer.remove_done_callback(self.__reset_writer)\n        self.__writer = writer\n        if writer is None:\n            return\n        if writer.done():\n            # The writer is already done, so we can reset it immediately.\n            self.__reset_writer()\n        else:\n            writer.add_done_callback(self.__reset_writer)\n\n    @reify\n    def url(self) -> URL:\n        return self._url\n\n    @reify\n    def url_obj(self) -> URL:\n        warnings.warn(\"Deprecated, use .url #1654\", DeprecationWarning, stacklevel=2)\n        return self._url\n\n    @reify\n    def real_url(self) -> URL:\n        return self._real_url\n\n    @reify\n    def host(self) -> str:\n        assert self._url.host is not None\n        return self._url.host\n\n    @reify\n    def headers(self) -> \"CIMultiDictProxy[str]\":\n        return self._headers\n\n    @reify\n    def raw_headers(self) -> RawHeaders:\n        return self._raw_headers\n\n    @reify\n    def request_info(self) -> RequestInfo:\n        return self._request_info\n\n    @reify\n    def content_disposition(self) -> Optional[ContentDisposition]:\n        raw = self._headers.get(hdrs.CONTENT_DISPOSITION)\n        if raw is None:\n            return None\n        disposition_type, params_dct = multipart.parse_content_disposition(raw)\n        params = MappingProxyType(params_dct)\n        filename = multipart.content_disposition_filename(params)\n        return ContentDisposition(disposition_type, params, filename)\n\n    def __del__(self, _warnings: Any = warnings) -> None:\n        if self._closed:\n            return\n\n        if self._connection is not None:\n            self._connection.release()\n            self._cleanup_writer()\n\n            if self._loop.get_debug():\n                kwargs = {\"source\": self}\n                _warnings.warn(f\"Unclosed response {self!r}\", ResourceWarning, **kwargs)\n                context = {\"client_response\": self, \"message\": \"Unclosed response\"}\n                if self._source_traceback:\n                    context[\"source_traceback\"] = self._source_traceback\n                self._loop.call_exception_handler(context)\n\n    def __repr__(self) -> str:\n        out = io.StringIO()\n        ascii_encodable_url = str(self.url)\n        if self.reason:\n            ascii_encodable_reason = self.reason.encode(\n                \"ascii\", \"backslashreplace\"\n            ).decode(\"ascii\")\n        else:\n            ascii_encodable_reason = \"None\"\n        print(\n            \"<ClientResponse({}) [{} {}]>\".format(\n                ascii_encodable_url, self.status, ascii_encodable_reason\n            ),\n            file=out,\n        )\n        print(self.headers, file=out)\n        return out.getvalue()\n\n    @property\n    def connection(self) -> Optional[\"Connection\"]:\n        return self._connection\n\n    @reify\n    def history(self) -> Tuple[\"ClientResponse\", ...]:\n        \"\"\"A sequence of of responses, if redirects occurred.\"\"\"\n        return self._history\n\n    @reify\n    def links(self) -> \"MultiDictProxy[MultiDictProxy[Union[str, URL]]]\":\n        links_str = \", \".join(self.headers.getall(\"link\", []))\n\n        if not links_str:\n            return MultiDictProxy(MultiDict())\n\n        links: MultiDict[MultiDictProxy[Union[str, URL]]] = MultiDict()\n\n        for val in re.split(r\",(?=\\s*<)\", links_str):\n            match = re.match(r\"\\s*<(.*)>(.*)\", val)\n            if match is None:  # pragma: no cover\n                # the check exists to suppress mypy error\n                continue\n            url, params_str = match.groups()\n            params = params_str.split(\";\")[1:]\n\n            link: MultiDict[Union[str, URL]] = MultiDict()\n\n            for param in params:\n                match = re.match(r\"^\\s*(\\S*)\\s*=\\s*(['\\\"]?)(.*?)(\\2)\\s*$\", param, re.M)\n                if match is None:  # pragma: no cover\n                    # the check exists to suppress mypy error\n                    continue\n                key, _, value, _ = match.groups()\n\n                link.add(key, value)\n\n            key = link.get(\"rel\", url)\n\n            link.add(\"url\", self.url.join(URL(url)))\n\n            links.add(str(key), MultiDictProxy(link))\n\n        return MultiDictProxy(links)\n\n    async def start(self, connection: \"Connection\") -> \"ClientResponse\":\n        \"\"\"Start response processing.\"\"\"\n        self._closed = False\n        self._protocol = connection.protocol\n        self._connection = connection\n\n        with self._timer:\n            while True:\n                # read response\n                try:\n                    protocol = self._protocol\n                    message, payload = await protocol.read()  # type: ignore[union-attr]\n                except http.HttpProcessingError as exc:\n                    raise ClientResponseError(\n                        self.request_info,\n                        self.history,\n                        status=exc.code,\n                        message=exc.message,\n                        headers=exc.headers,\n                    ) from exc\n\n                if message.code < 100 or message.code > 199 or message.code == 101:\n                    break\n\n                if self._continue is not None:\n                    set_result(self._continue, True)\n                    self._continue = None\n\n        # payload eof handler\n        payload.on_eof(self._response_eof)\n\n        # response status\n        self.version = message.version\n        self.status = message.code\n        self.reason = message.reason\n\n        # headers\n        self._headers = message.headers  # type is CIMultiDictProxy\n        self._raw_headers = message.raw_headers  # type is Tuple[bytes, bytes]\n\n        # payload\n        self.content = payload\n\n        # cookies\n        for hdr in self.headers.getall(hdrs.SET_COOKIE, ()):\n            try:\n                self.cookies.load(hdr)\n            except CookieError as exc:\n                client_logger.warning(\"Can not load response cookies: %s\", exc)\n        return self\n\n    def _response_eof(self) -> None:\n        if self._closed:\n            return\n\n        # protocol could be None because connection could be detached\n        protocol = self._connection and self._connection.protocol\n        if protocol is not None and protocol.upgraded:\n            return\n\n        self._closed = True\n        self._cleanup_writer()\n        self._release_connection()\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    def close(self) -> None:\n        if not self._released:\n            self._notify_content()\n\n        self._closed = True\n        if self._loop is None or self._loop.is_closed():\n            return\n\n        self._cleanup_writer()\n        if self._connection is not None:\n            self._connection.close()\n            self._connection = None\n\n    def release(self) -> Any:\n        if not self._released:\n            self._notify_content()\n\n        self._closed = True\n\n        self._cleanup_writer()\n        self._release_connection()\n        return noop()\n\n    @property\n    def ok(self) -> bool:\n        \"\"\"Returns ``True`` if ``status`` is less than ``400``, ``False`` if not.\n\n        This is **not** a check for ``200 OK`` but a check that the response\n        status is under 400.\n        \"\"\"\n        return 400 > self.status\n\n    def raise_for_status(self) -> None:\n        if not self.ok:\n            # reason should always be not None for a started response\n            assert self.reason is not None\n            self.release()\n            raise ClientResponseError(\n                self.request_info,\n                self.history,\n                status=self.status,\n                message=self.reason,\n                headers=self.headers,\n            )\n\n    def _release_connection(self) -> None:\n        if self._connection is not None:\n            if self._writer is None:\n                self._connection.release()\n                self._connection = None\n            else:\n                self._writer.add_done_callback(lambda f: self._release_connection())\n\n    async def _wait_released(self) -> None:\n        if self._writer is not None:\n            await self._writer\n        self._release_connection()\n\n    def _cleanup_writer(self) -> None:\n        if self._writer is not None:\n            self._writer.cancel()\n        self._session = None\n\n    def _notify_content(self) -> None:\n        content = self.content\n        if content and content.exception() is None:\n            set_exception(content, ClientConnectionError(\"Connection closed\"))\n        self._released = True\n\n    async def wait_for_close(self) -> None:\n        if self._writer is not None:\n            await self._writer\n        self.release()\n\n    async def read(self) -> bytes:\n        \"\"\"Read response payload.\"\"\"\n        if self._body is None:\n            try:\n                self._body = await self.content.read()\n                for trace in self._traces:\n                    await trace.send_response_chunk_received(\n                        self.method, self.url, self._body\n                    )\n            except BaseException:\n                self.close()\n                raise\n        elif self._released:  # Response explicitly released\n            raise ClientConnectionError(\"Connection closed\")\n\n        protocol = self._connection and self._connection.protocol\n        if protocol is None or not protocol.upgraded:\n            await self._wait_released()  # Underlying connection released\n        return self._body  # type: ignore[no-any-return]\n\n    def get_encoding(self) -> str:\n        ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n        mimetype = helpers.parse_mimetype(ctype)\n\n        encoding = mimetype.parameters.get(\"charset\")\n        if encoding:\n            with contextlib.suppress(LookupError):\n                return codecs.lookup(encoding).name\n\n        if mimetype.type == \"application\" and (\n            mimetype.subtype == \"json\" or mimetype.subtype == \"rdap\"\n        ):\n            # RFC 7159 states that the default encoding is UTF-8.\n            # RFC 7483 defines application/rdap+json\n            return \"utf-8\"\n\n        if self._body is None:\n            raise RuntimeError(\n                \"Cannot compute fallback encoding of a not yet read body\"\n            )\n\n        return self._resolve_charset(self, self._body)\n\n    async def text(self, encoding: Optional[str] = None, errors: str = \"strict\") -> str:\n        \"\"\"Read response payload and decode.\"\"\"\n        if self._body is None:\n            await self.read()\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return self._body.decode(  # type: ignore[no-any-return,union-attr]\n            encoding, errors=errors\n        )\n\n    async def json(\n        self,\n        *,\n        encoding: Optional[str] = None,\n        loads: JSONDecoder = DEFAULT_JSON_DECODER,\n        content_type: Optional[str] = \"application/json\",\n    ) -> Any:\n        \"\"\"Read and decodes JSON response.\"\"\"\n        if self._body is None:\n            await self.read()\n\n        if content_type:\n            ctype = self.headers.get(hdrs.CONTENT_TYPE, \"\").lower()\n            if not _is_expected_content_type(ctype, content_type):\n                raise ContentTypeError(\n                    self.request_info,\n                    self.history,\n                    status=self.status,\n                    message=(\n                        \"Attempt to decode JSON with \" \"unexpected mimetype: %s\" % ctype\n                    ),\n                    headers=self.headers,\n                )\n\n        stripped = self._body.strip()  # type: ignore[union-attr]\n        if not stripped:\n            return None\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return loads(stripped.decode(encoding))\n\n    async def __aenter__(self) -> \"ClientResponse\":\n        return self\n\n    async def __aexit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n        # similar to _RequestContextManager, we do not need to check\n        # for exceptions, response object can close connection\n        # if state is broken\n        self.release()\n        await self.wait_for_close()\n"}
A user will ask you to solve a task. You should first draft your thinking process (inner monologue). Then, generate the solution.

Your response format must follow the template below:
<think>
Your thoughts or/and draft, like working through an exercise on scratch paper. Be as casual and as long as you want until you are confident to generate a correct solution.
</think>
<solution>
Final solution presented to the user.
</solution>
system
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** Malformed charset in Content-Type header causes UnicodeEncodeError

**Description:**
When a response includes a `Content-Type` header with an improperly encoded `charset` parameter, invoking the method to retrieve the encoding results in a crash instead of handling the error gracefully.

**Example Code:**
```python
from aiohttp import ClientResponse
from multidict import CIMultiDict, CIMultiDictProxy
from yarl import URL
import asyncio
from unittest import mock

async def example():
    loop = asyncio.get_event_loop()
    session = mock.Mock()
    session._resolve_charset = lambda *_: "utf-8"
    response = ClientResponse(
        "get",
        URL("http://example.com"),
        request_info=mock.Mock(),
        writer=mock.Mock(),
        continue100=None,
        timer=mock.Mock(),
        traces=[],
        loop=loop,
        session=session,
    )
    
    # Malformed charset
    headers = {"Content-Type": "text/html; charset=\udc81gutf-8\udc81\udc8d"}
    response._headers = CIMultiDictProxy(CIMultiDict(headers))
    response.content = mock.Mock()
    response.content.read = mock.AsyncMock(return_value=b"foo")
    
    await response.read()
    encoding = response.get_encoding()
    print(encoding)
```

**Expected Behavior:**
The `get_encoding()` method should handle the malformed `charset` parameter gracefully, defaulting to a safe encoding like `"utf-8"` without raising an error.

**Actual Behavior:**
Invoking `get_encoding()` raises a `UnicodeEncodeError` due to the invalid characters in the `charset` parameter.

**Error Message:**
```
UnicodeEncodeError: 'utf-8' codec can't encode character '\udc81' in position 0: surrogates not allowed
```
[/ISSUE]
--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.

--- BEGIN FILE ---
```
### aiohttp/client_reqrep.py
import asyncio
import codecs
import contextlib
import functools
import io
import re
import sys
import traceback
import warnings
from hashlib import md5, sha1, sha256
from http.cookies import CookieError, Morsel, SimpleCookie
from types import MappingProxyType, TracebackType
from typing import (
    TYPE_CHECKING,
    Any,
    Callable,
    Dict,
    Iterable,
    List,
    Mapping,
    Optional,
    Tuple,
    Type,
    Union,
    cast,
)

import attr
from multidict import CIMultiDict, CIMultiDictProxy, MultiDict, MultiDictProxy
from yarl import URL, __version__ as yarl_version

from . import hdrs, helpers, http, multipart, payload
from .abc import AbstractStreamWriter
from .client_exceptions import (
    ClientConnectionError,
    ClientOSError,
    ClientResponseError,
    ContentTypeError,
    InvalidURL,
    ServerFingerprintMismatch,
)
from .compression_utils import HAS_BROTLI
from .formdata import FormData
from .helpers import (
    BaseTimerContext,
    BasicAuth,
    HeadersMixin,
    TimerNoop,
    basicauth_from_netrc,
    netrc_from_env,
    noop,
    reify,
    set_exception,
    set_result,
)
from .http import (
    SERVER_SOFTWARE,
    HttpVersion,
    HttpVersion10,
    HttpVersion11,
    StreamWriter,
)
from .log import client_logger
from .streams import StreamReader
from .typedefs import (
    DEFAULT_JSON_DECODER,
    JSONDecoder,
    LooseCookies,
    LooseHeaders,
    Query,
    RawHeaders,
)

try:
    import ssl
    from ssl import SSLContext
except ImportError:  # pragma: no cover
    ssl = None  # type: ignore[assignment]
    SSLContext = object  # type: ignore[misc,assignment]


__all__ = ("ClientRequest", "ClientResponse", "RequestInfo", "Fingerprint")


if TYPE_CHECKING:
    from .client import ClientSession
    from .connector import Connection
    from .tracing import Trace


_CONTAINS_CONTROL_CHAR_RE = re.compile(r"[^-!#$%&'*+.^_`|~0-9a-zA-Z]")
_YARL_SUPPORTS_EXTEND_QUERY = tuple(map(int, yarl_version.split(".")[:2])) >= (1, 11)
json_re = re.compile(r"^application/(?:[\w.+-]+?\+)?json")


def _gen_default_accept_encoding() -> str:
    return "gzip, deflate, br" if HAS_BROTLI else "gzip, deflate"


@attr.s(auto_attribs=True, frozen=True, slots=True)
class ContentDisposition:
    type: Optional[str]
    parameters: "MappingProxyType[str, str]"
    filename: Optional[str]


@attr.s(auto_attribs=True, frozen=True, slots=True)
class RequestInfo:
    url: URL
    method: str
    headers: "CIMultiDictProxy[str]"
    real_url: URL = attr.ib()

    @real_url.default
    def real_url_default(self) -> URL:
        return self.url


class Fingerprint:
    HASHFUNC_BY_DIGESTLEN = {
        16: md5,
        20: sha1,
        32: sha256,
    }

    def __init__(self, fingerprint: bytes) -> None:
        digestlen = len(fingerprint)
        hashfunc = self.HASHFUNC_BY_DIGESTLEN.get(digestlen)
        if not hashfunc:
            raise ValueError("fingerprint has invalid length")
        elif hashfunc is md5 or hashfunc is sha1:
            raise ValueError(
                "md5 and sha1 are insecure and " "not supported. Use sha256."
            )
        self._hashfunc = hashfunc
        self._fingerprint = fingerprint

    @property
    def fingerprint(self) -> bytes:
        return self._fingerprint

    def check(self, transport: asyncio.Transport) -> None:
        if not transport.get_extra_info("sslcontext"):
            return
        sslobj = transport.get_extra_info("ssl_object")
        cert = sslobj.getpeercert(binary_form=True)
        got = self._hashfunc(cert).digest()
        if got != self._fingerprint:
            host, port, *_ = transport.get_extra_info("peername")
            raise ServerFingerprintMismatch(self._fingerprint, got, host, port)


if ssl is not None:
    SSL_ALLOWED_TYPES = (ssl.SSLContext, bool, Fingerprint, type(None))
else:  # pragma: no cover
    SSL_ALLOWED_TYPES = (bool, type(None))


def _merge_ssl_params(
    ssl: Union["SSLContext", bool, Fingerprint],
    verify_ssl: Optional[bool],
    ssl_context: Optional["SSLContext"],
    fingerprint: Optional[bytes],
) -> Union["SSLContext", bool, Fingerprint]:
    if ssl is None:
        ssl = True  # Double check for backwards compatibility
    if verify_ssl is not None and not verify_ssl:
        warnings.warn(
            "verify_ssl is deprecated, use ssl=False instead",
            DeprecationWarning,
            stacklevel=3,
        )
        if ssl is not True:
            raise ValueError(
                "verify_ssl, ssl_context, fingerprint and ssl "
                "parameters are mutually exclusive"
            )
        else:
            ssl = False
    if ssl_context is not None:
        warnings.warn(
            "ssl_context is deprecated, use ssl=context instead",
            DeprecationWarning,
            stacklevel=3,
        )
        if ssl is not True:
            raise ValueError(
                "verify_ssl, ssl_context, fingerprint and ssl "
                "parameters are mutually exclusive"
            )
        else:
            ssl = ssl_context
    if fingerprint is not None:
        warnings.warn(
            "fingerprint is deprecated, " "use ssl=Fingerprint(fingerprint) instead",
            DeprecationWarning,
            stacklevel=3,
        )
        if ssl is not True:
            raise ValueError(
                "verify_ssl, ssl_context, fingerprint and ssl "
                "parameters are mutually exclusive"
            )
        else:
            ssl = Fingerprint(fingerprint)
    if not isinstance(ssl, SSL_ALLOWED_TYPES):
        raise TypeError(
            "ssl should be SSLContext, bool, Fingerprint or None, "
            "got {!r} instead.".format(ssl)
        )
    return ssl


@attr.s(auto_attribs=True, slots=True, frozen=True, cache_hash=True)
class ConnectionKey:
    # the key should contain an information about used proxy / TLS
    # to prevent reusing wrong connections from a pool
    host: str
    port: Optional[int]
    is_ssl: bool
    ssl: Union[SSLContext, bool, Fingerprint]
    proxy: Optional[URL]
    proxy_auth: Optional[BasicAuth]
    proxy_headers_hash: Optional[int]  # hash(CIMultiDict)


def _is_expected_content_type(
    response_content_type: str, expected_content_type: str
) -> bool:
    if expected_content_type == "application/json":
        return json_re.match(response_content_type) is not None
    return expected_content_type in response_content_type


class ClientRequest:
    GET_METHODS = {
        hdrs.METH_GET,
        hdrs.METH_HEAD,
        hdrs.METH_OPTIONS,
        hdrs.METH_TRACE,
    }
    POST_METHODS = {hdrs.METH_PATCH, hdrs.METH_POST, hdrs.METH_PUT}
    ALL_METHODS = GET_METHODS.union(POST_METHODS).union({hdrs.METH_DELETE})

    DEFAULT_HEADERS = {
        hdrs.ACCEPT: "*/*",
        hdrs.ACCEPT_ENCODING: _gen_default_accept_encoding(),
    }

    # Type of body depends on PAYLOAD_REGISTRY, which is dynamic.
    body: Any = b""
    auth = None
    response = None

    __writer = None  # async task for streaming data
    _continue = None  # waiter future for '100 Continue' response

    # N.B.
    # Adding __del__ method with self._writer closing doesn't make sense
    # because _writer is instance method, thus it keeps a reference to self.
    # Until writer has finished finalizer will not be called.

    def __init__(
        self,
        method: str,
        url: URL,
        *,
        params: Query = None,
        headers: Optional[LooseHeaders] = None,
        skip_auto_headers: Optional[Iterable[str]] = None,
        data: Any = None,
        cookies: Optional[LooseCookies] = None,
        auth: Optional[BasicAuth] = None,
        version: http.HttpVersion = http.HttpVersion11,
        compress: Union[str, bool, None] = None,
        chunked: Optional[bool] = None,
        expect100: bool = False,
        loop: Optional[asyncio.AbstractEventLoop] = None,
        response_class: Optional[Type["ClientResponse"]] = None,
        proxy: Optional[URL] = None,
        proxy_auth: Optional[BasicAuth] = None,
        timer: Optional[BaseTimerContext] = None,
        session: Optional["ClientSession"] = None,
        ssl: Union[SSLContext, bool, Fingerprint] = True,
        proxy_headers: Optional[LooseHeaders] = None,
        traces: Optional[List["Trace"]] = None,
        trust_env: bool = False,
        server_hostname: Optional[str] = None,
    ):
        if loop is None:
            loop = asyncio.get_event_loop()

        match = _CONTAINS_CONTROL_CHAR_RE.search(method)
        if match:
            raise ValueError(
                f"Method cannot contain non-token characters {method!r} "
                "(found at least {match.group()!r})"
            )

        assert isinstance(url, URL), url
        assert isinstance(proxy, (URL, type(None))), proxy
        # FIXME: session is None in tests only, need to fix tests
        # assert session is not None
        self._session = cast("ClientSession", session)
        if params:
            if _YARL_SUPPORTS_EXTEND_QUERY:
                url = url.extend_query(params)
            else:
                q = MultiDict(url.query)
                url2 = url.with_query(params)
                q.extend(url2.query)
                url = url.with_query(q)
        self.original_url = url
        self.url = url.with_fragment(None)
        self.method = method.upper()
        self.chunked = chunked
        self.compress = compress
        self.loop = loop
        self.length = None
        if response_class is None:
            real_response_class = ClientResponse
        else:
            real_response_class = response_class
        self.response_class: Type[ClientResponse] = real_response_class
        self._timer = timer if timer is not None else TimerNoop()
        self._ssl = ssl if ssl is not None else True
        self.server_hostname = server_hostname

        if loop.get_debug():
            self._source_traceback = traceback.extract_stack(sys._getframe(1))

        self.update_version(version)
        self.update_host(url)
        self.update_headers(headers)
        self.update_auto_headers(skip_auto_headers)
        self.update_cookies(cookies)
        self.update_content_encoding(data)
        self.update_auth(auth, trust_env)
        self.update_proxy(proxy, proxy_auth, proxy_headers)

        self.update_body_from_data(data)
        if data is not None or self.method not in self.GET_METHODS:
            self.update_transfer_encoding()
        self.update_expect_continue(expect100)
        if traces is None:
            traces = []
        self._traces = traces

    def __reset_writer(self, _: object = None) -> None:
        self.__writer = None

    @property
    def _writer(self) -> Optional["asyncio.Task[None]"]:
        return self.__writer

    @_writer.setter
    def _writer(self, writer: Optional["asyncio.Task[None]"]) -> None:
        if self.__writer is not None:
            self.__writer.remove_done_callback(self.__reset_writer)
        self.__writer = writer
        if writer is None:
            return
        if writer.done():
            # The writer is already done, so we can reset it immediately.
            self.__reset_writer()
        else:
            writer.add_done_callback(self.__reset_writer)

    def is_ssl(self) -> bool:
        return self.url.scheme in ("https", "wss")

    @property
    def ssl(self) -> Union["SSLContext", bool, Fingerprint]:
        return self._ssl

    @property
    def connection_key(self) -> ConnectionKey:
        proxy_headers = self.proxy_headers
        if proxy_headers:
            h: Optional[int] = hash(tuple((k, v) for k, v in proxy_headers.items()))
        else:
            h = None
        return ConnectionKey(
            self.host,
            self.port,
            self.is_ssl(),
            self.ssl,
            self.proxy,
            self.proxy_auth,
            h,
        )

    @property
    def host(self) -> str:
        ret = self.url.raw_host
        assert ret is not None
        return ret

    @property
    def port(self) -> Optional[int]:
        return self.url.port

    @property
    def request_info(self) -> RequestInfo:
        headers: CIMultiDictProxy[str] = CIMultiDictProxy(self.headers)
        return RequestInfo(self.url, self.method, headers, self.original_url)

    def update_host(self, url: URL) -> None:
        """Update destination host, port and connection type (ssl)."""
        # get host/port
        if not url.raw_host:
            raise InvalidURL(url)

        # basic auth info
        username, password = url.user, url.password
        if username or password:
            self.auth = helpers.BasicAuth(username or "", password or "")

    def update_version(self, version: Union[http.HttpVersion, str]) -> None:
        """Convert request version to two elements tuple.

        parser HTTP version '1.1' => (1, 1)
        """
        if isinstance(version, str):
            v = [part.strip() for part in version.split(".", 1)]
            try:
                version = http.HttpVersion(int(v[0]), int(v[1]))
            except ValueError:
                raise ValueError(
                    f"Can not parse http version number: {version}"
                ) from None
        self.version = version

    def update_headers(self, headers: Optional[LooseHeaders]) -> None:
        """Update request headers."""
        self.headers: CIMultiDict[str] = CIMultiDict()

        # add host
        netloc = cast(str, self.url.raw_host)
        if helpers.is_ipv6_address(netloc):
            netloc = f"[{netloc}]"
        # See https://github.com/aio-libs/aiohttp/issues/3636.
        netloc = netloc.rstrip(".")
        if self.url.port is not None and not self.url.is_default_port():
            netloc += ":" + str(self.url.port)
        self.headers[hdrs.HOST] = netloc

        if headers:
            if isinstance(headers, (dict, MultiDictProxy, MultiDict)):
                headers = headers.items()

            for key, value in headers:  # type: ignore[misc]
                # A special case for Host header
                if key.lower() == "host":
                    self.headers[key] = value
                else:
                    self.headers.add(key, value)

    def update_auto_headers(self, skip_auto_headers: Optional[Iterable[str]]) -> None:
        if skip_auto_headers is not None:
            self.skip_auto_headers = CIMultiDict(
                (hdr, None) for hdr in sorted(skip_auto_headers)
            )
            used_headers = self.headers.copy()
            used_headers.extend(self.skip_auto_headers)  # type: ignore[arg-type]
        else:
            # Fast path when there are no headers to skip
            # which is the most common case.
            self.skip_auto_headers = CIMultiDict()
            used_headers = self.headers

        for hdr, val in self.DEFAULT_HEADERS.items():
            if hdr not in used_headers:
                self.headers.add(hdr, val)

        if hdrs.USER_AGENT not in used_headers:
            self.headers[hdrs.USER_AGENT] = SERVER_SOFTWARE

    def update_cookies(self, cookies: Optional[LooseCookies]) -> None:
        """Update request cookies header."""
        if not cookies:
            return

        c = SimpleCookie()
        if hdrs.COOKIE in self.headers:
            c.load(self.headers.get(hdrs.COOKIE, ""))
            del self.headers[hdrs.COOKIE]

        if isinstance(cookies, Mapping):
            iter_cookies = cookies.items()
        else:
            iter_cookies = cookies  # type: ignore[assignment]
        for name, value in iter_cookies:
            if isinstance(value, Morsel):
                # Preserve coded_value
                mrsl_val = value.get(value.key, Morsel())
                mrsl_val.set(value.key, value.value, value.coded_value)
                c[name] = mrsl_val
            else:
                c[name] = value  # type: ignore[assignment]

        self.headers[hdrs.COOKIE] = c.output(header="", sep=";").strip()

    def update_content_encoding(self, data: Any) -> None:
        """Set request content encoding."""
        if not data:
            # Don't compress an empty body.
            self.compress = None
            return

        enc = self.headers.get(hdrs.CONTENT_ENCODING, "").lower()
        if enc:
            if self.compress:
                raise ValueError(
                    "compress can not be set " "if Content-Encoding header is set"
                )
        elif self.compress:
            if not isinstance(self.compress, str):
                self.compress = "deflate"
            self.headers[hdrs.CONTENT_ENCODING] = self.compress
            self.chunked = True  # enable chunked, no need to deal with length

    def update_transfer_encoding(self) -> None:
        """Analyze transfer-encoding header."""
        te = self.headers.get(hdrs.TRANSFER_ENCODING, "").lower()

        if "chunked" in te:
            if self.chunked:
                raise ValueError(
                    "chunked can not be set "
                    'if "Transfer-Encoding: chunked" header is set'
                )

        elif self.chunked:
            if hdrs.CONTENT_LENGTH in self.headers:
                raise ValueError(
                    "chunked can not be set " "if Content-Length header is set"
                )

            self.headers[hdrs.TRANSFER_ENCODING] = "chunked"
        else:
            if hdrs.CONTENT_LENGTH not in self.headers:
                self.headers[hdrs.CONTENT_LENGTH] = str(len(self.body))

    def update_auth(self, auth: Optional[BasicAuth], trust_env: bool = False) -> None:
        """Set basic auth."""
        if auth is None:
            auth = self.auth
        if auth is None and trust_env and self.url.host is not None:
            netrc_obj = netrc_from_env()
            with contextlib.suppress(LookupError):
                auth = basicauth_from_netrc(netrc_obj, self.url.host)
        if auth is None:
            return

        if not isinstance(auth, helpers.BasicAuth):
            raise TypeError("BasicAuth() tuple is required instead")

        self.headers[hdrs.AUTHORIZATION] = auth.encode()

    def update_body_from_data(self, body: Any) -> None:
        if body is None:
            return

        # FormData
        if isinstance(body, FormData):
            body = body()

        try:
            body = payload.PAYLOAD_REGISTRY.get(body, disposition=None)
        except payload.LookupError:
            body = FormData(body)()

        self.body = body

        # enable chunked encoding if needed
        if not self.chunked:
            if hdrs.CONTENT_LENGTH not in self.headers:
                size = body.size
                if size is None:
                    self.chunked = True
                else:
                    if hdrs.CONTENT_LENGTH not in self.headers:
                        self.headers[hdrs.CONTENT_LENGTH] = str(size)

        # copy payload headers
        assert body.headers
        for key, value in body.headers.items():
            if key in self.headers or key in self.skip_auto_headers:
                continue
            self.headers[key] = value

    def update_expect_continue(self, expect: bool = False) -> None:
        if expect:
            self.headers[hdrs.EXPECT] = "100-continue"
        elif self.headers.get(hdrs.EXPECT, "").lower() == "100-continue":
            expect = True

        if expect:
            self._continue = self.loop.create_future()

    def update_proxy(
        self,
        proxy: Optional[URL],
        proxy_auth: Optional[BasicAuth],
        proxy_headers: Optional[LooseHeaders],
    ) -> None:
        if proxy_auth and not isinstance(proxy_auth, helpers.BasicAuth):
            raise ValueError("proxy_auth must be None or BasicAuth() tuple")
        self.proxy = proxy
        self.proxy_auth = proxy_auth
        if proxy_headers is not None and not isinstance(
            proxy_headers, (MultiDict, MultiDictProxy)
        ):
            proxy_headers = CIMultiDict(proxy_headers)
        self.proxy_headers = proxy_headers

    def keep_alive(self) -> bool:
        if self.version < HttpVersion10:
            # keep alive not supported at all
            return False
        if self.version == HttpVersion10:
            if self.headers.get(hdrs.CONNECTION) == "keep-alive":
                return True
            else:  # no headers means we close for Http 1.0
                return False
        elif self.headers.get(hdrs.CONNECTION) == "close":
            return False

        return True

    async def write_bytes(
        self, writer: AbstractStreamWriter, conn: "Connection"
    ) -> None:
        """Support coroutines that yields bytes objects."""
        # 100 response
        if self._continue is not None:
            try:
                await writer.drain()
                await self._continue
            except asyncio.CancelledError:
                return

        protocol = conn.protocol
        assert protocol is not None
        try:
            if isinstance(self.body, payload.Payload):
                await self.body.write(writer)
            else:
                if isinstance(self.body, (bytes, bytearray)):
                    self.body = (self.body,)

                for chunk in self.body:
                    await writer.write(chunk)
        except OSError as underlying_exc:
            reraised_exc = underlying_exc

            exc_is_not_timeout = underlying_exc.errno is not None or not isinstance(
                underlying_exc, asyncio.TimeoutError
            )
            if exc_is_not_timeout:
                reraised_exc = ClientOSError(
                    underlying_exc.errno,
                    f"Can not write request body for {self.url !s}",
                )

            set_exception(protocol, reraised_exc, underlying_exc)
        except asyncio.CancelledError:
            # Body hasn't been fully sent, so connection can't be reused.
            conn.close()
        except Exception as underlying_exc:
            set_exception(
                protocol,
                ClientConnectionError(
                    f"Failed to send bytes into the underlying connection {conn !s}",
                ),
                underlying_exc,
            )
        else:
            await writer.write_eof()
            protocol.start_timeout()

    async def send(self, conn: "Connection") -> "ClientResponse":
        # Specify request target:
        # - CONNECT request must send authority form URI
        # - not CONNECT proxy must send absolute form URI
        # - most common is origin form URI
        if self.method == hdrs.METH_CONNECT:
            connect_host = self.url.raw_host
            assert connect_host is not None
            if helpers.is_ipv6_address(connect_host):
                connect_host = f"[{connect_host}]"
            path = f"{connect_host}:{self.url.port}"
        elif self.proxy and not self.is_ssl():
            path = str(self.url)
        else:
            path = self.url.raw_path
            if self.url.raw_query_string:
                path += "?" + self.url.raw_query_string

        protocol = conn.protocol
        assert protocol is not None
        writer = StreamWriter(
            protocol,
            self.loop,
            on_chunk_sent=(
                functools.partial(self._on_chunk_request_sent, self.method, self.url)
                if self._traces
                else None
            ),
            on_headers_sent=(
                functools.partial(self._on_headers_request_sent, self.method, self.url)
                if self._traces
                else None
            ),
        )

        if self.compress:
            writer.enable_compression(self.compress)  # type: ignore[arg-type]

        if self.chunked is not None:
            writer.enable_chunking()

        # set default content-type
        if (
            self.method in self.POST_METHODS
            and hdrs.CONTENT_TYPE not in self.skip_auto_headers
            and hdrs.CONTENT_TYPE not in self.headers
        ):
            self.headers[hdrs.CONTENT_TYPE] = "application/octet-stream"

        # set the connection header
        connection = self.headers.get(hdrs.CONNECTION)
        if not connection:
            if self.keep_alive():
                if self.version == HttpVersion10:
                    connection = "keep-alive"
            else:
                if self.version == HttpVersion11:
                    connection = "close"

        if connection is not None:
            self.headers[hdrs.CONNECTION] = connection

        # status + headers
        v = self.version
        status_line = f"{self.method} {path} HTTP/{v.major}.{v.minor}"
        await writer.write_headers(status_line, self.headers)
        coro = self.write_bytes(writer, conn)

        if sys.version_info >= (3, 12):
            # Optimization for Python 3.12, try to write
            # bytes immediately to avoid having to schedule
            # the task on the event loop.
            task = asyncio.Task(coro, loop=self.loop, eager_start=True)
        else:
            task = self.loop.create_task(coro)

        self._writer = task
        response_class = self.response_class
        assert response_class is not None
        self.response = response_class(
            self.method,
            self.original_url,
            writer=self._writer,
            continue100=self._continue,
            timer=self._timer,
            request_info=self.request_info,
            traces=self._traces,
            loop=self.loop,
            session=self._session,
        )
        return self.response

    async def close(self) -> None:
        if self._writer is not None:
            with contextlib.suppress(asyncio.CancelledError):
                await self._writer

    def terminate(self) -> None:
        if self._writer is not None:
            if not self.loop.is_closed():
                self._writer.cancel()
            self._writer.remove_done_callback(self.__reset_writer)
            self._writer = None

    async def _on_chunk_request_sent(self, method: str, url: URL, chunk: bytes) -> None:
        for trace in self._traces:
            await trace.send_request_chunk_sent(method, url, chunk)

    async def _on_headers_request_sent(
        self, method: str, url: URL, headers: "CIMultiDict[str]"
    ) -> None:
        for trace in self._traces:
            await trace.send_request_headers(method, url, headers)


class ClientResponse(HeadersMixin):

    # Some of these attributes are None when created,
    # but will be set by the start() method.
    # As the end user will likely never see the None values, we cheat the types below.
    # from the Status-Line of the response
    version: Optional[HttpVersion] = None  # HTTP-Version
    status: int = None  # type: ignore[assignment] # Status-Code
    reason: Optional[str] = None  # Reason-Phrase

    content: StreamReader = None  # type: ignore[assignment]  # Payload stream
    _headers: CIMultiDictProxy[str] = None  # type: ignore[assignment]
    _raw_headers: RawHeaders = None  # type: ignore[assignment]

    _connection = None  # current connection
    _source_traceback: Optional[traceback.StackSummary] = None
    # set up by ClientRequest after ClientResponse object creation
    # post-init stage allows to not change ctor signature
    _closed = True  # to allow __del__ for non-initialized properly response
    _released = False
    __writer = None

    def __init__(
        self,
        method: str,
        url: URL,
        *,
        writer: "asyncio.Task[None]",
        continue100: Optional["asyncio.Future[bool]"],
        timer: BaseTimerContext,
        request_info: RequestInfo,
        traces: List["Trace"],
        loop: asyncio.AbstractEventLoop,
        session: "ClientSession",
    ) -> None:
        assert isinstance(url, URL)

        self.method = method
        self.cookies = SimpleCookie()

        self._real_url = url
        self._url = url.with_fragment(None)
        self._body: Any = None
        self._writer: Optional[asyncio.Task[None]] = writer
        self._continue = continue100  # None by default
        self._closed = True
        self._history: Tuple[ClientResponse, ...] = ()
        self._request_info = request_info
        self._timer = timer if timer is not None else TimerNoop()
        self._cache: Dict[str, Any] = {}
        self._traces = traces
        self._loop = loop
        # store a reference to session #1985
        self._session: Optional[ClientSession] = session
        # Save reference to _resolve_charset, so that get_encoding() will still
        # work after the response has finished reading the body.
        if session is None:
            # TODO: Fix session=None in tests (see ClientRequest.__init__).
            self._resolve_charset: Callable[["ClientResponse", bytes], str] = (
                lambda *_: "utf-8"
            )
        else:
            self._resolve_charset = session._resolve_charset
        if loop.get_debug():
            self._source_traceback = traceback.extract_stack(sys._getframe(1))

    def __reset_writer(self, _: object = None) -> None:
        self.__writer = None

    @property
    def _writer(self) -> Optional["asyncio.Task[None]"]:
        return self.__writer

    @_writer.setter
    def _writer(self, writer: Optional["asyncio.Task[None]"]) -> None:
        if self.__writer is not None:
            self.__writer.remove_done_callback(self.__reset_writer)
        self.__writer = writer
        if writer is None:
            return
        if writer.done():
            # The writer is already done, so we can reset it immediately.
            self.__reset_writer()
        else:
            writer.add_done_callback(self.__reset_writer)

    @reify
    def url(self) -> URL:
        return self._url

    @reify
    def url_obj(self) -> URL:
        warnings.warn("Deprecated, use .url #1654", DeprecationWarning, stacklevel=2)
        return self._url

    @reify
    def real_url(self) -> URL:
        return self._real_url

    @reify
    def host(self) -> str:
        assert self._url.host is not None
        return self._url.host

    @reify
    def headers(self) -> "CIMultiDictProxy[str]":
        return self._headers

    @reify
    def raw_headers(self) -> RawHeaders:
        return self._raw_headers

    @reify
    def request_info(self) -> RequestInfo:
        return self._request_info

    @reify
    def content_disposition(self) -> Optional[ContentDisposition]:
        raw = self._headers.get(hdrs.CONTENT_DISPOSITION)
        if raw is None:
            return None
        disposition_type, params_dct = multipart.parse_content_disposition(raw)
        params = MappingProxyType(params_dct)
        filename = multipart.content_disposition_filename(params)
        return ContentDisposition(disposition_type, params, filename)

    def __del__(self, _warnings: Any = warnings) -> None:
        if self._closed:
            return

        if self._connection is not None:
            self._connection.release()
            self._cleanup_writer()

            if self._loop.get_debug():
                kwargs = {"source": self}
                _warnings.warn(f"Unclosed response {self!r}", ResourceWarning, **kwargs)
                context = {"client_response": self, "message": "Unclosed response"}
                if self._source_traceback:
                    context["source_traceback"] = self._source_traceback
                self._loop.call_exception_handler(context)

    def __repr__(self) -> str:
        out = io.StringIO()
        ascii_encodable_url = str(self.url)
        if self.reason:
            ascii_encodable_reason = self.reason.encode(
                "ascii", "backslashreplace"
            ).decode("ascii")
        else:
            ascii_encodable_reason = "None"
        print(
            "<ClientResponse({}) [{} {}]>".format(
                ascii_encodable_url, self.status, ascii_encodable_reason
            ),
            file=out,
        )
        print(self.headers, file=out)
        return out.getvalue()

    @property
    def connection(self) -> Optional["Connection"]:
        return self._connection

    @reify
    def history(self) -> Tuple["ClientResponse", ...]:
        """A sequence of of responses, if redirects occurred."""
        return self._history

    @reify
    def links(self) -> "MultiDictProxy[MultiDictProxy[Union[str, URL]]]":
        links_str = ", ".join(self.headers.getall("link", []))

        if not links_str:
            return MultiDictProxy(MultiDict())

        links: MultiDict[MultiDictProxy[Union[str, URL]]] = MultiDict()

        for val in re.split(r",(?=\s*<)", links_str):
            match = re.match(r"\s*<(.*)>(.*)", val)
            if match is None:  # pragma: no cover
                # the check exists to suppress mypy error
                continue
            url, params_str = match.groups()
            params = params_str.split(";")[1:]

            link: MultiDict[Union[str, URL]] = MultiDict()

            for param in params:
                match = re.match(r"^\s*(\S*)\s*=\s*(['\"]?)(.*?)(\2)\s*$", param, re.M)
                if match is None:  # pragma: no cover
                    # the check exists to suppress mypy error
                    continue
                key, _, value, _ = match.groups()

                link.add(key, value)

            key = link.get("rel", url)

            link.add("url", self.url.join(URL(url)))

            links.add(str(key), MultiDictProxy(link))

        return MultiDictProxy(links)

    async def start(self, connection: "Connection") -> "ClientResponse":
        """Start response processing."""
        self._closed = False
        self._protocol = connection.protocol
        self._connection = connection

        with self._timer:
            while True:
                # read response
                try:
                    protocol = self._protocol
                    message, payload = await protocol.read()  # type: ignore[union-attr]
                except http.HttpProcessingError as exc:
                    raise ClientResponseError(
                        self.request_info,
                        self.history,
                        status=exc.code,
                        message=exc.message,
                        headers=exc.headers,
                    ) from exc

                if message.code < 100 or message.code > 199 or message.code == 101:
                    break

                if self._continue is not None:
                    set_result(self._continue, True)
                    self._continue = None

        # payload eof handler
        payload.on_eof(self._response_eof)

        # response status
        self.version = message.version
        self.status = message.code
        self.reason = message.reason

        # headers
        self._headers = message.headers  # type is CIMultiDictProxy
        self._raw_headers = message.raw_headers  # type is Tuple[bytes, bytes]

        # payload
        self.content = payload

        # cookies
        for hdr in self.headers.getall(hdrs.SET_COOKIE, ()):
            try:
                self.cookies.load(hdr)
            except CookieError as exc:
                client_logger.warning("Can not load response cookies: %s", exc)
        return self

    def _response_eof(self) -> None:
        if self._closed:
            return

        # protocol could be None because connection could be detached
        protocol = self._connection and self._connection.protocol
        if protocol is not None and protocol.upgraded:
            return

        self._closed = True
        self._cleanup_writer()
        self._release_connection()

    @property
    def closed(self) -> bool:
        return self._closed

    def close(self) -> None:
        if not self._released:
            self._notify_content()

        self._closed = True
        if self._loop is None or self._loop.is_closed():
            return

        self._cleanup_writer()
        if self._connection is not None:
            self._connection.close()
            self._connection = None

    def release(self) -> Any:
        if not self._released:
            self._notify_content()

        self._closed = True

        self._cleanup_writer()
        self._release_connection()
        return noop()

    @property
    def ok(self) -> bool:
        """Returns ``True`` if ``status`` is less than ``400``, ``False`` if not.

        This is **not** a check for ``200 OK`` but a check that the response
        status is under 400.
        """
        return 400 > self.status

    def raise_for_status(self) -> None:
        if not self.ok:
            # reason should always be not None for a started response
            assert self.reason is not None
            self.release()
            raise ClientResponseError(
                self.request_info,
                self.history,
                status=self.status,
                message=self.reason,
                headers=self.headers,
            )

    def _release_connection(self) -> None:
        if self._connection is not None:
            if self._writer is None:
                self._connection.release()
                self._connection = None
            else:
                self._writer.add_done_callback(lambda f: self._release_connection())

    async def _wait_released(self) -> None:
        if self._writer is not None:
            await self._writer
        self._release_connection()

    def _cleanup_writer(self) -> None:
        if self._writer is not None:
            self._writer.cancel()
        self._session = None

    def _notify_content(self) -> None:
        content = self.content
        if content and content.exception() is None:
            set_exception(content, ClientConnectionError("Connection closed"))
        self._released = True

    async def wait_for_close(self) -> None:
        if self._writer is not None:
            await self._writer
        self.release()

    async def read(self) -> bytes:
        """Read response payload."""
        if self._body is None:
            try:
                self._body = await self.content.read()
                for trace in self._traces:
                    await trace.send_response_chunk_received(
                        self.method, self.url, self._body
                    )
            except BaseException:
                self.close()
                raise
        elif self._released:  # Response explicitly released
            raise ClientConnectionError("Connection closed")

        protocol = self._connection and self._connection.protocol
        if protocol is None or not protocol.upgraded:
            await self._wait_released()  # Underlying connection released
        return self._body  # type: ignore[no-any-return]

    def get_encoding(self) -> str:
        ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower()
        mimetype = helpers.parse_mimetype(ctype)

        encoding = mimetype.parameters.get("charset")
        if encoding:
            with contextlib.suppress(LookupError):
                return codecs.lookup(encoding).name

        if mimetype.type == "application" and (
            mimetype.subtype == "json" or mimetype.subtype == "rdap"
        ):
            # RFC 7159 states that the default encoding is UTF-8.
            # RFC 7483 defines application/rdap+json
            return "utf-8"

        if self._body is None:
            raise RuntimeError(
                "Cannot compute fallback encoding of a not yet read body"
            )

        return self._resolve_charset(self, self._body)

    async def text(self, encoding: Optional[str] = None, errors: str = "strict") -> str:
        """Read response payload and decode."""
        if self._body is None:
            await self.read()

        if encoding is None:
            encoding = self.get_encoding()

        return self._body.decode(  # type: ignore[no-any-return,union-attr]
            encoding, errors=errors
        )

    async def json(
        self,
        *,
        encoding: Optional[str] = None,
        loads: JSONDecoder = DEFAULT_JSON_DECODER,
        content_type: Optional[str] = "application/json",
    ) -> Any:
        """Read and decodes JSON response."""
        if self._body is None:
            await self.read()

        if content_type:
            ctype = self.headers.get(hdrs.CONTENT_TYPE, "").lower()
            if not _is_expected_content_type(ctype, content_type):
                raise ContentTypeError(
                    self.request_info,
                    self.history,
                    status=self.status,
                    message=(
                        "Attempt to decode JSON with " "unexpected mimetype: %s" % ctype
                    ),
                    headers=self.headers,
                )

        stripped = self._body.strip()  # type: ignore[union-attr]
        if not stripped:
            return None

        if encoding is None:
            encoding = self.get_encoding()

        return loads(stripped.decode(encoding))

    async def __aenter__(self) -> "ClientResponse":
        return self

    async def __aexit__(
        self,
        exc_type: Optional[Type[BaseException]],
        exc_val: Optional[BaseException],
        exc_tb: Optional[TracebackType],
    ) -> None:
        # similar to _RequestContextManager, we do not need to check
        # for exceptions, response object can close connection
        # if state is broken
        self.release()
        await self.wait_for_close()

```
--- END FILE ---

Please first localize the bug based on the issue statement, and then generate *SEARCH/REPLACE* edits to fix the issue.

Every *SEARCH/REPLACE* edit must use this format:
1. The file path
2. The start of search block: <<<<<<< SEARCH
3. A contiguous chunk of lines to search for in the existing source code
4. The dividing line: =======
5. The lines to replace into the source code
6. The end of the replace block: >>>>>>> REPLACE

Here is an example:

```python
### mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE
```
</solution>

Please note that the *SEARCH/REPLACE* edit REQUIRES PROPER INDENTATION. If you would like to add the line '        print(x)', you must fully write that out, with all those spaces before the code!
Wrap each *SEARCH/REPLACE* edit in a code block as shown in the example above. If you have multiple *SEARCH/REPLACE* edits, use a separate code block for each one.
user