{
    "file_diffs": [
        {
            "old_file_content": "",
            "new_file_content": "Fixed a race closing the server-side WebSocket where the close code would not reach the client. -- by :user:`bdraco`.\n",
            "header": {
                "file": {
                    "path": "CHANGES/8680.bugfix.rst"
                },
                "misc_line": "new file mode 100644"
            },
            "index_line": {
                "old_commit_hash": "00000000",
                "new_commit_hash": "2149f12a",
                "mode": ""
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "/dev/null"
            },
            "plus_file": {
                "path": "b/CHANGES/8680.bugfix.rst"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 0,
                            "length": 0
                        },
                        "new_range": {
                            "start": 1,
                            "length": null
                        },
                        "section": ""
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "Fixed a race closing the server-side WebSocket where the close code would not reach the client. -- by :user:`bdraco`.",
                                "type": "added"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "import asyncio\nimport base64\nimport binascii\nimport hashlib\nimport json\nimport sys\nfrom typing import Any, Final, Iterable, Optional, Tuple, cast\n\nimport attr\nfrom multidict import CIMultiDict\n\nfrom . import hdrs\nfrom .abc import AbstractStreamWriter\nfrom .helpers import calculate_timeout_when, set_exception, set_result\nfrom .http import (\n    WS_CLOSED_MESSAGE,\n    WS_CLOSING_MESSAGE,\n    WS_KEY,\n    WebSocketError,\n    WebSocketReader,\n    WebSocketWriter,\n    WSCloseCode,\n    WSMessage,\n    WSMsgType as WSMsgType,\n    ws_ext_gen,\n    ws_ext_parse,\n)\nfrom .log import ws_logger\nfrom .streams import EofStream, FlowControlDataQueue\nfrom .typedefs import JSONDecoder, JSONEncoder\nfrom .web_exceptions import HTTPBadRequest, HTTPException\nfrom .web_request import BaseRequest\nfrom .web_response import StreamResponse\n\nif sys.version_info >= (3, 11):\n    import asyncio as async_timeout\nelse:\n    import async_timeout\n\n__all__ = (\n    \"WebSocketResponse\",\n    \"WebSocketReady\",\n    \"WSMsgType\",\n)\n\nTHRESHOLD_CONNLOST_ACCESS: Final[int] = 5\n\n\n@attr.s(auto_attribs=True, frozen=True, slots=True)\nclass WebSocketReady:\n    ok: bool\n    protocol: Optional[str]\n\n    def __bool__(self) -> bool:\n        return self.ok\n\n\nclass WebSocketResponse(StreamResponse):\n\n    _length_check = False\n\n    def __init__(\n        self,\n        *,\n        timeout: float = 10.0,\n        receive_timeout: Optional[float] = None,\n        autoclose: bool = True,\n        autoping: bool = True,\n        heartbeat: Optional[float] = None,\n        protocols: Iterable[str] = (),\n        compress: bool = True,\n        max_msg_size: int = 4 * 1024 * 1024,\n    ) -> None:\n        super().__init__(status=101)\n        self._protocols = protocols\n        self._ws_protocol: Optional[str] = None\n        self._writer: Optional[WebSocketWriter] = None\n        self._reader: Optional[FlowControlDataQueue[WSMessage]] = None\n        self._closed = False\n        self._closing = False\n        self._conn_lost = 0\n        self._close_code: Optional[int] = None\n        self._loop: Optional[asyncio.AbstractEventLoop] = None\n        self._waiting: bool = False\n        self._close_wait: Optional[asyncio.Future[None]] = None\n        self._exception: Optional[BaseException] = None\n        self._timeout = timeout\n        self._receive_timeout = receive_timeout\n        self._autoclose = autoclose\n        self._autoping = autoping\n        self._heartbeat = heartbeat\n        self._heartbeat_when = 0.0\n        self._heartbeat_cb: Optional[asyncio.TimerHandle] = None\n        if heartbeat is not None:\n            self._pong_heartbeat = heartbeat / 2.0\n        self._pong_response_cb: Optional[asyncio.TimerHandle] = None\n        self._compress = compress\n        self._max_msg_size = max_msg_size\n        self._ping_task: Optional[asyncio.Task[None]] = None\n\n    def _cancel_heartbeat(self) -> None:\n        self._cancel_pong_response_cb()\n        if self._heartbeat_cb is not None:\n            self._heartbeat_cb.cancel()\n            self._heartbeat_cb = None\n        if self._ping_task is not None:\n            self._ping_task.cancel()\n            self._ping_task = None\n\n    def _cancel_pong_response_cb(self) -> None:\n        if self._pong_response_cb is not None:\n            self._pong_response_cb.cancel()\n            self._pong_response_cb = None\n\n    def _reset_heartbeat(self) -> None:\n        if self._heartbeat is None:\n            return\n        self._cancel_pong_response_cb()\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        loop = self._loop\n        assert loop is not None\n        now = loop.time()\n        when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold)\n        self._heartbeat_when = when\n        if self._heartbeat_cb is None:\n            # We do not cancel the previous heartbeat_cb here because\n            # it generates a significant amount of TimerHandle churn\n            # which causes asyncio to rebuild the heap frequently.\n            # Instead _send_heartbeat() will reschedule the next\n            # heartbeat if it fires too early.\n            self._heartbeat_cb = loop.call_at(when, self._send_heartbeat)\n\n    def _send_heartbeat(self) -> None:\n        self._heartbeat_cb = None\n        loop = self._loop\n        assert loop is not None and self._writer is not None\n        now = loop.time()\n        if now < self._heartbeat_when:\n            # Heartbeat fired too early, reschedule\n            self._heartbeat_cb = loop.call_at(\n                self._heartbeat_when, self._send_heartbeat\n            )\n            return\n\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold)\n        self._cancel_pong_response_cb()\n        self._pong_response_cb = loop.call_at(when, self._pong_not_received)\n\n        if sys.version_info >= (3, 12):\n            # Optimization for Python 3.12, try to send the ping\n            # immediately to avoid having to schedule\n            # the task on the event loop.\n            ping_task = asyncio.Task(self._writer.ping(), loop=loop, eager_start=True)\n        else:\n            ping_task = loop.create_task(self._writer.ping())\n\n        if not ping_task.done():\n            self._ping_task = ping_task\n            ping_task.add_done_callback(self._ping_task_done)\n\n    def _ping_task_done(self, task: \"asyncio.Task[None]\") -> None:\n        \"\"\"Callback for when the ping task completes.\"\"\"\n        self._ping_task = None\n\n    def _pong_not_received(self) -> None:\n        if self._req is not None and self._req.transport is not None:\n            self._set_closed()\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            self._exception = asyncio.TimeoutError()\n\n    def _set_closed(self) -> None:\n        \"\"\"Set the connection to closed.\n\n        Cancel any heartbeat timers and set the closed flag.\n        \"\"\"\n        self._closed = True\n        self._cancel_heartbeat()\n\n    async def prepare(self, request: BaseRequest) -> AbstractStreamWriter:\n        # make pre-check to don't hide it by do_handshake() exceptions\n        if self._payload_writer is not None:\n            return self._payload_writer\n\n        protocol, writer = self._pre_start(request)\n        payload_writer = await super().prepare(request)\n        assert payload_writer is not None\n        self._post_start(request, protocol, writer)\n        await payload_writer.drain()\n        return payload_writer\n\n    def _handshake(\n        self, request: BaseRequest\n    ) -> Tuple[\"CIMultiDict[str]\", str, bool, bool]:\n        headers = request.headers\n        if \"websocket\" != headers.get(hdrs.UPGRADE, \"\").lower().strip():\n            raise HTTPBadRequest(\n                text=(\n                    \"No WebSocket UPGRADE hdr: {}\\n Can \"\n                    '\"Upgrade\" only to \"WebSocket\".'\n                ).format(headers.get(hdrs.UPGRADE))\n            )\n\n        if \"upgrade\" not in headers.get(hdrs.CONNECTION, \"\").lower():\n            raise HTTPBadRequest(\n                text=\"No CONNECTION upgrade hdr: {}\".format(\n                    headers.get(hdrs.CONNECTION)\n                )\n            )\n\n        # find common sub-protocol between client and server\n        protocol = None\n        if hdrs.SEC_WEBSOCKET_PROTOCOL in headers:\n            req_protocols = [\n                str(proto.strip())\n                for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(\",\")\n            ]\n\n            for proto in req_protocols:\n                if proto in self._protocols:\n                    protocol = proto\n                    break\n            else:\n                # No overlap found: Return no protocol as per spec\n                ws_logger.warning(\n                    \"Client protocols %r don’t overlap server-known ones %r\",\n                    req_protocols,\n                    self._protocols,\n                )\n\n        # check supported version\n        version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, \"\")\n        if version not in (\"13\", \"8\", \"7\"):\n            raise HTTPBadRequest(text=f\"Unsupported version: {version}\")\n\n        # check client handshake for validity\n        key = headers.get(hdrs.SEC_WEBSOCKET_KEY)\n        try:\n            if not key or len(base64.b64decode(key)) != 16:\n                raise HTTPBadRequest(text=f\"Handshake error: {key!r}\")\n        except binascii.Error:\n            raise HTTPBadRequest(text=f\"Handshake error: {key!r}\") from None\n\n        accept_val = base64.b64encode(\n            hashlib.sha1(key.encode() + WS_KEY).digest()\n        ).decode()\n        response_headers = CIMultiDict(\n            {\n                hdrs.UPGRADE: \"websocket\",\n                hdrs.CONNECTION: \"upgrade\",\n                hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,\n            }\n        )\n\n        notakeover = False\n        compress = 0\n        if self._compress:\n            extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)\n            # Server side always get return with no exception.\n            # If something happened, just drop compress extension\n            compress, notakeover = ws_ext_parse(extensions, isserver=True)\n            if compress:\n                enabledext = ws_ext_gen(\n                    compress=compress, isserver=True, server_notakeover=notakeover\n                )\n                response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext\n\n        if protocol:\n            response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol\n        return (\n            response_headers,\n            protocol,\n            compress,\n            notakeover,\n        )  # type: ignore[return-value]\n\n    def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]:\n        self._loop = request._loop\n\n        headers, protocol, compress, notakeover = self._handshake(request)\n\n        self.set_status(101)\n        self.headers.update(headers)\n        self.force_close()\n        self._compress = compress\n        transport = request._protocol.transport\n        assert transport is not None\n        writer = WebSocketWriter(\n            request._protocol, transport, compress=compress, notakeover=notakeover\n        )\n\n        return protocol, writer\n\n    def _post_start(\n        self, request: BaseRequest, protocol: str, writer: WebSocketWriter\n    ) -> None:\n        self._ws_protocol = protocol\n        self._writer = writer\n\n        self._reset_heartbeat()\n\n        loop = self._loop\n        assert loop is not None\n        self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop)\n        request.protocol.set_parser(\n            WebSocketReader(self._reader, self._max_msg_size, compress=self._compress)\n        )\n        # disable HTTP keepalive for WebSocket\n        request.protocol.keep_alive(False)\n\n    def can_prepare(self, request: BaseRequest) -> WebSocketReady:\n        if self._writer is not None:\n            raise RuntimeError(\"Already started\")\n        try:\n            _, protocol, _, _ = self._handshake(request)\n        except HTTPException:\n            return WebSocketReady(False, None)\n        else:\n            return WebSocketReady(True, protocol)\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    @property\n    def close_code(self) -> Optional[int]:\n        return self._close_code\n\n    @property\n    def ws_protocol(self) -> Optional[str]:\n        return self._ws_protocol\n\n    @property\n    def compress(self) -> bool:\n        return self._compress\n\n    def get_extra_info(self, name: str, default: Any = None) -> Any:\n        \"\"\"Get optional transport information.\n\n        If no value associated with ``name`` is found, ``default`` is returned.\n        \"\"\"\n        writer = self._writer\n        if writer is None:\n            return default\n        transport = writer.transport\n        if transport is None:\n            return default\n        return transport.get_extra_info(name, default)\n\n    def exception(self) -> Optional[BaseException]:\n        return self._exception\n\n    async def ping(self, message: bytes = b\"\") -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.ping(message)\n\n    async def pong(self, message: bytes = b\"\") -> None:\n        # unsolicited pong\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.pong(message)\n\n    async def send_str(self, data: str, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, str):\n            raise TypeError(\"data argument must be str (%r)\" % type(data))\n        await self._writer.send(data, binary=False, compress=compress)\n\n    async def send_bytes(self, data: bytes, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError(\"data argument must be byte-ish (%r)\" % type(data))\n        await self._writer.send(data, binary=True, compress=compress)\n\n    async def send_json(\n        self,\n        data: Any,\n        compress: Optional[bool] = None,\n        *,\n        dumps: JSONEncoder = json.dumps,\n    ) -> None:\n        await self.send_str(dumps(data), compress=compress)\n\n    async def write_eof(self) -> None:  # type: ignore[override]\n        if self._eof_sent:\n            return\n        if self._payload_writer is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        await self.close()\n        self._eof_sent = True\n\n    async def close(\n        self, *, code: int = WSCloseCode.OK, message: bytes = b\"\", drain: bool = True\n    ) -> bool:\n        \"\"\"Close websocket connection.\"\"\"\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        self._cancel_heartbeat()\n        reader = self._reader\n        assert reader is not None\n\n        # we need to break `receive()` cycle first,\n        # `close()` may be called from different task\n        if self._waiting and not self._closed:\n            if not self._close_wait:\n                assert self._loop is not None\n                self._close_wait = self._loop.create_future()\n            reader.feed_data(WS_CLOSING_MESSAGE, 0)\n            await self._close_wait\n\n        if self._closed:\n            return False\n\n        self._set_closed()\n        try:\n            await self._writer.close(code, message)\n            writer = self._payload_writer\n            assert writer is not None\n            if drain:\n                await writer.drain()\n        except (asyncio.CancelledError, asyncio.TimeoutError):\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if self._closing:\n            self._close_transport()\n            return True\n\n        reader = self._reader\n        assert reader is not None\n        try:\n            async with async_timeout.timeout(self._timeout):\n                msg = await reader.read()\n        except asyncio.CancelledError:\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if msg.type is WSMsgType.CLOSE:\n            self._set_code_close_transport(msg.data)\n            return True\n\n        self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n        self._exception = asyncio.TimeoutError()\n        return True\n\n    def _set_closing(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and mark the connection as closing.\"\"\"\n        self._closing = True\n        self._close_code = code\n        self._cancel_heartbeat()\n\n    def _set_code_close_transport(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and close the transport.\"\"\"\n        self._close_code = code\n        self._close_transport()\n\n    def _close_transport(self) -> None:\n        \"\"\"Close the transport.\"\"\"\n        if self._req is not None and self._req.transport is not None:\n            self._req.transport.close()\n\n    async def receive(self, timeout: Optional[float] = None) -> WSMessage:\n        if self._reader is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        loop = self._loop\n        assert loop is not None\n        receive_timeout = timeout or self._receive_timeout\n        while True:\n            if self._waiting:\n                raise RuntimeError(\"Concurrent call to receive() is not allowed\")\n\n            if self._closed:\n                self._conn_lost += 1\n                if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS:\n                    raise RuntimeError(\"WebSocket connection is closed.\")\n                return WS_CLOSED_MESSAGE\n            elif self._closing:\n                return WS_CLOSING_MESSAGE\n\n            try:\n                self._waiting = True\n                try:\n                    if receive_timeout:\n                        # Entering the context manager and creating\n                        # Timeout() object can take almost 50% of the\n                        # run time in this loop so we avoid it if\n                        # there is no read timeout.\n                        async with async_timeout.timeout(receive_timeout):\n                            msg = await self._reader.read()\n                    else:\n                        msg = await self._reader.read()\n                    self._reset_heartbeat()\n                finally:\n                    self._waiting = False\n                    if self._close_wait:\n                        set_result(self._close_wait, None)\n            except asyncio.TimeoutError:\n                raise\n            except EofStream:\n                self._close_code = WSCloseCode.OK\n                await self.close()\n                return WSMessage(WSMsgType.CLOSED, None, None)\n            except WebSocketError as exc:\n                self._close_code = exc.code\n                await self.close(code=exc.code)\n                return WSMessage(WSMsgType.ERROR, exc, None)\n            except Exception as exc:\n                self._exception = exc\n                self._set_closing(WSCloseCode.ABNORMAL_CLOSURE)\n                await self.close()\n                return WSMessage(WSMsgType.ERROR, exc, None)\n\n            if msg.type is WSMsgType.CLOSE:\n                self._set_closing(msg.data)\n                # Could be closed while awaiting reader.\n                if not self._closed and self._autoclose:\n                    # The client is likely going to close the\n                    # connection out from under us so we do not\n                    # want to drain any pending writes as it will\n                    # likely result writing to a broken pipe.\n                    await self.close(drain=False)\n            elif msg.type is WSMsgType.CLOSING:\n                self._set_closing(WSCloseCode.OK)\n            elif msg.type is WSMsgType.PING and self._autoping:\n                await self.pong(msg.data)\n                continue\n            elif msg.type is WSMsgType.PONG and self._autoping:\n                continue\n\n            return msg\n\n    async def receive_str(self, *, timeout: Optional[float] = None) -> str:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.TEXT:\n            raise TypeError(\n                \"Received message {}:{!r} is not WSMsgType.TEXT\".format(\n                    msg.type, msg.data\n                )\n            )\n        return cast(str, msg.data)\n\n    async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.BINARY:\n            raise TypeError(f\"Received message {msg.type}:{msg.data!r} is not bytes\")\n        return cast(bytes, msg.data)\n\n    async def receive_json(\n        self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None\n    ) -> Any:\n        data = await self.receive_str(timeout=timeout)\n        return loads(data)\n\n    async def write(self, data: bytes) -> None:\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n    def __aiter__(self) -> \"WebSocketResponse\":\n        return self\n\n    async def __anext__(self) -> WSMessage:\n        msg = await self.receive()\n        if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):\n            raise StopAsyncIteration\n        return msg\n\n    def _cancel(self, exc: BaseException) -> None:\n        # web_protocol calls this from connection_lost\n        # or when the server is shutting down.\n        self._closing = True\n        self._cancel_heartbeat()\n        if self._reader is not None:\n            set_exception(self._reader, exc)\n",
            "new_file_content": "import asyncio\nimport base64\nimport binascii\nimport hashlib\nimport json\nimport sys\nfrom typing import Any, Final, Iterable, Optional, Tuple, cast\n\nimport attr\nfrom multidict import CIMultiDict\n\nfrom . import hdrs\nfrom .abc import AbstractStreamWriter\nfrom .helpers import calculate_timeout_when, set_exception, set_result\nfrom .http import (\n    WS_CLOSED_MESSAGE,\n    WS_CLOSING_MESSAGE,\n    WS_KEY,\n    WebSocketError,\n    WebSocketReader,\n    WebSocketWriter,\n    WSCloseCode,\n    WSMessage,\n    WSMsgType as WSMsgType,\n    ws_ext_gen,\n    ws_ext_parse,\n)\nfrom .log import ws_logger\nfrom .streams import EofStream, FlowControlDataQueue\nfrom .typedefs import JSONDecoder, JSONEncoder\nfrom .web_exceptions import HTTPBadRequest, HTTPException\nfrom .web_request import BaseRequest\nfrom .web_response import StreamResponse\n\nif sys.version_info >= (3, 11):\n    import asyncio as async_timeout\nelse:\n    import async_timeout\n\n__all__ = (\n    \"WebSocketResponse\",\n    \"WebSocketReady\",\n    \"WSMsgType\",\n)\n\nTHRESHOLD_CONNLOST_ACCESS: Final[int] = 5\n\n\n@attr.s(auto_attribs=True, frozen=True, slots=True)\nclass WebSocketReady:\n    ok: bool\n    protocol: Optional[str]\n\n    def __bool__(self) -> bool:\n        return self.ok\n\n\nclass WebSocketResponse(StreamResponse):\n\n    _length_check = False\n\n    def __init__(\n        self,\n        *,\n        timeout: float = 10.0,\n        receive_timeout: Optional[float] = None,\n        autoclose: bool = True,\n        autoping: bool = True,\n        heartbeat: Optional[float] = None,\n        protocols: Iterable[str] = (),\n        compress: bool = True,\n        max_msg_size: int = 4 * 1024 * 1024,\n    ) -> None:\n        super().__init__(status=101)\n        self._protocols = protocols\n        self._ws_protocol: Optional[str] = None\n        self._writer: Optional[WebSocketWriter] = None\n        self._reader: Optional[FlowControlDataQueue[WSMessage]] = None\n        self._closed = False\n        self._closing = False\n        self._conn_lost = 0\n        self._close_code: Optional[int] = None\n        self._loop: Optional[asyncio.AbstractEventLoop] = None\n        self._waiting: bool = False\n        self._close_wait: Optional[asyncio.Future[None]] = None\n        self._exception: Optional[BaseException] = None\n        self._timeout = timeout\n        self._receive_timeout = receive_timeout\n        self._autoclose = autoclose\n        self._autoping = autoping\n        self._heartbeat = heartbeat\n        self._heartbeat_when = 0.0\n        self._heartbeat_cb: Optional[asyncio.TimerHandle] = None\n        if heartbeat is not None:\n            self._pong_heartbeat = heartbeat / 2.0\n        self._pong_response_cb: Optional[asyncio.TimerHandle] = None\n        self._compress = compress\n        self._max_msg_size = max_msg_size\n        self._ping_task: Optional[asyncio.Task[None]] = None\n\n    def _cancel_heartbeat(self) -> None:\n        self._cancel_pong_response_cb()\n        if self._heartbeat_cb is not None:\n            self._heartbeat_cb.cancel()\n            self._heartbeat_cb = None\n        if self._ping_task is not None:\n            self._ping_task.cancel()\n            self._ping_task = None\n\n    def _cancel_pong_response_cb(self) -> None:\n        if self._pong_response_cb is not None:\n            self._pong_response_cb.cancel()\n            self._pong_response_cb = None\n\n    def _reset_heartbeat(self) -> None:\n        if self._heartbeat is None:\n            return\n        self._cancel_pong_response_cb()\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        loop = self._loop\n        assert loop is not None\n        now = loop.time()\n        when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold)\n        self._heartbeat_when = when\n        if self._heartbeat_cb is None:\n            # We do not cancel the previous heartbeat_cb here because\n            # it generates a significant amount of TimerHandle churn\n            # which causes asyncio to rebuild the heap frequently.\n            # Instead _send_heartbeat() will reschedule the next\n            # heartbeat if it fires too early.\n            self._heartbeat_cb = loop.call_at(when, self._send_heartbeat)\n\n    def _send_heartbeat(self) -> None:\n        self._heartbeat_cb = None\n        loop = self._loop\n        assert loop is not None and self._writer is not None\n        now = loop.time()\n        if now < self._heartbeat_when:\n            # Heartbeat fired too early, reschedule\n            self._heartbeat_cb = loop.call_at(\n                self._heartbeat_when, self._send_heartbeat\n            )\n            return\n\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold)\n        self._cancel_pong_response_cb()\n        self._pong_response_cb = loop.call_at(when, self._pong_not_received)\n\n        if sys.version_info >= (3, 12):\n            # Optimization for Python 3.12, try to send the ping\n            # immediately to avoid having to schedule\n            # the task on the event loop.\n            ping_task = asyncio.Task(self._writer.ping(), loop=loop, eager_start=True)\n        else:\n            ping_task = loop.create_task(self._writer.ping())\n\n        if not ping_task.done():\n            self._ping_task = ping_task\n            ping_task.add_done_callback(self._ping_task_done)\n\n    def _ping_task_done(self, task: \"asyncio.Task[None]\") -> None:\n        \"\"\"Callback for when the ping task completes.\"\"\"\n        self._ping_task = None\n\n    def _pong_not_received(self) -> None:\n        if self._req is not None and self._req.transport is not None:\n            self._set_closed()\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            self._exception = asyncio.TimeoutError()\n\n    def _set_closed(self) -> None:\n        \"\"\"Set the connection to closed.\n\n        Cancel any heartbeat timers and set the closed flag.\n        \"\"\"\n        self._closed = True\n        self._cancel_heartbeat()\n\n    async def prepare(self, request: BaseRequest) -> AbstractStreamWriter:\n        # make pre-check to don't hide it by do_handshake() exceptions\n        if self._payload_writer is not None:\n            return self._payload_writer\n\n        protocol, writer = self._pre_start(request)\n        payload_writer = await super().prepare(request)\n        assert payload_writer is not None\n        self._post_start(request, protocol, writer)\n        await payload_writer.drain()\n        return payload_writer\n\n    def _handshake(\n        self, request: BaseRequest\n    ) -> Tuple[\"CIMultiDict[str]\", str, bool, bool]:\n        headers = request.headers\n        if \"websocket\" != headers.get(hdrs.UPGRADE, \"\").lower().strip():\n            raise HTTPBadRequest(\n                text=(\n                    \"No WebSocket UPGRADE hdr: {}\\n Can \"\n                    '\"Upgrade\" only to \"WebSocket\".'\n                ).format(headers.get(hdrs.UPGRADE))\n            )\n\n        if \"upgrade\" not in headers.get(hdrs.CONNECTION, \"\").lower():\n            raise HTTPBadRequest(\n                text=\"No CONNECTION upgrade hdr: {}\".format(\n                    headers.get(hdrs.CONNECTION)\n                )\n            )\n\n        # find common sub-protocol between client and server\n        protocol = None\n        if hdrs.SEC_WEBSOCKET_PROTOCOL in headers:\n            req_protocols = [\n                str(proto.strip())\n                for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(\",\")\n            ]\n\n            for proto in req_protocols:\n                if proto in self._protocols:\n                    protocol = proto\n                    break\n            else:\n                # No overlap found: Return no protocol as per spec\n                ws_logger.warning(\n                    \"Client protocols %r don’t overlap server-known ones %r\",\n                    req_protocols,\n                    self._protocols,\n                )\n\n        # check supported version\n        version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, \"\")\n        if version not in (\"13\", \"8\", \"7\"):\n            raise HTTPBadRequest(text=f\"Unsupported version: {version}\")\n\n        # check client handshake for validity\n        key = headers.get(hdrs.SEC_WEBSOCKET_KEY)\n        try:\n            if not key or len(base64.b64decode(key)) != 16:\n                raise HTTPBadRequest(text=f\"Handshake error: {key!r}\")\n        except binascii.Error:\n            raise HTTPBadRequest(text=f\"Handshake error: {key!r}\") from None\n\n        accept_val = base64.b64encode(\n            hashlib.sha1(key.encode() + WS_KEY).digest()\n        ).decode()\n        response_headers = CIMultiDict(\n            {\n                hdrs.UPGRADE: \"websocket\",\n                hdrs.CONNECTION: \"upgrade\",\n                hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,\n            }\n        )\n\n        notakeover = False\n        compress = 0\n        if self._compress:\n            extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)\n            # Server side always get return with no exception.\n            # If something happened, just drop compress extension\n            compress, notakeover = ws_ext_parse(extensions, isserver=True)\n            if compress:\n                enabledext = ws_ext_gen(\n                    compress=compress, isserver=True, server_notakeover=notakeover\n                )\n                response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext\n\n        if protocol:\n            response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol\n        return (\n            response_headers,\n            protocol,\n            compress,\n            notakeover,\n        )  # type: ignore[return-value]\n\n    def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]:\n        self._loop = request._loop\n\n        headers, protocol, compress, notakeover = self._handshake(request)\n\n        self.set_status(101)\n        self.headers.update(headers)\n        self.force_close()\n        self._compress = compress\n        transport = request._protocol.transport\n        assert transport is not None\n        writer = WebSocketWriter(\n            request._protocol, transport, compress=compress, notakeover=notakeover\n        )\n\n        return protocol, writer\n\n    def _post_start(\n        self, request: BaseRequest, protocol: str, writer: WebSocketWriter\n    ) -> None:\n        self._ws_protocol = protocol\n        self._writer = writer\n\n        self._reset_heartbeat()\n\n        loop = self._loop\n        assert loop is not None\n        self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop)\n        request.protocol.set_parser(\n            WebSocketReader(self._reader, self._max_msg_size, compress=self._compress)\n        )\n        # disable HTTP keepalive for WebSocket\n        request.protocol.keep_alive(False)\n\n    def can_prepare(self, request: BaseRequest) -> WebSocketReady:\n        if self._writer is not None:\n            raise RuntimeError(\"Already started\")\n        try:\n            _, protocol, _, _ = self._handshake(request)\n        except HTTPException:\n            return WebSocketReady(False, None)\n        else:\n            return WebSocketReady(True, protocol)\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    @property\n    def close_code(self) -> Optional[int]:\n        return self._close_code\n\n    @property\n    def ws_protocol(self) -> Optional[str]:\n        return self._ws_protocol\n\n    @property\n    def compress(self) -> bool:\n        return self._compress\n\n    def get_extra_info(self, name: str, default: Any = None) -> Any:\n        \"\"\"Get optional transport information.\n\n        If no value associated with ``name`` is found, ``default`` is returned.\n        \"\"\"\n        writer = self._writer\n        if writer is None:\n            return default\n        transport = writer.transport\n        if transport is None:\n            return default\n        return transport.get_extra_info(name, default)\n\n    def exception(self) -> Optional[BaseException]:\n        return self._exception\n\n    async def ping(self, message: bytes = b\"\") -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.ping(message)\n\n    async def pong(self, message: bytes = b\"\") -> None:\n        # unsolicited pong\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.pong(message)\n\n    async def send_str(self, data: str, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, str):\n            raise TypeError(\"data argument must be str (%r)\" % type(data))\n        await self._writer.send(data, binary=False, compress=compress)\n\n    async def send_bytes(self, data: bytes, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError(\"data argument must be byte-ish (%r)\" % type(data))\n        await self._writer.send(data, binary=True, compress=compress)\n\n    async def send_json(\n        self,\n        data: Any,\n        compress: Optional[bool] = None,\n        *,\n        dumps: JSONEncoder = json.dumps,\n    ) -> None:\n        await self.send_str(dumps(data), compress=compress)\n\n    async def write_eof(self) -> None:  # type: ignore[override]\n        if self._eof_sent:\n            return\n        if self._payload_writer is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        await self.close()\n        self._eof_sent = True\n\n    async def close(\n        self, *, code: int = WSCloseCode.OK, message: bytes = b\"\", drain: bool = True\n    ) -> bool:\n        \"\"\"Close websocket connection.\"\"\"\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        if self._closed:\n            return False\n        self._set_closed()\n\n        try:\n            await self._writer.close(code, message)\n            writer = self._payload_writer\n            assert writer is not None\n            if drain:\n                await writer.drain()\n        except (asyncio.CancelledError, asyncio.TimeoutError):\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        reader = self._reader\n        assert reader is not None\n        # we need to break `receive()` cycle before we can call\n        # `reader.read()` as `close()` may be called from different task\n        if self._waiting:\n            assert self._loop is not None\n            assert self._close_wait is None\n            self._close_wait = self._loop.create_future()\n            reader.feed_data(WS_CLOSING_MESSAGE)\n            await self._close_wait\n\n        if self._closing:\n            self._close_transport()\n            return True\n\n        try:\n            async with async_timeout.timeout(self._timeout):\n                msg = await reader.read()\n        except asyncio.CancelledError:\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if msg.type is WSMsgType.CLOSE:\n            self._set_code_close_transport(msg.data)\n            return True\n\n        self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n        self._exception = asyncio.TimeoutError()\n        return True\n\n    def _set_closing(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and mark the connection as closing.\"\"\"\n        self._closing = True\n        self._close_code = code\n        self._cancel_heartbeat()\n\n    def _set_code_close_transport(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and close the transport.\"\"\"\n        self._close_code = code\n        self._close_transport()\n\n    def _close_transport(self) -> None:\n        \"\"\"Close the transport.\"\"\"\n        if self._req is not None and self._req.transport is not None:\n            self._req.transport.close()\n\n    async def receive(self, timeout: Optional[float] = None) -> WSMessage:\n        if self._reader is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        loop = self._loop\n        assert loop is not None\n        receive_timeout = timeout or self._receive_timeout\n        while True:\n            if self._waiting:\n                raise RuntimeError(\"Concurrent call to receive() is not allowed\")\n\n            if self._closed:\n                self._conn_lost += 1\n                if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS:\n                    raise RuntimeError(\"WebSocket connection is closed.\")\n                return WS_CLOSED_MESSAGE\n            elif self._closing:\n                return WS_CLOSING_MESSAGE\n\n            try:\n                self._waiting = True\n                try:\n                    if receive_timeout:\n                        # Entering the context manager and creating\n                        # Timeout() object can take almost 50% of the\n                        # run time in this loop so we avoid it if\n                        # there is no read timeout.\n                        async with async_timeout.timeout(receive_timeout):\n                            msg = await self._reader.read()\n                    else:\n                        msg = await self._reader.read()\n                    self._reset_heartbeat()\n                finally:\n                    self._waiting = False\n                    if self._close_wait:\n                        set_result(self._close_wait, None)\n            except asyncio.TimeoutError:\n                raise\n            except EofStream:\n                self._close_code = WSCloseCode.OK\n                await self.close()\n                return WSMessage(WSMsgType.CLOSED, None, None)\n            except WebSocketError as exc:\n                self._close_code = exc.code\n                await self.close(code=exc.code)\n                return WSMessage(WSMsgType.ERROR, exc, None)\n            except Exception as exc:\n                self._exception = exc\n                self._set_closing(WSCloseCode.ABNORMAL_CLOSURE)\n                await self.close()\n                return WSMessage(WSMsgType.ERROR, exc, None)\n\n            if msg.type is WSMsgType.CLOSE:\n                self._set_closing(msg.data)\n                # Could be closed while awaiting reader.\n                if not self._closed and self._autoclose:\n                    # The client is likely going to close the\n                    # connection out from under us so we do not\n                    # want to drain any pending writes as it will\n                    # likely result writing to a broken pipe.\n                    await self.close(drain=False)\n            elif msg.type is WSMsgType.CLOSING:\n                self._set_closing(WSCloseCode.OK)\n            elif msg.type is WSMsgType.PING and self._autoping:\n                await self.pong(msg.data)\n                continue\n            elif msg.type is WSMsgType.PONG and self._autoping:\n                continue\n\n            return msg\n\n    async def receive_str(self, *, timeout: Optional[float] = None) -> str:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.TEXT:\n            raise TypeError(\n                \"Received message {}:{!r} is not WSMsgType.TEXT\".format(\n                    msg.type, msg.data\n                )\n            )\n        return cast(str, msg.data)\n\n    async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.BINARY:\n            raise TypeError(f\"Received message {msg.type}:{msg.data!r} is not bytes\")\n        return cast(bytes, msg.data)\n\n    async def receive_json(\n        self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None\n    ) -> Any:\n        data = await self.receive_str(timeout=timeout)\n        return loads(data)\n\n    async def write(self, data: bytes) -> None:\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n    def __aiter__(self) -> \"WebSocketResponse\":\n        return self\n\n    async def __anext__(self) -> WSMessage:\n        msg = await self.receive()\n        if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):\n            raise StopAsyncIteration\n        return msg\n\n    def _cancel(self, exc: BaseException) -> None:\n        # web_protocol calls this from connection_lost\n        # or when the server is shutting down.\n        self._closing = True\n        self._cancel_heartbeat()\n        if self._reader is not None:\n            set_exception(self._reader, exc)\n",
            "header": {
                "file": {
                    "path": "aiohttp/web_ws.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "fe8f537d",
                "new_commit_hash": "98f26cc4",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/aiohttp/web_ws.py"
            },
            "plus_file": {
                "path": "b/aiohttp/web_ws.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 406,
                            "length": 23
                        },
                        "new_range": {
                            "start": 406,
                            "length": 10
                        },
                        "section": "class WebSocketResponse(StreamResponse):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "        if self._writer is None:",
                                "type": "context"
                            },
                            {
                                "content": "            raise RuntimeError(\"Call .prepare() first\")",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        self._cancel_heartbeat()",
                                "type": "deleted"
                            },
                            {
                                "content": "        reader = self._reader",
                                "type": "deleted"
                            },
                            {
                                "content": "        assert reader is not None",
                                "type": "deleted"
                            },
                            {
                                "content": "",
                                "type": "deleted"
                            },
                            {
                                "content": "        # we need to break `receive()` cycle first,",
                                "type": "deleted"
                            },
                            {
                                "content": "        # `close()` may be called from different task",
                                "type": "deleted"
                            },
                            {
                                "content": "        if self._waiting and not self._closed:",
                                "type": "deleted"
                            },
                            {
                                "content": "            if not self._close_wait:",
                                "type": "deleted"
                            },
                            {
                                "content": "                assert self._loop is not None",
                                "type": "deleted"
                            },
                            {
                                "content": "                self._close_wait = self._loop.create_future()",
                                "type": "deleted"
                            },
                            {
                                "content": "            reader.feed_data(WS_CLOSING_MESSAGE, 0)",
                                "type": "deleted"
                            },
                            {
                                "content": "            await self._close_wait",
                                "type": "deleted"
                            },
                            {
                                "content": "",
                                "type": "deleted"
                            },
                            {
                                "content": "        if self._closed:",
                                "type": "context"
                            },
                            {
                                "content": "            return False",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "deleted"
                            },
                            {
                                "content": "        self._set_closed()",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        try:",
                                "type": "context"
                            },
                            {
                                "content": "            await self._writer.close(code, message)",
                                "type": "context"
                            },
                            {
                                "content": "            writer = self._payload_writer",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "aiohttp/web_ws.py",
                            "type": "class",
                            "name": "WebSocketResponse",
                            "content": "class WebSocketResponse(StreamResponse):\n\n    _length_check = False\n\n    def __init__(\n        self,\n        *,\n        timeout: float = 10.0,\n        receive_timeout: Optional[float] = None,\n        autoclose: bool = True,\n        autoping: bool = True,\n        heartbeat: Optional[float] = None,\n        protocols: Iterable[str] = (),\n        compress: bool = True,\n        max_msg_size: int = 4 * 1024 * 1024,\n    ) -> None:\n        super().__init__(status=101)\n        self._protocols = protocols\n        self._ws_protocol: Optional[str] = None\n        self._writer: Optional[WebSocketWriter] = None\n        self._reader: Optional[FlowControlDataQueue[WSMessage]] = None\n        self._closed = False\n        self._closing = False\n        self._conn_lost = 0\n        self._close_code: Optional[int] = None\n        self._loop: Optional[asyncio.AbstractEventLoop] = None\n        self._waiting: bool = False\n        self._close_wait: Optional[asyncio.Future[None]] = None\n        self._exception: Optional[BaseException] = None\n        self._timeout = timeout\n        self._receive_timeout = receive_timeout\n        self._autoclose = autoclose\n        self._autoping = autoping\n        self._heartbeat = heartbeat\n        self._heartbeat_when = 0.0\n        self._heartbeat_cb: Optional[asyncio.TimerHandle] = None\n        if heartbeat is not None:\n            self._pong_heartbeat = heartbeat / 2.0\n        self._pong_response_cb: Optional[asyncio.TimerHandle] = None\n        self._compress = compress\n        self._max_msg_size = max_msg_size\n        self._ping_task: Optional[asyncio.Task[None]] = None\n\n    def _cancel_heartbeat(self) -> None:\n        self._cancel_pong_response_cb()\n        if self._heartbeat_cb is not None:\n            self._heartbeat_cb.cancel()\n            self._heartbeat_cb = None\n        if self._ping_task is not None:\n            self._ping_task.cancel()\n            self._ping_task = None\n\n    def _cancel_pong_response_cb(self) -> None:\n        if self._pong_response_cb is not None:\n            self._pong_response_cb.cancel()\n            self._pong_response_cb = None\n\n    def _reset_heartbeat(self) -> None:\n        if self._heartbeat is None:\n            return\n        self._cancel_pong_response_cb()\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        loop = self._loop\n        assert loop is not None\n        now = loop.time()\n        when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold)\n        self._heartbeat_when = when\n        if self._heartbeat_cb is None:\n            # We do not cancel the previous heartbeat_cb here because\n            # it generates a significant amount of TimerHandle churn\n            # which causes asyncio to rebuild the heap frequently.\n            # Instead _send_heartbeat() will reschedule the next\n            # heartbeat if it fires too early.\n            self._heartbeat_cb = loop.call_at(when, self._send_heartbeat)\n\n    def _send_heartbeat(self) -> None:\n        self._heartbeat_cb = None\n        loop = self._loop\n        assert loop is not None and self._writer is not None\n        now = loop.time()\n        if now < self._heartbeat_when:\n            # Heartbeat fired too early, reschedule\n            self._heartbeat_cb = loop.call_at(\n                self._heartbeat_when, self._send_heartbeat\n            )\n            return\n\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold)\n        self._cancel_pong_response_cb()\n        self._pong_response_cb = loop.call_at(when, self._pong_not_received)\n\n        if sys.version_info >= (3, 12):\n            # Optimization for Python 3.12, try to send the ping\n            # immediately to avoid having to schedule\n            # the task on the event loop.\n            ping_task = asyncio.Task(self._writer.ping(), loop=loop, eager_start=True)\n        else:\n            ping_task = loop.create_task(self._writer.ping())\n\n        if not ping_task.done():\n            self._ping_task = ping_task\n            ping_task.add_done_callback(self._ping_task_done)\n\n    def _ping_task_done(self, task: \"asyncio.Task[None]\") -> None:\n        \"\"\"Callback for when the ping task completes.\"\"\"\n        self._ping_task = None\n\n    def _pong_not_received(self) -> None:\n        if self._req is not None and self._req.transport is not None:\n            self._set_closed()\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            self._exception = asyncio.TimeoutError()\n\n    def _set_closed(self) -> None:\n        \"\"\"Set the connection to closed.\n\n        Cancel any heartbeat timers and set the closed flag.\n        \"\"\"\n        self._closed = True\n        self._cancel_heartbeat()\n\n    async def prepare(self, request: BaseRequest) -> AbstractStreamWriter:\n        # make pre-check to don't hide it by do_handshake() exceptions\n        if self._payload_writer is not None:\n            return self._payload_writer\n\n        protocol, writer = self._pre_start(request)\n        payload_writer = await super().prepare(request)\n        assert payload_writer is not None\n        self._post_start(request, protocol, writer)\n        await payload_writer.drain()\n        return payload_writer\n\n    def _handshake(\n        self, request: BaseRequest\n    ) -> Tuple[\"CIMultiDict[str]\", str, bool, bool]:\n        headers = request.headers\n        if \"websocket\" != headers.get(hdrs.UPGRADE, \"\").lower().strip():\n            raise HTTPBadRequest(\n                text=(\n                    \"No WebSocket UPGRADE hdr: {}\\n Can \"\n                    '\"Upgrade\" only to \"WebSocket\".'\n                ).format(headers.get(hdrs.UPGRADE))\n            )\n\n        if \"upgrade\" not in headers.get(hdrs.CONNECTION, \"\").lower():\n            raise HTTPBadRequest(\n                text=\"No CONNECTION upgrade hdr: {}\".format(\n                    headers.get(hdrs.CONNECTION)\n                )\n            )\n\n        # find common sub-protocol between client and server\n        protocol = None\n        if hdrs.SEC_WEBSOCKET_PROTOCOL in headers:\n            req_protocols = [\n                str(proto.strip())\n                for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(\",\")\n            ]\n\n            for proto in req_protocols:\n                if proto in self._protocols:\n                    protocol = proto\n                    break\n            else:\n                # No overlap found: Return no protocol as per spec\n                ws_logger.warning(\n                    \"Client protocols %r don’t overlap server-known ones %r\",\n                    req_protocols,\n                    self._protocols,\n                )\n\n        # check supported version\n        version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, \"\")\n        if version not in (\"13\", \"8\", \"7\"):\n            raise HTTPBadRequest(text=f\"Unsupported version: {version}\")\n\n        # check client handshake for validity\n        key = headers.get(hdrs.SEC_WEBSOCKET_KEY)\n        try:\n            if not key or len(base64.b64decode(key)) != 16:\n                raise HTTPBadRequest(text=f\"Handshake error: {key!r}\")\n        except binascii.Error:\n            raise HTTPBadRequest(text=f\"Handshake error: {key!r}\") from None\n\n        accept_val = base64.b64encode(\n            hashlib.sha1(key.encode() + WS_KEY).digest()\n        ).decode()\n        response_headers = CIMultiDict(\n            {\n                hdrs.UPGRADE: \"websocket\",\n                hdrs.CONNECTION: \"upgrade\",\n                hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,\n            }\n        )\n\n        notakeover = False\n        compress = 0\n        if self._compress:\n            extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)\n            # Server side always get return with no exception.\n            # If something happened, just drop compress extension\n            compress, notakeover = ws_ext_parse(extensions, isserver=True)\n            if compress:\n                enabledext = ws_ext_gen(\n                    compress=compress, isserver=True, server_notakeover=notakeover\n                )\n                response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext\n\n        if protocol:\n            response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol\n        return (\n            response_headers,\n            protocol,\n            compress,\n            notakeover,\n        )  # type: ignore[return-value]\n\n    def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]:\n        self._loop = request._loop\n\n        headers, protocol, compress, notakeover = self._handshake(request)\n\n        self.set_status(101)\n        self.headers.update(headers)\n        self.force_close()\n        self._compress = compress\n        transport = request._protocol.transport\n        assert transport is not None\n        writer = WebSocketWriter(\n            request._protocol, transport, compress=compress, notakeover=notakeover\n        )\n\n        return protocol, writer\n\n    def _post_start(\n        self, request: BaseRequest, protocol: str, writer: WebSocketWriter\n    ) -> None:\n        self._ws_protocol = protocol\n        self._writer = writer\n\n        self._reset_heartbeat()\n\n        loop = self._loop\n        assert loop is not None\n        self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop)\n        request.protocol.set_parser(\n            WebSocketReader(self._reader, self._max_msg_size, compress=self._compress)\n        )\n        # disable HTTP keepalive for WebSocket\n        request.protocol.keep_alive(False)\n\n    def can_prepare(self, request: BaseRequest) -> WebSocketReady:\n        if self._writer is not None:\n            raise RuntimeError(\"Already started\")\n        try:\n            _, protocol, _, _ = self._handshake(request)\n        except HTTPException:\n            return WebSocketReady(False, None)\n        else:\n            return WebSocketReady(True, protocol)\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    @property\n    def close_code(self) -> Optional[int]:\n        return self._close_code\n\n    @property\n    def ws_protocol(self) -> Optional[str]:\n        return self._ws_protocol\n\n    @property\n    def compress(self) -> bool:\n        return self._compress\n\n    def get_extra_info(self, name: str, default: Any = None) -> Any:\n        \"\"\"Get optional transport information.\n\n        If no value associated with ``name`` is found, ``default`` is returned.\n        \"\"\"\n        writer = self._writer\n        if writer is None:\n            return default\n        transport = writer.transport\n        if transport is None:\n            return default\n        return transport.get_extra_info(name, default)\n\n    def exception(self) -> Optional[BaseException]:\n        return self._exception\n\n    async def ping(self, message: bytes = b\"\") -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.ping(message)\n\n    async def pong(self, message: bytes = b\"\") -> None:\n        # unsolicited pong\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.pong(message)\n\n    async def send_str(self, data: str, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, str):\n            raise TypeError(\"data argument must be str (%r)\" % type(data))\n        await self._writer.send(data, binary=False, compress=compress)\n\n    async def send_bytes(self, data: bytes, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError(\"data argument must be byte-ish (%r)\" % type(data))\n        await self._writer.send(data, binary=True, compress=compress)\n\n    async def send_json(\n        self,\n        data: Any,\n        compress: Optional[bool] = None,\n        *,\n        dumps: JSONEncoder = json.dumps,\n    ) -> None:\n        await self.send_str(dumps(data), compress=compress)\n\n    async def write_eof(self) -> None:  # type: ignore[override]\n        if self._eof_sent:\n            return\n        if self._payload_writer is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        await self.close()\n        self._eof_sent = True\n\n    async def close(\n        self, *, code: int = WSCloseCode.OK, message: bytes = b\"\", drain: bool = True\n    ) -> bool:\n        \"\"\"Close websocket connection.\"\"\"\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        self._cancel_heartbeat()\n        reader = self._reader\n        assert reader is not None\n\n        # we need to break `receive()` cycle first,\n        # `close()` may be called from different task\n        if self._waiting and not self._closed:\n            if not self._close_wait:\n                assert self._loop is not None\n                self._close_wait = self._loop.create_future()\n            reader.feed_data(WS_CLOSING_MESSAGE, 0)\n            await self._close_wait\n\n        if self._closed:\n            return False\n\n        self._set_closed()\n        try:\n            await self._writer.close(code, message)\n            writer = self._payload_writer\n            assert writer is not None\n            if drain:\n                await writer.drain()\n        except (asyncio.CancelledError, asyncio.TimeoutError):\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if self._closing:\n            self._close_transport()\n            return True\n\n        reader = self._reader\n        assert reader is not None\n        try:\n            async with async_timeout.timeout(self._timeout):\n                msg = await reader.read()\n        except asyncio.CancelledError:\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if msg.type is WSMsgType.CLOSE:\n            self._set_code_close_transport(msg.data)\n            return True\n\n        self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n        self._exception = asyncio.TimeoutError()\n        return True\n\n    def _set_closing(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and mark the connection as closing.\"\"\"\n        self._closing = True\n        self._close_code = code\n        self._cancel_heartbeat()\n\n    def _set_code_close_transport(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and close the transport.\"\"\"\n        self._close_code = code\n        self._close_transport()\n\n    def _close_transport(self) -> None:\n        \"\"\"Close the transport.\"\"\"\n        if self._req is not None and self._req.transport is not None:\n            self._req.transport.close()\n\n    async def receive(self, timeout: Optional[float] = None) -> WSMessage:\n        if self._reader is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        loop = self._loop\n        assert loop is not None\n        receive_timeout = timeout or self._receive_timeout\n        while True:\n            if self._waiting:\n                raise RuntimeError(\"Concurrent call to receive() is not allowed\")\n\n            if self._closed:\n                self._conn_lost += 1\n                if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS:\n                    raise RuntimeError(\"WebSocket connection is closed.\")\n                return WS_CLOSED_MESSAGE\n            elif self._closing:\n                return WS_CLOSING_MESSAGE\n\n            try:\n                self._waiting = True\n                try:\n                    if receive_timeout:\n                        # Entering the context manager and creating\n                        # Timeout() object can take almost 50% of the\n                        # run time in this loop so we avoid it if\n                        # there is no read timeout.\n                        async with async_timeout.timeout(receive_timeout):\n                            msg = await self._reader.read()\n                    else:\n                        msg = await self._reader.read()\n                    self._reset_heartbeat()\n                finally:\n                    self._waiting = False\n                    if self._close_wait:\n                        set_result(self._close_wait, None)\n            except asyncio.TimeoutError:\n                raise\n            except EofStream:\n                self._close_code = WSCloseCode.OK\n                await self.close()\n                return WSMessage(WSMsgType.CLOSED, None, None)\n            except WebSocketError as exc:\n                self._close_code = exc.code\n                await self.close(code=exc.code)\n                return WSMessage(WSMsgType.ERROR, exc, None)\n            except Exception as exc:\n                self._exception = exc\n                self._set_closing(WSCloseCode.ABNORMAL_CLOSURE)\n                await self.close()\n                return WSMessage(WSMsgType.ERROR, exc, None)\n\n            if msg.type is WSMsgType.CLOSE:\n                self._set_closing(msg.data)\n                # Could be closed while awaiting reader.\n                if not self._closed and self._autoclose:\n                    # The client is likely going to close the\n                    # connection out from under us so we do not\n                    # want to drain any pending writes as it will\n                    # likely result writing to a broken pipe.\n                    await self.close(drain=False)\n            elif msg.type is WSMsgType.CLOSING:\n                self._set_closing(WSCloseCode.OK)\n            elif msg.type is WSMsgType.PING and self._autoping:\n                await self.pong(msg.data)\n                continue\n            elif msg.type is WSMsgType.PONG and self._autoping:\n                continue\n\n            return msg\n\n    async def receive_str(self, *, timeout: Optional[float] = None) -> str:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.TEXT:\n            raise TypeError(\n                \"Received message {}:{!r} is not WSMsgType.TEXT\".format(\n                    msg.type, msg.data\n                )\n            )\n        return cast(str, msg.data)\n\n    async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.BINARY:\n            raise TypeError(f\"Received message {msg.type}:{msg.data!r} is not bytes\")\n        return cast(bytes, msg.data)\n\n    async def receive_json(\n        self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None\n    ) -> Any:\n        data = await self.receive_str(timeout=timeout)\n        return loads(data)\n\n    async def write(self, data: bytes) -> None:\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n    def __aiter__(self) -> \"WebSocketResponse\":\n        return self\n\n    async def __anext__(self) -> WSMessage:\n        msg = await self.receive()\n        if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):\n            raise StopAsyncIteration\n        return msg\n\n    def _cancel(self, exc: BaseException) -> None:\n        # web_protocol calls this from connection_lost\n        # or when the server is shutting down.\n        self._closing = True\n        self._cancel_heartbeat()\n        if self._reader is not None:\n            set_exception(self._reader, exc)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 58,
                            "end_lineno": 592,
                            "parent": null
                        },
                        {
                            "file_name": "aiohttp/web_ws.py",
                            "type": "method",
                            "name": "WebSocketResponse.close",
                            "content": "class WebSocketResponse(StreamResponse):\n\n    _length_check = False\n\n    def __init__(\n        self,\n        *,\n        timeout: float = 10.0,\n        receive_timeout: Optional[float] = None,\n        autoclose: bool = True,\n        autoping: bool = True,\n        heartbeat: Optional[float] = None,\n        protocols: Iterable[str] = (),\n        compress: bool = True,\n        max_msg_size: int = 4 * 1024 * 1024,\n    ) -> None:\n        super().__init__(status=101)\n        self._protocols = protocols\n        self._ws_protocol: Optional[str] = None\n        self._writer: Optional[WebSocketWriter] = None\n        self._reader: Optional[FlowControlDataQueue[WSMessage]] = None\n        self._closed = False\n        self._closing = False\n        self._conn_lost = 0\n        self._close_code: Optional[int] = None\n        self._loop: Optional[asyncio.AbstractEventLoop] = None\n        self._waiting: bool = False\n        self._close_wait: Optional[asyncio.Future[None]] = None\n        self._exception: Optional[BaseException] = None\n        self._timeout = timeout\n        self._receive_timeout = receive_timeout\n        self._autoclose = autoclose\n        self._autoping = autoping\n        self._heartbeat = heartbeat\n        self._heartbeat_when = 0.0\n        self._heartbeat_cb: Optional[asyncio.TimerHandle] = None\n        if heartbeat is not None:\n            self._pong_heartbeat = heartbeat / 2.0\n        self._pong_response_cb: Optional[asyncio.TimerHandle] = None\n        self._compress = compress\n        self._max_msg_size = max_msg_size\n        self._ping_task: Optional[asyncio.Task[None]] = None\n\n    def _cancel_heartbeat(self) -> None:\n        self._cancel_pong_response_cb()\n        if self._heartbeat_cb is not None:\n            self._heartbeat_cb.cancel()\n            self._heartbeat_cb = None\n        if self._ping_task is not None:\n            self._ping_task.cancel()\n            self._ping_task = None\n\n    def _cancel_pong_response_cb(self) -> None:\n        if self._pong_response_cb is not None:\n            self._pong_response_cb.cancel()\n            self._pong_response_cb = None\n\n    def _reset_heartbeat(self) -> None:\n        if self._heartbeat is None:\n            return\n        self._cancel_pong_response_cb()\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        loop = self._loop\n        assert loop is not None\n        now = loop.time()\n        when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold)\n        self._heartbeat_when = when\n        if self._heartbeat_cb is None:\n            # We do not cancel the previous heartbeat_cb here because\n            # it generates a significant amount of TimerHandle churn\n            # which causes asyncio to rebuild the heap frequently.\n            # Instead _send_heartbeat() will reschedule the next\n            # heartbeat if it fires too early.\n            self._heartbeat_cb = loop.call_at(when, self._send_heartbeat)\n\n    def _send_heartbeat(self) -> None:\n        self._heartbeat_cb = None\n        loop = self._loop\n        assert loop is not None and self._writer is not None\n        now = loop.time()\n        if now < self._heartbeat_when:\n            # Heartbeat fired too early, reschedule\n            self._heartbeat_cb = loop.call_at(\n                self._heartbeat_when, self._send_heartbeat\n            )\n            return\n\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold)\n        self._cancel_pong_response_cb()\n        self._pong_response_cb = loop.call_at(when, self._pong_not_received)\n\n        if sys.version_info >= (3, 12):\n            # Optimization for Python 3.12, try to send the ping\n            # immediately to avoid having to schedule\n            # the task on the event loop.\n            ping_task = asyncio.Task(self._writer.ping(), loop=loop, eager_start=True)\n        else:\n            ping_task = loop.create_task(self._writer.ping())\n\n        if not ping_task.done():\n            self._ping_task = ping_task\n            ping_task.add_done_callback(self._ping_task_done)\n\n    def _ping_task_done(self, task: \"asyncio.Task[None]\") -> None:\n        \"\"\"Callback for when the ping task completes.\"\"\"\n        self._ping_task = None\n\n    def _pong_not_received(self) -> None:\n        if self._req is not None and self._req.transport is not None:\n            self._set_closed()\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            self._exception = asyncio.TimeoutError()\n\n    def _set_closed(self) -> None:\n        \"\"\"Set the connection to closed.\n\n        Cancel any heartbeat timers and set the closed flag.\n        \"\"\"\n        self._closed = True\n        self._cancel_heartbeat()\n\n    async def prepare(self, request: BaseRequest) -> AbstractStreamWriter:\n        # make pre-check to don't hide it by do_handshake() exceptions\n        if self._payload_writer is not None:\n            return self._payload_writer\n\n        protocol, writer = self._pre_start(request)\n        payload_writer = await super().prepare(request)\n        assert payload_writer is not None\n        self._post_start(request, protocol, writer)\n        await payload_writer.drain()\n        return payload_writer\n\n    def _handshake(\n        self, request: BaseRequest\n    ) -> Tuple[\"CIMultiDict[str]\", str, bool, bool]:\n        headers = request.headers\n        if \"websocket\" != headers.get(hdrs.UPGRADE, \"\").lower().strip():\n            raise HTTPBadRequest(\n                text=(\n                    \"No WebSocket UPGRADE hdr: {}\\n Can \"\n                    '\"Upgrade\" only to \"WebSocket\".'\n                ).format(headers.get(hdrs.UPGRADE))\n            )\n\n        if \"upgrade\" not in headers.get(hdrs.CONNECTION, \"\").lower():\n            raise HTTPBadRequest(\n                text=\"No CONNECTION upgrade hdr: {}\".format(\n                    headers.get(hdrs.CONNECTION)\n                )\n            )\n\n        # find common sub-protocol between client and server\n        protocol = None\n        if hdrs.SEC_WEBSOCKET_PROTOCOL in headers:\n            req_protocols = [\n                str(proto.strip())\n                for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(\",\")\n            ]\n\n            for proto in req_protocols:\n                if proto in self._protocols:\n                    protocol = proto\n                    break\n            else:\n                # No overlap found: Return no protocol as per spec\n                ws_logger.warning(\n                    \"Client protocols %r don’t overlap server-known ones %r\",\n                    req_protocols,\n                    self._protocols,\n                )\n\n        # check supported version\n        version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, \"\")\n        if version not in (\"13\", \"8\", \"7\"):\n            raise HTTPBadRequest(text=f\"Unsupported version: {version}\")\n\n        # check client handshake for validity\n        key = headers.get(hdrs.SEC_WEBSOCKET_KEY)\n        try:\n            if not key or len(base64.b64decode(key)) != 16:\n                raise HTTPBadRequest(text=f\"Handshake error: {key!r}\")\n        except binascii.Error:\n            raise HTTPBadRequest(text=f\"Handshake error: {key!r}\") from None\n\n        accept_val = base64.b64encode(\n            hashlib.sha1(key.encode() + WS_KEY).digest()\n        ).decode()\n        response_headers = CIMultiDict(\n            {\n                hdrs.UPGRADE: \"websocket\",\n                hdrs.CONNECTION: \"upgrade\",\n                hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,\n            }\n        )\n\n        notakeover = False\n        compress = 0\n        if self._compress:\n            extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)\n            # Server side always get return with no exception.\n            # If something happened, just drop compress extension\n            compress, notakeover = ws_ext_parse(extensions, isserver=True)\n            if compress:\n                enabledext = ws_ext_gen(\n                    compress=compress, isserver=True, server_notakeover=notakeover\n                )\n                response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext\n\n        if protocol:\n            response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol\n        return (\n            response_headers,\n            protocol,\n            compress,\n            notakeover,\n        )  # type: ignore[return-value]\n\n    def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]:\n        self._loop = request._loop\n\n        headers, protocol, compress, notakeover = self._handshake(request)\n\n        self.set_status(101)\n        self.headers.update(headers)\n        self.force_close()\n        self._compress = compress\n        transport = request._protocol.transport\n        assert transport is not None\n        writer = WebSocketWriter(\n            request._protocol, transport, compress=compress, notakeover=notakeover\n        )\n\n        return protocol, writer\n\n    def _post_start(\n        self, request: BaseRequest, protocol: str, writer: WebSocketWriter\n    ) -> None:\n        self._ws_protocol = protocol\n        self._writer = writer\n\n        self._reset_heartbeat()\n\n        loop = self._loop\n        assert loop is not None\n        self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop)\n        request.protocol.set_parser(\n            WebSocketReader(self._reader, self._max_msg_size, compress=self._compress)\n        )\n        # disable HTTP keepalive for WebSocket\n        request.protocol.keep_alive(False)\n\n    def can_prepare(self, request: BaseRequest) -> WebSocketReady:\n        if self._writer is not None:\n            raise RuntimeError(\"Already started\")\n        try:\n            _, protocol, _, _ = self._handshake(request)\n        except HTTPException:\n            return WebSocketReady(False, None)\n        else:\n            return WebSocketReady(True, protocol)\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    @property\n    def close_code(self) -> Optional[int]:\n        return self._close_code\n\n    @property\n    def ws_protocol(self) -> Optional[str]:\n        return self._ws_protocol\n\n    @property\n    def compress(self) -> bool:\n        return self._compress\n\n    def get_extra_info(self, name: str, default: Any = None) -> Any:\n        \"\"\"Get optional transport information.\n\n        If no value associated with ``name`` is found, ``default`` is returned.\n        \"\"\"\n        writer = self._writer\n        if writer is None:\n            return default\n        transport = writer.transport\n        if transport is None:\n            return default\n        return transport.get_extra_info(name, default)\n\n    def exception(self) -> Optional[BaseException]:\n        return self._exception\n\n    async def ping(self, message: bytes = b\"\") -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.ping(message)\n\n    async def pong(self, message: bytes = b\"\") -> None:\n        # unsolicited pong\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.pong(message)\n\n    async def send_str(self, data: str, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, str):\n            raise TypeError(\"data argument must be str (%r)\" % type(data))\n        await self._writer.send(data, binary=False, compress=compress)\n\n    async def send_bytes(self, data: bytes, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError(\"data argument must be byte-ish (%r)\" % type(data))\n        await self._writer.send(data, binary=True, compress=compress)\n\n    async def send_json(\n        self,\n        data: Any,\n        compress: Optional[bool] = None,\n        *,\n        dumps: JSONEncoder = json.dumps,\n    ) -> None:\n        await self.send_str(dumps(data), compress=compress)\n\n    async def write_eof(self) -> None:  # type: ignore[override]\n        if self._eof_sent:\n            return\n        if self._payload_writer is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        await self.close()\n        self._eof_sent = True\n\n    async def close(\n        self, *, code: int = WSCloseCode.OK, message: bytes = b\"\", drain: bool = True\n    ) -> bool:\n        \"\"\"Close websocket connection.\"\"\"\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        self._cancel_heartbeat()\n        reader = self._reader\n        assert reader is not None\n\n        # we need to break `receive()` cycle first,\n        # `close()` may be called from different task\n        if self._waiting and not self._closed:\n            if not self._close_wait:\n                assert self._loop is not None\n                self._close_wait = self._loop.create_future()\n            reader.feed_data(WS_CLOSING_MESSAGE, 0)\n            await self._close_wait\n\n        if self._closed:\n            return False\n\n        self._set_closed()\n        try:\n            await self._writer.close(code, message)\n            writer = self._payload_writer\n            assert writer is not None\n            if drain:\n                await writer.drain()\n        except (asyncio.CancelledError, asyncio.TimeoutError):\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if self._closing:\n            self._close_transport()\n            return True\n\n        reader = self._reader\n        assert reader is not None\n        try:\n            async with async_timeout.timeout(self._timeout):\n                msg = await reader.read()\n        except asyncio.CancelledError:\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if msg.type is WSMsgType.CLOSE:\n            self._set_code_close_transport(msg.data)\n            return True\n\n        self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n        self._exception = asyncio.TimeoutError()\n        return True\n\n    def _set_closing(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and mark the connection as closing.\"\"\"\n        self._closing = True\n        self._close_code = code\n        self._cancel_heartbeat()\n\n    def _set_code_close_transport(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and close the transport.\"\"\"\n        self._close_code = code\n        self._close_transport()\n\n    def _close_transport(self) -> None:\n        \"\"\"Close the transport.\"\"\"\n        if self._req is not None and self._req.transport is not None:\n            self._req.transport.close()\n\n    async def receive(self, timeout: Optional[float] = None) -> WSMessage:\n        if self._reader is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        loop = self._loop\n        assert loop is not None\n        receive_timeout = timeout or self._receive_timeout\n        while True:\n            if self._waiting:\n                raise RuntimeError(\"Concurrent call to receive() is not allowed\")\n\n            if self._closed:\n                self._conn_lost += 1\n                if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS:\n                    raise RuntimeError(\"WebSocket connection is closed.\")\n                return WS_CLOSED_MESSAGE\n            elif self._closing:\n                return WS_CLOSING_MESSAGE\n\n            try:\n                self._waiting = True\n                try:\n                    if receive_timeout:\n                        # Entering the context manager and creating\n                        # Timeout() object can take almost 50% of the\n                        # run time in this loop so we avoid it if\n                        # there is no read timeout.\n                        async with async_timeout.timeout(receive_timeout):\n                            msg = await self._reader.read()\n                    else:\n                        msg = await self._reader.read()\n                    self._reset_heartbeat()\n                finally:\n                    self._waiting = False\n                    if self._close_wait:\n                        set_result(self._close_wait, None)\n            except asyncio.TimeoutError:\n                raise\n            except EofStream:\n                self._close_code = WSCloseCode.OK\n                await self.close()\n                return WSMessage(WSMsgType.CLOSED, None, None)\n            except WebSocketError as exc:\n                self._close_code = exc.code\n                await self.close(code=exc.code)\n                return WSMessage(WSMsgType.ERROR, exc, None)\n            except Exception as exc:\n                self._exception = exc\n                self._set_closing(WSCloseCode.ABNORMAL_CLOSURE)\n                await self.close()\n                return WSMessage(WSMsgType.ERROR, exc, None)\n\n            if msg.type is WSMsgType.CLOSE:\n                self._set_closing(msg.data)\n                # Could be closed while awaiting reader.\n                if not self._closed and self._autoclose:\n                    # The client is likely going to close the\n                    # connection out from under us so we do not\n                    # want to drain any pending writes as it will\n                    # likely result writing to a broken pipe.\n                    await self.close(drain=False)\n            elif msg.type is WSMsgType.CLOSING:\n                self._set_closing(WSCloseCode.OK)\n            elif msg.type is WSMsgType.PING and self._autoping:\n                await self.pong(msg.data)\n                continue\n            elif msg.type is WSMsgType.PONG and self._autoping:\n                continue\n\n            return msg\n\n    async def receive_str(self, *, timeout: Optional[float] = None) -> str:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.TEXT:\n            raise TypeError(\n                \"Received message {}:{!r} is not WSMsgType.TEXT\".format(\n                    msg.type, msg.data\n                )\n            )\n        return cast(str, msg.data)\n\n    async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.BINARY:\n            raise TypeError(f\"Received message {msg.type}:{msg.data!r} is not bytes\")\n        return cast(bytes, msg.data)\n\n    async def receive_json(\n        self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None\n    ) -> Any:\n        data = await self.receive_str(timeout=timeout)\n        return loads(data)\n\n    async def write(self, data: bytes) -> None:\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n    def __aiter__(self) -> \"WebSocketResponse\":\n        return self\n\n    async def __anext__(self) -> WSMessage:\n        msg = await self.receive()\n        if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):\n            raise StopAsyncIteration\n        return msg\n\n    def _cancel(self, exc: BaseException) -> None:\n        # web_protocol calls this from connection_lost\n        # or when the server is shutting down.\n        self._closing = True\n        self._cancel_heartbeat()\n        if self._reader is not None:\n            set_exception(self._reader, exc)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 402,
                            "end_lineno": 463,
                            "parent": {
                                "file_name": "aiohttp/web_ws.py",
                                "type": "class",
                                "name": "WebSocketResponse",
                                "content": "class WebSocketResponse(StreamResponse):\n\n    _length_check = False\n\n    def __init__(\n        self,\n        *,\n        timeout: float = 10.0,\n        receive_timeout: Optional[float] = None,\n        autoclose: bool = True,\n        autoping: bool = True,\n        heartbeat: Optional[float] = None,\n        protocols: Iterable[str] = (),\n        compress: bool = True,\n        max_msg_size: int = 4 * 1024 * 1024,\n    ) -> None:\n        super().__init__(status=101)\n        self._protocols = protocols\n        self._ws_protocol: Optional[str] = None\n        self._writer: Optional[WebSocketWriter] = None\n        self._reader: Optional[FlowControlDataQueue[WSMessage]] = None\n        self._closed = False\n        self._closing = False\n        self._conn_lost = 0\n        self._close_code: Optional[int] = None\n        self._loop: Optional[asyncio.AbstractEventLoop] = None\n        self._waiting: bool = False\n        self._close_wait: Optional[asyncio.Future[None]] = None\n        self._exception: Optional[BaseException] = None\n        self._timeout = timeout\n        self._receive_timeout = receive_timeout\n        self._autoclose = autoclose\n        self._autoping = autoping\n        self._heartbeat = heartbeat\n        self._heartbeat_when = 0.0\n        self._heartbeat_cb: Optional[asyncio.TimerHandle] = None\n        if heartbeat is not None:\n            self._pong_heartbeat = heartbeat / 2.0\n        self._pong_response_cb: Optional[asyncio.TimerHandle] = None\n        self._compress = compress\n        self._max_msg_size = max_msg_size\n        self._ping_task: Optional[asyncio.Task[None]] = None\n\n    def _cancel_heartbeat(self) -> None:\n        self._cancel_pong_response_cb()\n        if self._heartbeat_cb is not None:\n            self._heartbeat_cb.cancel()\n            self._heartbeat_cb = None\n        if self._ping_task is not None:\n            self._ping_task.cancel()\n            self._ping_task = None\n\n    def _cancel_pong_response_cb(self) -> None:\n        if self._pong_response_cb is not None:\n            self._pong_response_cb.cancel()\n            self._pong_response_cb = None\n\n    def _reset_heartbeat(self) -> None:\n        if self._heartbeat is None:\n            return\n        self._cancel_pong_response_cb()\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        loop = self._loop\n        assert loop is not None\n        now = loop.time()\n        when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold)\n        self._heartbeat_when = when\n        if self._heartbeat_cb is None:\n            # We do not cancel the previous heartbeat_cb here because\n            # it generates a significant amount of TimerHandle churn\n            # which causes asyncio to rebuild the heap frequently.\n            # Instead _send_heartbeat() will reschedule the next\n            # heartbeat if it fires too early.\n            self._heartbeat_cb = loop.call_at(when, self._send_heartbeat)\n\n    def _send_heartbeat(self) -> None:\n        self._heartbeat_cb = None\n        loop = self._loop\n        assert loop is not None and self._writer is not None\n        now = loop.time()\n        if now < self._heartbeat_when:\n            # Heartbeat fired too early, reschedule\n            self._heartbeat_cb = loop.call_at(\n                self._heartbeat_when, self._send_heartbeat\n            )\n            return\n\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold)\n        self._cancel_pong_response_cb()\n        self._pong_response_cb = loop.call_at(when, self._pong_not_received)\n\n        if sys.version_info >= (3, 12):\n            # Optimization for Python 3.12, try to send the ping\n            # immediately to avoid having to schedule\n            # the task on the event loop.\n            ping_task = asyncio.Task(self._writer.ping(), loop=loop, eager_start=True)\n        else:\n            ping_task = loop.create_task(self._writer.ping())\n\n        if not ping_task.done():\n            self._ping_task = ping_task\n            ping_task.add_done_callback(self._ping_task_done)\n\n    def _ping_task_done(self, task: \"asyncio.Task[None]\") -> None:\n        \"\"\"Callback for when the ping task completes.\"\"\"\n        self._ping_task = None\n\n    def _pong_not_received(self) -> None:\n        if self._req is not None and self._req.transport is not None:\n            self._set_closed()\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            self._exception = asyncio.TimeoutError()\n\n    def _set_closed(self) -> None:\n        \"\"\"Set the connection to closed.\n\n        Cancel any heartbeat timers and set the closed flag.\n        \"\"\"\n        self._closed = True\n        self._cancel_heartbeat()\n\n    async def prepare(self, request: BaseRequest) -> AbstractStreamWriter:\n        # make pre-check to don't hide it by do_handshake() exceptions\n        if self._payload_writer is not None:\n            return self._payload_writer\n\n        protocol, writer = self._pre_start(request)\n        payload_writer = await super().prepare(request)\n        assert payload_writer is not None\n        self._post_start(request, protocol, writer)\n        await payload_writer.drain()\n        return payload_writer\n\n    def _handshake(\n        self, request: BaseRequest\n    ) -> Tuple[\"CIMultiDict[str]\", str, bool, bool]:\n        headers = request.headers\n        if \"websocket\" != headers.get(hdrs.UPGRADE, \"\").lower().strip():\n            raise HTTPBadRequest(\n                text=(\n                    \"No WebSocket UPGRADE hdr: {}\\n Can \"\n                    '\"Upgrade\" only to \"WebSocket\".'\n                ).format(headers.get(hdrs.UPGRADE))\n            )\n\n        if \"upgrade\" not in headers.get(hdrs.CONNECTION, \"\").lower():\n            raise HTTPBadRequest(\n                text=\"No CONNECTION upgrade hdr: {}\".format(\n                    headers.get(hdrs.CONNECTION)\n                )\n            )\n\n        # find common sub-protocol between client and server\n        protocol = None\n        if hdrs.SEC_WEBSOCKET_PROTOCOL in headers:\n            req_protocols = [\n                str(proto.strip())\n                for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(\",\")\n            ]\n\n            for proto in req_protocols:\n                if proto in self._protocols:\n                    protocol = proto\n                    break\n            else:\n                # No overlap found: Return no protocol as per spec\n                ws_logger.warning(\n                    \"Client protocols %r don’t overlap server-known ones %r\",\n                    req_protocols,\n                    self._protocols,\n                )\n\n        # check supported version\n        version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, \"\")\n        if version not in (\"13\", \"8\", \"7\"):\n            raise HTTPBadRequest(text=f\"Unsupported version: {version}\")\n\n        # check client handshake for validity\n        key = headers.get(hdrs.SEC_WEBSOCKET_KEY)\n        try:\n            if not key or len(base64.b64decode(key)) != 16:\n                raise HTTPBadRequest(text=f\"Handshake error: {key!r}\")\n        except binascii.Error:\n            raise HTTPBadRequest(text=f\"Handshake error: {key!r}\") from None\n\n        accept_val = base64.b64encode(\n            hashlib.sha1(key.encode() + WS_KEY).digest()\n        ).decode()\n        response_headers = CIMultiDict(\n            {\n                hdrs.UPGRADE: \"websocket\",\n                hdrs.CONNECTION: \"upgrade\",\n                hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,\n            }\n        )\n\n        notakeover = False\n        compress = 0\n        if self._compress:\n            extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)\n            # Server side always get return with no exception.\n            # If something happened, just drop compress extension\n            compress, notakeover = ws_ext_parse(extensions, isserver=True)\n            if compress:\n                enabledext = ws_ext_gen(\n                    compress=compress, isserver=True, server_notakeover=notakeover\n                )\n                response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext\n\n        if protocol:\n            response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol\n        return (\n            response_headers,\n            protocol,\n            compress,\n            notakeover,\n        )  # type: ignore[return-value]\n\n    def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]:\n        self._loop = request._loop\n\n        headers, protocol, compress, notakeover = self._handshake(request)\n\n        self.set_status(101)\n        self.headers.update(headers)\n        self.force_close()\n        self._compress = compress\n        transport = request._protocol.transport\n        assert transport is not None\n        writer = WebSocketWriter(\n            request._protocol, transport, compress=compress, notakeover=notakeover\n        )\n\n        return protocol, writer\n\n    def _post_start(\n        self, request: BaseRequest, protocol: str, writer: WebSocketWriter\n    ) -> None:\n        self._ws_protocol = protocol\n        self._writer = writer\n\n        self._reset_heartbeat()\n\n        loop = self._loop\n        assert loop is not None\n        self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop)\n        request.protocol.set_parser(\n            WebSocketReader(self._reader, self._max_msg_size, compress=self._compress)\n        )\n        # disable HTTP keepalive for WebSocket\n        request.protocol.keep_alive(False)\n\n    def can_prepare(self, request: BaseRequest) -> WebSocketReady:\n        if self._writer is not None:\n            raise RuntimeError(\"Already started\")\n        try:\n            _, protocol, _, _ = self._handshake(request)\n        except HTTPException:\n            return WebSocketReady(False, None)\n        else:\n            return WebSocketReady(True, protocol)\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    @property\n    def close_code(self) -> Optional[int]:\n        return self._close_code\n\n    @property\n    def ws_protocol(self) -> Optional[str]:\n        return self._ws_protocol\n\n    @property\n    def compress(self) -> bool:\n        return self._compress\n\n    def get_extra_info(self, name: str, default: Any = None) -> Any:\n        \"\"\"Get optional transport information.\n\n        If no value associated with ``name`` is found, ``default`` is returned.\n        \"\"\"\n        writer = self._writer\n        if writer is None:\n            return default\n        transport = writer.transport\n        if transport is None:\n            return default\n        return transport.get_extra_info(name, default)\n\n    def exception(self) -> Optional[BaseException]:\n        return self._exception\n\n    async def ping(self, message: bytes = b\"\") -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.ping(message)\n\n    async def pong(self, message: bytes = b\"\") -> None:\n        # unsolicited pong\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.pong(message)\n\n    async def send_str(self, data: str, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, str):\n            raise TypeError(\"data argument must be str (%r)\" % type(data))\n        await self._writer.send(data, binary=False, compress=compress)\n\n    async def send_bytes(self, data: bytes, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError(\"data argument must be byte-ish (%r)\" % type(data))\n        await self._writer.send(data, binary=True, compress=compress)\n\n    async def send_json(\n        self,\n        data: Any,\n        compress: Optional[bool] = None,\n        *,\n        dumps: JSONEncoder = json.dumps,\n    ) -> None:\n        await self.send_str(dumps(data), compress=compress)\n\n    async def write_eof(self) -> None:  # type: ignore[override]\n        if self._eof_sent:\n            return\n        if self._payload_writer is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        await self.close()\n        self._eof_sent = True\n\n    async def close(\n        self, *, code: int = WSCloseCode.OK, message: bytes = b\"\", drain: bool = True\n    ) -> bool:\n        \"\"\"Close websocket connection.\"\"\"\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        self._cancel_heartbeat()\n        reader = self._reader\n        assert reader is not None\n\n        # we need to break `receive()` cycle first,\n        # `close()` may be called from different task\n        if self._waiting and not self._closed:\n            if not self._close_wait:\n                assert self._loop is not None\n                self._close_wait = self._loop.create_future()\n            reader.feed_data(WS_CLOSING_MESSAGE, 0)\n            await self._close_wait\n\n        if self._closed:\n            return False\n\n        self._set_closed()\n        try:\n            await self._writer.close(code, message)\n            writer = self._payload_writer\n            assert writer is not None\n            if drain:\n                await writer.drain()\n        except (asyncio.CancelledError, asyncio.TimeoutError):\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if self._closing:\n            self._close_transport()\n            return True\n\n        reader = self._reader\n        assert reader is not None\n        try:\n            async with async_timeout.timeout(self._timeout):\n                msg = await reader.read()\n        except asyncio.CancelledError:\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if msg.type is WSMsgType.CLOSE:\n            self._set_code_close_transport(msg.data)\n            return True\n\n        self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n        self._exception = asyncio.TimeoutError()\n        return True\n\n    def _set_closing(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and mark the connection as closing.\"\"\"\n        self._closing = True\n        self._close_code = code\n        self._cancel_heartbeat()\n\n    def _set_code_close_transport(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and close the transport.\"\"\"\n        self._close_code = code\n        self._close_transport()\n\n    def _close_transport(self) -> None:\n        \"\"\"Close the transport.\"\"\"\n        if self._req is not None and self._req.transport is not None:\n            self._req.transport.close()\n\n    async def receive(self, timeout: Optional[float] = None) -> WSMessage:\n        if self._reader is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        loop = self._loop\n        assert loop is not None\n        receive_timeout = timeout or self._receive_timeout\n        while True:\n            if self._waiting:\n                raise RuntimeError(\"Concurrent call to receive() is not allowed\")\n\n            if self._closed:\n                self._conn_lost += 1\n                if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS:\n                    raise RuntimeError(\"WebSocket connection is closed.\")\n                return WS_CLOSED_MESSAGE\n            elif self._closing:\n                return WS_CLOSING_MESSAGE\n\n            try:\n                self._waiting = True\n                try:\n                    if receive_timeout:\n                        # Entering the context manager and creating\n                        # Timeout() object can take almost 50% of the\n                        # run time in this loop so we avoid it if\n                        # there is no read timeout.\n                        async with async_timeout.timeout(receive_timeout):\n                            msg = await self._reader.read()\n                    else:\n                        msg = await self._reader.read()\n                    self._reset_heartbeat()\n                finally:\n                    self._waiting = False\n                    if self._close_wait:\n                        set_result(self._close_wait, None)\n            except asyncio.TimeoutError:\n                raise\n            except EofStream:\n                self._close_code = WSCloseCode.OK\n                await self.close()\n                return WSMessage(WSMsgType.CLOSED, None, None)\n            except WebSocketError as exc:\n                self._close_code = exc.code\n                await self.close(code=exc.code)\n                return WSMessage(WSMsgType.ERROR, exc, None)\n            except Exception as exc:\n                self._exception = exc\n                self._set_closing(WSCloseCode.ABNORMAL_CLOSURE)\n                await self.close()\n                return WSMessage(WSMsgType.ERROR, exc, None)\n\n            if msg.type is WSMsgType.CLOSE:\n                self._set_closing(msg.data)\n                # Could be closed while awaiting reader.\n                if not self._closed and self._autoclose:\n                    # The client is likely going to close the\n                    # connection out from under us so we do not\n                    # want to drain any pending writes as it will\n                    # likely result writing to a broken pipe.\n                    await self.close(drain=False)\n            elif msg.type is WSMsgType.CLOSING:\n                self._set_closing(WSCloseCode.OK)\n            elif msg.type is WSMsgType.PING and self._autoping:\n                await self.pong(msg.data)\n                continue\n            elif msg.type is WSMsgType.PONG and self._autoping:\n                continue\n\n            return msg\n\n    async def receive_str(self, *, timeout: Optional[float] = None) -> str:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.TEXT:\n            raise TypeError(\n                \"Received message {}:{!r} is not WSMsgType.TEXT\".format(\n                    msg.type, msg.data\n                )\n            )\n        return cast(str, msg.data)\n\n    async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.BINARY:\n            raise TypeError(f\"Received message {msg.type}:{msg.data!r} is not bytes\")\n        return cast(bytes, msg.data)\n\n    async def receive_json(\n        self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None\n    ) -> Any:\n        data = await self.receive_str(timeout=timeout)\n        return loads(data)\n\n    async def write(self, data: bytes) -> None:\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n    def __aiter__(self) -> \"WebSocketResponse\":\n        return self\n\n    async def __anext__(self) -> WSMessage:\n        msg = await self.receive()\n        if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):\n            raise StopAsyncIteration\n        return msg\n\n    def _cancel(self, exc: BaseException) -> None:\n        # web_protocol calls this from connection_lost\n        # or when the server is shutting down.\n        self._closing = True\n        self._cancel_heartbeat()\n        if self._reader is not None:\n            set_exception(self._reader, exc)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 58,
                                "end_lineno": 592,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 437,
                            "length": 12
                        },
                        "new_range": {
                            "start": 424,
                            "length": 21
                        },
                        "section": "class WebSocketResponse(StreamResponse):"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)",
                                "type": "context"
                            },
                            {
                                "content": "            return True",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        reader = self._reader",
                                "type": "added"
                            },
                            {
                                "content": "        assert reader is not None",
                                "type": "added"
                            },
                            {
                                "content": "        # we need to break `receive()` cycle before we can call",
                                "type": "added"
                            },
                            {
                                "content": "        # `reader.read()` as `close()` may be called from different task",
                                "type": "added"
                            },
                            {
                                "content": "        if self._waiting:",
                                "type": "added"
                            },
                            {
                                "content": "            assert self._loop is not None",
                                "type": "added"
                            },
                            {
                                "content": "            assert self._close_wait is None",
                                "type": "added"
                            },
                            {
                                "content": "            self._close_wait = self._loop.create_future()",
                                "type": "added"
                            },
                            {
                                "content": "            reader.feed_data(WS_CLOSING_MESSAGE)",
                                "type": "added"
                            },
                            {
                                "content": "            await self._close_wait",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        if self._closing:",
                                "type": "context"
                            },
                            {
                                "content": "            self._close_transport()",
                                "type": "context"
                            },
                            {
                                "content": "            return True",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "        reader = self._reader",
                                "type": "deleted"
                            },
                            {
                                "content": "        assert reader is not None",
                                "type": "deleted"
                            },
                            {
                                "content": "        try:",
                                "type": "context"
                            },
                            {
                                "content": "            async with async_timeout.timeout(self._timeout):",
                                "type": "context"
                            },
                            {
                                "content": "                msg = await reader.read()",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [
                        {
                            "file_name": "aiohttp/web_ws.py",
                            "type": "class",
                            "name": "WebSocketResponse",
                            "content": "class WebSocketResponse(StreamResponse):\n\n    _length_check = False\n\n    def __init__(\n        self,\n        *,\n        timeout: float = 10.0,\n        receive_timeout: Optional[float] = None,\n        autoclose: bool = True,\n        autoping: bool = True,\n        heartbeat: Optional[float] = None,\n        protocols: Iterable[str] = (),\n        compress: bool = True,\n        max_msg_size: int = 4 * 1024 * 1024,\n    ) -> None:\n        super().__init__(status=101)\n        self._protocols = protocols\n        self._ws_protocol: Optional[str] = None\n        self._writer: Optional[WebSocketWriter] = None\n        self._reader: Optional[FlowControlDataQueue[WSMessage]] = None\n        self._closed = False\n        self._closing = False\n        self._conn_lost = 0\n        self._close_code: Optional[int] = None\n        self._loop: Optional[asyncio.AbstractEventLoop] = None\n        self._waiting: bool = False\n        self._close_wait: Optional[asyncio.Future[None]] = None\n        self._exception: Optional[BaseException] = None\n        self._timeout = timeout\n        self._receive_timeout = receive_timeout\n        self._autoclose = autoclose\n        self._autoping = autoping\n        self._heartbeat = heartbeat\n        self._heartbeat_when = 0.0\n        self._heartbeat_cb: Optional[asyncio.TimerHandle] = None\n        if heartbeat is not None:\n            self._pong_heartbeat = heartbeat / 2.0\n        self._pong_response_cb: Optional[asyncio.TimerHandle] = None\n        self._compress = compress\n        self._max_msg_size = max_msg_size\n        self._ping_task: Optional[asyncio.Task[None]] = None\n\n    def _cancel_heartbeat(self) -> None:\n        self._cancel_pong_response_cb()\n        if self._heartbeat_cb is not None:\n            self._heartbeat_cb.cancel()\n            self._heartbeat_cb = None\n        if self._ping_task is not None:\n            self._ping_task.cancel()\n            self._ping_task = None\n\n    def _cancel_pong_response_cb(self) -> None:\n        if self._pong_response_cb is not None:\n            self._pong_response_cb.cancel()\n            self._pong_response_cb = None\n\n    def _reset_heartbeat(self) -> None:\n        if self._heartbeat is None:\n            return\n        self._cancel_pong_response_cb()\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        loop = self._loop\n        assert loop is not None\n        now = loop.time()\n        when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold)\n        self._heartbeat_when = when\n        if self._heartbeat_cb is None:\n            # We do not cancel the previous heartbeat_cb here because\n            # it generates a significant amount of TimerHandle churn\n            # which causes asyncio to rebuild the heap frequently.\n            # Instead _send_heartbeat() will reschedule the next\n            # heartbeat if it fires too early.\n            self._heartbeat_cb = loop.call_at(when, self._send_heartbeat)\n\n    def _send_heartbeat(self) -> None:\n        self._heartbeat_cb = None\n        loop = self._loop\n        assert loop is not None and self._writer is not None\n        now = loop.time()\n        if now < self._heartbeat_when:\n            # Heartbeat fired too early, reschedule\n            self._heartbeat_cb = loop.call_at(\n                self._heartbeat_when, self._send_heartbeat\n            )\n            return\n\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold)\n        self._cancel_pong_response_cb()\n        self._pong_response_cb = loop.call_at(when, self._pong_not_received)\n\n        if sys.version_info >= (3, 12):\n            # Optimization for Python 3.12, try to send the ping\n            # immediately to avoid having to schedule\n            # the task on the event loop.\n            ping_task = asyncio.Task(self._writer.ping(), loop=loop, eager_start=True)\n        else:\n            ping_task = loop.create_task(self._writer.ping())\n\n        if not ping_task.done():\n            self._ping_task = ping_task\n            ping_task.add_done_callback(self._ping_task_done)\n\n    def _ping_task_done(self, task: \"asyncio.Task[None]\") -> None:\n        \"\"\"Callback for when the ping task completes.\"\"\"\n        self._ping_task = None\n\n    def _pong_not_received(self) -> None:\n        if self._req is not None and self._req.transport is not None:\n            self._set_closed()\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            self._exception = asyncio.TimeoutError()\n\n    def _set_closed(self) -> None:\n        \"\"\"Set the connection to closed.\n\n        Cancel any heartbeat timers and set the closed flag.\n        \"\"\"\n        self._closed = True\n        self._cancel_heartbeat()\n\n    async def prepare(self, request: BaseRequest) -> AbstractStreamWriter:\n        # make pre-check to don't hide it by do_handshake() exceptions\n        if self._payload_writer is not None:\n            return self._payload_writer\n\n        protocol, writer = self._pre_start(request)\n        payload_writer = await super().prepare(request)\n        assert payload_writer is not None\n        self._post_start(request, protocol, writer)\n        await payload_writer.drain()\n        return payload_writer\n\n    def _handshake(\n        self, request: BaseRequest\n    ) -> Tuple[\"CIMultiDict[str]\", str, bool, bool]:\n        headers = request.headers\n        if \"websocket\" != headers.get(hdrs.UPGRADE, \"\").lower().strip():\n            raise HTTPBadRequest(\n                text=(\n                    \"No WebSocket UPGRADE hdr: {}\\n Can \"\n                    '\"Upgrade\" only to \"WebSocket\".'\n                ).format(headers.get(hdrs.UPGRADE))\n            )\n\n        if \"upgrade\" not in headers.get(hdrs.CONNECTION, \"\").lower():\n            raise HTTPBadRequest(\n                text=\"No CONNECTION upgrade hdr: {}\".format(\n                    headers.get(hdrs.CONNECTION)\n                )\n            )\n\n        # find common sub-protocol between client and server\n        protocol = None\n        if hdrs.SEC_WEBSOCKET_PROTOCOL in headers:\n            req_protocols = [\n                str(proto.strip())\n                for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(\",\")\n            ]\n\n            for proto in req_protocols:\n                if proto in self._protocols:\n                    protocol = proto\n                    break\n            else:\n                # No overlap found: Return no protocol as per spec\n                ws_logger.warning(\n                    \"Client protocols %r don’t overlap server-known ones %r\",\n                    req_protocols,\n                    self._protocols,\n                )\n\n        # check supported version\n        version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, \"\")\n        if version not in (\"13\", \"8\", \"7\"):\n            raise HTTPBadRequest(text=f\"Unsupported version: {version}\")\n\n        # check client handshake for validity\n        key = headers.get(hdrs.SEC_WEBSOCKET_KEY)\n        try:\n            if not key or len(base64.b64decode(key)) != 16:\n                raise HTTPBadRequest(text=f\"Handshake error: {key!r}\")\n        except binascii.Error:\n            raise HTTPBadRequest(text=f\"Handshake error: {key!r}\") from None\n\n        accept_val = base64.b64encode(\n            hashlib.sha1(key.encode() + WS_KEY).digest()\n        ).decode()\n        response_headers = CIMultiDict(\n            {\n                hdrs.UPGRADE: \"websocket\",\n                hdrs.CONNECTION: \"upgrade\",\n                hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,\n            }\n        )\n\n        notakeover = False\n        compress = 0\n        if self._compress:\n            extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)\n            # Server side always get return with no exception.\n            # If something happened, just drop compress extension\n            compress, notakeover = ws_ext_parse(extensions, isserver=True)\n            if compress:\n                enabledext = ws_ext_gen(\n                    compress=compress, isserver=True, server_notakeover=notakeover\n                )\n                response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext\n\n        if protocol:\n            response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol\n        return (\n            response_headers,\n            protocol,\n            compress,\n            notakeover,\n        )  # type: ignore[return-value]\n\n    def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]:\n        self._loop = request._loop\n\n        headers, protocol, compress, notakeover = self._handshake(request)\n\n        self.set_status(101)\n        self.headers.update(headers)\n        self.force_close()\n        self._compress = compress\n        transport = request._protocol.transport\n        assert transport is not None\n        writer = WebSocketWriter(\n            request._protocol, transport, compress=compress, notakeover=notakeover\n        )\n\n        return protocol, writer\n\n    def _post_start(\n        self, request: BaseRequest, protocol: str, writer: WebSocketWriter\n    ) -> None:\n        self._ws_protocol = protocol\n        self._writer = writer\n\n        self._reset_heartbeat()\n\n        loop = self._loop\n        assert loop is not None\n        self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop)\n        request.protocol.set_parser(\n            WebSocketReader(self._reader, self._max_msg_size, compress=self._compress)\n        )\n        # disable HTTP keepalive for WebSocket\n        request.protocol.keep_alive(False)\n\n    def can_prepare(self, request: BaseRequest) -> WebSocketReady:\n        if self._writer is not None:\n            raise RuntimeError(\"Already started\")\n        try:\n            _, protocol, _, _ = self._handshake(request)\n        except HTTPException:\n            return WebSocketReady(False, None)\n        else:\n            return WebSocketReady(True, protocol)\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    @property\n    def close_code(self) -> Optional[int]:\n        return self._close_code\n\n    @property\n    def ws_protocol(self) -> Optional[str]:\n        return self._ws_protocol\n\n    @property\n    def compress(self) -> bool:\n        return self._compress\n\n    def get_extra_info(self, name: str, default: Any = None) -> Any:\n        \"\"\"Get optional transport information.\n\n        If no value associated with ``name`` is found, ``default`` is returned.\n        \"\"\"\n        writer = self._writer\n        if writer is None:\n            return default\n        transport = writer.transport\n        if transport is None:\n            return default\n        return transport.get_extra_info(name, default)\n\n    def exception(self) -> Optional[BaseException]:\n        return self._exception\n\n    async def ping(self, message: bytes = b\"\") -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.ping(message)\n\n    async def pong(self, message: bytes = b\"\") -> None:\n        # unsolicited pong\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.pong(message)\n\n    async def send_str(self, data: str, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, str):\n            raise TypeError(\"data argument must be str (%r)\" % type(data))\n        await self._writer.send(data, binary=False, compress=compress)\n\n    async def send_bytes(self, data: bytes, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError(\"data argument must be byte-ish (%r)\" % type(data))\n        await self._writer.send(data, binary=True, compress=compress)\n\n    async def send_json(\n        self,\n        data: Any,\n        compress: Optional[bool] = None,\n        *,\n        dumps: JSONEncoder = json.dumps,\n    ) -> None:\n        await self.send_str(dumps(data), compress=compress)\n\n    async def write_eof(self) -> None:  # type: ignore[override]\n        if self._eof_sent:\n            return\n        if self._payload_writer is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        await self.close()\n        self._eof_sent = True\n\n    async def close(\n        self, *, code: int = WSCloseCode.OK, message: bytes = b\"\", drain: bool = True\n    ) -> bool:\n        \"\"\"Close websocket connection.\"\"\"\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        self._cancel_heartbeat()\n        reader = self._reader\n        assert reader is not None\n\n        # we need to break `receive()` cycle first,\n        # `close()` may be called from different task\n        if self._waiting and not self._closed:\n            if not self._close_wait:\n                assert self._loop is not None\n                self._close_wait = self._loop.create_future()\n            reader.feed_data(WS_CLOSING_MESSAGE, 0)\n            await self._close_wait\n\n        if self._closed:\n            return False\n\n        self._set_closed()\n        try:\n            await self._writer.close(code, message)\n            writer = self._payload_writer\n            assert writer is not None\n            if drain:\n                await writer.drain()\n        except (asyncio.CancelledError, asyncio.TimeoutError):\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if self._closing:\n            self._close_transport()\n            return True\n\n        reader = self._reader\n        assert reader is not None\n        try:\n            async with async_timeout.timeout(self._timeout):\n                msg = await reader.read()\n        except asyncio.CancelledError:\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if msg.type is WSMsgType.CLOSE:\n            self._set_code_close_transport(msg.data)\n            return True\n\n        self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n        self._exception = asyncio.TimeoutError()\n        return True\n\n    def _set_closing(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and mark the connection as closing.\"\"\"\n        self._closing = True\n        self._close_code = code\n        self._cancel_heartbeat()\n\n    def _set_code_close_transport(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and close the transport.\"\"\"\n        self._close_code = code\n        self._close_transport()\n\n    def _close_transport(self) -> None:\n        \"\"\"Close the transport.\"\"\"\n        if self._req is not None and self._req.transport is not None:\n            self._req.transport.close()\n\n    async def receive(self, timeout: Optional[float] = None) -> WSMessage:\n        if self._reader is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        loop = self._loop\n        assert loop is not None\n        receive_timeout = timeout or self._receive_timeout\n        while True:\n            if self._waiting:\n                raise RuntimeError(\"Concurrent call to receive() is not allowed\")\n\n            if self._closed:\n                self._conn_lost += 1\n                if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS:\n                    raise RuntimeError(\"WebSocket connection is closed.\")\n                return WS_CLOSED_MESSAGE\n            elif self._closing:\n                return WS_CLOSING_MESSAGE\n\n            try:\n                self._waiting = True\n                try:\n                    if receive_timeout:\n                        # Entering the context manager and creating\n                        # Timeout() object can take almost 50% of the\n                        # run time in this loop so we avoid it if\n                        # there is no read timeout.\n                        async with async_timeout.timeout(receive_timeout):\n                            msg = await self._reader.read()\n                    else:\n                        msg = await self._reader.read()\n                    self._reset_heartbeat()\n                finally:\n                    self._waiting = False\n                    if self._close_wait:\n                        set_result(self._close_wait, None)\n            except asyncio.TimeoutError:\n                raise\n            except EofStream:\n                self._close_code = WSCloseCode.OK\n                await self.close()\n                return WSMessage(WSMsgType.CLOSED, None, None)\n            except WebSocketError as exc:\n                self._close_code = exc.code\n                await self.close(code=exc.code)\n                return WSMessage(WSMsgType.ERROR, exc, None)\n            except Exception as exc:\n                self._exception = exc\n                self._set_closing(WSCloseCode.ABNORMAL_CLOSURE)\n                await self.close()\n                return WSMessage(WSMsgType.ERROR, exc, None)\n\n            if msg.type is WSMsgType.CLOSE:\n                self._set_closing(msg.data)\n                # Could be closed while awaiting reader.\n                if not self._closed and self._autoclose:\n                    # The client is likely going to close the\n                    # connection out from under us so we do not\n                    # want to drain any pending writes as it will\n                    # likely result writing to a broken pipe.\n                    await self.close(drain=False)\n            elif msg.type is WSMsgType.CLOSING:\n                self._set_closing(WSCloseCode.OK)\n            elif msg.type is WSMsgType.PING and self._autoping:\n                await self.pong(msg.data)\n                continue\n            elif msg.type is WSMsgType.PONG and self._autoping:\n                continue\n\n            return msg\n\n    async def receive_str(self, *, timeout: Optional[float] = None) -> str:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.TEXT:\n            raise TypeError(\n                \"Received message {}:{!r} is not WSMsgType.TEXT\".format(\n                    msg.type, msg.data\n                )\n            )\n        return cast(str, msg.data)\n\n    async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.BINARY:\n            raise TypeError(f\"Received message {msg.type}:{msg.data!r} is not bytes\")\n        return cast(bytes, msg.data)\n\n    async def receive_json(\n        self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None\n    ) -> Any:\n        data = await self.receive_str(timeout=timeout)\n        return loads(data)\n\n    async def write(self, data: bytes) -> None:\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n    def __aiter__(self) -> \"WebSocketResponse\":\n        return self\n\n    async def __anext__(self) -> WSMessage:\n        msg = await self.receive()\n        if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):\n            raise StopAsyncIteration\n        return msg\n\n    def _cancel(self, exc: BaseException) -> None:\n        # web_protocol calls this from connection_lost\n        # or when the server is shutting down.\n        self._closing = True\n        self._cancel_heartbeat()\n        if self._reader is not None:\n            set_exception(self._reader, exc)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 58,
                            "end_lineno": 592,
                            "parent": null
                        },
                        {
                            "file_name": "aiohttp/web_ws.py",
                            "type": "method",
                            "name": "WebSocketResponse.close",
                            "content": "class WebSocketResponse(StreamResponse):\n\n    _length_check = False\n\n    def __init__(\n        self,\n        *,\n        timeout: float = 10.0,\n        receive_timeout: Optional[float] = None,\n        autoclose: bool = True,\n        autoping: bool = True,\n        heartbeat: Optional[float] = None,\n        protocols: Iterable[str] = (),\n        compress: bool = True,\n        max_msg_size: int = 4 * 1024 * 1024,\n    ) -> None:\n        super().__init__(status=101)\n        self._protocols = protocols\n        self._ws_protocol: Optional[str] = None\n        self._writer: Optional[WebSocketWriter] = None\n        self._reader: Optional[FlowControlDataQueue[WSMessage]] = None\n        self._closed = False\n        self._closing = False\n        self._conn_lost = 0\n        self._close_code: Optional[int] = None\n        self._loop: Optional[asyncio.AbstractEventLoop] = None\n        self._waiting: bool = False\n        self._close_wait: Optional[asyncio.Future[None]] = None\n        self._exception: Optional[BaseException] = None\n        self._timeout = timeout\n        self._receive_timeout = receive_timeout\n        self._autoclose = autoclose\n        self._autoping = autoping\n        self._heartbeat = heartbeat\n        self._heartbeat_when = 0.0\n        self._heartbeat_cb: Optional[asyncio.TimerHandle] = None\n        if heartbeat is not None:\n            self._pong_heartbeat = heartbeat / 2.0\n        self._pong_response_cb: Optional[asyncio.TimerHandle] = None\n        self._compress = compress\n        self._max_msg_size = max_msg_size\n        self._ping_task: Optional[asyncio.Task[None]] = None\n\n    def _cancel_heartbeat(self) -> None:\n        self._cancel_pong_response_cb()\n        if self._heartbeat_cb is not None:\n            self._heartbeat_cb.cancel()\n            self._heartbeat_cb = None\n        if self._ping_task is not None:\n            self._ping_task.cancel()\n            self._ping_task = None\n\n    def _cancel_pong_response_cb(self) -> None:\n        if self._pong_response_cb is not None:\n            self._pong_response_cb.cancel()\n            self._pong_response_cb = None\n\n    def _reset_heartbeat(self) -> None:\n        if self._heartbeat is None:\n            return\n        self._cancel_pong_response_cb()\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        loop = self._loop\n        assert loop is not None\n        now = loop.time()\n        when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold)\n        self._heartbeat_when = when\n        if self._heartbeat_cb is None:\n            # We do not cancel the previous heartbeat_cb here because\n            # it generates a significant amount of TimerHandle churn\n            # which causes asyncio to rebuild the heap frequently.\n            # Instead _send_heartbeat() will reschedule the next\n            # heartbeat if it fires too early.\n            self._heartbeat_cb = loop.call_at(when, self._send_heartbeat)\n\n    def _send_heartbeat(self) -> None:\n        self._heartbeat_cb = None\n        loop = self._loop\n        assert loop is not None and self._writer is not None\n        now = loop.time()\n        if now < self._heartbeat_when:\n            # Heartbeat fired too early, reschedule\n            self._heartbeat_cb = loop.call_at(\n                self._heartbeat_when, self._send_heartbeat\n            )\n            return\n\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold)\n        self._cancel_pong_response_cb()\n        self._pong_response_cb = loop.call_at(when, self._pong_not_received)\n\n        if sys.version_info >= (3, 12):\n            # Optimization for Python 3.12, try to send the ping\n            # immediately to avoid having to schedule\n            # the task on the event loop.\n            ping_task = asyncio.Task(self._writer.ping(), loop=loop, eager_start=True)\n        else:\n            ping_task = loop.create_task(self._writer.ping())\n\n        if not ping_task.done():\n            self._ping_task = ping_task\n            ping_task.add_done_callback(self._ping_task_done)\n\n    def _ping_task_done(self, task: \"asyncio.Task[None]\") -> None:\n        \"\"\"Callback for when the ping task completes.\"\"\"\n        self._ping_task = None\n\n    def _pong_not_received(self) -> None:\n        if self._req is not None and self._req.transport is not None:\n            self._set_closed()\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            self._exception = asyncio.TimeoutError()\n\n    def _set_closed(self) -> None:\n        \"\"\"Set the connection to closed.\n\n        Cancel any heartbeat timers and set the closed flag.\n        \"\"\"\n        self._closed = True\n        self._cancel_heartbeat()\n\n    async def prepare(self, request: BaseRequest) -> AbstractStreamWriter:\n        # make pre-check to don't hide it by do_handshake() exceptions\n        if self._payload_writer is not None:\n            return self._payload_writer\n\n        protocol, writer = self._pre_start(request)\n        payload_writer = await super().prepare(request)\n        assert payload_writer is not None\n        self._post_start(request, protocol, writer)\n        await payload_writer.drain()\n        return payload_writer\n\n    def _handshake(\n        self, request: BaseRequest\n    ) -> Tuple[\"CIMultiDict[str]\", str, bool, bool]:\n        headers = request.headers\n        if \"websocket\" != headers.get(hdrs.UPGRADE, \"\").lower().strip():\n            raise HTTPBadRequest(\n                text=(\n                    \"No WebSocket UPGRADE hdr: {}\\n Can \"\n                    '\"Upgrade\" only to \"WebSocket\".'\n                ).format(headers.get(hdrs.UPGRADE))\n            )\n\n        if \"upgrade\" not in headers.get(hdrs.CONNECTION, \"\").lower():\n            raise HTTPBadRequest(\n                text=\"No CONNECTION upgrade hdr: {}\".format(\n                    headers.get(hdrs.CONNECTION)\n                )\n            )\n\n        # find common sub-protocol between client and server\n        protocol = None\n        if hdrs.SEC_WEBSOCKET_PROTOCOL in headers:\n            req_protocols = [\n                str(proto.strip())\n                for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(\",\")\n            ]\n\n            for proto in req_protocols:\n                if proto in self._protocols:\n                    protocol = proto\n                    break\n            else:\n                # No overlap found: Return no protocol as per spec\n                ws_logger.warning(\n                    \"Client protocols %r don’t overlap server-known ones %r\",\n                    req_protocols,\n                    self._protocols,\n                )\n\n        # check supported version\n        version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, \"\")\n        if version not in (\"13\", \"8\", \"7\"):\n            raise HTTPBadRequest(text=f\"Unsupported version: {version}\")\n\n        # check client handshake for validity\n        key = headers.get(hdrs.SEC_WEBSOCKET_KEY)\n        try:\n            if not key or len(base64.b64decode(key)) != 16:\n                raise HTTPBadRequest(text=f\"Handshake error: {key!r}\")\n        except binascii.Error:\n            raise HTTPBadRequest(text=f\"Handshake error: {key!r}\") from None\n\n        accept_val = base64.b64encode(\n            hashlib.sha1(key.encode() + WS_KEY).digest()\n        ).decode()\n        response_headers = CIMultiDict(\n            {\n                hdrs.UPGRADE: \"websocket\",\n                hdrs.CONNECTION: \"upgrade\",\n                hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,\n            }\n        )\n\n        notakeover = False\n        compress = 0\n        if self._compress:\n            extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)\n            # Server side always get return with no exception.\n            # If something happened, just drop compress extension\n            compress, notakeover = ws_ext_parse(extensions, isserver=True)\n            if compress:\n                enabledext = ws_ext_gen(\n                    compress=compress, isserver=True, server_notakeover=notakeover\n                )\n                response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext\n\n        if protocol:\n            response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol\n        return (\n            response_headers,\n            protocol,\n            compress,\n            notakeover,\n        )  # type: ignore[return-value]\n\n    def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]:\n        self._loop = request._loop\n\n        headers, protocol, compress, notakeover = self._handshake(request)\n\n        self.set_status(101)\n        self.headers.update(headers)\n        self.force_close()\n        self._compress = compress\n        transport = request._protocol.transport\n        assert transport is not None\n        writer = WebSocketWriter(\n            request._protocol, transport, compress=compress, notakeover=notakeover\n        )\n\n        return protocol, writer\n\n    def _post_start(\n        self, request: BaseRequest, protocol: str, writer: WebSocketWriter\n    ) -> None:\n        self._ws_protocol = protocol\n        self._writer = writer\n\n        self._reset_heartbeat()\n\n        loop = self._loop\n        assert loop is not None\n        self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop)\n        request.protocol.set_parser(\n            WebSocketReader(self._reader, self._max_msg_size, compress=self._compress)\n        )\n        # disable HTTP keepalive for WebSocket\n        request.protocol.keep_alive(False)\n\n    def can_prepare(self, request: BaseRequest) -> WebSocketReady:\n        if self._writer is not None:\n            raise RuntimeError(\"Already started\")\n        try:\n            _, protocol, _, _ = self._handshake(request)\n        except HTTPException:\n            return WebSocketReady(False, None)\n        else:\n            return WebSocketReady(True, protocol)\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    @property\n    def close_code(self) -> Optional[int]:\n        return self._close_code\n\n    @property\n    def ws_protocol(self) -> Optional[str]:\n        return self._ws_protocol\n\n    @property\n    def compress(self) -> bool:\n        return self._compress\n\n    def get_extra_info(self, name: str, default: Any = None) -> Any:\n        \"\"\"Get optional transport information.\n\n        If no value associated with ``name`` is found, ``default`` is returned.\n        \"\"\"\n        writer = self._writer\n        if writer is None:\n            return default\n        transport = writer.transport\n        if transport is None:\n            return default\n        return transport.get_extra_info(name, default)\n\n    def exception(self) -> Optional[BaseException]:\n        return self._exception\n\n    async def ping(self, message: bytes = b\"\") -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.ping(message)\n\n    async def pong(self, message: bytes = b\"\") -> None:\n        # unsolicited pong\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.pong(message)\n\n    async def send_str(self, data: str, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, str):\n            raise TypeError(\"data argument must be str (%r)\" % type(data))\n        await self._writer.send(data, binary=False, compress=compress)\n\n    async def send_bytes(self, data: bytes, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError(\"data argument must be byte-ish (%r)\" % type(data))\n        await self._writer.send(data, binary=True, compress=compress)\n\n    async def send_json(\n        self,\n        data: Any,\n        compress: Optional[bool] = None,\n        *,\n        dumps: JSONEncoder = json.dumps,\n    ) -> None:\n        await self.send_str(dumps(data), compress=compress)\n\n    async def write_eof(self) -> None:  # type: ignore[override]\n        if self._eof_sent:\n            return\n        if self._payload_writer is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        await self.close()\n        self._eof_sent = True\n\n    async def close(\n        self, *, code: int = WSCloseCode.OK, message: bytes = b\"\", drain: bool = True\n    ) -> bool:\n        \"\"\"Close websocket connection.\"\"\"\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        self._cancel_heartbeat()\n        reader = self._reader\n        assert reader is not None\n\n        # we need to break `receive()` cycle first,\n        # `close()` may be called from different task\n        if self._waiting and not self._closed:\n            if not self._close_wait:\n                assert self._loop is not None\n                self._close_wait = self._loop.create_future()\n            reader.feed_data(WS_CLOSING_MESSAGE, 0)\n            await self._close_wait\n\n        if self._closed:\n            return False\n\n        self._set_closed()\n        try:\n            await self._writer.close(code, message)\n            writer = self._payload_writer\n            assert writer is not None\n            if drain:\n                await writer.drain()\n        except (asyncio.CancelledError, asyncio.TimeoutError):\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if self._closing:\n            self._close_transport()\n            return True\n\n        reader = self._reader\n        assert reader is not None\n        try:\n            async with async_timeout.timeout(self._timeout):\n                msg = await reader.read()\n        except asyncio.CancelledError:\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if msg.type is WSMsgType.CLOSE:\n            self._set_code_close_transport(msg.data)\n            return True\n\n        self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n        self._exception = asyncio.TimeoutError()\n        return True\n\n    def _set_closing(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and mark the connection as closing.\"\"\"\n        self._closing = True\n        self._close_code = code\n        self._cancel_heartbeat()\n\n    def _set_code_close_transport(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and close the transport.\"\"\"\n        self._close_code = code\n        self._close_transport()\n\n    def _close_transport(self) -> None:\n        \"\"\"Close the transport.\"\"\"\n        if self._req is not None and self._req.transport is not None:\n            self._req.transport.close()\n\n    async def receive(self, timeout: Optional[float] = None) -> WSMessage:\n        if self._reader is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        loop = self._loop\n        assert loop is not None\n        receive_timeout = timeout or self._receive_timeout\n        while True:\n            if self._waiting:\n                raise RuntimeError(\"Concurrent call to receive() is not allowed\")\n\n            if self._closed:\n                self._conn_lost += 1\n                if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS:\n                    raise RuntimeError(\"WebSocket connection is closed.\")\n                return WS_CLOSED_MESSAGE\n            elif self._closing:\n                return WS_CLOSING_MESSAGE\n\n            try:\n                self._waiting = True\n                try:\n                    if receive_timeout:\n                        # Entering the context manager and creating\n                        # Timeout() object can take almost 50% of the\n                        # run time in this loop so we avoid it if\n                        # there is no read timeout.\n                        async with async_timeout.timeout(receive_timeout):\n                            msg = await self._reader.read()\n                    else:\n                        msg = await self._reader.read()\n                    self._reset_heartbeat()\n                finally:\n                    self._waiting = False\n                    if self._close_wait:\n                        set_result(self._close_wait, None)\n            except asyncio.TimeoutError:\n                raise\n            except EofStream:\n                self._close_code = WSCloseCode.OK\n                await self.close()\n                return WSMessage(WSMsgType.CLOSED, None, None)\n            except WebSocketError as exc:\n                self._close_code = exc.code\n                await self.close(code=exc.code)\n                return WSMessage(WSMsgType.ERROR, exc, None)\n            except Exception as exc:\n                self._exception = exc\n                self._set_closing(WSCloseCode.ABNORMAL_CLOSURE)\n                await self.close()\n                return WSMessage(WSMsgType.ERROR, exc, None)\n\n            if msg.type is WSMsgType.CLOSE:\n                self._set_closing(msg.data)\n                # Could be closed while awaiting reader.\n                if not self._closed and self._autoclose:\n                    # The client is likely going to close the\n                    # connection out from under us so we do not\n                    # want to drain any pending writes as it will\n                    # likely result writing to a broken pipe.\n                    await self.close(drain=False)\n            elif msg.type is WSMsgType.CLOSING:\n                self._set_closing(WSCloseCode.OK)\n            elif msg.type is WSMsgType.PING and self._autoping:\n                await self.pong(msg.data)\n                continue\n            elif msg.type is WSMsgType.PONG and self._autoping:\n                continue\n\n            return msg\n\n    async def receive_str(self, *, timeout: Optional[float] = None) -> str:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.TEXT:\n            raise TypeError(\n                \"Received message {}:{!r} is not WSMsgType.TEXT\".format(\n                    msg.type, msg.data\n                )\n            )\n        return cast(str, msg.data)\n\n    async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.BINARY:\n            raise TypeError(f\"Received message {msg.type}:{msg.data!r} is not bytes\")\n        return cast(bytes, msg.data)\n\n    async def receive_json(\n        self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None\n    ) -> Any:\n        data = await self.receive_str(timeout=timeout)\n        return loads(data)\n\n    async def write(self, data: bytes) -> None:\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n    def __aiter__(self) -> \"WebSocketResponse\":\n        return self\n\n    async def __anext__(self) -> WSMessage:\n        msg = await self.receive()\n        if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):\n            raise StopAsyncIteration\n        return msg\n\n    def _cancel(self, exc: BaseException) -> None:\n        # web_protocol calls this from connection_lost\n        # or when the server is shutting down.\n        self._closing = True\n        self._cancel_heartbeat()\n        if self._reader is not None:\n            set_exception(self._reader, exc)",
                            "ast_type_str": "ClassDef",
                            "start_lineno": 402,
                            "end_lineno": 463,
                            "parent": {
                                "file_name": "aiohttp/web_ws.py",
                                "type": "class",
                                "name": "WebSocketResponse",
                                "content": "class WebSocketResponse(StreamResponse):\n\n    _length_check = False\n\n    def __init__(\n        self,\n        *,\n        timeout: float = 10.0,\n        receive_timeout: Optional[float] = None,\n        autoclose: bool = True,\n        autoping: bool = True,\n        heartbeat: Optional[float] = None,\n        protocols: Iterable[str] = (),\n        compress: bool = True,\n        max_msg_size: int = 4 * 1024 * 1024,\n    ) -> None:\n        super().__init__(status=101)\n        self._protocols = protocols\n        self._ws_protocol: Optional[str] = None\n        self._writer: Optional[WebSocketWriter] = None\n        self._reader: Optional[FlowControlDataQueue[WSMessage]] = None\n        self._closed = False\n        self._closing = False\n        self._conn_lost = 0\n        self._close_code: Optional[int] = None\n        self._loop: Optional[asyncio.AbstractEventLoop] = None\n        self._waiting: bool = False\n        self._close_wait: Optional[asyncio.Future[None]] = None\n        self._exception: Optional[BaseException] = None\n        self._timeout = timeout\n        self._receive_timeout = receive_timeout\n        self._autoclose = autoclose\n        self._autoping = autoping\n        self._heartbeat = heartbeat\n        self._heartbeat_when = 0.0\n        self._heartbeat_cb: Optional[asyncio.TimerHandle] = None\n        if heartbeat is not None:\n            self._pong_heartbeat = heartbeat / 2.0\n        self._pong_response_cb: Optional[asyncio.TimerHandle] = None\n        self._compress = compress\n        self._max_msg_size = max_msg_size\n        self._ping_task: Optional[asyncio.Task[None]] = None\n\n    def _cancel_heartbeat(self) -> None:\n        self._cancel_pong_response_cb()\n        if self._heartbeat_cb is not None:\n            self._heartbeat_cb.cancel()\n            self._heartbeat_cb = None\n        if self._ping_task is not None:\n            self._ping_task.cancel()\n            self._ping_task = None\n\n    def _cancel_pong_response_cb(self) -> None:\n        if self._pong_response_cb is not None:\n            self._pong_response_cb.cancel()\n            self._pong_response_cb = None\n\n    def _reset_heartbeat(self) -> None:\n        if self._heartbeat is None:\n            return\n        self._cancel_pong_response_cb()\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        loop = self._loop\n        assert loop is not None\n        now = loop.time()\n        when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold)\n        self._heartbeat_when = when\n        if self._heartbeat_cb is None:\n            # We do not cancel the previous heartbeat_cb here because\n            # it generates a significant amount of TimerHandle churn\n            # which causes asyncio to rebuild the heap frequently.\n            # Instead _send_heartbeat() will reschedule the next\n            # heartbeat if it fires too early.\n            self._heartbeat_cb = loop.call_at(when, self._send_heartbeat)\n\n    def _send_heartbeat(self) -> None:\n        self._heartbeat_cb = None\n        loop = self._loop\n        assert loop is not None and self._writer is not None\n        now = loop.time()\n        if now < self._heartbeat_when:\n            # Heartbeat fired too early, reschedule\n            self._heartbeat_cb = loop.call_at(\n                self._heartbeat_when, self._send_heartbeat\n            )\n            return\n\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold)\n        self._cancel_pong_response_cb()\n        self._pong_response_cb = loop.call_at(when, self._pong_not_received)\n\n        if sys.version_info >= (3, 12):\n            # Optimization for Python 3.12, try to send the ping\n            # immediately to avoid having to schedule\n            # the task on the event loop.\n            ping_task = asyncio.Task(self._writer.ping(), loop=loop, eager_start=True)\n        else:\n            ping_task = loop.create_task(self._writer.ping())\n\n        if not ping_task.done():\n            self._ping_task = ping_task\n            ping_task.add_done_callback(self._ping_task_done)\n\n    def _ping_task_done(self, task: \"asyncio.Task[None]\") -> None:\n        \"\"\"Callback for when the ping task completes.\"\"\"\n        self._ping_task = None\n\n    def _pong_not_received(self) -> None:\n        if self._req is not None and self._req.transport is not None:\n            self._set_closed()\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            self._exception = asyncio.TimeoutError()\n\n    def _set_closed(self) -> None:\n        \"\"\"Set the connection to closed.\n\n        Cancel any heartbeat timers and set the closed flag.\n        \"\"\"\n        self._closed = True\n        self._cancel_heartbeat()\n\n    async def prepare(self, request: BaseRequest) -> AbstractStreamWriter:\n        # make pre-check to don't hide it by do_handshake() exceptions\n        if self._payload_writer is not None:\n            return self._payload_writer\n\n        protocol, writer = self._pre_start(request)\n        payload_writer = await super().prepare(request)\n        assert payload_writer is not None\n        self._post_start(request, protocol, writer)\n        await payload_writer.drain()\n        return payload_writer\n\n    def _handshake(\n        self, request: BaseRequest\n    ) -> Tuple[\"CIMultiDict[str]\", str, bool, bool]:\n        headers = request.headers\n        if \"websocket\" != headers.get(hdrs.UPGRADE, \"\").lower().strip():\n            raise HTTPBadRequest(\n                text=(\n                    \"No WebSocket UPGRADE hdr: {}\\n Can \"\n                    '\"Upgrade\" only to \"WebSocket\".'\n                ).format(headers.get(hdrs.UPGRADE))\n            )\n\n        if \"upgrade\" not in headers.get(hdrs.CONNECTION, \"\").lower():\n            raise HTTPBadRequest(\n                text=\"No CONNECTION upgrade hdr: {}\".format(\n                    headers.get(hdrs.CONNECTION)\n                )\n            )\n\n        # find common sub-protocol between client and server\n        protocol = None\n        if hdrs.SEC_WEBSOCKET_PROTOCOL in headers:\n            req_protocols = [\n                str(proto.strip())\n                for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(\",\")\n            ]\n\n            for proto in req_protocols:\n                if proto in self._protocols:\n                    protocol = proto\n                    break\n            else:\n                # No overlap found: Return no protocol as per spec\n                ws_logger.warning(\n                    \"Client protocols %r don’t overlap server-known ones %r\",\n                    req_protocols,\n                    self._protocols,\n                )\n\n        # check supported version\n        version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, \"\")\n        if version not in (\"13\", \"8\", \"7\"):\n            raise HTTPBadRequest(text=f\"Unsupported version: {version}\")\n\n        # check client handshake for validity\n        key = headers.get(hdrs.SEC_WEBSOCKET_KEY)\n        try:\n            if not key or len(base64.b64decode(key)) != 16:\n                raise HTTPBadRequest(text=f\"Handshake error: {key!r}\")\n        except binascii.Error:\n            raise HTTPBadRequest(text=f\"Handshake error: {key!r}\") from None\n\n        accept_val = base64.b64encode(\n            hashlib.sha1(key.encode() + WS_KEY).digest()\n        ).decode()\n        response_headers = CIMultiDict(\n            {\n                hdrs.UPGRADE: \"websocket\",\n                hdrs.CONNECTION: \"upgrade\",\n                hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,\n            }\n        )\n\n        notakeover = False\n        compress = 0\n        if self._compress:\n            extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)\n            # Server side always get return with no exception.\n            # If something happened, just drop compress extension\n            compress, notakeover = ws_ext_parse(extensions, isserver=True)\n            if compress:\n                enabledext = ws_ext_gen(\n                    compress=compress, isserver=True, server_notakeover=notakeover\n                )\n                response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext\n\n        if protocol:\n            response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol\n        return (\n            response_headers,\n            protocol,\n            compress,\n            notakeover,\n        )  # type: ignore[return-value]\n\n    def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]:\n        self._loop = request._loop\n\n        headers, protocol, compress, notakeover = self._handshake(request)\n\n        self.set_status(101)\n        self.headers.update(headers)\n        self.force_close()\n        self._compress = compress\n        transport = request._protocol.transport\n        assert transport is not None\n        writer = WebSocketWriter(\n            request._protocol, transport, compress=compress, notakeover=notakeover\n        )\n\n        return protocol, writer\n\n    def _post_start(\n        self, request: BaseRequest, protocol: str, writer: WebSocketWriter\n    ) -> None:\n        self._ws_protocol = protocol\n        self._writer = writer\n\n        self._reset_heartbeat()\n\n        loop = self._loop\n        assert loop is not None\n        self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop)\n        request.protocol.set_parser(\n            WebSocketReader(self._reader, self._max_msg_size, compress=self._compress)\n        )\n        # disable HTTP keepalive for WebSocket\n        request.protocol.keep_alive(False)\n\n    def can_prepare(self, request: BaseRequest) -> WebSocketReady:\n        if self._writer is not None:\n            raise RuntimeError(\"Already started\")\n        try:\n            _, protocol, _, _ = self._handshake(request)\n        except HTTPException:\n            return WebSocketReady(False, None)\n        else:\n            return WebSocketReady(True, protocol)\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    @property\n    def close_code(self) -> Optional[int]:\n        return self._close_code\n\n    @property\n    def ws_protocol(self) -> Optional[str]:\n        return self._ws_protocol\n\n    @property\n    def compress(self) -> bool:\n        return self._compress\n\n    def get_extra_info(self, name: str, default: Any = None) -> Any:\n        \"\"\"Get optional transport information.\n\n        If no value associated with ``name`` is found, ``default`` is returned.\n        \"\"\"\n        writer = self._writer\n        if writer is None:\n            return default\n        transport = writer.transport\n        if transport is None:\n            return default\n        return transport.get_extra_info(name, default)\n\n    def exception(self) -> Optional[BaseException]:\n        return self._exception\n\n    async def ping(self, message: bytes = b\"\") -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.ping(message)\n\n    async def pong(self, message: bytes = b\"\") -> None:\n        # unsolicited pong\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.pong(message)\n\n    async def send_str(self, data: str, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, str):\n            raise TypeError(\"data argument must be str (%r)\" % type(data))\n        await self._writer.send(data, binary=False, compress=compress)\n\n    async def send_bytes(self, data: bytes, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError(\"data argument must be byte-ish (%r)\" % type(data))\n        await self._writer.send(data, binary=True, compress=compress)\n\n    async def send_json(\n        self,\n        data: Any,\n        compress: Optional[bool] = None,\n        *,\n        dumps: JSONEncoder = json.dumps,\n    ) -> None:\n        await self.send_str(dumps(data), compress=compress)\n\n    async def write_eof(self) -> None:  # type: ignore[override]\n        if self._eof_sent:\n            return\n        if self._payload_writer is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        await self.close()\n        self._eof_sent = True\n\n    async def close(\n        self, *, code: int = WSCloseCode.OK, message: bytes = b\"\", drain: bool = True\n    ) -> bool:\n        \"\"\"Close websocket connection.\"\"\"\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        self._cancel_heartbeat()\n        reader = self._reader\n        assert reader is not None\n\n        # we need to break `receive()` cycle first,\n        # `close()` may be called from different task\n        if self._waiting and not self._closed:\n            if not self._close_wait:\n                assert self._loop is not None\n                self._close_wait = self._loop.create_future()\n            reader.feed_data(WS_CLOSING_MESSAGE, 0)\n            await self._close_wait\n\n        if self._closed:\n            return False\n\n        self._set_closed()\n        try:\n            await self._writer.close(code, message)\n            writer = self._payload_writer\n            assert writer is not None\n            if drain:\n                await writer.drain()\n        except (asyncio.CancelledError, asyncio.TimeoutError):\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if self._closing:\n            self._close_transport()\n            return True\n\n        reader = self._reader\n        assert reader is not None\n        try:\n            async with async_timeout.timeout(self._timeout):\n                msg = await reader.read()\n        except asyncio.CancelledError:\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if msg.type is WSMsgType.CLOSE:\n            self._set_code_close_transport(msg.data)\n            return True\n\n        self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n        self._exception = asyncio.TimeoutError()\n        return True\n\n    def _set_closing(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and mark the connection as closing.\"\"\"\n        self._closing = True\n        self._close_code = code\n        self._cancel_heartbeat()\n\n    def _set_code_close_transport(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and close the transport.\"\"\"\n        self._close_code = code\n        self._close_transport()\n\n    def _close_transport(self) -> None:\n        \"\"\"Close the transport.\"\"\"\n        if self._req is not None and self._req.transport is not None:\n            self._req.transport.close()\n\n    async def receive(self, timeout: Optional[float] = None) -> WSMessage:\n        if self._reader is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        loop = self._loop\n        assert loop is not None\n        receive_timeout = timeout or self._receive_timeout\n        while True:\n            if self._waiting:\n                raise RuntimeError(\"Concurrent call to receive() is not allowed\")\n\n            if self._closed:\n                self._conn_lost += 1\n                if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS:\n                    raise RuntimeError(\"WebSocket connection is closed.\")\n                return WS_CLOSED_MESSAGE\n            elif self._closing:\n                return WS_CLOSING_MESSAGE\n\n            try:\n                self._waiting = True\n                try:\n                    if receive_timeout:\n                        # Entering the context manager and creating\n                        # Timeout() object can take almost 50% of the\n                        # run time in this loop so we avoid it if\n                        # there is no read timeout.\n                        async with async_timeout.timeout(receive_timeout):\n                            msg = await self._reader.read()\n                    else:\n                        msg = await self._reader.read()\n                    self._reset_heartbeat()\n                finally:\n                    self._waiting = False\n                    if self._close_wait:\n                        set_result(self._close_wait, None)\n            except asyncio.TimeoutError:\n                raise\n            except EofStream:\n                self._close_code = WSCloseCode.OK\n                await self.close()\n                return WSMessage(WSMsgType.CLOSED, None, None)\n            except WebSocketError as exc:\n                self._close_code = exc.code\n                await self.close(code=exc.code)\n                return WSMessage(WSMsgType.ERROR, exc, None)\n            except Exception as exc:\n                self._exception = exc\n                self._set_closing(WSCloseCode.ABNORMAL_CLOSURE)\n                await self.close()\n                return WSMessage(WSMsgType.ERROR, exc, None)\n\n            if msg.type is WSMsgType.CLOSE:\n                self._set_closing(msg.data)\n                # Could be closed while awaiting reader.\n                if not self._closed and self._autoclose:\n                    # The client is likely going to close the\n                    # connection out from under us so we do not\n                    # want to drain any pending writes as it will\n                    # likely result writing to a broken pipe.\n                    await self.close(drain=False)\n            elif msg.type is WSMsgType.CLOSING:\n                self._set_closing(WSCloseCode.OK)\n            elif msg.type is WSMsgType.PING and self._autoping:\n                await self.pong(msg.data)\n                continue\n            elif msg.type is WSMsgType.PONG and self._autoping:\n                continue\n\n            return msg\n\n    async def receive_str(self, *, timeout: Optional[float] = None) -> str:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.TEXT:\n            raise TypeError(\n                \"Received message {}:{!r} is not WSMsgType.TEXT\".format(\n                    msg.type, msg.data\n                )\n            )\n        return cast(str, msg.data)\n\n    async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.BINARY:\n            raise TypeError(f\"Received message {msg.type}:{msg.data!r} is not bytes\")\n        return cast(bytes, msg.data)\n\n    async def receive_json(\n        self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None\n    ) -> Any:\n        data = await self.receive_str(timeout=timeout)\n        return loads(data)\n\n    async def write(self, data: bytes) -> None:\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n    def __aiter__(self) -> \"WebSocketResponse\":\n        return self\n\n    async def __anext__(self) -> WSMessage:\n        msg = await self.receive()\n        if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):\n            raise StopAsyncIteration\n        return msg\n\n    def _cancel(self, exc: BaseException) -> None:\n        # web_protocol calls this from connection_lost\n        # or when the server is shutting down.\n        self._closing = True\n        self._cancel_heartbeat()\n        if self._reader is not None:\n            set_exception(self._reader, exc)",
                                "ast_type_str": "ClassDef",
                                "start_lineno": 58,
                                "end_lineno": 592,
                                "parent": null
                            }
                        }
                    ],
                    "added_entities": [],
                    "deleted_entities": []
                }
            ]
        },
        {
            "old_file_content": "# HTTP websocket server functional tests\n\nimport asyncio\nimport contextlib\nimport sys\nfrom typing import Any, Optional\n\nimport pytest\n\nimport aiohttp\nfrom aiohttp import web\nfrom aiohttp.http import WSCloseCode, WSMsgType\n\n\nasync def test_websocket_can_prepare(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        if not ws.can_prepare(request):\n            raise web.HTTPUpgradeRequired()\n\n        return web.Response()\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/\")\n    assert resp.status == 426\n\n\nasync def test_websocket_json(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        if not ws.can_prepare(request):\n            return web.HTTPUpgradeRequired()\n\n        await ws.prepare(request)\n        msg = await ws.receive()\n\n        msg_json = msg.json()\n        answer = msg_json[\"test\"]\n        await ws.send_str(answer)\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    expected_value = \"value\"\n    payload = '{\"test\": \"%s\"}' % expected_value\n    await ws.send_str(payload)\n\n    resp = await ws.receive()\n    assert resp.data == expected_value\n\n\nasync def test_websocket_json_invalid_message(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        try:\n            await ws.receive_json()\n        except ValueError:\n            await ws.send_str(\"ValueError was raised\")\n        else:\n            raise Exception(\"No Exception\")\n        finally:\n            await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    payload = \"NOT A VALID JSON STRING\"\n    await ws.send_str(payload)\n\n    data = await ws.receive_str()\n    assert \"ValueError was raised\" in data\n\n\nasync def test_websocket_send_json(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        data = await ws.receive_json()\n        await ws.send_json(data)\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    expected_value = \"value\"\n    await ws.send_json({\"test\": expected_value})\n\n    data = await ws.receive_json()\n    assert data[\"test\"] == expected_value\n\n\nasync def test_websocket_receive_json(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        data = await ws.receive_json()\n        answer = data[\"test\"]\n        await ws.send_str(answer)\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    expected_value = \"value\"\n    payload = '{\"test\": \"%s\"}' % expected_value\n    await ws.send_str(payload)\n\n    resp = await ws.receive()\n    assert resp.data == expected_value\n\n\nasync def test_send_recv_text(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        msg = await ws.receive_str()\n        await ws.send_str(msg + \"/answer\")\n        await ws.close()\n        closed.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.send_str(\"ask\")\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.TEXT\n    assert \"ask/answer\" == msg.data\n\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.CLOSE\n    assert msg.data == WSCloseCode.OK\n    assert msg.extra == \"\"\n\n    assert ws.closed\n    assert ws.close_code == WSCloseCode.OK\n\n    await closed\n\n\nasync def test_send_recv_bytes(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        msg = await ws.receive_bytes()\n        await ws.send_bytes(msg + b\"/answer\")\n        await ws.close()\n        closed.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.send_bytes(b\"ask\")\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.BINARY\n    assert b\"ask/answer\" == msg.data\n\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.CLOSE\n    assert msg.data == WSCloseCode.OK\n    assert msg.extra == \"\"\n\n    assert ws.closed\n    assert ws.close_code == WSCloseCode.OK\n\n    await closed\n\n\nasync def test_send_recv_json(loop, aiohttp_client) -> None:\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        data = await ws.receive_json()\n        await ws.send_json({\"response\": data[\"request\"]})\n        await ws.close()\n        closed.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n\n    await ws.send_str('{\"request\": \"test\"}')\n    msg = await ws.receive()\n    data = msg.json()\n    assert msg.type == aiohttp.WSMsgType.TEXT\n    assert data[\"response\"] == \"test\"\n\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.CLOSE\n    assert msg.data == WSCloseCode.OK\n    assert msg.extra == \"\"\n\n    await ws.close()\n\n    await closed\n\n\nasync def test_close_timeout(loop, aiohttp_client) -> None:\n    aborted = loop.create_future()\n    elapsed = 1e10  # something big\n\n    async def handler(request):\n        nonlocal elapsed\n        ws = web.WebSocketResponse(timeout=0.1)\n        await ws.prepare(request)\n        assert \"request\" == (await ws.receive_str())\n        await ws.send_str(\"reply\")\n        begin = ws._loop.time()\n        assert await ws.close()\n        elapsed = ws._loop.time() - begin\n        assert ws.close_code == WSCloseCode.ABNORMAL_CLOSURE\n        assert isinstance(ws.exception(), asyncio.TimeoutError)\n        aborted.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.send_str(\"request\")\n    assert \"reply\" == (await ws.receive_str())\n\n    # The server closes here.  Then the client sends bogus messages with an\n    # interval shorter than server-side close timeout, to make the server\n    # hanging indefinitely.\n    await asyncio.sleep(0.08)\n    msg = await ws._reader.read()\n    assert msg.type == WSMsgType.CLOSE\n\n    await asyncio.sleep(0.08)\n    assert await aborted\n\n    assert elapsed < 0.25, \"close() should have returned before \" \"at most 2x timeout.\"\n\n    await ws.close()\n\n\nasync def test_concurrent_close(loop, aiohttp_client) -> None:\n\n    srv_ws = None\n\n    async def handler(request):\n        nonlocal srv_ws\n        ws = srv_ws = web.WebSocketResponse(autoclose=False, protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSING\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSING\n\n        await asyncio.sleep(0)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSED\n\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, protocols=(\"eggs\", \"bar\"))\n\n    await srv_ws.close(code=WSCloseCode.INVALID_TEXT)\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n\n    await asyncio.sleep(0)\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSED\n\n\nasync def test_concurrent_close_multiple_tasks(loop: Any, aiohttp_client: Any) -> None:\n    srv_ws = None\n\n    async def handler(request):\n        nonlocal srv_ws\n        ws = srv_ws = web.WebSocketResponse(autoclose=False, protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSING\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSING\n\n        await asyncio.sleep(0)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSED\n\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, protocols=(\"eggs\", \"bar\"))\n\n    task1 = asyncio.create_task(srv_ws.close(code=WSCloseCode.INVALID_TEXT))\n    task2 = asyncio.create_task(srv_ws.close(code=WSCloseCode.INVALID_TEXT))\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n\n    await task1\n    await task2\n\n    await asyncio.sleep(0)\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSED\n\n\nasync def test_close_op_code_from_client(loop: Any, aiohttp_client: Any) -> None:\n    srv_ws: Optional[web.WebSocketResponse] = None\n\n    async def handler(request):\n        nonlocal srv_ws\n        ws = srv_ws = web.WebSocketResponse(protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        await asyncio.sleep(0)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws: web.WebSocketResponse = await client.ws_connect(\"/\", protocols=(\"eggs\", \"bar\"))\n\n    await ws._writer._send_frame(b\"\", WSMsgType.CLOSE)\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n\n    await asyncio.sleep(0)\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSED\n\n\nasync def test_auto_pong_with_closing_by_peer(loop: Any, aiohttp_client: Any) -> None:\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        await ws.receive()\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert msg.data == WSCloseCode.OK\n        assert msg.extra == \"exit message\"\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, autoping=False)\n    await ws.ping()\n    await ws.send_str(\"ask\")\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    await ws.close(code=WSCloseCode.OK, message=\"exit message\")\n    await closed\n\n\nasync def test_ping(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        await ws.ping(\"data\")\n        await ws.receive()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PING\n    assert msg.data == b\"data\"\n    await ws.pong()\n    await ws.close()\n    await closed\n\n\nasync def aiohttp_client_ping(loop, aiohttp_client):\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        await ws.receive()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await ws.ping(\"data\")\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    assert msg.data == b\"data\"\n    await ws.pong()\n    await ws.close()\n\n\nasync def test_pong(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(autoping=False)\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.PING\n        await ws.pong(\"data\")\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert msg.data == WSCloseCode.OK\n        assert msg.extra == \"exit message\"\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await ws.ping(\"data\")\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    assert msg.data == b\"data\"\n\n    await ws.close(code=WSCloseCode.OK, message=\"exit message\")\n\n    await closed\n\n\nasync def test_change_status(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        ws.set_status(200)\n        assert 200 == ws.status\n        await ws.prepare(request)\n        assert 101 == ws.status\n        await ws.close()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await ws.close()\n    await closed\n    await ws.close()\n\n\nasync def test_handle_protocol(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n        await ws.close()\n        assert \"bar\" == ws.ws_protocol\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", protocols=(\"eggs\", \"bar\"))\n\n    await ws.close()\n    await closed\n\n\nasync def test_server_close_handshake(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n        await ws.close()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, protocols=(\"eggs\", \"bar\"))\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n    await ws.close()\n    await closed\n\n\nasync def aiohttp_client_close_handshake(loop, aiohttp_client):\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(autoclose=False, protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert not ws.closed\n        await ws.close()\n        assert ws.closed\n        assert ws.close_code == WSCloseCode.INVALID_TEXT\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSED\n\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, protocols=(\"eggs\", \"bar\"))\n\n    await ws.close(code=WSCloseCode.INVALID_TEXT)\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSED\n    await closed\n\n\nasync def test_server_close_handshake_server_eats_client_messages(loop, aiohttp_client):\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n        await ws.close()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\n        \"/\", autoclose=False, autoping=False, protocols=(\"eggs\", \"bar\")\n    )\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n\n    await ws.send_str(\"text\")\n    await ws.send_bytes(b\"bytes\")\n    await ws.ping()\n\n    await ws.close()\n    await closed\n\n\nasync def test_receive_timeout(loop, aiohttp_client) -> None:\n    raised = False\n\n    async def handler(request):\n        ws = web.WebSocketResponse(receive_timeout=0.1)\n        await ws.prepare(request)\n\n        try:\n            await ws.receive()\n        except asyncio.TimeoutError:\n            nonlocal raised\n            raised = True\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.receive()\n    await ws.close()\n    assert raised\n\n\nasync def test_custom_receive_timeout(loop, aiohttp_client) -> None:\n    raised = False\n\n    async def handler(request):\n        ws = web.WebSocketResponse(receive_timeout=None)\n        await ws.prepare(request)\n\n        try:\n            await ws.receive(0.1)\n        except asyncio.TimeoutError:\n            nonlocal raised\n            raised = True\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.receive()\n    await ws.close()\n    assert raised\n\n\nasync def test_heartbeat(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse(heartbeat=0.05)\n        await ws.prepare(request)\n        await ws.receive()\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n\n    client = await aiohttp_client(app)\n    ws = await client.ws_connect(\"/\", autoping=False)\n    msg = await ws.receive()\n\n    assert msg.type == aiohttp.WSMsgType.ping\n\n    await ws.close()\n\n\nasync def test_heartbeat_no_pong(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse(heartbeat=0.05)\n        await ws.prepare(request)\n\n        await ws.receive()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n\n    client = await aiohttp_client(app)\n    ws = await client.ws_connect(\"/\", autoping=False)\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.ping\n    await ws.close()\n\n\nasync def test_heartbeat_no_pong_send_many_messages(\n    loop: Any, aiohttp_client: Any\n) -> None:\n    \"\"\"Test no pong after sending many messages.\"\"\"\n\n    async def handler(request):\n        ws = web.WebSocketResponse(heartbeat=0.05)\n        await ws.prepare(request)\n        for _ in range(10):\n            await ws.send_str(\"test\")\n\n        await ws.receive()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n\n    client = await aiohttp_client(app)\n    ws = await client.ws_connect(\"/\", autoping=False)\n    for _ in range(10):\n        msg = await ws.receive()\n        assert msg.type is aiohttp.WSMsgType.TEXT\n        assert msg.data == \"test\"\n\n    msg = await ws.receive()\n    assert msg.type is aiohttp.WSMsgType.PING\n    await ws.close()\n\n\nasync def test_heartbeat_no_pong_receive_many_messages(\n    loop: Any, aiohttp_client: Any\n) -> None:\n    \"\"\"Test no pong after receiving many messages.\"\"\"\n\n    async def handler(request):\n        ws = web.WebSocketResponse(heartbeat=0.05)\n        await ws.prepare(request)\n        for _ in range(10):\n            server_msg = await ws.receive()\n            assert server_msg.type is aiohttp.WSMsgType.TEXT\n\n        await ws.receive()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n\n    client = await aiohttp_client(app)\n    ws = await client.ws_connect(\"/\", autoping=False)\n    for _ in range(10):\n        await ws.send_str(\"test\")\n\n    msg = await ws.receive()\n    assert msg.type is aiohttp.WSMsgType.PING\n    await ws.close()\n\n\nasync def test_server_ws_async_for(loop: Any, aiohttp_server: Any) -> None:\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        async for msg in ws:\n            assert msg.type == aiohttp.WSMsgType.TEXT\n            s = msg.data\n            await ws.send_str(s + \"/answer\")\n        await ws.close()\n        closed.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    server = await aiohttp_server(app)\n\n    async with aiohttp.ClientSession() as sm:\n        async with sm.ws_connect(server.make_url(\"/\")) as resp:\n\n            items = [\"q1\", \"q2\", \"q3\"]\n            for item in items:\n                await resp.send_str(item)\n                msg = await resp.receive()\n                assert msg.type == aiohttp.WSMsgType.TEXT\n                assert item + \"/answer\" == msg.data\n\n            await resp.close()\n            await closed\n\n\nasync def test_closed_async_for(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        messages = []\n        async for msg in ws:\n            messages.append(msg)\n            if \"stop\" == msg.data:\n                await ws.send_str(\"stopping\")\n                await ws.close()\n\n        assert 1 == len(messages)\n        assert messages[0].type == WSMsgType.TEXT\n        assert messages[0].data == \"stop\"\n\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.send_str(\"stop\")\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.TEXT\n    assert msg.data == \"stopping\"\n\n    await ws.close()\n    await closed\n\n\nasync def test_websocket_disable_keepalive(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        if not ws.can_prepare(request):\n            return web.Response(text=\"OK\")\n        assert request.protocol._keepalive\n        await ws.prepare(request)\n        assert not request.protocol._keepalive\n        assert not request.protocol._keepalive_handle\n\n        await ws.send_str(\"OK\")\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/\")\n    txt = await resp.text()\n    assert txt == \"OK\"\n\n    ws = await client.ws_connect(\"/\")\n    data = await ws.receive_str()\n    assert data == \"OK\"\n\n\nasync def test_bug3380(loop, aiohttp_client) -> None:\n    async def handle_null(request):\n        return aiohttp.web.json_response({\"err\": None})\n\n    async def ws_handler(request):\n        return web.Response(status=401)\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/ws\", ws_handler)\n    app.router.add_route(\"GET\", \"/api/null\", handle_null)\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/api/null\")\n    assert (await resp.json()) == {\"err\": None}\n    resp.close()\n\n    with pytest.raises(aiohttp.WSServerHandshakeError):\n        await client.ws_connect(\"/ws\")\n\n    resp = await client.get(\"/api/null\", timeout=1)\n    assert (await resp.json()) == {\"err\": None}\n    resp.close()\n\n\nasync def test_receive_being_cancelled_keeps_connection_open(\n    loop: Any, aiohttp_client: Any\n) -> None:\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(autoping=False)\n        await ws.prepare(request)\n\n        task = asyncio.create_task(ws.receive())\n        await asyncio.sleep(0)\n        task.cancel()\n        with contextlib.suppress(asyncio.CancelledError):\n            await task\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.PING\n        await asyncio.sleep(0)\n        await ws.pong(\"data\")\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert msg.data == WSCloseCode.OK\n        assert msg.extra == \"exit message\"\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await asyncio.sleep(0)\n    await ws.ping(\"data\")\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    assert msg.data == b\"data\"\n\n    await ws.close(code=WSCloseCode.OK, message=\"exit message\")\n\n    await closed\n\n\nasync def test_receive_timeout_keeps_connection_open(\n    loop: Any, aiohttp_client: Any\n) -> None:\n    closed = loop.create_future()\n    timed_out = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(autoping=False)\n        await ws.prepare(request)\n\n        task = asyncio.create_task(ws.receive(sys.float_info.min))\n        with contextlib.suppress(asyncio.TimeoutError):\n            await task\n\n        timed_out.set_result(None)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.PING\n        await asyncio.sleep(0)\n        await ws.pong(\"data\")\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert msg.data == WSCloseCode.OK\n        assert msg.extra == \"exit message\"\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await timed_out\n    await ws.ping(\"data\")\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    assert msg.data == b\"data\"\n\n    await ws.close(code=WSCloseCode.OK, message=\"exit message\")\n\n    await closed\n",
            "new_file_content": "# HTTP websocket server functional tests\n\nimport asyncio\nimport contextlib\nimport sys\nimport weakref\nfrom typing import Any, Optional\n\nimport pytest\n\nimport aiohttp\nfrom aiohttp import web\nfrom aiohttp.http import WSCloseCode, WSMsgType\nfrom aiohttp.pytest_plugin import AiohttpClient\n\n\nasync def test_websocket_can_prepare(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        if not ws.can_prepare(request):\n            raise web.HTTPUpgradeRequired()\n\n        return web.Response()\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/\")\n    assert resp.status == 426\n\n\nasync def test_websocket_json(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        if not ws.can_prepare(request):\n            return web.HTTPUpgradeRequired()\n\n        await ws.prepare(request)\n        msg = await ws.receive()\n\n        msg_json = msg.json()\n        answer = msg_json[\"test\"]\n        await ws.send_str(answer)\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    expected_value = \"value\"\n    payload = '{\"test\": \"%s\"}' % expected_value\n    await ws.send_str(payload)\n\n    resp = await ws.receive()\n    assert resp.data == expected_value\n\n\nasync def test_websocket_json_invalid_message(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        try:\n            await ws.receive_json()\n        except ValueError:\n            await ws.send_str(\"ValueError was raised\")\n        else:\n            raise Exception(\"No Exception\")\n        finally:\n            await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    payload = \"NOT A VALID JSON STRING\"\n    await ws.send_str(payload)\n\n    data = await ws.receive_str()\n    assert \"ValueError was raised\" in data\n\n\nasync def test_websocket_send_json(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        data = await ws.receive_json()\n        await ws.send_json(data)\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    expected_value = \"value\"\n    await ws.send_json({\"test\": expected_value})\n\n    data = await ws.receive_json()\n    assert data[\"test\"] == expected_value\n\n\nasync def test_websocket_receive_json(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        data = await ws.receive_json()\n        answer = data[\"test\"]\n        await ws.send_str(answer)\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    expected_value = \"value\"\n    payload = '{\"test\": \"%s\"}' % expected_value\n    await ws.send_str(payload)\n\n    resp = await ws.receive()\n    assert resp.data == expected_value\n\n\nasync def test_send_recv_text(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        msg = await ws.receive_str()\n        await ws.send_str(msg + \"/answer\")\n        await ws.close()\n        closed.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.send_str(\"ask\")\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.TEXT\n    assert \"ask/answer\" == msg.data\n\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.CLOSE\n    assert msg.data == WSCloseCode.OK\n    assert msg.extra == \"\"\n\n    assert ws.closed\n    assert ws.close_code == WSCloseCode.OK\n\n    await closed\n\n\nasync def test_send_recv_bytes(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        msg = await ws.receive_bytes()\n        await ws.send_bytes(msg + b\"/answer\")\n        await ws.close()\n        closed.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.send_bytes(b\"ask\")\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.BINARY\n    assert b\"ask/answer\" == msg.data\n\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.CLOSE\n    assert msg.data == WSCloseCode.OK\n    assert msg.extra == \"\"\n\n    assert ws.closed\n    assert ws.close_code == WSCloseCode.OK\n\n    await closed\n\n\nasync def test_send_recv_json(loop, aiohttp_client) -> None:\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        data = await ws.receive_json()\n        await ws.send_json({\"response\": data[\"request\"]})\n        await ws.close()\n        closed.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n\n    await ws.send_str('{\"request\": \"test\"}')\n    msg = await ws.receive()\n    data = msg.json()\n    assert msg.type == aiohttp.WSMsgType.TEXT\n    assert data[\"response\"] == \"test\"\n\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.CLOSE\n    assert msg.data == WSCloseCode.OK\n    assert msg.extra == \"\"\n\n    await ws.close()\n\n    await closed\n\n\nasync def test_close_timeout(loop, aiohttp_client) -> None:\n    aborted = loop.create_future()\n    elapsed = 1e10  # something big\n\n    async def handler(request):\n        nonlocal elapsed\n        ws = web.WebSocketResponse(timeout=0.1)\n        await ws.prepare(request)\n        assert \"request\" == (await ws.receive_str())\n        await ws.send_str(\"reply\")\n        begin = ws._loop.time()\n        assert await ws.close()\n        elapsed = ws._loop.time() - begin\n        assert ws.close_code == WSCloseCode.ABNORMAL_CLOSURE\n        assert isinstance(ws.exception(), asyncio.TimeoutError)\n        aborted.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.send_str(\"request\")\n    assert \"reply\" == (await ws.receive_str())\n\n    # The server closes here.  Then the client sends bogus messages with an\n    # interval shorter than server-side close timeout, to make the server\n    # hanging indefinitely.\n    await asyncio.sleep(0.08)\n    msg = await ws._reader.read()\n    assert msg.type == WSMsgType.CLOSE\n\n    await asyncio.sleep(0.08)\n    assert await aborted\n\n    assert elapsed < 0.25, \"close() should have returned before \" \"at most 2x timeout.\"\n\n    await ws.close()\n\n\nasync def test_concurrent_close(loop, aiohttp_client) -> None:\n\n    srv_ws = None\n\n    async def handler(request):\n        nonlocal srv_ws\n        ws = srv_ws = web.WebSocketResponse(autoclose=False, protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSING\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSING\n\n        await asyncio.sleep(0)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSED\n\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, protocols=(\"eggs\", \"bar\"))\n\n    await srv_ws.close(code=WSCloseCode.INVALID_TEXT)\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n\n    await asyncio.sleep(0)\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSED\n\n\nasync def test_concurrent_close_multiple_tasks(loop: Any, aiohttp_client: Any) -> None:\n    srv_ws = None\n\n    async def handler(request):\n        nonlocal srv_ws\n        ws = srv_ws = web.WebSocketResponse(autoclose=False, protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSING\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSING\n\n        await asyncio.sleep(0)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSED\n\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, protocols=(\"eggs\", \"bar\"))\n\n    task1 = asyncio.create_task(srv_ws.close(code=WSCloseCode.INVALID_TEXT))\n    task2 = asyncio.create_task(srv_ws.close(code=WSCloseCode.INVALID_TEXT))\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n\n    await task1\n    await task2\n\n    await asyncio.sleep(0)\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSED\n\n\nasync def test_close_op_code_from_client(loop: Any, aiohttp_client: Any) -> None:\n    srv_ws: Optional[web.WebSocketResponse] = None\n\n    async def handler(request):\n        nonlocal srv_ws\n        ws = srv_ws = web.WebSocketResponse(protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        await asyncio.sleep(0)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws: web.WebSocketResponse = await client.ws_connect(\"/\", protocols=(\"eggs\", \"bar\"))\n\n    await ws._writer._send_frame(b\"\", WSMsgType.CLOSE)\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n\n    await asyncio.sleep(0)\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSED\n\n\nasync def test_auto_pong_with_closing_by_peer(loop: Any, aiohttp_client: Any) -> None:\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        await ws.receive()\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert msg.data == WSCloseCode.OK\n        assert msg.extra == \"exit message\"\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, autoping=False)\n    await ws.ping()\n    await ws.send_str(\"ask\")\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    await ws.close(code=WSCloseCode.OK, message=\"exit message\")\n    await closed\n\n\nasync def test_ping(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        await ws.ping(\"data\")\n        await ws.receive()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PING\n    assert msg.data == b\"data\"\n    await ws.pong()\n    await ws.close()\n    await closed\n\n\nasync def aiohttp_client_ping(loop, aiohttp_client):\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        await ws.receive()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await ws.ping(\"data\")\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    assert msg.data == b\"data\"\n    await ws.pong()\n    await ws.close()\n\n\nasync def test_pong(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(autoping=False)\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.PING\n        await ws.pong(\"data\")\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert msg.data == WSCloseCode.OK\n        assert msg.extra == \"exit message\"\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await ws.ping(\"data\")\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    assert msg.data == b\"data\"\n\n    await ws.close(code=WSCloseCode.OK, message=\"exit message\")\n\n    await closed\n\n\nasync def test_change_status(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        ws.set_status(200)\n        assert 200 == ws.status\n        await ws.prepare(request)\n        assert 101 == ws.status\n        await ws.close()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await ws.close()\n    await closed\n    await ws.close()\n\n\nasync def test_handle_protocol(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n        await ws.close()\n        assert \"bar\" == ws.ws_protocol\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", protocols=(\"eggs\", \"bar\"))\n\n    await ws.close()\n    await closed\n\n\nasync def test_server_close_handshake(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n        await ws.close()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, protocols=(\"eggs\", \"bar\"))\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n    await ws.close()\n    await closed\n\n\nasync def aiohttp_client_close_handshake(loop, aiohttp_client):\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(autoclose=False, protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert not ws.closed\n        await ws.close()\n        assert ws.closed\n        assert ws.close_code == WSCloseCode.INVALID_TEXT\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSED\n\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, protocols=(\"eggs\", \"bar\"))\n\n    await ws.close(code=WSCloseCode.INVALID_TEXT)\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSED\n    await closed\n\n\nasync def test_server_close_handshake_server_eats_client_messages(loop, aiohttp_client):\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n        await ws.close()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\n        \"/\", autoclose=False, autoping=False, protocols=(\"eggs\", \"bar\")\n    )\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n\n    await ws.send_str(\"text\")\n    await ws.send_bytes(b\"bytes\")\n    await ws.ping()\n\n    await ws.close()\n    await closed\n\n\nasync def test_receive_timeout(loop, aiohttp_client) -> None:\n    raised = False\n\n    async def handler(request):\n        ws = web.WebSocketResponse(receive_timeout=0.1)\n        await ws.prepare(request)\n\n        try:\n            await ws.receive()\n        except asyncio.TimeoutError:\n            nonlocal raised\n            raised = True\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.receive()\n    await ws.close()\n    assert raised\n\n\nasync def test_custom_receive_timeout(loop, aiohttp_client) -> None:\n    raised = False\n\n    async def handler(request):\n        ws = web.WebSocketResponse(receive_timeout=None)\n        await ws.prepare(request)\n\n        try:\n            await ws.receive(0.1)\n        except asyncio.TimeoutError:\n            nonlocal raised\n            raised = True\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.receive()\n    await ws.close()\n    assert raised\n\n\nasync def test_heartbeat(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse(heartbeat=0.05)\n        await ws.prepare(request)\n        await ws.receive()\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n\n    client = await aiohttp_client(app)\n    ws = await client.ws_connect(\"/\", autoping=False)\n    msg = await ws.receive()\n\n    assert msg.type == aiohttp.WSMsgType.ping\n\n    await ws.close()\n\n\nasync def test_heartbeat_no_pong(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse(heartbeat=0.05)\n        await ws.prepare(request)\n\n        await ws.receive()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n\n    client = await aiohttp_client(app)\n    ws = await client.ws_connect(\"/\", autoping=False)\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.ping\n    await ws.close()\n\n\nasync def test_heartbeat_no_pong_send_many_messages(\n    loop: Any, aiohttp_client: Any\n) -> None:\n    \"\"\"Test no pong after sending many messages.\"\"\"\n\n    async def handler(request):\n        ws = web.WebSocketResponse(heartbeat=0.05)\n        await ws.prepare(request)\n        for _ in range(10):\n            await ws.send_str(\"test\")\n\n        await ws.receive()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n\n    client = await aiohttp_client(app)\n    ws = await client.ws_connect(\"/\", autoping=False)\n    for _ in range(10):\n        msg = await ws.receive()\n        assert msg.type is aiohttp.WSMsgType.TEXT\n        assert msg.data == \"test\"\n\n    msg = await ws.receive()\n    assert msg.type is aiohttp.WSMsgType.PING\n    await ws.close()\n\n\nasync def test_heartbeat_no_pong_receive_many_messages(\n    loop: Any, aiohttp_client: Any\n) -> None:\n    \"\"\"Test no pong after receiving many messages.\"\"\"\n\n    async def handler(request):\n        ws = web.WebSocketResponse(heartbeat=0.05)\n        await ws.prepare(request)\n        for _ in range(10):\n            server_msg = await ws.receive()\n            assert server_msg.type is aiohttp.WSMsgType.TEXT\n\n        await ws.receive()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n\n    client = await aiohttp_client(app)\n    ws = await client.ws_connect(\"/\", autoping=False)\n    for _ in range(10):\n        await ws.send_str(\"test\")\n\n    msg = await ws.receive()\n    assert msg.type is aiohttp.WSMsgType.PING\n    await ws.close()\n\n\nasync def test_server_ws_async_for(loop: Any, aiohttp_server: Any) -> None:\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        async for msg in ws:\n            assert msg.type == aiohttp.WSMsgType.TEXT\n            s = msg.data\n            await ws.send_str(s + \"/answer\")\n        await ws.close()\n        closed.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    server = await aiohttp_server(app)\n\n    async with aiohttp.ClientSession() as sm:\n        async with sm.ws_connect(server.make_url(\"/\")) as resp:\n\n            items = [\"q1\", \"q2\", \"q3\"]\n            for item in items:\n                await resp.send_str(item)\n                msg = await resp.receive()\n                assert msg.type == aiohttp.WSMsgType.TEXT\n                assert item + \"/answer\" == msg.data\n\n            await resp.close()\n            await closed\n\n\nasync def test_closed_async_for(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        messages = []\n        async for msg in ws:\n            messages.append(msg)\n            if \"stop\" == msg.data:\n                await ws.send_str(\"stopping\")\n                await ws.close()\n\n        assert 1 == len(messages)\n        assert messages[0].type == WSMsgType.TEXT\n        assert messages[0].data == \"stop\"\n\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.send_str(\"stop\")\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.TEXT\n    assert msg.data == \"stopping\"\n\n    await ws.close()\n    await closed\n\n\nasync def test_websocket_disable_keepalive(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        if not ws.can_prepare(request):\n            return web.Response(text=\"OK\")\n        assert request.protocol._keepalive\n        await ws.prepare(request)\n        assert not request.protocol._keepalive\n        assert not request.protocol._keepalive_handle\n\n        await ws.send_str(\"OK\")\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/\")\n    txt = await resp.text()\n    assert txt == \"OK\"\n\n    ws = await client.ws_connect(\"/\")\n    data = await ws.receive_str()\n    assert data == \"OK\"\n\n\nasync def test_bug3380(loop, aiohttp_client) -> None:\n    async def handle_null(request):\n        return aiohttp.web.json_response({\"err\": None})\n\n    async def ws_handler(request):\n        return web.Response(status=401)\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/ws\", ws_handler)\n    app.router.add_route(\"GET\", \"/api/null\", handle_null)\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/api/null\")\n    assert (await resp.json()) == {\"err\": None}\n    resp.close()\n\n    with pytest.raises(aiohttp.WSServerHandshakeError):\n        await client.ws_connect(\"/ws\")\n\n    resp = await client.get(\"/api/null\", timeout=1)\n    assert (await resp.json()) == {\"err\": None}\n    resp.close()\n\n\nasync def test_receive_being_cancelled_keeps_connection_open(\n    loop: Any, aiohttp_client: Any\n) -> None:\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(autoping=False)\n        await ws.prepare(request)\n\n        task = asyncio.create_task(ws.receive())\n        await asyncio.sleep(0)\n        task.cancel()\n        with contextlib.suppress(asyncio.CancelledError):\n            await task\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.PING\n        await asyncio.sleep(0)\n        await ws.pong(\"data\")\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert msg.data == WSCloseCode.OK\n        assert msg.extra == \"exit message\"\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await asyncio.sleep(0)\n    await ws.ping(\"data\")\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    assert msg.data == b\"data\"\n\n    await ws.close(code=WSCloseCode.OK, message=\"exit message\")\n\n    await closed\n\n\nasync def test_receive_timeout_keeps_connection_open(\n    loop: Any, aiohttp_client: Any\n) -> None:\n    closed = loop.create_future()\n    timed_out = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(autoping=False)\n        await ws.prepare(request)\n\n        task = asyncio.create_task(ws.receive(sys.float_info.min))\n        with contextlib.suppress(asyncio.TimeoutError):\n            await task\n\n        timed_out.set_result(None)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.PING\n        await asyncio.sleep(0)\n        await ws.pong(\"data\")\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert msg.data == WSCloseCode.OK\n        assert msg.extra == \"exit message\"\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await timed_out\n    await ws.ping(\"data\")\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    assert msg.data == b\"data\"\n\n    await ws.close(code=WSCloseCode.OK, message=\"exit message\")\n\n    await closed\n\n\nasync def test_websocket_shutdown(aiohttp_client: AiohttpClient) -> None:\n    \"\"\"Test that the client websocket gets the close message when the server is shutting down.\"\"\"\n    url = \"/ws\"\n    app = web.Application()\n    websockets = web.AppKey(\"websockets\", weakref.WeakSet)\n    app[websockets] = weakref.WeakSet()\n\n    # need for send signal shutdown server\n    shutdown_websockets = web.AppKey(\"shutdown_websockets\", weakref.WeakSet)\n    app[shutdown_websockets] = weakref.WeakSet()\n\n    async def websocket_handler(request: web.Request) -> web.WebSocketResponse:\n        websocket = web.WebSocketResponse()\n        await websocket.prepare(request)\n        request.app[websockets].add(websocket)\n        request.app[shutdown_websockets].add(websocket)\n\n        try:\n            async for message in websocket:\n                await websocket.send_json({\"ok\": True, \"message\": message.json()})\n        finally:\n            request.app[websockets].discard(websocket)\n\n        return websocket\n\n    async def on_shutdown(app: web.Application) -> None:\n        while app[shutdown_websockets]:\n            websocket = app[shutdown_websockets].pop()\n            await websocket.close(\n                code=aiohttp.WSCloseCode.GOING_AWAY,\n                message=\"Server shutdown\",\n            )\n\n    app.router.add_get(url, websocket_handler)\n    app.on_shutdown.append(on_shutdown)\n\n    client = await aiohttp_client(app)\n\n    websocket = await client.ws_connect(url)\n\n    message = {\"message\": \"hi\"}\n    await websocket.send_json(message)\n    reply = await websocket.receive_json()\n    assert reply == {\"ok\": True, \"message\": message}\n\n    await app.shutdown()\n\n    assert websocket.closed is False\n\n    reply = await websocket.receive()\n\n    assert reply.type is aiohttp.http.WSMsgType.CLOSE\n    assert reply.data == aiohttp.WSCloseCode.GOING_AWAY\n    assert reply.extra == \"Server shutdown\"\n\n    assert websocket.closed is True\n",
            "header": {
                "file": {
                    "path": "tests/test_web_websocket_functional.py"
                },
                "misc_line": null
            },
            "index_line": {
                "old_commit_hash": "15ef33e3",
                "new_commit_hash": "6540f134",
                "mode": "100644"
            },
            "is_binary_file": false,
            "binary_line": null,
            "minus_file": {
                "path": "a/tests/test_web_websocket_functional.py"
            },
            "plus_file": {
                "path": "b/tests/test_web_websocket_functional.py"
            },
            "hunks": [
                {
                    "descriptor": {
                        "old_range": {
                            "start": 3,
                            "length": 6
                        },
                        "new_range": {
                            "start": 3,
                            "length": 7
                        },
                        "section": ""
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "import asyncio",
                                "type": "context"
                            },
                            {
                                "content": "import contextlib",
                                "type": "context"
                            },
                            {
                                "content": "import sys",
                                "type": "context"
                            },
                            {
                                "content": "import weakref",
                                "type": "added"
                            },
                            {
                                "content": "from typing import Any, Optional",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "import pytest",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "tests/test_web_websocket_functional.py",
                            "type": "import",
                            "name": "weakref",
                            "content": "import weakref",
                            "ast_type_str": "Import",
                            "start_lineno": 6,
                            "end_lineno": 6,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 10,
                            "length": 6
                        },
                        "new_range": {
                            "start": 11,
                            "length": 7
                        },
                        "section": "import pytest"
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "import aiohttp",
                                "type": "context"
                            },
                            {
                                "content": "from aiohttp import web",
                                "type": "context"
                            },
                            {
                                "content": "from aiohttp.http import WSCloseCode, WSMsgType",
                                "type": "context"
                            },
                            {
                                "content": "from aiohttp.pytest_plugin import AiohttpClient",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "async def test_websocket_can_prepare(loop, aiohttp_client) -> None:",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "tests/test_web_websocket_functional.py",
                            "type": "statement",
                            "name": "",
                            "content": "from aiohttp.pytest_plugin import AiohttpClient",
                            "ast_type_str": "ImportFrom",
                            "start_lineno": 14,
                            "end_lineno": 14,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                },
                {
                    "descriptor": {
                        "old_range": {
                            "start": 988,
                            "length": 3
                        },
                        "new_range": {
                            "start": 990,
                            "length": 61
                        },
                        "section": "async def test_receive_timeout_keeps_connection_open("
                    },
                    "line_group": {
                        "all_lines": [
                            {
                                "content": "    await ws.close(code=WSCloseCode.OK, message=\"exit message\")",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "context"
                            },
                            {
                                "content": "    await closed",
                                "type": "context"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "async def test_websocket_shutdown(aiohttp_client: AiohttpClient) -> None:",
                                "type": "added"
                            },
                            {
                                "content": "    \"\"\"Test that the client websocket gets the close message when the server is shutting down.\"\"\"",
                                "type": "added"
                            },
                            {
                                "content": "    url = \"/ws\"",
                                "type": "added"
                            },
                            {
                                "content": "    app = web.Application()",
                                "type": "added"
                            },
                            {
                                "content": "    websockets = web.AppKey(\"websockets\", weakref.WeakSet)",
                                "type": "added"
                            },
                            {
                                "content": "    app[websockets] = weakref.WeakSet()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    # need for send signal shutdown server",
                                "type": "added"
                            },
                            {
                                "content": "    shutdown_websockets = web.AppKey(\"shutdown_websockets\", weakref.WeakSet)",
                                "type": "added"
                            },
                            {
                                "content": "    app[shutdown_websockets] = weakref.WeakSet()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    async def websocket_handler(request: web.Request) -> web.WebSocketResponse:",
                                "type": "added"
                            },
                            {
                                "content": "        websocket = web.WebSocketResponse()",
                                "type": "added"
                            },
                            {
                                "content": "        await websocket.prepare(request)",
                                "type": "added"
                            },
                            {
                                "content": "        request.app[websockets].add(websocket)",
                                "type": "added"
                            },
                            {
                                "content": "        request.app[shutdown_websockets].add(websocket)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        try:",
                                "type": "added"
                            },
                            {
                                "content": "            async for message in websocket:",
                                "type": "added"
                            },
                            {
                                "content": "                await websocket.send_json({\"ok\": True, \"message\": message.json()})",
                                "type": "added"
                            },
                            {
                                "content": "        finally:",
                                "type": "added"
                            },
                            {
                                "content": "            request.app[websockets].discard(websocket)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "        return websocket",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    async def on_shutdown(app: web.Application) -> None:",
                                "type": "added"
                            },
                            {
                                "content": "        while app[shutdown_websockets]:",
                                "type": "added"
                            },
                            {
                                "content": "            websocket = app[shutdown_websockets].pop()",
                                "type": "added"
                            },
                            {
                                "content": "            await websocket.close(",
                                "type": "added"
                            },
                            {
                                "content": "                code=aiohttp.WSCloseCode.GOING_AWAY,",
                                "type": "added"
                            },
                            {
                                "content": "                message=\"Server shutdown\",",
                                "type": "added"
                            },
                            {
                                "content": "            )",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    app.router.add_get(url, websocket_handler)",
                                "type": "added"
                            },
                            {
                                "content": "    app.on_shutdown.append(on_shutdown)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    client = await aiohttp_client(app)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    websocket = await client.ws_connect(url)",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    message = {\"message\": \"hi\"}",
                                "type": "added"
                            },
                            {
                                "content": "    await websocket.send_json(message)",
                                "type": "added"
                            },
                            {
                                "content": "    reply = await websocket.receive_json()",
                                "type": "added"
                            },
                            {
                                "content": "    assert reply == {\"ok\": True, \"message\": message}",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    await app.shutdown()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    assert websocket.closed is False",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    reply = await websocket.receive()",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    assert reply.type is aiohttp.http.WSMsgType.CLOSE",
                                "type": "added"
                            },
                            {
                                "content": "    assert reply.data == aiohttp.WSCloseCode.GOING_AWAY",
                                "type": "added"
                            },
                            {
                                "content": "    assert reply.extra == \"Server shutdown\"",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "added"
                            },
                            {
                                "content": "    assert websocket.closed is True",
                                "type": "added"
                            },
                            {
                                "content": "",
                                "type": "context"
                            }
                        ]
                    },
                    "modified_entities": [],
                    "added_entities": [
                        {
                            "file_name": "tests/test_web_websocket_functional.py",
                            "type": "function",
                            "name": "test_websocket_shutdown",
                            "content": "async def test_websocket_shutdown(aiohttp_client: AiohttpClient) -> None:\n    \"\"\"Test that the client websocket gets the close message when the server is shutting down.\"\"\"\n    url = \"/ws\"\n    app = web.Application()\n    websockets = web.AppKey(\"websockets\", weakref.WeakSet)\n    app[websockets] = weakref.WeakSet()\n\n    # need for send signal shutdown server\n    shutdown_websockets = web.AppKey(\"shutdown_websockets\", weakref.WeakSet)\n    app[shutdown_websockets] = weakref.WeakSet()\n\n    async def websocket_handler(request: web.Request) -> web.WebSocketResponse:\n        websocket = web.WebSocketResponse()\n        await websocket.prepare(request)\n        request.app[websockets].add(websocket)\n        request.app[shutdown_websockets].add(websocket)\n\n        try:\n            async for message in websocket:\n                await websocket.send_json({\"ok\": True, \"message\": message.json()})\n        finally:\n            request.app[websockets].discard(websocket)\n\n        return websocket\n\n    async def on_shutdown(app: web.Application) -> None:\n        while app[shutdown_websockets]:\n            websocket = app[shutdown_websockets].pop()\n            await websocket.close(\n                code=aiohttp.WSCloseCode.GOING_AWAY,\n                message=\"Server shutdown\",\n            )\n\n    app.router.add_get(url, websocket_handler)\n    app.on_shutdown.append(on_shutdown)\n\n    client = await aiohttp_client(app)\n\n    websocket = await client.ws_connect(url)\n\n    message = {\"message\": \"hi\"}\n    await websocket.send_json(message)\n    reply = await websocket.receive_json()\n    assert reply == {\"ok\": True, \"message\": message}\n\n    await app.shutdown()\n\n    assert websocket.closed is False\n\n    reply = await websocket.receive()\n\n    assert reply.type is aiohttp.http.WSMsgType.CLOSE\n    assert reply.data == aiohttp.WSCloseCode.GOING_AWAY\n    assert reply.extra == \"Server shutdown\"\n\n    assert websocket.closed is True",
                            "ast_type_str": "AsyncFunctionDef",
                            "start_lineno": 995,
                            "end_lineno": 1050,
                            "parent": null
                        }
                    ],
                    "deleted_entities": []
                }
            ]
        }
    ],
    "old_commit_hash": "50efb2a389414cf36012d3f6e86623f8eac3a42e^",
    "new_commit_hash": "50efb2a389414cf36012d3f6e86623f8eac3a42e",
    "commit_message": "[PR #8680/4f41d05 backport][3.10] Fix close race that prevented the close code from reaching the client (#8686)",
    "commit_date": "2024-08-12T09:40:34-05:00",
    "metadata": {}
}
{
    "repo_name": "aiohttp",
    "new_commit_hash": "50efb2a389414cf36012d3f6e86623f8eac3a42e",
    "test_file_codes": [
        "# HTTP websocket server functional tests\n\nimport asyncio\nimport contextlib\nimport sys\nimport weakref\nfrom typing import Any, Optional\n\nimport pytest\n\nimport aiohttp\nfrom aiohttp import web\nfrom aiohttp.http import WSCloseCode, WSMsgType\nfrom aiohttp.pytest_plugin import AiohttpClient\n\n\nasync def test_websocket_can_prepare(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        if not ws.can_prepare(request):\n            raise web.HTTPUpgradeRequired()\n\n        return web.Response()\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/\")\n    assert resp.status == 426\n\n\nasync def test_websocket_json(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        if not ws.can_prepare(request):\n            return web.HTTPUpgradeRequired()\n\n        await ws.prepare(request)\n        msg = await ws.receive()\n\n        msg_json = msg.json()\n        answer = msg_json[\"test\"]\n        await ws.send_str(answer)\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    expected_value = \"value\"\n    payload = '{\"test\": \"%s\"}' % expected_value\n    await ws.send_str(payload)\n\n    resp = await ws.receive()\n    assert resp.data == expected_value\n\n\nasync def test_websocket_json_invalid_message(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        try:\n            await ws.receive_json()\n        except ValueError:\n            await ws.send_str(\"ValueError was raised\")\n        else:\n            raise Exception(\"No Exception\")\n        finally:\n            await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    payload = \"NOT A VALID JSON STRING\"\n    await ws.send_str(payload)\n\n    data = await ws.receive_str()\n    assert \"ValueError was raised\" in data\n\n\nasync def test_websocket_send_json(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        data = await ws.receive_json()\n        await ws.send_json(data)\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    expected_value = \"value\"\n    await ws.send_json({\"test\": expected_value})\n\n    data = await ws.receive_json()\n    assert data[\"test\"] == expected_value\n\n\nasync def test_websocket_receive_json(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        data = await ws.receive_json()\n        answer = data[\"test\"]\n        await ws.send_str(answer)\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    expected_value = \"value\"\n    payload = '{\"test\": \"%s\"}' % expected_value\n    await ws.send_str(payload)\n\n    resp = await ws.receive()\n    assert resp.data == expected_value\n\n\nasync def test_send_recv_text(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        msg = await ws.receive_str()\n        await ws.send_str(msg + \"/answer\")\n        await ws.close()\n        closed.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.send_str(\"ask\")\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.TEXT\n    assert \"ask/answer\" == msg.data\n\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.CLOSE\n    assert msg.data == WSCloseCode.OK\n    assert msg.extra == \"\"\n\n    assert ws.closed\n    assert ws.close_code == WSCloseCode.OK\n\n    await closed\n\n\nasync def test_send_recv_bytes(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        msg = await ws.receive_bytes()\n        await ws.send_bytes(msg + b\"/answer\")\n        await ws.close()\n        closed.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.send_bytes(b\"ask\")\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.BINARY\n    assert b\"ask/answer\" == msg.data\n\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.CLOSE\n    assert msg.data == WSCloseCode.OK\n    assert msg.extra == \"\"\n\n    assert ws.closed\n    assert ws.close_code == WSCloseCode.OK\n\n    await closed\n\n\nasync def test_send_recv_json(loop, aiohttp_client) -> None:\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        data = await ws.receive_json()\n        await ws.send_json({\"response\": data[\"request\"]})\n        await ws.close()\n        closed.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n\n    await ws.send_str('{\"request\": \"test\"}')\n    msg = await ws.receive()\n    data = msg.json()\n    assert msg.type == aiohttp.WSMsgType.TEXT\n    assert data[\"response\"] == \"test\"\n\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.CLOSE\n    assert msg.data == WSCloseCode.OK\n    assert msg.extra == \"\"\n\n    await ws.close()\n\n    await closed\n\n\nasync def test_close_timeout(loop, aiohttp_client) -> None:\n    aborted = loop.create_future()\n    elapsed = 1e10  # something big\n\n    async def handler(request):\n        nonlocal elapsed\n        ws = web.WebSocketResponse(timeout=0.1)\n        await ws.prepare(request)\n        assert \"request\" == (await ws.receive_str())\n        await ws.send_str(\"reply\")\n        begin = ws._loop.time()\n        assert await ws.close()\n        elapsed = ws._loop.time() - begin\n        assert ws.close_code == WSCloseCode.ABNORMAL_CLOSURE\n        assert isinstance(ws.exception(), asyncio.TimeoutError)\n        aborted.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.send_str(\"request\")\n    assert \"reply\" == (await ws.receive_str())\n\n    # The server closes here.  Then the client sends bogus messages with an\n    # interval shorter than server-side close timeout, to make the server\n    # hanging indefinitely.\n    await asyncio.sleep(0.08)\n    msg = await ws._reader.read()\n    assert msg.type == WSMsgType.CLOSE\n\n    await asyncio.sleep(0.08)\n    assert await aborted\n\n    assert elapsed < 0.25, \"close() should have returned before \" \"at most 2x timeout.\"\n\n    await ws.close()\n\n\nasync def test_concurrent_close(loop, aiohttp_client) -> None:\n\n    srv_ws = None\n\n    async def handler(request):\n        nonlocal srv_ws\n        ws = srv_ws = web.WebSocketResponse(autoclose=False, protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSING\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSING\n\n        await asyncio.sleep(0)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSED\n\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, protocols=(\"eggs\", \"bar\"))\n\n    await srv_ws.close(code=WSCloseCode.INVALID_TEXT)\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n\n    await asyncio.sleep(0)\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSED\n\n\nasync def test_concurrent_close_multiple_tasks(loop: Any, aiohttp_client: Any) -> None:\n    srv_ws = None\n\n    async def handler(request):\n        nonlocal srv_ws\n        ws = srv_ws = web.WebSocketResponse(autoclose=False, protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSING\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSING\n\n        await asyncio.sleep(0)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSED\n\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, protocols=(\"eggs\", \"bar\"))\n\n    task1 = asyncio.create_task(srv_ws.close(code=WSCloseCode.INVALID_TEXT))\n    task2 = asyncio.create_task(srv_ws.close(code=WSCloseCode.INVALID_TEXT))\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n\n    await task1\n    await task2\n\n    await asyncio.sleep(0)\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSED\n\n\nasync def test_close_op_code_from_client(loop: Any, aiohttp_client: Any) -> None:\n    srv_ws: Optional[web.WebSocketResponse] = None\n\n    async def handler(request):\n        nonlocal srv_ws\n        ws = srv_ws = web.WebSocketResponse(protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        await asyncio.sleep(0)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws: web.WebSocketResponse = await client.ws_connect(\"/\", protocols=(\"eggs\", \"bar\"))\n\n    await ws._writer._send_frame(b\"\", WSMsgType.CLOSE)\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n\n    await asyncio.sleep(0)\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSED\n\n\nasync def test_auto_pong_with_closing_by_peer(loop: Any, aiohttp_client: Any) -> None:\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        await ws.receive()\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert msg.data == WSCloseCode.OK\n        assert msg.extra == \"exit message\"\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, autoping=False)\n    await ws.ping()\n    await ws.send_str(\"ask\")\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    await ws.close(code=WSCloseCode.OK, message=\"exit message\")\n    await closed\n\n\nasync def test_ping(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        await ws.ping(\"data\")\n        await ws.receive()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PING\n    assert msg.data == b\"data\"\n    await ws.pong()\n    await ws.close()\n    await closed\n\n\nasync def aiohttp_client_ping(loop, aiohttp_client):\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        await ws.receive()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await ws.ping(\"data\")\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    assert msg.data == b\"data\"\n    await ws.pong()\n    await ws.close()\n\n\nasync def test_pong(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(autoping=False)\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.PING\n        await ws.pong(\"data\")\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert msg.data == WSCloseCode.OK\n        assert msg.extra == \"exit message\"\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await ws.ping(\"data\")\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    assert msg.data == b\"data\"\n\n    await ws.close(code=WSCloseCode.OK, message=\"exit message\")\n\n    await closed\n\n\nasync def test_change_status(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        ws.set_status(200)\n        assert 200 == ws.status\n        await ws.prepare(request)\n        assert 101 == ws.status\n        await ws.close()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await ws.close()\n    await closed\n    await ws.close()\n\n\nasync def test_handle_protocol(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n        await ws.close()\n        assert \"bar\" == ws.ws_protocol\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", protocols=(\"eggs\", \"bar\"))\n\n    await ws.close()\n    await closed\n\n\nasync def test_server_close_handshake(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n        await ws.close()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, protocols=(\"eggs\", \"bar\"))\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n    await ws.close()\n    await closed\n\n\nasync def aiohttp_client_close_handshake(loop, aiohttp_client):\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(autoclose=False, protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert not ws.closed\n        await ws.close()\n        assert ws.closed\n        assert ws.close_code == WSCloseCode.INVALID_TEXT\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSED\n\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoclose=False, protocols=(\"eggs\", \"bar\"))\n\n    await ws.close(code=WSCloseCode.INVALID_TEXT)\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSED\n    await closed\n\n\nasync def test_server_close_handshake_server_eats_client_messages(loop, aiohttp_client):\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(protocols=(\"foo\", \"bar\"))\n        await ws.prepare(request)\n        await ws.close()\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\n        \"/\", autoclose=False, autoping=False, protocols=(\"eggs\", \"bar\")\n    )\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.CLOSE\n\n    await ws.send_str(\"text\")\n    await ws.send_bytes(b\"bytes\")\n    await ws.ping()\n\n    await ws.close()\n    await closed\n\n\nasync def test_receive_timeout(loop, aiohttp_client) -> None:\n    raised = False\n\n    async def handler(request):\n        ws = web.WebSocketResponse(receive_timeout=0.1)\n        await ws.prepare(request)\n\n        try:\n            await ws.receive()\n        except asyncio.TimeoutError:\n            nonlocal raised\n            raised = True\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.receive()\n    await ws.close()\n    assert raised\n\n\nasync def test_custom_receive_timeout(loop, aiohttp_client) -> None:\n    raised = False\n\n    async def handler(request):\n        ws = web.WebSocketResponse(receive_timeout=None)\n        await ws.prepare(request)\n\n        try:\n            await ws.receive(0.1)\n        except asyncio.TimeoutError:\n            nonlocal raised\n            raised = True\n\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.receive()\n    await ws.close()\n    assert raised\n\n\nasync def test_heartbeat(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse(heartbeat=0.05)\n        await ws.prepare(request)\n        await ws.receive()\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n\n    client = await aiohttp_client(app)\n    ws = await client.ws_connect(\"/\", autoping=False)\n    msg = await ws.receive()\n\n    assert msg.type == aiohttp.WSMsgType.ping\n\n    await ws.close()\n\n\nasync def test_heartbeat_no_pong(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse(heartbeat=0.05)\n        await ws.prepare(request)\n\n        await ws.receive()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n\n    client = await aiohttp_client(app)\n    ws = await client.ws_connect(\"/\", autoping=False)\n    msg = await ws.receive()\n    assert msg.type == aiohttp.WSMsgType.ping\n    await ws.close()\n\n\nasync def test_heartbeat_no_pong_send_many_messages(\n    loop: Any, aiohttp_client: Any\n) -> None:\n    \"\"\"Test no pong after sending many messages.\"\"\"\n\n    async def handler(request):\n        ws = web.WebSocketResponse(heartbeat=0.05)\n        await ws.prepare(request)\n        for _ in range(10):\n            await ws.send_str(\"test\")\n\n        await ws.receive()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n\n    client = await aiohttp_client(app)\n    ws = await client.ws_connect(\"/\", autoping=False)\n    for _ in range(10):\n        msg = await ws.receive()\n        assert msg.type is aiohttp.WSMsgType.TEXT\n        assert msg.data == \"test\"\n\n    msg = await ws.receive()\n    assert msg.type is aiohttp.WSMsgType.PING\n    await ws.close()\n\n\nasync def test_heartbeat_no_pong_receive_many_messages(\n    loop: Any, aiohttp_client: Any\n) -> None:\n    \"\"\"Test no pong after receiving many messages.\"\"\"\n\n    async def handler(request):\n        ws = web.WebSocketResponse(heartbeat=0.05)\n        await ws.prepare(request)\n        for _ in range(10):\n            server_msg = await ws.receive()\n            assert server_msg.type is aiohttp.WSMsgType.TEXT\n\n        await ws.receive()\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n\n    client = await aiohttp_client(app)\n    ws = await client.ws_connect(\"/\", autoping=False)\n    for _ in range(10):\n        await ws.send_str(\"test\")\n\n    msg = await ws.receive()\n    assert msg.type is aiohttp.WSMsgType.PING\n    await ws.close()\n\n\nasync def test_server_ws_async_for(loop: Any, aiohttp_server: Any) -> None:\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n        async for msg in ws:\n            assert msg.type == aiohttp.WSMsgType.TEXT\n            s = msg.data\n            await ws.send_str(s + \"/answer\")\n        await ws.close()\n        closed.set_result(1)\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    server = await aiohttp_server(app)\n\n    async with aiohttp.ClientSession() as sm:\n        async with sm.ws_connect(server.make_url(\"/\")) as resp:\n\n            items = [\"q1\", \"q2\", \"q3\"]\n            for item in items:\n                await resp.send_str(item)\n                msg = await resp.receive()\n                assert msg.type == aiohttp.WSMsgType.TEXT\n                assert item + \"/answer\" == msg.data\n\n            await resp.close()\n            await closed\n\n\nasync def test_closed_async_for(loop, aiohttp_client) -> None:\n\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        await ws.prepare(request)\n\n        messages = []\n        async for msg in ws:\n            messages.append(msg)\n            if \"stop\" == msg.data:\n                await ws.send_str(\"stopping\")\n                await ws.close()\n\n        assert 1 == len(messages)\n        assert messages[0].type == WSMsgType.TEXT\n        assert messages[0].data == \"stop\"\n\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\")\n    await ws.send_str(\"stop\")\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.TEXT\n    assert msg.data == \"stopping\"\n\n    await ws.close()\n    await closed\n\n\nasync def test_websocket_disable_keepalive(loop, aiohttp_client) -> None:\n    async def handler(request):\n        ws = web.WebSocketResponse()\n        if not ws.can_prepare(request):\n            return web.Response(text=\"OK\")\n        assert request.protocol._keepalive\n        await ws.prepare(request)\n        assert not request.protocol._keepalive\n        assert not request.protocol._keepalive_handle\n\n        await ws.send_str(\"OK\")\n        await ws.close()\n        return ws\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/\", handler)\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/\")\n    txt = await resp.text()\n    assert txt == \"OK\"\n\n    ws = await client.ws_connect(\"/\")\n    data = await ws.receive_str()\n    assert data == \"OK\"\n\n\nasync def test_bug3380(loop, aiohttp_client) -> None:\n    async def handle_null(request):\n        return aiohttp.web.json_response({\"err\": None})\n\n    async def ws_handler(request):\n        return web.Response(status=401)\n\n    app = web.Application()\n    app.router.add_route(\"GET\", \"/ws\", ws_handler)\n    app.router.add_route(\"GET\", \"/api/null\", handle_null)\n\n    client = await aiohttp_client(app)\n\n    resp = await client.get(\"/api/null\")\n    assert (await resp.json()) == {\"err\": None}\n    resp.close()\n\n    with pytest.raises(aiohttp.WSServerHandshakeError):\n        await client.ws_connect(\"/ws\")\n\n    resp = await client.get(\"/api/null\", timeout=1)\n    assert (await resp.json()) == {\"err\": None}\n    resp.close()\n\n\nasync def test_receive_being_cancelled_keeps_connection_open(\n    loop: Any, aiohttp_client: Any\n) -> None:\n    closed = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(autoping=False)\n        await ws.prepare(request)\n\n        task = asyncio.create_task(ws.receive())\n        await asyncio.sleep(0)\n        task.cancel()\n        with contextlib.suppress(asyncio.CancelledError):\n            await task\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.PING\n        await asyncio.sleep(0)\n        await ws.pong(\"data\")\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert msg.data == WSCloseCode.OK\n        assert msg.extra == \"exit message\"\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await asyncio.sleep(0)\n    await ws.ping(\"data\")\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    assert msg.data == b\"data\"\n\n    await ws.close(code=WSCloseCode.OK, message=\"exit message\")\n\n    await closed\n\n\nasync def test_receive_timeout_keeps_connection_open(\n    loop: Any, aiohttp_client: Any\n) -> None:\n    closed = loop.create_future()\n    timed_out = loop.create_future()\n\n    async def handler(request):\n        ws = web.WebSocketResponse(autoping=False)\n        await ws.prepare(request)\n\n        task = asyncio.create_task(ws.receive(sys.float_info.min))\n        with contextlib.suppress(asyncio.TimeoutError):\n            await task\n\n        timed_out.set_result(None)\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.PING\n        await asyncio.sleep(0)\n        await ws.pong(\"data\")\n\n        msg = await ws.receive()\n        assert msg.type == WSMsgType.CLOSE\n        assert msg.data == WSCloseCode.OK\n        assert msg.extra == \"exit message\"\n        closed.set_result(None)\n        return ws\n\n    app = web.Application()\n    app.router.add_get(\"/\", handler)\n    client = await aiohttp_client(app)\n\n    ws = await client.ws_connect(\"/\", autoping=False)\n\n    await timed_out\n    await ws.ping(\"data\")\n\n    msg = await ws.receive()\n    assert msg.type == WSMsgType.PONG\n    assert msg.data == b\"data\"\n\n    await ws.close(code=WSCloseCode.OK, message=\"exit message\")\n\n    await closed\n\n\nasync def test_websocket_shutdown(aiohttp_client: AiohttpClient) -> None:\n    \"\"\"Test that the client websocket gets the close message when the server is shutting down.\"\"\"\n    url = \"/ws\"\n    app = web.Application()\n    websockets = web.AppKey(\"websockets\", weakref.WeakSet)\n    app[websockets] = weakref.WeakSet()\n\n    # need for send signal shutdown server\n    shutdown_websockets = web.AppKey(\"shutdown_websockets\", weakref.WeakSet)\n    app[shutdown_websockets] = weakref.WeakSet()\n\n    async def websocket_handler(request: web.Request) -> web.WebSocketResponse:\n        websocket = web.WebSocketResponse()\n        await websocket.prepare(request)\n        request.app[websockets].add(websocket)\n        request.app[shutdown_websockets].add(websocket)\n\n        try:\n            async for message in websocket:\n                await websocket.send_json({\"ok\": True, \"message\": message.json()})\n        finally:\n            request.app[websockets].discard(websocket)\n\n        return websocket\n\n    async def on_shutdown(app: web.Application) -> None:\n        while app[shutdown_websockets]:\n            websocket = app[shutdown_websockets].pop()\n            await websocket.close(\n                code=aiohttp.WSCloseCode.GOING_AWAY,\n                message=\"Server shutdown\",\n            )\n\n    app.router.add_get(url, websocket_handler)\n    app.on_shutdown.append(on_shutdown)\n\n    client = await aiohttp_client(app)\n\n    websocket = await client.ws_connect(url)\n\n    message = {\"message\": \"hi\"}\n    await websocket.send_json(message)\n    reply = await websocket.receive_json()\n    assert reply == {\"ok\": True, \"message\": message}\n\n    await app.shutdown()\n\n    assert websocket.closed is False\n\n    reply = await websocket.receive()\n\n    assert reply.type is aiohttp.http.WSMsgType.CLOSE\n    assert reply.data == aiohttp.WSCloseCode.GOING_AWAY\n    assert reply.extra == \"Server shutdown\"\n\n    assert websocket.closed is True\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.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.Mock(**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_50efb2a389414cf36012d3f6e86623f8eac3a42e/requirements/cython.txt\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/tools/gen.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/hdrs.py\n./tools/gen.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/_helpers.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/_http_parser.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/_http_writer.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/_websocket.pyx\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/_helpers.pyi\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/_cparser.pxd\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/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_50efb2a389414cf36012d3f6e86623f8eac3a42e/vendor/llhttp'\nnpx ts-node bin/generate.ts\nmake[1]: Leaving directory '/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/vendor/llhttp'\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/__init__.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/abc.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/base_protocol.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/client.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/client_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/client_proto.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/client_reqrep.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/client_ws.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/compression_utils.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/connector.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/cookiejar.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/formdata.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/helpers.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/http.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/http_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/http_parser.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/http_websocket.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/http_writer.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/locks.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/log.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/multipart.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/payload.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/payload_streamer.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/pytest_plugin.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/resolver.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/streams.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/tcp_helpers.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/test_utils.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/tracing.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/typedefs.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_app.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_exceptions.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_fileresponse.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_log.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_middlewares.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_protocol.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_request.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_response.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_routedef.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_runner.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_server.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_urldispatcher.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_ws.py\nre-hash /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/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.28ms\nInstalled 1 package in 14ms\n + pip==24.3.1\nResolved 2 packages in 8ms\nInstalled 2 packages in 12ms\n + cython==3.0.10\n + multidict==6.0.5\nResolved 96 packages in 48ms\nUninstalled 1 package in 4ms\nInstalled 95 packages in 139ms\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.15.1\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.9.4\n + zipp==3.17.0\nResolved 9 packages in 1.67s\nPrepared 1 package in 16.50s\nInstalled 1 package in 1ms\n + aiohttp==3.10.4.dev0 (from file:///home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e)\nResolved 24 packages in 14ms\nInstalled 5 packages in 6ms\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_50efb2a389414cf36012d3f6e86623f8eac3a42e/.venv/bin/python\ncachedir: .pytest_cache\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e\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 32 items\n\nr2e_tests/test_1.py::test_websocket_can_prepare[pyloop] PASSED           [  3%]\nr2e_tests/test_1.py::test_websocket_json[pyloop] PASSED                  [  6%]\nr2e_tests/test_1.py::test_websocket_json_invalid_message[pyloop] PASSED  [  9%]\nr2e_tests/test_1.py::test_websocket_send_json[pyloop] PASSED             [ 12%]\nr2e_tests/test_1.py::test_websocket_receive_json[pyloop] PASSED          [ 15%]\nr2e_tests/test_1.py::test_send_recv_text[pyloop] PASSED                  [ 18%]\nr2e_tests/test_1.py::test_send_recv_bytes[pyloop] PASSED                 [ 21%]\nr2e_tests/test_1.py::test_send_recv_json[pyloop] PASSED                  [ 25%]\nr2e_tests/test_1.py::test_close_timeout[pyloop] PASSED                   [ 28%]\nr2e_tests/test_1.py::test_concurrent_close[pyloop] PASSED                [ 31%]\nr2e_tests/test_1.py::test_concurrent_close_multiple_tasks[pyloop] PASSED [ 34%]\nr2e_tests/test_1.py::test_close_op_code_from_client[pyloop] PASSED       [ 37%]\nr2e_tests/test_1.py::test_auto_pong_with_closing_by_peer[pyloop] PASSED  [ 40%]\nr2e_tests/test_1.py::test_ping[pyloop] PASSED                            [ 43%]\nr2e_tests/test_1.py::test_pong[pyloop] PASSED                            [ 46%]\nr2e_tests/test_1.py::test_change_status[pyloop] PASSED                   [ 50%]\nr2e_tests/test_1.py::test_handle_protocol[pyloop] PASSED                 [ 53%]\nr2e_tests/test_1.py::test_server_close_handshake[pyloop] PASSED          [ 56%]\nr2e_tests/test_1.py::test_server_close_handshake_server_eats_client_messages[pyloop] PASSED [ 59%]\nr2e_tests/test_1.py::test_receive_timeout[pyloop] PASSED                 [ 62%]\nr2e_tests/test_1.py::test_custom_receive_timeout[pyloop] PASSED          [ 65%]\nr2e_tests/test_1.py::test_heartbeat[pyloop] PASSED                       [ 68%]\nr2e_tests/test_1.py::test_heartbeat_no_pong[pyloop] PASSED               [ 71%]\nr2e_tests/test_1.py::test_heartbeat_no_pong_send_many_messages[pyloop] PASSED [ 75%]\nr2e_tests/test_1.py::test_heartbeat_no_pong_receive_many_messages[pyloop] PASSED [ 78%]\nr2e_tests/test_1.py::test_server_ws_async_for[pyloop] PASSED             [ 81%]\nr2e_tests/test_1.py::test_closed_async_for[pyloop] PASSED                [ 84%]\nr2e_tests/test_1.py::test_websocket_disable_keepalive[pyloop] PASSED     [ 87%]\nr2e_tests/test_1.py::test_bug3380[pyloop] PASSED                         [ 90%]\nr2e_tests/test_1.py::test_receive_being_cancelled_keeps_connection_open[pyloop] PASSED [ 93%]\nr2e_tests/test_1.py::test_receive_timeout_keeps_connection_open[pyloop] PASSED [ 96%]\nr2e_tests/test_1.py::test_websocket_shutdown[pyloop] PASSED              [100%]\n\n==================================== PASSES ====================================\n________________________ test_concurrent_close[pyloop] _________________________\n------------------------------ Captured log call -------------------------------\nERROR    aiohttp.server:web_protocol.py:427 Error handling request\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_protocol.py\", line 456, in _handle_request\n    resp = await request_handler(request)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_app.py\", line 537, in _handle\n    resp = await handler(request)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/r2e_tests/test_1.py\", line 293, in handler\n    assert msg.type == WSMsgType.CLOSING\nAssertionError: assert <WSMsgType.CLOSED: 257> == <WSMsgType.CLOSING: 256>\n +  where <WSMsgType.CLOSED: 257> = WSMessage(type=<WSMsgType.CLOSED: 257>, data=None, extra=None).type\n +  and   <WSMsgType.CLOSING: 256> = WSMsgType.CLOSING\n_________________ test_concurrent_close_multiple_tasks[pyloop] _________________\n------------------------------ Captured log call -------------------------------\nERROR    aiohttp.server:web_protocol.py:427 Error handling request\nTraceback (most recent call last):\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_protocol.py\", line 456, in _handle_request\n    resp = await request_handler(request)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/aiohttp/web_app.py\", line 537, in _handle\n    resp = await handler(request)\n  File \"/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/r2e_tests/test_1.py\", line 330, in handler\n    assert msg.type == WSMsgType.CLOSING\nAssertionError: assert <WSMsgType.CLOSED: 257> == <WSMsgType.CLOSING: 256>\n +  where <WSMsgType.CLOSED: 257> = WSMessage(type=<WSMsgType.CLOSED: 257>, data=None, extra=None).type\n +  and   <WSMsgType.CLOSING: 256> = WSMsgType.CLOSING\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      2      0      0    98%\naiohttp/base_protocol.py                    67     36     22      3    38%\naiohttp/client.py                          545    201    196     61    58%\naiohttp/client_exceptions.py               153     57     20      3    57%\naiohttp/client_proto.py                    165     36     56     14    73%\naiohttp/client_reqrep.py                   693    264    300     79    54%\naiohttp/client_ws.py                       253    112     78     18    52%\naiohttp/compression_utils.py                70     36     14      0    40%\naiohttp/connector.py                       770    430    319     50    37%\naiohttp/cookiejar.py                       255    173    118      8    25%\naiohttp/formdata.py                         97     79     46      0    13%\naiohttp/hdrs.py                             90      0      0      0   100%\naiohttp/helpers.py                         499    210    172     27    51%\naiohttp/http.py                             11      0      2      0   100%\naiohttp/http_exceptions.py                  50     20      4      0    56%\naiohttp/http_parser.py                     502    402    206      1    14%\naiohttp/http_websocket.py                  389    124    152     29    61%\naiohttp/http_writer.py                     118     43     50     12    54%\naiohttp/locks.py                            24     16      4      0    29%\naiohttp/log.py                               7      0      0      0   100%\naiohttp/multipart.py                       595    488    252      0    13%\naiohttp/payload.py                         219    107     56      1    43%\naiohttp/payload_streamer.py                 29     10      4      0    70%\naiohttp/pytest_plugin.py                   162     54     42      9    63%\naiohttp/resolver.py                         80     55     26      2    25%\naiohttp/streams.py                         396    192    144     26    44%\naiohttp/tcp_helpers.py                      19      2      6      3    80%\naiohttp/test_utils.py                      316    117     52     12    58%\naiohttp/tracing.py                         191     69     32      0    69%\naiohttp/typedefs.py                         23      0      0      0   100%\naiohttp/web.py                             118     81     52      0    23%\naiohttp/web_app.py                         282     96     74     21    59%\naiohttp/web_exceptions.py                  157     20      8      1    85%\naiohttp/web_fileresponse.py                163    127     54      1    19%\naiohttp/web_log.py                         102     37     20      1    59%\naiohttp/web_middlewares.py                  55     37     20      0    24%\naiohttp/web_protocol.py                    357    103    163     48    64%\naiohttp/web_request.py                     441    243    134      9    37%\naiohttp/web_response.py                    483    208    218     38    48%\naiohttp/web_routedef.py                    100     44     10      0    55%\naiohttp/web_runner.py                      213     72     36     11    62%\naiohttp/web_server.py                       42      5     12      1    85%\naiohttp/web_urldispatcher.py               741    372    161     23    44%\naiohttp/web_ws.py                          373     67    126     32    78%\naiohttp/worker.py                          126    126     30      0     0%\ntests/conftest.py                          130    130     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            198    198     14      0     0%\ntests/test_client_fingerprint.py            59     59      0      0     0%\ntests/test_client_functional.py           2403   2403     84      0     0%\ntests/test_client_proto.py                  99     99      0      0     0%\ntests/test_client_request.py               803    803     18      0     0%\ntests/test_client_response.py              464    464      4      0     0%\ntests/test_client_session.py               556    556     29      0     0%\ntests/test_client_ws.py                    486    486      2      0     0%\ntests/test_client_ws_functional.py         733    733     32      0     0%\ntests/test_connector.py                   1676   1676     82      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                      72     72      6      0     0%\ntests/test_helpers.py                      464    464     24      0     0%\ntests/test_http_exceptions.py              109    109     10      0     0%\ntests/test_http_parser.py                 1063   1063     64      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                    818    818     30      0     0%\ntests/test_multipart_helpers.py            446    446      0      0     0%\ntests/test_payload.py                       94     94      0      0     0%\ntests/test_proxy.py                        301    301      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                     193    193     20      0     0%\ntests/test_route_def.py                    210    210      0      0     0%\ntests/test_run_app.py                      554    554     30      0     0%\ntests/test_streams.py                     1076   1076     38      0     0%\ntests/test_tcp_helpers.py                   52     52      2      0     0%\ntests/test_test_utils.py                   241    241      6      0     0%\ntests/test_tracing.py                       48     48      0      0     0%\ntests/test_urldispatch.py                  854    854     24      0     0%\ntests/test_web_app.py                      453    453      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              1535   1535     34      0     0%\ntests/test_web_log.py                      116    116      2      0     0%\ntests/test_web_middleware.py               266    266     12      0     0%\ntests/test_web_request.py                  490    490      8      0     0%\ntests/test_web_request_handler.py           42     42      0      0     0%\ntests/test_web_response.py                 863    863      4      0     0%\ntests/test_web_runner.py                   146    146      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            519    519     24      0     0%\ntests/test_web_websocket.py                401    401      8      0     0%\ntests/test_web_websocket_functional.py     727    727     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                                    33902  28218   4305    545    17%\n\n============================= slowest 10 durations =============================\n0.16s call     r2e_tests/test_1.py::test_close_timeout[pyloop]\n0.10s call     r2e_tests/test_1.py::test_custom_receive_timeout[pyloop]\n0.10s call     r2e_tests/test_1.py::test_receive_timeout[pyloop]\n0.05s call     r2e_tests/test_1.py::test_heartbeat_no_pong_receive_many_messages[pyloop]\n0.05s call     r2e_tests/test_1.py::test_heartbeat_no_pong_send_many_messages[pyloop]\n0.05s call     r2e_tests/test_1.py::test_heartbeat[pyloop]\n0.05s call     r2e_tests/test_1.py::test_heartbeat_no_pong[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_receive_timeout[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_custom_receive_timeout[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_heartbeat[pyloop]\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_websocket_can_prepare[pyloop]\nPASSED r2e_tests/test_1.py::test_websocket_json[pyloop]\nPASSED r2e_tests/test_1.py::test_websocket_json_invalid_message[pyloop]\nPASSED r2e_tests/test_1.py::test_websocket_send_json[pyloop]\nPASSED r2e_tests/test_1.py::test_websocket_receive_json[pyloop]\nPASSED r2e_tests/test_1.py::test_send_recv_text[pyloop]\nPASSED r2e_tests/test_1.py::test_send_recv_bytes[pyloop]\nPASSED r2e_tests/test_1.py::test_send_recv_json[pyloop]\nPASSED r2e_tests/test_1.py::test_close_timeout[pyloop]\nPASSED r2e_tests/test_1.py::test_concurrent_close[pyloop]\nPASSED r2e_tests/test_1.py::test_concurrent_close_multiple_tasks[pyloop]\nPASSED r2e_tests/test_1.py::test_close_op_code_from_client[pyloop]\nPASSED r2e_tests/test_1.py::test_auto_pong_with_closing_by_peer[pyloop]\nPASSED r2e_tests/test_1.py::test_ping[pyloop]\nPASSED r2e_tests/test_1.py::test_pong[pyloop]\nPASSED r2e_tests/test_1.py::test_change_status[pyloop]\nPASSED r2e_tests/test_1.py::test_handle_protocol[pyloop]\nPASSED r2e_tests/test_1.py::test_server_close_handshake[pyloop]\nPASSED r2e_tests/test_1.py::test_server_close_handshake_server_eats_client_messages[pyloop]\nPASSED r2e_tests/test_1.py::test_receive_timeout[pyloop]\nPASSED r2e_tests/test_1.py::test_custom_receive_timeout[pyloop]\nPASSED r2e_tests/test_1.py::test_heartbeat[pyloop]\nPASSED r2e_tests/test_1.py::test_heartbeat_no_pong[pyloop]\nPASSED r2e_tests/test_1.py::test_heartbeat_no_pong_send_many_messages[pyloop]\nPASSED r2e_tests/test_1.py::test_heartbeat_no_pong_receive_many_messages[pyloop]\nPASSED r2e_tests/test_1.py::test_server_ws_async_for[pyloop]\nPASSED r2e_tests/test_1.py::test_closed_async_for[pyloop]\nPASSED r2e_tests/test_1.py::test_websocket_disable_keepalive[pyloop]\nPASSED r2e_tests/test_1.py::test_bug3380[pyloop]\nPASSED r2e_tests/test_1.py::test_receive_being_cancelled_keeps_connection_open[pyloop]\nPASSED r2e_tests/test_1.py::test_receive_timeout_keeps_connection_open[pyloop]\nPASSED r2e_tests/test_1.py::test_websocket_shutdown[pyloop]\n============================== 32 passed in 5.31s ==============================\n",
    "new_commit_res_stderr": "/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/.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_50efb2a389414cf36012d3f6e86623f8eac3a42e/.venv/bin/python\ncachedir: .pytest_cache\nrootdir: /home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e\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 32 items\n\nr2e_tests/test_1.py::test_websocket_can_prepare[pyloop] PASSED           [  3%]\nr2e_tests/test_1.py::test_websocket_json[pyloop] PASSED                  [  6%]\nr2e_tests/test_1.py::test_websocket_json_invalid_message[pyloop] PASSED  [  9%]\nr2e_tests/test_1.py::test_websocket_send_json[pyloop] PASSED             [ 12%]\nr2e_tests/test_1.py::test_websocket_receive_json[pyloop] PASSED          [ 15%]\nr2e_tests/test_1.py::test_send_recv_text[pyloop] PASSED                  [ 18%]\nr2e_tests/test_1.py::test_send_recv_bytes[pyloop] PASSED                 [ 21%]\nr2e_tests/test_1.py::test_send_recv_json[pyloop] PASSED                  [ 25%]\nr2e_tests/test_1.py::test_close_timeout[pyloop] PASSED                   [ 28%]\nr2e_tests/test_1.py::test_concurrent_close[pyloop] PASSED                [ 31%]\nr2e_tests/test_1.py::test_concurrent_close_multiple_tasks[pyloop] PASSED [ 34%]\nr2e_tests/test_1.py::test_close_op_code_from_client[pyloop] PASSED       [ 37%]\nr2e_tests/test_1.py::test_auto_pong_with_closing_by_peer[pyloop] PASSED  [ 40%]\nr2e_tests/test_1.py::test_ping[pyloop] PASSED                            [ 43%]\nr2e_tests/test_1.py::test_pong[pyloop] PASSED                            [ 46%]\nr2e_tests/test_1.py::test_change_status[pyloop] PASSED                   [ 50%]\nr2e_tests/test_1.py::test_handle_protocol[pyloop] PASSED                 [ 53%]\nr2e_tests/test_1.py::test_server_close_handshake[pyloop] PASSED          [ 56%]\nr2e_tests/test_1.py::test_server_close_handshake_server_eats_client_messages[pyloop] PASSED [ 59%]\nr2e_tests/test_1.py::test_receive_timeout[pyloop] PASSED                 [ 62%]\nr2e_tests/test_1.py::test_custom_receive_timeout[pyloop] PASSED          [ 65%]\nr2e_tests/test_1.py::test_heartbeat[pyloop] PASSED                       [ 68%]\nr2e_tests/test_1.py::test_heartbeat_no_pong[pyloop] PASSED               [ 71%]\nr2e_tests/test_1.py::test_heartbeat_no_pong_send_many_messages[pyloop] PASSED [ 75%]\nr2e_tests/test_1.py::test_heartbeat_no_pong_receive_many_messages[pyloop] PASSED [ 78%]\nr2e_tests/test_1.py::test_server_ws_async_for[pyloop] PASSED             [ 81%]\nr2e_tests/test_1.py::test_closed_async_for[pyloop] PASSED                [ 84%]\nr2e_tests/test_1.py::test_websocket_disable_keepalive[pyloop] PASSED     [ 87%]\nr2e_tests/test_1.py::test_bug3380[pyloop] PASSED                         [ 90%]\nr2e_tests/test_1.py::test_receive_being_cancelled_keeps_connection_open[pyloop] PASSED [ 93%]\nr2e_tests/test_1.py::test_receive_timeout_keeps_connection_open[pyloop] PASSED [ 96%]\nr2e_tests/test_1.py::test_websocket_shutdown[pyloop] FAILED              [100%]\n\n=================================== FAILURES ===================================\n_______________________ test_websocket_shutdown[pyloop] ________________________\n\naiohttp_client = <function aiohttp_client.<locals>.go at 0x72c89e073f70>\n\n    async def test_websocket_shutdown(aiohttp_client: AiohttpClient) -> None:\n        \"\"\"Test that the client websocket gets the close message when the server is shutting down.\"\"\"\n        url = \"/ws\"\n        app = web.Application()\n        websockets = web.AppKey(\"websockets\", weakref.WeakSet)\n        app[websockets] = weakref.WeakSet()\n    \n        # need for send signal shutdown server\n        shutdown_websockets = web.AppKey(\"shutdown_websockets\", weakref.WeakSet)\n        app[shutdown_websockets] = weakref.WeakSet()\n    \n        async def websocket_handler(request: web.Request) -> web.WebSocketResponse:\n            websocket = web.WebSocketResponse()\n            await websocket.prepare(request)\n            request.app[websockets].add(websocket)\n            request.app[shutdown_websockets].add(websocket)\n    \n            try:\n                async for message in websocket:\n                    await websocket.send_json({\"ok\": True, \"message\": message.json()})\n            finally:\n                request.app[websockets].discard(websocket)\n    \n            return websocket\n    \n        async def on_shutdown(app: web.Application) -> None:\n            while app[shutdown_websockets]:\n                websocket = app[shutdown_websockets].pop()\n                await websocket.close(\n                    code=aiohttp.WSCloseCode.GOING_AWAY,\n                    message=\"Server shutdown\",\n                )\n    \n        app.router.add_get(url, websocket_handler)\n        app.on_shutdown.append(on_shutdown)\n    \n        client = await aiohttp_client(app)\n    \n        websocket = await client.ws_connect(url)\n    \n        message = {\"message\": \"hi\"}\n        await websocket.send_json(message)\n        reply = await websocket.receive_json()\n        assert reply == {\"ok\": True, \"message\": message}\n    \n        await app.shutdown()\n    \n        assert websocket.closed is False\n    \n        reply = await websocket.receive()\n    \n        assert reply.type is aiohttp.http.WSMsgType.CLOSE\n>       assert reply.data == aiohttp.WSCloseCode.GOING_AWAY\nE       AssertionError: assert 1000 == <WSCloseCode.GOING_AWAY: 1001>\nE        +  where 1000 = WSMessage(type=<WSMsgType.CLOSE: 8>, data=1000, extra='').data\nE        +  and   <WSCloseCode.GOING_AWAY: 1001> = <enum 'WSCloseCode'>.GOING_AWAY\nE        +    where <enum 'WSCloseCode'> = aiohttp.WSCloseCode\n\naiohttp_client = <function aiohttp_client.<locals>.go at 0x72c89e073f70>\napp        = <Application 0x72c89e043ca0>\nclient     = <aiohttp.test_utils.TestClient object at 0x72c89e0436d0>\nmessage    = {'message': 'hi'}\non_shutdown = <function test_websocket_shutdown.<locals>.on_shutdown at 0x72c89e058040>\nreply      = WSMessage(type=<WSMsgType.CLOSE: 8>, data=1000, extra='')\nshutdown_websockets = <AppKey(__main__.shutdown_websockets, type=_weakrefset.WeakSet)>\nurl        = '/ws'\nwebsocket  = <aiohttp.client_ws.ClientWebSocketResponse object at 0x72c89e043640>\nwebsocket_handler = <function test_websocket_shutdown.<locals>.websocket_handler at 0x72c89e058280>\nwebsockets = <AppKey(__main__.websockets, type=_weakrefset.WeakSet)>\n\nr2e_tests/test_1.py:1047: AssertionError\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      2      0      0    98%\naiohttp/base_protocol.py                    67     36     22      3    38%\naiohttp/client.py                          545    201    196     61    58%\naiohttp/client_exceptions.py               153     57     20      3    57%\naiohttp/client_proto.py                    165     36     56     14    73%\naiohttp/client_reqrep.py                   693    264    300     79    54%\naiohttp/client_ws.py                       253    112     78     18    52%\naiohttp/compression_utils.py                70     36     14      0    40%\naiohttp/connector.py                       770    430    319     50    37%\naiohttp/cookiejar.py                       255    173    118      8    25%\naiohttp/formdata.py                         97     79     46      0    13%\naiohttp/hdrs.py                             90      0      0      0   100%\naiohttp/helpers.py                         499    203    172     31    53%\naiohttp/http.py                             11      0      2      0   100%\naiohttp/http_exceptions.py                  50     20      4      0    56%\naiohttp/http_parser.py                     502    402    206      1    14%\naiohttp/http_websocket.py                  389    124    152     29    61%\naiohttp/http_writer.py                     118     43     50     12    54%\naiohttp/locks.py                            24     16      4      0    29%\naiohttp/log.py                               7      0      0      0   100%\naiohttp/multipart.py                       595    488    252      0    13%\naiohttp/payload.py                         219    107     56      1    43%\naiohttp/payload_streamer.py                 29     10      4      0    70%\naiohttp/pytest_plugin.py                   162     53     42      9    64%\naiohttp/resolver.py                         80     55     26      2    25%\naiohttp/streams.py                         396    192    144     26    44%\naiohttp/tcp_helpers.py                      19      2      6      3    80%\naiohttp/test_utils.py                      316    117     52     12    58%\naiohttp/tracing.py                         191     69     32      0    69%\naiohttp/typedefs.py                         23      0      0      0   100%\naiohttp/web.py                             118     81     52      0    23%\naiohttp/web_app.py                         282     95     74     21    60%\naiohttp/web_exceptions.py                  157     20      8      1    85%\naiohttp/web_fileresponse.py                163    127     54      1    19%\naiohttp/web_log.py                         102     37     20      1    59%\naiohttp/web_middlewares.py                  55     37     20      0    24%\naiohttp/web_protocol.py                    357    111    163     48    61%\naiohttp/web_request.py                     441    244    134      9    37%\naiohttp/web_response.py                    483    208    218     38    48%\naiohttp/web_routedef.py                    100     44     10      0    55%\naiohttp/web_runner.py                      213     72     36     11    62%\naiohttp/web_server.py                       42      5     12      1    85%\naiohttp/web_urldispatcher.py               741    372    161     23    44%\naiohttp/web_ws.py                          376     66    128     31    79%\naiohttp/worker.py                          126    126     30      0     0%\ntests/conftest.py                          130    130     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            198    198     14      0     0%\ntests/test_client_fingerprint.py            59     59      0      0     0%\ntests/test_client_functional.py           2403   2403     84      0     0%\ntests/test_client_proto.py                  99     99      0      0     0%\ntests/test_client_request.py               803    803     18      0     0%\ntests/test_client_response.py              464    464      4      0     0%\ntests/test_client_session.py               556    556     29      0     0%\ntests/test_client_ws.py                    486    486      2      0     0%\ntests/test_client_ws_functional.py         733    733     32      0     0%\ntests/test_connector.py                   1676   1676     82      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                      72     72      6      0     0%\ntests/test_helpers.py                      464    464     24      0     0%\ntests/test_http_exceptions.py              109    109     10      0     0%\ntests/test_http_parser.py                 1063   1063     64      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                    818    818     30      0     0%\ntests/test_multipart_helpers.py            446    446      0      0     0%\ntests/test_payload.py                       94     94      0      0     0%\ntests/test_proxy.py                        301    301      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                     193    193     20      0     0%\ntests/test_route_def.py                    210    210      0      0     0%\ntests/test_run_app.py                      554    554     30      0     0%\ntests/test_streams.py                     1076   1076     38      0     0%\ntests/test_tcp_helpers.py                   52     52      2      0     0%\ntests/test_test_utils.py                   241    241      6      0     0%\ntests/test_tracing.py                       48     48      0      0     0%\ntests/test_urldispatch.py                  854    854     24      0     0%\ntests/test_web_app.py                      453    453      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              1535   1535     34      0     0%\ntests/test_web_log.py                      116    116      2      0     0%\ntests/test_web_middleware.py               266    266     12      0     0%\ntests/test_web_request.py                  490    490      8      0     0%\ntests/test_web_request_handler.py           42     42      0      0     0%\ntests/test_web_response.py                 863    863      4      0     0%\ntests/test_web_runner.py                   146    146      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            519    519     24      0     0%\ntests/test_web_websocket.py                401    401      8      0     0%\ntests/test_web_websocket_functional.py     689    689     24      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                                    33867  28179   4303    548    17%\n\n============================= slowest 10 durations =============================\n0.17s call     r2e_tests/test_1.py::test_close_timeout[pyloop]\n0.11s call     r2e_tests/test_1.py::test_custom_receive_timeout[pyloop]\n0.11s call     r2e_tests/test_1.py::test_receive_timeout[pyloop]\n0.06s call     r2e_tests/test_1.py::test_heartbeat_no_pong[pyloop]\n0.06s call     r2e_tests/test_1.py::test_heartbeat[pyloop]\n0.06s call     r2e_tests/test_1.py::test_heartbeat_no_pong_send_many_messages[pyloop]\n0.05s call     r2e_tests/test_1.py::test_heartbeat_no_pong_receive_many_messages[pyloop]\n0.03s teardown r2e_tests/test_1.py::test_heartbeat_no_pong_receive_many_messages[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_receive_timeout[pyloop]\n0.02s teardown r2e_tests/test_1.py::test_concurrent_close[pyloop]\n=========================== short test summary info ============================\nPASSED r2e_tests/test_1.py::test_websocket_can_prepare[pyloop]\nPASSED r2e_tests/test_1.py::test_websocket_json[pyloop]\nPASSED r2e_tests/test_1.py::test_websocket_json_invalid_message[pyloop]\nPASSED r2e_tests/test_1.py::test_websocket_send_json[pyloop]\nPASSED r2e_tests/test_1.py::test_websocket_receive_json[pyloop]\nPASSED r2e_tests/test_1.py::test_send_recv_text[pyloop]\nPASSED r2e_tests/test_1.py::test_send_recv_bytes[pyloop]\nPASSED r2e_tests/test_1.py::test_send_recv_json[pyloop]\nPASSED r2e_tests/test_1.py::test_close_timeout[pyloop]\nPASSED r2e_tests/test_1.py::test_concurrent_close[pyloop]\nPASSED r2e_tests/test_1.py::test_concurrent_close_multiple_tasks[pyloop]\nPASSED r2e_tests/test_1.py::test_close_op_code_from_client[pyloop]\nPASSED r2e_tests/test_1.py::test_auto_pong_with_closing_by_peer[pyloop]\nPASSED r2e_tests/test_1.py::test_ping[pyloop]\nPASSED r2e_tests/test_1.py::test_pong[pyloop]\nPASSED r2e_tests/test_1.py::test_change_status[pyloop]\nPASSED r2e_tests/test_1.py::test_handle_protocol[pyloop]\nPASSED r2e_tests/test_1.py::test_server_close_handshake[pyloop]\nPASSED r2e_tests/test_1.py::test_server_close_handshake_server_eats_client_messages[pyloop]\nPASSED r2e_tests/test_1.py::test_receive_timeout[pyloop]\nPASSED r2e_tests/test_1.py::test_custom_receive_timeout[pyloop]\nPASSED r2e_tests/test_1.py::test_heartbeat[pyloop]\nPASSED r2e_tests/test_1.py::test_heartbeat_no_pong[pyloop]\nPASSED r2e_tests/test_1.py::test_heartbeat_no_pong_send_many_messages[pyloop]\nPASSED r2e_tests/test_1.py::test_heartbeat_no_pong_receive_many_messages[pyloop]\nPASSED r2e_tests/test_1.py::test_server_ws_async_for[pyloop]\nPASSED r2e_tests/test_1.py::test_closed_async_for[pyloop]\nPASSED r2e_tests/test_1.py::test_websocket_disable_keepalive[pyloop]\nPASSED r2e_tests/test_1.py::test_bug3380[pyloop]\nPASSED r2e_tests/test_1.py::test_receive_being_cancelled_keeps_connection_open[pyloop]\nPASSED r2e_tests/test_1.py::test_receive_timeout_keeps_connection_open[pyloop]\nFAILED r2e_tests/test_1.py::test_websocket_shutdown[pyloop] - AssertionError:...\n========================= 1 failed, 31 passed in 5.86s =========================\n",
    "old_commit_res_stderr": "/home/gcpuser/buckets/local_repoeval_bucket/repos/aiohttp_50efb2a389414cf36012d3f6e86623f8eac3a42e/.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/8680.bugfix.rst aiohttp/web_ws.py tests/test_web_websocket_functional.py
We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[ISSUE]
**Title:** WebSocket Close Race Condition Prevents Proper Close Code Delivery on Server Shutdown

**Description:**
When the server initiates a shutdown, the client websocket is expected to receive a close message with the appropriate close code. However, under certain conditions, a race condition occurs that prevents the close code from reaching the client correctly.

**Example Buggy Code:**

```python
async def test_websocket_shutdown(aiohttp_client: AiohttpClient) -> None:
    """Test that the client websocket gets the close message when the server is shutting down."""
    url = '/ws'
    app = web.Application()
    websockets = web.AppKey('websockets', weakref.WeakSet)
    app[websockets] = weakref.WeakSet()
    shutdown_websockets = web.AppKey('shutdown_websockets', weakref.WeakSet)
    app[shutdown_websockets] = weakref.WeakSet()

    async def websocket_handler(request: web.Request) -> web.WebSocketResponse:
        websocket = web.WebSocketResponse()
        await websocket.prepare(request)
        request.app[websockets].add(websocket)
        request.app[shutdown_websockets].add(websocket)
        try:
            async for message in websocket:
                await websocket.send_json({'ok': True, 'message': message.json()})
        finally:
            request.app[websockets].discard(websocket)
        return websocket

    async def on_shutdown(app: web.Application) -> None:
        while app[shutdown_websockets]:
            websocket = app[shutdown_websockets].pop()
            await websocket.close(code=aiohttp.WSCloseCode.GOING_AWAY, message='Server shutdown')
    app.router.add_get(url, websocket_handler)
    app.on_shutdown.append(on_shutdown)
    client = await aiohttp_client(app)
    websocket = await client.ws_connect(url)
    message = {'message': 'hi'}
    await websocket.send_json(message)
    reply = await websocket.receive_json()
    assert reply == {'ok': True, 'message': message}
    await app.shutdown()
    assert websocket.closed is False
    reply = await websocket.receive()
    assert reply.type is aiohttp.http.WSMsgType.CLOSE
    assert reply.data == aiohttp.WSCloseCode.GOING_AWAY
    assert reply.extra == 'Server shutdown'
    assert websocket.closed is True
```

**Expected Behavior:**
When the server shuts down, the client websocket should receive a close message with the close code `WSCloseCode.GOING_AWAY` and the message `"Server shutdown"`. This ensures that the client is properly informed about the server's shutdown reason.

**Actual Behavior:**
Instead of receiving the expected close code and message, the client receives a close message with the default close code `1000` (normal closure) and an empty message. This indicates that the close code intended to signal the server's shutdown does not reach the client due to a race condition in the websocket handling logic.

[/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/web_ws.py
import asyncio
import base64
import binascii
import hashlib
import json
import sys
from typing import Any, Final, Iterable, Optional, Tuple, cast

import attr
from multidict import CIMultiDict

from . import hdrs
from .abc import AbstractStreamWriter
from .helpers import calculate_timeout_when, set_exception, set_result
from .http import (
    WS_CLOSED_MESSAGE,
    WS_CLOSING_MESSAGE,
    WS_KEY,
    WebSocketError,
    WebSocketReader,
    WebSocketWriter,
    WSCloseCode,
    WSMessage,
    WSMsgType as WSMsgType,
    ws_ext_gen,
    ws_ext_parse,
)
from .log import ws_logger
from .streams import EofStream, FlowControlDataQueue
from .typedefs import JSONDecoder, JSONEncoder
from .web_exceptions import HTTPBadRequest, HTTPException
from .web_request import BaseRequest
from .web_response import StreamResponse

if sys.version_info >= (3, 11):
    import asyncio as async_timeout
else:
    import async_timeout

__all__ = (
    "WebSocketResponse",
    "WebSocketReady",
    "WSMsgType",
)

THRESHOLD_CONNLOST_ACCESS: Final[int] = 5


@attr.s(auto_attribs=True, frozen=True, slots=True)
class WebSocketReady:
    ok: bool
    protocol: Optional[str]

    def __bool__(self) -> bool:
        return self.ok


class WebSocketResponse(StreamResponse):

    _length_check = False

    def __init__(
        self,
        *,
        timeout: float = 10.0,
        receive_timeout: Optional[float] = None,
        autoclose: bool = True,
        autoping: bool = True,
        heartbeat: Optional[float] = None,
        protocols: Iterable[str] = (),
        compress: bool = True,
        max_msg_size: int = 4 * 1024 * 1024,
    ) -> None:
        super().__init__(status=101)
        self._protocols = protocols
        self._ws_protocol: Optional[str] = None
        self._writer: Optional[WebSocketWriter] = None
        self._reader: Optional[FlowControlDataQueue[WSMessage]] = None
        self._closed = False
        self._closing = False
        self._conn_lost = 0
        self._close_code: Optional[int] = None
        self._loop: Optional[asyncio.AbstractEventLoop] = None
        self._waiting: bool = False
        self._close_wait: Optional[asyncio.Future[None]] = None
        self._exception: Optional[BaseException] = None
        self._timeout = timeout
        self._receive_timeout = receive_timeout
        self._autoclose = autoclose
        self._autoping = autoping
        self._heartbeat = heartbeat
        self._heartbeat_when = 0.0
        self._heartbeat_cb: Optional[asyncio.TimerHandle] = None
        if heartbeat is not None:
            self._pong_heartbeat = heartbeat / 2.0
        self._pong_response_cb: Optional[asyncio.TimerHandle] = None
        self._compress = compress
        self._max_msg_size = max_msg_size
        self._ping_task: Optional[asyncio.Task[None]] = None

    def _cancel_heartbeat(self) -> None:
        self._cancel_pong_response_cb()
        if self._heartbeat_cb is not None:
            self._heartbeat_cb.cancel()
            self._heartbeat_cb = None
        if self._ping_task is not None:
            self._ping_task.cancel()
            self._ping_task = None

    def _cancel_pong_response_cb(self) -> None:
        if self._pong_response_cb is not None:
            self._pong_response_cb.cancel()
            self._pong_response_cb = None

    def _reset_heartbeat(self) -> None:
        if self._heartbeat is None:
            return
        self._cancel_pong_response_cb()
        req = self._req
        timeout_ceil_threshold = (
            req._protocol._timeout_ceil_threshold if req is not None else 5
        )
        loop = self._loop
        assert loop is not None
        now = loop.time()
        when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold)
        self._heartbeat_when = when
        if self._heartbeat_cb is None:
            # We do not cancel the previous heartbeat_cb here because
            # it generates a significant amount of TimerHandle churn
            # which causes asyncio to rebuild the heap frequently.
            # Instead _send_heartbeat() will reschedule the next
            # heartbeat if it fires too early.
            self._heartbeat_cb = loop.call_at(when, self._send_heartbeat)

    def _send_heartbeat(self) -> None:
        self._heartbeat_cb = None
        loop = self._loop
        assert loop is not None and self._writer is not None
        now = loop.time()
        if now < self._heartbeat_when:
            # Heartbeat fired too early, reschedule
            self._heartbeat_cb = loop.call_at(
                self._heartbeat_when, self._send_heartbeat
            )
            return

        req = self._req
        timeout_ceil_threshold = (
            req._protocol._timeout_ceil_threshold if req is not None else 5
        )
        when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold)
        self._cancel_pong_response_cb()
        self._pong_response_cb = loop.call_at(when, self._pong_not_received)

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

        if not ping_task.done():
            self._ping_task = ping_task
            ping_task.add_done_callback(self._ping_task_done)

    def _ping_task_done(self, task: "asyncio.Task[None]") -> None:
        """Callback for when the ping task completes."""
        self._ping_task = None

    def _pong_not_received(self) -> None:
        if self._req is not None and self._req.transport is not None:
            self._set_closed()
            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
            self._exception = asyncio.TimeoutError()

    def _set_closed(self) -> None:
        """Set the connection to closed.

        Cancel any heartbeat timers and set the closed flag.
        """
        self._closed = True
        self._cancel_heartbeat()

    async def prepare(self, request: BaseRequest) -> AbstractStreamWriter:
        # make pre-check to don't hide it by do_handshake() exceptions
        if self._payload_writer is not None:
            return self._payload_writer

        protocol, writer = self._pre_start(request)
        payload_writer = await super().prepare(request)
        assert payload_writer is not None
        self._post_start(request, protocol, writer)
        await payload_writer.drain()
        return payload_writer

    def _handshake(
        self, request: BaseRequest
    ) -> Tuple["CIMultiDict[str]", str, bool, bool]:
        headers = request.headers
        if "websocket" != headers.get(hdrs.UPGRADE, "").lower().strip():
            raise HTTPBadRequest(
                text=(
                    "No WebSocket UPGRADE hdr: {}\n Can "
                    '"Upgrade" only to "WebSocket".'
                ).format(headers.get(hdrs.UPGRADE))
            )

        if "upgrade" not in headers.get(hdrs.CONNECTION, "").lower():
            raise HTTPBadRequest(
                text="No CONNECTION upgrade hdr: {}".format(
                    headers.get(hdrs.CONNECTION)
                )
            )

        # find common sub-protocol between client and server
        protocol = None
        if hdrs.SEC_WEBSOCKET_PROTOCOL in headers:
            req_protocols = [
                str(proto.strip())
                for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",")
            ]

            for proto in req_protocols:
                if proto in self._protocols:
                    protocol = proto
                    break
            else:
                # No overlap found: Return no protocol as per spec
                ws_logger.warning(
                    "Client protocols %r don’t overlap server-known ones %r",
                    req_protocols,
                    self._protocols,
                )

        # check supported version
        version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, "")
        if version not in ("13", "8", "7"):
            raise HTTPBadRequest(text=f"Unsupported version: {version}")

        # check client handshake for validity
        key = headers.get(hdrs.SEC_WEBSOCKET_KEY)
        try:
            if not key or len(base64.b64decode(key)) != 16:
                raise HTTPBadRequest(text=f"Handshake error: {key!r}")
        except binascii.Error:
            raise HTTPBadRequest(text=f"Handshake error: {key!r}") from None

        accept_val = base64.b64encode(
            hashlib.sha1(key.encode() + WS_KEY).digest()
        ).decode()
        response_headers = CIMultiDict(
            {
                hdrs.UPGRADE: "websocket",
                hdrs.CONNECTION: "upgrade",
                hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,
            }
        )

        notakeover = False
        compress = 0
        if self._compress:
            extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)
            # Server side always get return with no exception.
            # If something happened, just drop compress extension
            compress, notakeover = ws_ext_parse(extensions, isserver=True)
            if compress:
                enabledext = ws_ext_gen(
                    compress=compress, isserver=True, server_notakeover=notakeover
                )
                response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext

        if protocol:
            response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol
        return (
            response_headers,
            protocol,
            compress,
            notakeover,
        )  # type: ignore[return-value]

    def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]:
        self._loop = request._loop

        headers, protocol, compress, notakeover = self._handshake(request)

        self.set_status(101)
        self.headers.update(headers)
        self.force_close()
        self._compress = compress
        transport = request._protocol.transport
        assert transport is not None
        writer = WebSocketWriter(
            request._protocol, transport, compress=compress, notakeover=notakeover
        )

        return protocol, writer

    def _post_start(
        self, request: BaseRequest, protocol: str, writer: WebSocketWriter
    ) -> None:
        self._ws_protocol = protocol
        self._writer = writer

        self._reset_heartbeat()

        loop = self._loop
        assert loop is not None
        self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop)
        request.protocol.set_parser(
            WebSocketReader(self._reader, self._max_msg_size, compress=self._compress)
        )
        # disable HTTP keepalive for WebSocket
        request.protocol.keep_alive(False)

    def can_prepare(self, request: BaseRequest) -> WebSocketReady:
        if self._writer is not None:
            raise RuntimeError("Already started")
        try:
            _, protocol, _, _ = self._handshake(request)
        except HTTPException:
            return WebSocketReady(False, None)
        else:
            return WebSocketReady(True, protocol)

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

    @property
    def close_code(self) -> Optional[int]:
        return self._close_code

    @property
    def ws_protocol(self) -> Optional[str]:
        return self._ws_protocol

    @property
    def compress(self) -> bool:
        return self._compress

    def get_extra_info(self, name: str, default: Any = None) -> Any:
        """Get optional transport information.

        If no value associated with ``name`` is found, ``default`` is returned.
        """
        writer = self._writer
        if writer is None:
            return default
        transport = writer.transport
        if transport is None:
            return default
        return transport.get_extra_info(name, default)

    def exception(self) -> Optional[BaseException]:
        return self._exception

    async def ping(self, message: bytes = b"") -> None:
        if self._writer is None:
            raise RuntimeError("Call .prepare() first")
        await self._writer.ping(message)

    async def pong(self, message: bytes = b"") -> None:
        # unsolicited pong
        if self._writer is None:
            raise RuntimeError("Call .prepare() first")
        await self._writer.pong(message)

    async def send_str(self, data: str, compress: Optional[bool] = None) -> None:
        if self._writer is None:
            raise RuntimeError("Call .prepare() first")
        if not isinstance(data, str):
            raise TypeError("data argument must be str (%r)" % type(data))
        await self._writer.send(data, binary=False, compress=compress)

    async def send_bytes(self, data: bytes, compress: Optional[bool] = None) -> None:
        if self._writer is None:
            raise RuntimeError("Call .prepare() first")
        if not isinstance(data, (bytes, bytearray, memoryview)):
            raise TypeError("data argument must be byte-ish (%r)" % type(data))
        await self._writer.send(data, binary=True, compress=compress)

    async def send_json(
        self,
        data: Any,
        compress: Optional[bool] = None,
        *,
        dumps: JSONEncoder = json.dumps,
    ) -> None:
        await self.send_str(dumps(data), compress=compress)

    async def write_eof(self) -> None:  # type: ignore[override]
        if self._eof_sent:
            return
        if self._payload_writer is None:
            raise RuntimeError("Response has not been started")

        await self.close()
        self._eof_sent = True

    async def close(
        self, *, code: int = WSCloseCode.OK, message: bytes = b"", drain: bool = True
    ) -> bool:
        """Close websocket connection."""
        if self._writer is None:
            raise RuntimeError("Call .prepare() first")

        self._cancel_heartbeat()
        reader = self._reader
        assert reader is not None

        # we need to break `receive()` cycle first,
        # `close()` may be called from different task
        if self._waiting and not self._closed:
            if not self._close_wait:
                assert self._loop is not None
                self._close_wait = self._loop.create_future()
            reader.feed_data(WS_CLOSING_MESSAGE, 0)
            await self._close_wait

        if self._closed:
            return False

        self._set_closed()
        try:
            await self._writer.close(code, message)
            writer = self._payload_writer
            assert writer is not None
            if drain:
                await writer.drain()
        except (asyncio.CancelledError, asyncio.TimeoutError):
            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
            raise
        except Exception as exc:
            self._exception = exc
            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
            return True

        if self._closing:
            self._close_transport()
            return True

        reader = self._reader
        assert reader is not None
        try:
            async with async_timeout.timeout(self._timeout):
                msg = await reader.read()
        except asyncio.CancelledError:
            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
            raise
        except Exception as exc:
            self._exception = exc
            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
            return True

        if msg.type is WSMsgType.CLOSE:
            self._set_code_close_transport(msg.data)
            return True

        self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
        self._exception = asyncio.TimeoutError()
        return True

    def _set_closing(self, code: WSCloseCode) -> None:
        """Set the close code and mark the connection as closing."""
        self._closing = True
        self._close_code = code
        self._cancel_heartbeat()

    def _set_code_close_transport(self, code: WSCloseCode) -> None:
        """Set the close code and close the transport."""
        self._close_code = code
        self._close_transport()

    def _close_transport(self) -> None:
        """Close the transport."""
        if self._req is not None and self._req.transport is not None:
            self._req.transport.close()

    async def receive(self, timeout: Optional[float] = None) -> WSMessage:
        if self._reader is None:
            raise RuntimeError("Call .prepare() first")

        loop = self._loop
        assert loop is not None
        receive_timeout = timeout or self._receive_timeout
        while True:
            if self._waiting:
                raise RuntimeError("Concurrent call to receive() is not allowed")

            if self._closed:
                self._conn_lost += 1
                if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS:
                    raise RuntimeError("WebSocket connection is closed.")
                return WS_CLOSED_MESSAGE
            elif self._closing:
                return WS_CLOSING_MESSAGE

            try:
                self._waiting = True
                try:
                    if receive_timeout:
                        # Entering the context manager and creating
                        # Timeout() object can take almost 50% of the
                        # run time in this loop so we avoid it if
                        # there is no read timeout.
                        async with async_timeout.timeout(receive_timeout):
                            msg = await self._reader.read()
                    else:
                        msg = await self._reader.read()
                    self._reset_heartbeat()
                finally:
                    self._waiting = False
                    if self._close_wait:
                        set_result(self._close_wait, None)
            except asyncio.TimeoutError:
                raise
            except EofStream:
                self._close_code = WSCloseCode.OK
                await self.close()
                return WSMessage(WSMsgType.CLOSED, None, None)
            except WebSocketError as exc:
                self._close_code = exc.code
                await self.close(code=exc.code)
                return WSMessage(WSMsgType.ERROR, exc, None)
            except Exception as exc:
                self._exception = exc
                self._set_closing(WSCloseCode.ABNORMAL_CLOSURE)
                await self.close()
                return WSMessage(WSMsgType.ERROR, exc, None)

            if msg.type is WSMsgType.CLOSE:
                self._set_closing(msg.data)
                # Could be closed while awaiting reader.
                if not self._closed and self._autoclose:
                    # The client is likely going to close the
                    # connection out from under us so we do not
                    # want to drain any pending writes as it will
                    # likely result writing to a broken pipe.
                    await self.close(drain=False)
            elif msg.type is WSMsgType.CLOSING:
                self._set_closing(WSCloseCode.OK)
            elif msg.type is WSMsgType.PING and self._autoping:
                await self.pong(msg.data)
                continue
            elif msg.type is WSMsgType.PONG and self._autoping:
                continue

            return msg

    async def receive_str(self, *, timeout: Optional[float] = None) -> str:
        msg = await self.receive(timeout)
        if msg.type is not WSMsgType.TEXT:
            raise TypeError(
                "Received message {}:{!r} is not WSMsgType.TEXT".format(
                    msg.type, msg.data
                )
            )
        return cast(str, msg.data)

    async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:
        msg = await self.receive(timeout)
        if msg.type is not WSMsgType.BINARY:
            raise TypeError(f"Received message {msg.type}:{msg.data!r} is not bytes")
        return cast(bytes, msg.data)

    async def receive_json(
        self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None
    ) -> Any:
        data = await self.receive_str(timeout=timeout)
        return loads(data)

    async def write(self, data: bytes) -> None:
        raise RuntimeError("Cannot call .write() for websocket")

    def __aiter__(self) -> "WebSocketResponse":
        return self

    async def __anext__(self) -> WSMessage:
        msg = await self.receive()
        if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):
            raise StopAsyncIteration
        return msg

    def _cancel(self, exc: BaseException) -> None:
        # web_protocol calls this from connection_lost
        # or when the server is shutting down.
        self._closing = True
        self._cancel_heartbeat()
        if self._reader is not None:
            set_exception(self._reader, exc)

```
--- 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:** WebSocket Close Race Condition Prevents Proper Close Code Delivery on Server Shutdown

**Description:**
When the server initiates a shutdown, the client websocket is expected to receive a close message with the appropriate close code. However, under certain conditions, a race condition occurs that prevents the close code from reaching the client correctly.

**Example Buggy Code:**

```python
async def test_websocket_shutdown(aiohttp_client: AiohttpClient) -> None:
    """Test that the client websocket gets the close message when the server is shutting down."""
    url = '/ws'
    app = web.Application()
    websockets = web.AppKey('websockets', weakref.WeakSet)
    app[websockets] = weakref.WeakSet()
    shutdown_websockets = web.AppKey('shutdown_websockets', weakref.WeakSet)
    app[shutdown_websockets] = weakref.WeakSet()

    async def websocket_handler(request: web.Request) -> web.WebSocketResponse:
        websocket = web.WebSocketResponse()
        await websocket.prepare(request)
        request.app[websockets].add(websocket)
        request.app[shutdown_websockets].add(websocket)
        try:
            async for message in websocket:
                await websocket.send_json({'ok': True, 'message': message.json()})
        finally:
            request.app[websockets].discard(websocket)
        return websocket

    async def on_shutdown(app: web.Application) -> None:
        while app[shutdown_websockets]:
            websocket = app[shutdown_websockets].pop()
            await websocket.close(code=aiohttp.WSCloseCode.GOING_AWAY, message='Server shutdown')
    app.router.add_get(url, websocket_handler)
    app.on_shutdown.append(on_shutdown)
    client = await aiohttp_client(app)
    websocket = await client.ws_connect(url)
    message = {'message': 'hi'}
    await websocket.send_json(message)
    reply = await websocket.receive_json()
    assert reply == {'ok': True, 'message': message}
    await app.shutdown()
    assert websocket.closed is False
    reply = await websocket.receive()
    assert reply.type is aiohttp.http.WSMsgType.CLOSE
    assert reply.data == aiohttp.WSCloseCode.GOING_AWAY
    assert reply.extra == 'Server shutdown'
    assert websocket.closed is True
```

**Expected Behavior:**
When the server shuts down, the client websocket should receive a close message with the close code `WSCloseCode.GOING_AWAY` and the message `"Server shutdown"`. This ensures that the client is properly informed about the server's shutdown reason.

**Actual Behavior:**
Instead of receiving the expected close code and message, the client receives a close message with the default close code `1000` (normal closure) and an empty message. This indicates that the close code intended to signal the server's shutdown does not reach the client due to a race condition in the websocket handling logic.

[/ISSUE]
{
    "test_websocket_can_prepare[pyloop]": "PASSED",
    "test_websocket_json[pyloop]": "PASSED",
    "test_websocket_json_invalid_message[pyloop]": "PASSED",
    "test_websocket_send_json[pyloop]": "PASSED",
    "test_websocket_receive_json[pyloop]": "PASSED",
    "test_send_recv_text[pyloop]": "PASSED",
    "test_send_recv_bytes[pyloop]": "PASSED",
    "test_send_recv_json[pyloop]": "PASSED",
    "test_close_timeout[pyloop]": "PASSED",
    "test_concurrent_close[pyloop]": "PASSED",
    "test_concurrent_close_multiple_tasks[pyloop]": "PASSED",
    "test_close_op_code_from_client[pyloop]": "PASSED",
    "test_auto_pong_with_closing_by_peer[pyloop]": "PASSED",
    "test_ping[pyloop]": "PASSED",
    "test_pong[pyloop]": "PASSED",
    "test_change_status[pyloop]": "PASSED",
    "test_handle_protocol[pyloop]": "PASSED",
    "test_server_close_handshake[pyloop]": "PASSED",
    "test_server_close_handshake_server_eats_client_messages[pyloop]": "PASSED",
    "test_receive_timeout[pyloop]": "PASSED",
    "test_custom_receive_timeout[pyloop]": "PASSED",
    "test_heartbeat[pyloop]": "PASSED",
    "test_heartbeat_no_pong[pyloop]": "PASSED",
    "test_heartbeat_no_pong_send_many_messages[pyloop]": "PASSED",
    "test_heartbeat_no_pong_receive_many_messages[pyloop]": "PASSED",
    "test_server_ws_async_for[pyloop]": "PASSED",
    "test_closed_async_for[pyloop]": "PASSED",
    "test_websocket_disable_keepalive[pyloop]": "PASSED",
    "test_bug3380[pyloop]": "PASSED",
    "test_receive_being_cancelled_keeps_connection_open[pyloop]": "PASSED",
    "test_receive_timeout_keeps_connection_open[pyloop]": "PASSED",
    "test_websocket_shutdown[pyloop]": "PASSED"
}
diff --git a/aiohttp/web_ws.py b/aiohttp/web_ws.py
index fe8f537d..98f26cc4 100644
--- a/aiohttp/web_ws.py
+++ b/aiohttp/web_ws.py
@@ -406,23 +406,10 @@ class WebSocketResponse(StreamResponse):
         if self._writer is None:
             raise RuntimeError("Call .prepare() first")
 
-        self._cancel_heartbeat()
-        reader = self._reader
-        assert reader is not None
-
-        # we need to break `receive()` cycle first,
-        # `close()` may be called from different task
-        if self._waiting and not self._closed:
-            if not self._close_wait:
-                assert self._loop is not None
-                self._close_wait = self._loop.create_future()
-            reader.feed_data(WS_CLOSING_MESSAGE, 0)
-            await self._close_wait
-
         if self._closed:
             return False
-
         self._set_closed()
+
         try:
             await self._writer.close(code, message)
             writer = self._payload_writer
@@ -437,12 +424,21 @@ class WebSocketResponse(StreamResponse):
             self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
             return True
 
+        reader = self._reader
+        assert reader is not None
+        # we need to break `receive()` cycle before we can call
+        # `reader.read()` as `close()` may be called from different task
+        if self._waiting:
+            assert self._loop is not None
+            assert self._close_wait is None
+            self._close_wait = self._loop.create_future()
+            reader.feed_data(WS_CLOSING_MESSAGE)
+            await self._close_wait
+
         if self._closing:
             self._close_transport()
             return True
 
-        reader = self._reader
-        assert reader is not None
         try:
             async with async_timeout.timeout(self._timeout):
                 msg = await reader.read()

{"aiohttp/web_ws.py": "import asyncio\nimport base64\nimport binascii\nimport hashlib\nimport json\nimport sys\nfrom typing import Any, Final, Iterable, Optional, Tuple, cast\n\nimport attr\nfrom multidict import CIMultiDict\n\nfrom . import hdrs\nfrom .abc import AbstractStreamWriter\nfrom .helpers import calculate_timeout_when, set_exception, set_result\nfrom .http import (\n    WS_CLOSED_MESSAGE,\n    WS_CLOSING_MESSAGE,\n    WS_KEY,\n    WebSocketError,\n    WebSocketReader,\n    WebSocketWriter,\n    WSCloseCode,\n    WSMessage,\n    WSMsgType as WSMsgType,\n    ws_ext_gen,\n    ws_ext_parse,\n)\nfrom .log import ws_logger\nfrom .streams import EofStream, FlowControlDataQueue\nfrom .typedefs import JSONDecoder, JSONEncoder\nfrom .web_exceptions import HTTPBadRequest, HTTPException\nfrom .web_request import BaseRequest\nfrom .web_response import StreamResponse\n\nif sys.version_info >= (3, 11):\n    import asyncio as async_timeout\nelse:\n    import async_timeout\n\n__all__ = (\n    \"WebSocketResponse\",\n    \"WebSocketReady\",\n    \"WSMsgType\",\n)\n\nTHRESHOLD_CONNLOST_ACCESS: Final[int] = 5\n\n\n@attr.s(auto_attribs=True, frozen=True, slots=True)\nclass WebSocketReady:\n    ok: bool\n    protocol: Optional[str]\n\n    def __bool__(self) -> bool:\n        return self.ok\n\n\nclass WebSocketResponse(StreamResponse):\n\n    _length_check = False\n\n    def __init__(\n        self,\n        *,\n        timeout: float = 10.0,\n        receive_timeout: Optional[float] = None,\n        autoclose: bool = True,\n        autoping: bool = True,\n        heartbeat: Optional[float] = None,\n        protocols: Iterable[str] = (),\n        compress: bool = True,\n        max_msg_size: int = 4 * 1024 * 1024,\n    ) -> None:\n        super().__init__(status=101)\n        self._protocols = protocols\n        self._ws_protocol: Optional[str] = None\n        self._writer: Optional[WebSocketWriter] = None\n        self._reader: Optional[FlowControlDataQueue[WSMessage]] = None\n        self._closed = False\n        self._closing = False\n        self._conn_lost = 0\n        self._close_code: Optional[int] = None\n        self._loop: Optional[asyncio.AbstractEventLoop] = None\n        self._waiting: bool = False\n        self._close_wait: Optional[asyncio.Future[None]] = None\n        self._exception: Optional[BaseException] = None\n        self._timeout = timeout\n        self._receive_timeout = receive_timeout\n        self._autoclose = autoclose\n        self._autoping = autoping\n        self._heartbeat = heartbeat\n        self._heartbeat_when = 0.0\n        self._heartbeat_cb: Optional[asyncio.TimerHandle] = None\n        if heartbeat is not None:\n            self._pong_heartbeat = heartbeat / 2.0\n        self._pong_response_cb: Optional[asyncio.TimerHandle] = None\n        self._compress = compress\n        self._max_msg_size = max_msg_size\n        self._ping_task: Optional[asyncio.Task[None]] = None\n\n    def _cancel_heartbeat(self) -> None:\n        self._cancel_pong_response_cb()\n        if self._heartbeat_cb is not None:\n            self._heartbeat_cb.cancel()\n            self._heartbeat_cb = None\n        if self._ping_task is not None:\n            self._ping_task.cancel()\n            self._ping_task = None\n\n    def _cancel_pong_response_cb(self) -> None:\n        if self._pong_response_cb is not None:\n            self._pong_response_cb.cancel()\n            self._pong_response_cb = None\n\n    def _reset_heartbeat(self) -> None:\n        if self._heartbeat is None:\n            return\n        self._cancel_pong_response_cb()\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        loop = self._loop\n        assert loop is not None\n        now = loop.time()\n        when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold)\n        self._heartbeat_when = when\n        if self._heartbeat_cb is None:\n            # We do not cancel the previous heartbeat_cb here because\n            # it generates a significant amount of TimerHandle churn\n            # which causes asyncio to rebuild the heap frequently.\n            # Instead _send_heartbeat() will reschedule the next\n            # heartbeat if it fires too early.\n            self._heartbeat_cb = loop.call_at(when, self._send_heartbeat)\n\n    def _send_heartbeat(self) -> None:\n        self._heartbeat_cb = None\n        loop = self._loop\n        assert loop is not None and self._writer is not None\n        now = loop.time()\n        if now < self._heartbeat_when:\n            # Heartbeat fired too early, reschedule\n            self._heartbeat_cb = loop.call_at(\n                self._heartbeat_when, self._send_heartbeat\n            )\n            return\n\n        req = self._req\n        timeout_ceil_threshold = (\n            req._protocol._timeout_ceil_threshold if req is not None else 5\n        )\n        when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold)\n        self._cancel_pong_response_cb()\n        self._pong_response_cb = loop.call_at(when, self._pong_not_received)\n\n        if sys.version_info >= (3, 12):\n            # Optimization for Python 3.12, try to send the ping\n            # immediately to avoid having to schedule\n            # the task on the event loop.\n            ping_task = asyncio.Task(self._writer.ping(), loop=loop, eager_start=True)\n        else:\n            ping_task = loop.create_task(self._writer.ping())\n\n        if not ping_task.done():\n            self._ping_task = ping_task\n            ping_task.add_done_callback(self._ping_task_done)\n\n    def _ping_task_done(self, task: \"asyncio.Task[None]\") -> None:\n        \"\"\"Callback for when the ping task completes.\"\"\"\n        self._ping_task = None\n\n    def _pong_not_received(self) -> None:\n        if self._req is not None and self._req.transport is not None:\n            self._set_closed()\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            self._exception = asyncio.TimeoutError()\n\n    def _set_closed(self) -> None:\n        \"\"\"Set the connection to closed.\n\n        Cancel any heartbeat timers and set the closed flag.\n        \"\"\"\n        self._closed = True\n        self._cancel_heartbeat()\n\n    async def prepare(self, request: BaseRequest) -> AbstractStreamWriter:\n        # make pre-check to don't hide it by do_handshake() exceptions\n        if self._payload_writer is not None:\n            return self._payload_writer\n\n        protocol, writer = self._pre_start(request)\n        payload_writer = await super().prepare(request)\n        assert payload_writer is not None\n        self._post_start(request, protocol, writer)\n        await payload_writer.drain()\n        return payload_writer\n\n    def _handshake(\n        self, request: BaseRequest\n    ) -> Tuple[\"CIMultiDict[str]\", str, bool, bool]:\n        headers = request.headers\n        if \"websocket\" != headers.get(hdrs.UPGRADE, \"\").lower().strip():\n            raise HTTPBadRequest(\n                text=(\n                    \"No WebSocket UPGRADE hdr: {}\\n Can \"\n                    '\"Upgrade\" only to \"WebSocket\".'\n                ).format(headers.get(hdrs.UPGRADE))\n            )\n\n        if \"upgrade\" not in headers.get(hdrs.CONNECTION, \"\").lower():\n            raise HTTPBadRequest(\n                text=\"No CONNECTION upgrade hdr: {}\".format(\n                    headers.get(hdrs.CONNECTION)\n                )\n            )\n\n        # find common sub-protocol between client and server\n        protocol = None\n        if hdrs.SEC_WEBSOCKET_PROTOCOL in headers:\n            req_protocols = [\n                str(proto.strip())\n                for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(\",\")\n            ]\n\n            for proto in req_protocols:\n                if proto in self._protocols:\n                    protocol = proto\n                    break\n            else:\n                # No overlap found: Return no protocol as per spec\n                ws_logger.warning(\n                    \"Client protocols %r don\u2019t overlap server-known ones %r\",\n                    req_protocols,\n                    self._protocols,\n                )\n\n        # check supported version\n        version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, \"\")\n        if version not in (\"13\", \"8\", \"7\"):\n            raise HTTPBadRequest(text=f\"Unsupported version: {version}\")\n\n        # check client handshake for validity\n        key = headers.get(hdrs.SEC_WEBSOCKET_KEY)\n        try:\n            if not key or len(base64.b64decode(key)) != 16:\n                raise HTTPBadRequest(text=f\"Handshake error: {key!r}\")\n        except binascii.Error:\n            raise HTTPBadRequest(text=f\"Handshake error: {key!r}\") from None\n\n        accept_val = base64.b64encode(\n            hashlib.sha1(key.encode() + WS_KEY).digest()\n        ).decode()\n        response_headers = CIMultiDict(\n            {\n                hdrs.UPGRADE: \"websocket\",\n                hdrs.CONNECTION: \"upgrade\",\n                hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,\n            }\n        )\n\n        notakeover = False\n        compress = 0\n        if self._compress:\n            extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)\n            # Server side always get return with no exception.\n            # If something happened, just drop compress extension\n            compress, notakeover = ws_ext_parse(extensions, isserver=True)\n            if compress:\n                enabledext = ws_ext_gen(\n                    compress=compress, isserver=True, server_notakeover=notakeover\n                )\n                response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext\n\n        if protocol:\n            response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol\n        return (\n            response_headers,\n            protocol,\n            compress,\n            notakeover,\n        )  # type: ignore[return-value]\n\n    def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]:\n        self._loop = request._loop\n\n        headers, protocol, compress, notakeover = self._handshake(request)\n\n        self.set_status(101)\n        self.headers.update(headers)\n        self.force_close()\n        self._compress = compress\n        transport = request._protocol.transport\n        assert transport is not None\n        writer = WebSocketWriter(\n            request._protocol, transport, compress=compress, notakeover=notakeover\n        )\n\n        return protocol, writer\n\n    def _post_start(\n        self, request: BaseRequest, protocol: str, writer: WebSocketWriter\n    ) -> None:\n        self._ws_protocol = protocol\n        self._writer = writer\n\n        self._reset_heartbeat()\n\n        loop = self._loop\n        assert loop is not None\n        self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop)\n        request.protocol.set_parser(\n            WebSocketReader(self._reader, self._max_msg_size, compress=self._compress)\n        )\n        # disable HTTP keepalive for WebSocket\n        request.protocol.keep_alive(False)\n\n    def can_prepare(self, request: BaseRequest) -> WebSocketReady:\n        if self._writer is not None:\n            raise RuntimeError(\"Already started\")\n        try:\n            _, protocol, _, _ = self._handshake(request)\n        except HTTPException:\n            return WebSocketReady(False, None)\n        else:\n            return WebSocketReady(True, protocol)\n\n    @property\n    def closed(self) -> bool:\n        return self._closed\n\n    @property\n    def close_code(self) -> Optional[int]:\n        return self._close_code\n\n    @property\n    def ws_protocol(self) -> Optional[str]:\n        return self._ws_protocol\n\n    @property\n    def compress(self) -> bool:\n        return self._compress\n\n    def get_extra_info(self, name: str, default: Any = None) -> Any:\n        \"\"\"Get optional transport information.\n\n        If no value associated with ``name`` is found, ``default`` is returned.\n        \"\"\"\n        writer = self._writer\n        if writer is None:\n            return default\n        transport = writer.transport\n        if transport is None:\n            return default\n        return transport.get_extra_info(name, default)\n\n    def exception(self) -> Optional[BaseException]:\n        return self._exception\n\n    async def ping(self, message: bytes = b\"\") -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.ping(message)\n\n    async def pong(self, message: bytes = b\"\") -> None:\n        # unsolicited pong\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        await self._writer.pong(message)\n\n    async def send_str(self, data: str, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, str):\n            raise TypeError(\"data argument must be str (%r)\" % type(data))\n        await self._writer.send(data, binary=False, compress=compress)\n\n    async def send_bytes(self, data: bytes, compress: Optional[bool] = None) -> None:\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n        if not isinstance(data, (bytes, bytearray, memoryview)):\n            raise TypeError(\"data argument must be byte-ish (%r)\" % type(data))\n        await self._writer.send(data, binary=True, compress=compress)\n\n    async def send_json(\n        self,\n        data: Any,\n        compress: Optional[bool] = None,\n        *,\n        dumps: JSONEncoder = json.dumps,\n    ) -> None:\n        await self.send_str(dumps(data), compress=compress)\n\n    async def write_eof(self) -> None:  # type: ignore[override]\n        if self._eof_sent:\n            return\n        if self._payload_writer is None:\n            raise RuntimeError(\"Response has not been started\")\n\n        await self.close()\n        self._eof_sent = True\n\n    async def close(\n        self, *, code: int = WSCloseCode.OK, message: bytes = b\"\", drain: bool = True\n    ) -> bool:\n        \"\"\"Close websocket connection.\"\"\"\n        if self._writer is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        self._cancel_heartbeat()\n        reader = self._reader\n        assert reader is not None\n\n        # we need to break `receive()` cycle first,\n        # `close()` may be called from different task\n        if self._waiting and not self._closed:\n            if not self._close_wait:\n                assert self._loop is not None\n                self._close_wait = self._loop.create_future()\n            reader.feed_data(WS_CLOSING_MESSAGE, 0)\n            await self._close_wait\n\n        if self._closed:\n            return False\n\n        self._set_closed()\n        try:\n            await self._writer.close(code, message)\n            writer = self._payload_writer\n            assert writer is not None\n            if drain:\n                await writer.drain()\n        except (asyncio.CancelledError, asyncio.TimeoutError):\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if self._closing:\n            self._close_transport()\n            return True\n\n        reader = self._reader\n        assert reader is not None\n        try:\n            async with async_timeout.timeout(self._timeout):\n                msg = await reader.read()\n        except asyncio.CancelledError:\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            raise\n        except Exception as exc:\n            self._exception = exc\n            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n            return True\n\n        if msg.type is WSMsgType.CLOSE:\n            self._set_code_close_transport(msg.data)\n            return True\n\n        self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)\n        self._exception = asyncio.TimeoutError()\n        return True\n\n    def _set_closing(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and mark the connection as closing.\"\"\"\n        self._closing = True\n        self._close_code = code\n        self._cancel_heartbeat()\n\n    def _set_code_close_transport(self, code: WSCloseCode) -> None:\n        \"\"\"Set the close code and close the transport.\"\"\"\n        self._close_code = code\n        self._close_transport()\n\n    def _close_transport(self) -> None:\n        \"\"\"Close the transport.\"\"\"\n        if self._req is not None and self._req.transport is not None:\n            self._req.transport.close()\n\n    async def receive(self, timeout: Optional[float] = None) -> WSMessage:\n        if self._reader is None:\n            raise RuntimeError(\"Call .prepare() first\")\n\n        loop = self._loop\n        assert loop is not None\n        receive_timeout = timeout or self._receive_timeout\n        while True:\n            if self._waiting:\n                raise RuntimeError(\"Concurrent call to receive() is not allowed\")\n\n            if self._closed:\n                self._conn_lost += 1\n                if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS:\n                    raise RuntimeError(\"WebSocket connection is closed.\")\n                return WS_CLOSED_MESSAGE\n            elif self._closing:\n                return WS_CLOSING_MESSAGE\n\n            try:\n                self._waiting = True\n                try:\n                    if receive_timeout:\n                        # Entering the context manager and creating\n                        # Timeout() object can take almost 50% of the\n                        # run time in this loop so we avoid it if\n                        # there is no read timeout.\n                        async with async_timeout.timeout(receive_timeout):\n                            msg = await self._reader.read()\n                    else:\n                        msg = await self._reader.read()\n                    self._reset_heartbeat()\n                finally:\n                    self._waiting = False\n                    if self._close_wait:\n                        set_result(self._close_wait, None)\n            except asyncio.TimeoutError:\n                raise\n            except EofStream:\n                self._close_code = WSCloseCode.OK\n                await self.close()\n                return WSMessage(WSMsgType.CLOSED, None, None)\n            except WebSocketError as exc:\n                self._close_code = exc.code\n                await self.close(code=exc.code)\n                return WSMessage(WSMsgType.ERROR, exc, None)\n            except Exception as exc:\n                self._exception = exc\n                self._set_closing(WSCloseCode.ABNORMAL_CLOSURE)\n                await self.close()\n                return WSMessage(WSMsgType.ERROR, exc, None)\n\n            if msg.type is WSMsgType.CLOSE:\n                self._set_closing(msg.data)\n                # Could be closed while awaiting reader.\n                if not self._closed and self._autoclose:\n                    # The client is likely going to close the\n                    # connection out from under us so we do not\n                    # want to drain any pending writes as it will\n                    # likely result writing to a broken pipe.\n                    await self.close(drain=False)\n            elif msg.type is WSMsgType.CLOSING:\n                self._set_closing(WSCloseCode.OK)\n            elif msg.type is WSMsgType.PING and self._autoping:\n                await self.pong(msg.data)\n                continue\n            elif msg.type is WSMsgType.PONG and self._autoping:\n                continue\n\n            return msg\n\n    async def receive_str(self, *, timeout: Optional[float] = None) -> str:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.TEXT:\n            raise TypeError(\n                \"Received message {}:{!r} is not WSMsgType.TEXT\".format(\n                    msg.type, msg.data\n                )\n            )\n        return cast(str, msg.data)\n\n    async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:\n        msg = await self.receive(timeout)\n        if msg.type is not WSMsgType.BINARY:\n            raise TypeError(f\"Received message {msg.type}:{msg.data!r} is not bytes\")\n        return cast(bytes, msg.data)\n\n    async def receive_json(\n        self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None\n    ) -> Any:\n        data = await self.receive_str(timeout=timeout)\n        return loads(data)\n\n    async def write(self, data: bytes) -> None:\n        raise RuntimeError(\"Cannot call .write() for websocket\")\n\n    def __aiter__(self) -> \"WebSocketResponse\":\n        return self\n\n    async def __anext__(self) -> WSMessage:\n        msg = await self.receive()\n        if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):\n            raise StopAsyncIteration\n        return msg\n\n    def _cancel(self, exc: BaseException) -> None:\n        # web_protocol calls this from connection_lost\n        # or when the server is shutting down.\n        self._closing = True\n        self._cancel_heartbeat()\n        if self._reader is not None:\n            set_exception(self._reader, exc)\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:** WebSocket Close Race Condition Prevents Proper Close Code Delivery on Server Shutdown

**Description:**
When the server initiates a shutdown, the client websocket is expected to receive a close message with the appropriate close code. However, under certain conditions, a race condition occurs that prevents the close code from reaching the client correctly.

**Example Buggy Code:**

```python
async def test_websocket_shutdown(aiohttp_client: AiohttpClient) -> None:
    """Test that the client websocket gets the close message when the server is shutting down."""
    url = '/ws'
    app = web.Application()
    websockets = web.AppKey('websockets', weakref.WeakSet)
    app[websockets] = weakref.WeakSet()
    shutdown_websockets = web.AppKey('shutdown_websockets', weakref.WeakSet)
    app[shutdown_websockets] = weakref.WeakSet()

    async def websocket_handler(request: web.Request) -> web.WebSocketResponse:
        websocket = web.WebSocketResponse()
        await websocket.prepare(request)
        request.app[websockets].add(websocket)
        request.app[shutdown_websockets].add(websocket)
        try:
            async for message in websocket:
                await websocket.send_json({'ok': True, 'message': message.json()})
        finally:
            request.app[websockets].discard(websocket)
        return websocket

    async def on_shutdown(app: web.Application) -> None:
        while app[shutdown_websockets]:
            websocket = app[shutdown_websockets].pop()
            await websocket.close(code=aiohttp.WSCloseCode.GOING_AWAY, message='Server shutdown')
    app.router.add_get(url, websocket_handler)
    app.on_shutdown.append(on_shutdown)
    client = await aiohttp_client(app)
    websocket = await client.ws_connect(url)
    message = {'message': 'hi'}
    await websocket.send_json(message)
    reply = await websocket.receive_json()
    assert reply == {'ok': True, 'message': message}
    await app.shutdown()
    assert websocket.closed is False
    reply = await websocket.receive()
    assert reply.type is aiohttp.http.WSMsgType.CLOSE
    assert reply.data == aiohttp.WSCloseCode.GOING_AWAY
    assert reply.extra == 'Server shutdown'
    assert websocket.closed is True
```

**Expected Behavior:**
When the server shuts down, the client websocket should receive a close message with the close code `WSCloseCode.GOING_AWAY` and the message `"Server shutdown"`. This ensures that the client is properly informed about the server's shutdown reason.

**Actual Behavior:**
Instead of receiving the expected close code and message, the client receives a close message with the default close code `1000` (normal closure) and an empty message. This indicates that the close code intended to signal the server's shutdown does not reach the client due to a race condition in the websocket handling logic.

[/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/web_ws.py
import asyncio
import base64
import binascii
import hashlib
import json
import sys
from typing import Any, Final, Iterable, Optional, Tuple, cast

import attr
from multidict import CIMultiDict

from . import hdrs
from .abc import AbstractStreamWriter
from .helpers import calculate_timeout_when, set_exception, set_result
from .http import (
    WS_CLOSED_MESSAGE,
    WS_CLOSING_MESSAGE,
    WS_KEY,
    WebSocketError,
    WebSocketReader,
    WebSocketWriter,
    WSCloseCode,
    WSMessage,
    WSMsgType as WSMsgType,
    ws_ext_gen,
    ws_ext_parse,
)
from .log import ws_logger
from .streams import EofStream, FlowControlDataQueue
from .typedefs import JSONDecoder, JSONEncoder
from .web_exceptions import HTTPBadRequest, HTTPException
from .web_request import BaseRequest
from .web_response import StreamResponse

if sys.version_info >= (3, 11):
    import asyncio as async_timeout
else:
    import async_timeout

__all__ = (
    "WebSocketResponse",
    "WebSocketReady",
    "WSMsgType",
)

THRESHOLD_CONNLOST_ACCESS: Final[int] = 5


@attr.s(auto_attribs=True, frozen=True, slots=True)
class WebSocketReady:
    ok: bool
    protocol: Optional[str]

    def __bool__(self) -> bool:
        return self.ok


class WebSocketResponse(StreamResponse):

    _length_check = False

    def __init__(
        self,
        *,
        timeout: float = 10.0,
        receive_timeout: Optional[float] = None,
        autoclose: bool = True,
        autoping: bool = True,
        heartbeat: Optional[float] = None,
        protocols: Iterable[str] = (),
        compress: bool = True,
        max_msg_size: int = 4 * 1024 * 1024,
    ) -> None:
        super().__init__(status=101)
        self._protocols = protocols
        self._ws_protocol: Optional[str] = None
        self._writer: Optional[WebSocketWriter] = None
        self._reader: Optional[FlowControlDataQueue[WSMessage]] = None
        self._closed = False
        self._closing = False
        self._conn_lost = 0
        self._close_code: Optional[int] = None
        self._loop: Optional[asyncio.AbstractEventLoop] = None
        self._waiting: bool = False
        self._close_wait: Optional[asyncio.Future[None]] = None
        self._exception: Optional[BaseException] = None
        self._timeout = timeout
        self._receive_timeout = receive_timeout
        self._autoclose = autoclose
        self._autoping = autoping
        self._heartbeat = heartbeat
        self._heartbeat_when = 0.0
        self._heartbeat_cb: Optional[asyncio.TimerHandle] = None
        if heartbeat is not None:
            self._pong_heartbeat = heartbeat / 2.0
        self._pong_response_cb: Optional[asyncio.TimerHandle] = None
        self._compress = compress
        self._max_msg_size = max_msg_size
        self._ping_task: Optional[asyncio.Task[None]] = None

    def _cancel_heartbeat(self) -> None:
        self._cancel_pong_response_cb()
        if self._heartbeat_cb is not None:
            self._heartbeat_cb.cancel()
            self._heartbeat_cb = None
        if self._ping_task is not None:
            self._ping_task.cancel()
            self._ping_task = None

    def _cancel_pong_response_cb(self) -> None:
        if self._pong_response_cb is not None:
            self._pong_response_cb.cancel()
            self._pong_response_cb = None

    def _reset_heartbeat(self) -> None:
        if self._heartbeat is None:
            return
        self._cancel_pong_response_cb()
        req = self._req
        timeout_ceil_threshold = (
            req._protocol._timeout_ceil_threshold if req is not None else 5
        )
        loop = self._loop
        assert loop is not None
        now = loop.time()
        when = calculate_timeout_when(now, self._heartbeat, timeout_ceil_threshold)
        self._heartbeat_when = when
        if self._heartbeat_cb is None:
            # We do not cancel the previous heartbeat_cb here because
            # it generates a significant amount of TimerHandle churn
            # which causes asyncio to rebuild the heap frequently.
            # Instead _send_heartbeat() will reschedule the next
            # heartbeat if it fires too early.
            self._heartbeat_cb = loop.call_at(when, self._send_heartbeat)

    def _send_heartbeat(self) -> None:
        self._heartbeat_cb = None
        loop = self._loop
        assert loop is not None and self._writer is not None
        now = loop.time()
        if now < self._heartbeat_when:
            # Heartbeat fired too early, reschedule
            self._heartbeat_cb = loop.call_at(
                self._heartbeat_when, self._send_heartbeat
            )
            return

        req = self._req
        timeout_ceil_threshold = (
            req._protocol._timeout_ceil_threshold if req is not None else 5
        )
        when = calculate_timeout_when(now, self._pong_heartbeat, timeout_ceil_threshold)
        self._cancel_pong_response_cb()
        self._pong_response_cb = loop.call_at(when, self._pong_not_received)

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

        if not ping_task.done():
            self._ping_task = ping_task
            ping_task.add_done_callback(self._ping_task_done)

    def _ping_task_done(self, task: "asyncio.Task[None]") -> None:
        """Callback for when the ping task completes."""
        self._ping_task = None

    def _pong_not_received(self) -> None:
        if self._req is not None and self._req.transport is not None:
            self._set_closed()
            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
            self._exception = asyncio.TimeoutError()

    def _set_closed(self) -> None:
        """Set the connection to closed.

        Cancel any heartbeat timers and set the closed flag.
        """
        self._closed = True
        self._cancel_heartbeat()

    async def prepare(self, request: BaseRequest) -> AbstractStreamWriter:
        # make pre-check to don't hide it by do_handshake() exceptions
        if self._payload_writer is not None:
            return self._payload_writer

        protocol, writer = self._pre_start(request)
        payload_writer = await super().prepare(request)
        assert payload_writer is not None
        self._post_start(request, protocol, writer)
        await payload_writer.drain()
        return payload_writer

    def _handshake(
        self, request: BaseRequest
    ) -> Tuple["CIMultiDict[str]", str, bool, bool]:
        headers = request.headers
        if "websocket" != headers.get(hdrs.UPGRADE, "").lower().strip():
            raise HTTPBadRequest(
                text=(
                    "No WebSocket UPGRADE hdr: {}\n Can "
                    '"Upgrade" only to "WebSocket".'
                ).format(headers.get(hdrs.UPGRADE))
            )

        if "upgrade" not in headers.get(hdrs.CONNECTION, "").lower():
            raise HTTPBadRequest(
                text="No CONNECTION upgrade hdr: {}".format(
                    headers.get(hdrs.CONNECTION)
                )
            )

        # find common sub-protocol between client and server
        protocol = None
        if hdrs.SEC_WEBSOCKET_PROTOCOL in headers:
            req_protocols = [
                str(proto.strip())
                for proto in headers[hdrs.SEC_WEBSOCKET_PROTOCOL].split(",")
            ]

            for proto in req_protocols:
                if proto in self._protocols:
                    protocol = proto
                    break
            else:
                # No overlap found: Return no protocol as per spec
                ws_logger.warning(
                    "Client protocols %r don’t overlap server-known ones %r",
                    req_protocols,
                    self._protocols,
                )

        # check supported version
        version = headers.get(hdrs.SEC_WEBSOCKET_VERSION, "")
        if version not in ("13", "8", "7"):
            raise HTTPBadRequest(text=f"Unsupported version: {version}")

        # check client handshake for validity
        key = headers.get(hdrs.SEC_WEBSOCKET_KEY)
        try:
            if not key or len(base64.b64decode(key)) != 16:
                raise HTTPBadRequest(text=f"Handshake error: {key!r}")
        except binascii.Error:
            raise HTTPBadRequest(text=f"Handshake error: {key!r}") from None

        accept_val = base64.b64encode(
            hashlib.sha1(key.encode() + WS_KEY).digest()
        ).decode()
        response_headers = CIMultiDict(
            {
                hdrs.UPGRADE: "websocket",
                hdrs.CONNECTION: "upgrade",
                hdrs.SEC_WEBSOCKET_ACCEPT: accept_val,
            }
        )

        notakeover = False
        compress = 0
        if self._compress:
            extensions = headers.get(hdrs.SEC_WEBSOCKET_EXTENSIONS)
            # Server side always get return with no exception.
            # If something happened, just drop compress extension
            compress, notakeover = ws_ext_parse(extensions, isserver=True)
            if compress:
                enabledext = ws_ext_gen(
                    compress=compress, isserver=True, server_notakeover=notakeover
                )
                response_headers[hdrs.SEC_WEBSOCKET_EXTENSIONS] = enabledext

        if protocol:
            response_headers[hdrs.SEC_WEBSOCKET_PROTOCOL] = protocol
        return (
            response_headers,
            protocol,
            compress,
            notakeover,
        )  # type: ignore[return-value]

    def _pre_start(self, request: BaseRequest) -> Tuple[str, WebSocketWriter]:
        self._loop = request._loop

        headers, protocol, compress, notakeover = self._handshake(request)

        self.set_status(101)
        self.headers.update(headers)
        self.force_close()
        self._compress = compress
        transport = request._protocol.transport
        assert transport is not None
        writer = WebSocketWriter(
            request._protocol, transport, compress=compress, notakeover=notakeover
        )

        return protocol, writer

    def _post_start(
        self, request: BaseRequest, protocol: str, writer: WebSocketWriter
    ) -> None:
        self._ws_protocol = protocol
        self._writer = writer

        self._reset_heartbeat()

        loop = self._loop
        assert loop is not None
        self._reader = FlowControlDataQueue(request._protocol, 2**16, loop=loop)
        request.protocol.set_parser(
            WebSocketReader(self._reader, self._max_msg_size, compress=self._compress)
        )
        # disable HTTP keepalive for WebSocket
        request.protocol.keep_alive(False)

    def can_prepare(self, request: BaseRequest) -> WebSocketReady:
        if self._writer is not None:
            raise RuntimeError("Already started")
        try:
            _, protocol, _, _ = self._handshake(request)
        except HTTPException:
            return WebSocketReady(False, None)
        else:
            return WebSocketReady(True, protocol)

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

    @property
    def close_code(self) -> Optional[int]:
        return self._close_code

    @property
    def ws_protocol(self) -> Optional[str]:
        return self._ws_protocol

    @property
    def compress(self) -> bool:
        return self._compress

    def get_extra_info(self, name: str, default: Any = None) -> Any:
        """Get optional transport information.

        If no value associated with ``name`` is found, ``default`` is returned.
        """
        writer = self._writer
        if writer is None:
            return default
        transport = writer.transport
        if transport is None:
            return default
        return transport.get_extra_info(name, default)

    def exception(self) -> Optional[BaseException]:
        return self._exception

    async def ping(self, message: bytes = b"") -> None:
        if self._writer is None:
            raise RuntimeError("Call .prepare() first")
        await self._writer.ping(message)

    async def pong(self, message: bytes = b"") -> None:
        # unsolicited pong
        if self._writer is None:
            raise RuntimeError("Call .prepare() first")
        await self._writer.pong(message)

    async def send_str(self, data: str, compress: Optional[bool] = None) -> None:
        if self._writer is None:
            raise RuntimeError("Call .prepare() first")
        if not isinstance(data, str):
            raise TypeError("data argument must be str (%r)" % type(data))
        await self._writer.send(data, binary=False, compress=compress)

    async def send_bytes(self, data: bytes, compress: Optional[bool] = None) -> None:
        if self._writer is None:
            raise RuntimeError("Call .prepare() first")
        if not isinstance(data, (bytes, bytearray, memoryview)):
            raise TypeError("data argument must be byte-ish (%r)" % type(data))
        await self._writer.send(data, binary=True, compress=compress)

    async def send_json(
        self,
        data: Any,
        compress: Optional[bool] = None,
        *,
        dumps: JSONEncoder = json.dumps,
    ) -> None:
        await self.send_str(dumps(data), compress=compress)

    async def write_eof(self) -> None:  # type: ignore[override]
        if self._eof_sent:
            return
        if self._payload_writer is None:
            raise RuntimeError("Response has not been started")

        await self.close()
        self._eof_sent = True

    async def close(
        self, *, code: int = WSCloseCode.OK, message: bytes = b"", drain: bool = True
    ) -> bool:
        """Close websocket connection."""
        if self._writer is None:
            raise RuntimeError("Call .prepare() first")

        self._cancel_heartbeat()
        reader = self._reader
        assert reader is not None

        # we need to break `receive()` cycle first,
        # `close()` may be called from different task
        if self._waiting and not self._closed:
            if not self._close_wait:
                assert self._loop is not None
                self._close_wait = self._loop.create_future()
            reader.feed_data(WS_CLOSING_MESSAGE, 0)
            await self._close_wait

        if self._closed:
            return False

        self._set_closed()
        try:
            await self._writer.close(code, message)
            writer = self._payload_writer
            assert writer is not None
            if drain:
                await writer.drain()
        except (asyncio.CancelledError, asyncio.TimeoutError):
            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
            raise
        except Exception as exc:
            self._exception = exc
            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
            return True

        if self._closing:
            self._close_transport()
            return True

        reader = self._reader
        assert reader is not None
        try:
            async with async_timeout.timeout(self._timeout):
                msg = await reader.read()
        except asyncio.CancelledError:
            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
            raise
        except Exception as exc:
            self._exception = exc
            self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
            return True

        if msg.type is WSMsgType.CLOSE:
            self._set_code_close_transport(msg.data)
            return True

        self._set_code_close_transport(WSCloseCode.ABNORMAL_CLOSURE)
        self._exception = asyncio.TimeoutError()
        return True

    def _set_closing(self, code: WSCloseCode) -> None:
        """Set the close code and mark the connection as closing."""
        self._closing = True
        self._close_code = code
        self._cancel_heartbeat()

    def _set_code_close_transport(self, code: WSCloseCode) -> None:
        """Set the close code and close the transport."""
        self._close_code = code
        self._close_transport()

    def _close_transport(self) -> None:
        """Close the transport."""
        if self._req is not None and self._req.transport is not None:
            self._req.transport.close()

    async def receive(self, timeout: Optional[float] = None) -> WSMessage:
        if self._reader is None:
            raise RuntimeError("Call .prepare() first")

        loop = self._loop
        assert loop is not None
        receive_timeout = timeout or self._receive_timeout
        while True:
            if self._waiting:
                raise RuntimeError("Concurrent call to receive() is not allowed")

            if self._closed:
                self._conn_lost += 1
                if self._conn_lost >= THRESHOLD_CONNLOST_ACCESS:
                    raise RuntimeError("WebSocket connection is closed.")
                return WS_CLOSED_MESSAGE
            elif self._closing:
                return WS_CLOSING_MESSAGE

            try:
                self._waiting = True
                try:
                    if receive_timeout:
                        # Entering the context manager and creating
                        # Timeout() object can take almost 50% of the
                        # run time in this loop so we avoid it if
                        # there is no read timeout.
                        async with async_timeout.timeout(receive_timeout):
                            msg = await self._reader.read()
                    else:
                        msg = await self._reader.read()
                    self._reset_heartbeat()
                finally:
                    self._waiting = False
                    if self._close_wait:
                        set_result(self._close_wait, None)
            except asyncio.TimeoutError:
                raise
            except EofStream:
                self._close_code = WSCloseCode.OK
                await self.close()
                return WSMessage(WSMsgType.CLOSED, None, None)
            except WebSocketError as exc:
                self._close_code = exc.code
                await self.close(code=exc.code)
                return WSMessage(WSMsgType.ERROR, exc, None)
            except Exception as exc:
                self._exception = exc
                self._set_closing(WSCloseCode.ABNORMAL_CLOSURE)
                await self.close()
                return WSMessage(WSMsgType.ERROR, exc, None)

            if msg.type is WSMsgType.CLOSE:
                self._set_closing(msg.data)
                # Could be closed while awaiting reader.
                if not self._closed and self._autoclose:
                    # The client is likely going to close the
                    # connection out from under us so we do not
                    # want to drain any pending writes as it will
                    # likely result writing to a broken pipe.
                    await self.close(drain=False)
            elif msg.type is WSMsgType.CLOSING:
                self._set_closing(WSCloseCode.OK)
            elif msg.type is WSMsgType.PING and self._autoping:
                await self.pong(msg.data)
                continue
            elif msg.type is WSMsgType.PONG and self._autoping:
                continue

            return msg

    async def receive_str(self, *, timeout: Optional[float] = None) -> str:
        msg = await self.receive(timeout)
        if msg.type is not WSMsgType.TEXT:
            raise TypeError(
                "Received message {}:{!r} is not WSMsgType.TEXT".format(
                    msg.type, msg.data
                )
            )
        return cast(str, msg.data)

    async def receive_bytes(self, *, timeout: Optional[float] = None) -> bytes:
        msg = await self.receive(timeout)
        if msg.type is not WSMsgType.BINARY:
            raise TypeError(f"Received message {msg.type}:{msg.data!r} is not bytes")
        return cast(bytes, msg.data)

    async def receive_json(
        self, *, loads: JSONDecoder = json.loads, timeout: Optional[float] = None
    ) -> Any:
        data = await self.receive_str(timeout=timeout)
        return loads(data)

    async def write(self, data: bytes) -> None:
        raise RuntimeError("Cannot call .write() for websocket")

    def __aiter__(self) -> "WebSocketResponse":
        return self

    async def __anext__(self) -> WSMessage:
        msg = await self.receive()
        if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):
            raise StopAsyncIteration
        return msg

    def _cancel(self, exc: BaseException) -> None:
        # web_protocol calls this from connection_lost
        # or when the server is shutting down.
        self._closing = True
        self._cancel_heartbeat()
        if self._reader is not None:
            set_exception(self._reader, exc)

```
--- 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