We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Allow later websockets releases
**Describe the bug**
`websockets` is [pinned](https://github.com/sanic-org/sanic/blob/main/setup.py#L91
). The latest `websockets` is 9.1 and this release is fixing a [authentication vulnerability](https://websockets.readthedocs.io/en/stable/changelog.html) which was introduced with 8.0.

**Expected behavior**
Allow to use `websockets>9`

**Environment (please complete the following information):**
 - OS: probably all
 - Version: current

**Additional context**
n/a

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sanic/websocket.py`
Content:
```
1 from typing import (
2     Any,
3     Awaitable,
4     Callable,
5     Dict,
6     List,
7     MutableMapping,
8     Optional,
9     Union,
10 )
11 
12 from httptools import HttpParserUpgrade  # type: ignore
13 from websockets import (  # type: ignore
14     ConnectionClosed,
15     InvalidHandshake,
16     WebSocketCommonProtocol,
17     handshake,
18 )
19 
20 from sanic.exceptions import InvalidUsage
21 from sanic.server import HttpProtocol
22 
23 
24 __all__ = ["ConnectionClosed", "WebSocketProtocol", "WebSocketConnection"]
25 
26 ASIMessage = MutableMapping[str, Any]
27 
28 
29 class WebSocketProtocol(HttpProtocol):
30     def __init__(
31         self,
32         *args,
33         websocket_timeout=10,
34         websocket_max_size=None,
35         websocket_max_queue=None,
36         websocket_read_limit=2 ** 16,
37         websocket_write_limit=2 ** 16,
38         websocket_ping_interval=20,
39         websocket_ping_timeout=20,
40         **kwargs
41     ):
42         super().__init__(*args, **kwargs)
43         self.websocket = None
44         # self.app = None
45         self.websocket_timeout = websocket_timeout
46         self.websocket_max_size = websocket_max_size
47         self.websocket_max_queue = websocket_max_queue
48         self.websocket_read_limit = websocket_read_limit
49         self.websocket_write_limit = websocket_write_limit
50         self.websocket_ping_interval = websocket_ping_interval
51         self.websocket_ping_timeout = websocket_ping_timeout
52 
53     # timeouts make no sense for websocket routes
54     def request_timeout_callback(self):
55         if self.websocket is None:
56             super().request_timeout_callback()
57 
58     def response_timeout_callback(self):
59         if self.websocket is None:
60             super().response_timeout_callback()
61 
62     def keep_alive_timeout_callback(self):
63         if self.websocket is None:
64             super().keep_alive_timeout_callback()
65 
66     def connection_lost(self, exc):
67         if self.websocket is not None:
68             self.websocket.connection_lost(exc)
69         super().connection_lost(exc)
70 
71     def data_received(self, data):
72         if self.websocket is not None:
73             # pass the data to the websocket protocol
74             self.websocket.data_received(data)
75         else:
76             try:
77                 super().data_received(data)
78             except HttpParserUpgrade:
79                 # this is okay, it just indicates we've got an upgrade request
80                 pass
81 
82     def write_response(self, response):
83         if self.websocket is not None:
84             # websocket requests do not write a response
85             self.transport.close()
86         else:
87             super().write_response(response)
88 
89     async def websocket_handshake(self, request, subprotocols=None):
90         # let the websockets package do the handshake with the client
91         headers = {}
92 
93         try:
94             key = handshake.check_request(request.headers)
95             handshake.build_response(headers, key)
96         except InvalidHandshake:
97             raise InvalidUsage("Invalid websocket request")
98 
99         subprotocol = None
100         if subprotocols and "Sec-Websocket-Protocol" in request.headers:
101             # select a subprotocol
102             client_subprotocols = [
103                 p.strip()
104                 for p in request.headers["Sec-Websocket-Protocol"].split(",")
105             ]
106             for p in client_subprotocols:
107                 if p in subprotocols:
108                     subprotocol = p
109                     headers["Sec-Websocket-Protocol"] = subprotocol
110                     break
111 
112         # write the 101 response back to the client
113         rv = b"HTTP/1.1 101 Switching Protocols\r\n"
114         for k, v in headers.items():
115             rv += k.encode("utf-8") + b": " + v.encode("utf-8") + b"\r\n"
116         rv += b"\r\n"
117         request.transport.write(rv)
118 
119         # hook up the websocket protocol
120         self.websocket = WebSocketCommonProtocol(
121             close_timeout=self.websocket_timeout,
122             max_size=self.websocket_max_size,
123             max_queue=self.websocket_max_queue,
124             read_limit=self.websocket_read_limit,
125             write_limit=self.websocket_write_limit,
126             ping_interval=self.websocket_ping_interval,
127             ping_timeout=self.websocket_ping_timeout,
128         )
129         # Following two lines are required for websockets 8.x
130         self.websocket.is_client = False
131         self.websocket.side = "server"
132         self.websocket.subprotocol = subprotocol
133         self.websocket.connection_made(request.transport)
134         self.websocket.connection_open()
135         return self.websocket
136 
137 
138 class WebSocketConnection:
139 
140     # TODO
141     # - Implement ping/pong
142 
143     def __init__(
144         self,
145         send: Callable[[ASIMessage], Awaitable[None]],
146         receive: Callable[[], Awaitable[ASIMessage]],
147         subprotocols: Optional[List[str]] = None,
148     ) -> None:
149         self._send = send
150         self._receive = receive
151         self.subprotocols = subprotocols or []
152 
153     async def send(self, data: Union[str, bytes], *args, **kwargs) -> None:
154         message: Dict[str, Union[str, bytes]] = {"type": "websocket.send"}
155 
156         if isinstance(data, bytes):
157             message.update({"bytes": data})
158         else:
159             message.update({"text": str(data)})
160 
161         await self._send(message)
162 
163     async def recv(self, *args, **kwargs) -> Optional[str]:
164         message = await self._receive()
165 
166         if message["type"] == "websocket.receive":
167             return message["text"]
168         elif message["type"] == "websocket.disconnect":
169             pass
170 
171         return None
172 
173     receive = recv
174 
175     async def accept(self) -> None:
176         await self._send(
177             {
178                 "type": "websocket.accept",
179                 "subprotocol": ",".join(list(self.subprotocols)),
180             }
181         )
182 
183     async def close(self) -> None:
184         pass
185
```

Path: `setup.py`
Content:
```
1 """
2 Sanic
3 """
4 import codecs
5 import os
6 import re
7 import sys
8 
9 from distutils.util import strtobool
10 
11 from setuptools import find_packages, setup
12 from setuptools.command.test import test as TestCommand
13 
14 
15 class PyTest(TestCommand):
16     """
17     Provide a Test runner to be used from setup.py to run unit tests
18     """
19 
20     user_options = [("pytest-args=", "a", "Arguments to pass to pytest")]
21 
22     def initialize_options(self):
23         TestCommand.initialize_options(self)
24         self.pytest_args = ""
25 
26     def run_tests(self):
27         import shlex
28 
29         import pytest
30 
31         errno = pytest.main(shlex.split(self.pytest_args))
32         sys.exit(errno)
33 
34 
35 def open_local(paths, mode="r", encoding="utf8"):
36     path = os.path.join(os.path.abspath(os.path.dirname(__file__)), *paths)
37 
38     return codecs.open(path, mode, encoding)
39 
40 
41 with open_local(["sanic", "__version__.py"], encoding="latin1") as fp:
42     try:
43         version = re.findall(
44             r"^__version__ = \"([^']+)\"\r?$", fp.read(), re.M
45         )[0]
46     except IndexError:
47         raise RuntimeError("Unable to determine version.")
48 
49 with open_local(["README.rst"]) as rm:
50     long_description = rm.read()
51 
52 setup_kwargs = {
53     "name": "sanic",
54     "version": version,
55     "url": "http://github.com/sanic-org/sanic/",
56     "license": "MIT",
57     "author": "Sanic Community",
58     "author_email": "admhpkns@gmail.com",
59     "description": (
60         "A web server and web framework that's written to go fast. "
61         "Build fast. Run fast."
62     ),
63     "long_description": long_description,
64     "packages": find_packages(),
65     "package_data": {"sanic": ["py.typed"]},
66     "platforms": "any",
67     "python_requires": ">=3.7",
68     "classifiers": [
69         "Development Status :: 4 - Beta",
70         "Environment :: Web Environment",
71         "License :: OSI Approved :: MIT License",
72         "Programming Language :: Python :: 3.7",
73         "Programming Language :: Python :: 3.8",
74         "Programming Language :: Python :: 3.9",
75     ],
76     "entry_points": {"console_scripts": ["sanic = sanic.__main__:main"]},
77 }
78 
79 env_dependency = (
80     '; sys_platform != "win32" ' 'and implementation_name == "cpython"'
81 )
82 ujson = "ujson>=1.35" + env_dependency
83 uvloop = "uvloop>=0.5.3" + env_dependency
84 
85 requirements = [
86     "sanic-routing>=0.6.0",
87     "httptools>=0.0.10",
88     uvloop,
89     ujson,
90     "aiofiles>=0.6.0",
91     "websockets>=8.1,<9.0",
92     "multidict>=5.0,<6.0",
93 ]
94 
95 tests_require = [
96     "sanic-testing",
97     "pytest==5.2.1",
98     "multidict>=5.0,<6.0",
99     "gunicorn==20.0.4",
100     "pytest-cov",
101     "beautifulsoup4",
102     uvloop,
103     ujson,
104     "pytest-sanic",
105     "pytest-sugar",
106     "pytest-benchmark",
107 ]
108 
109 docs_require = [
110     "sphinx>=2.1.2",
111     "sphinx_rtd_theme",
112     "recommonmark>=0.5.0",
113     "docutils",
114     "pygments",
115 ]
116 
117 dev_require = tests_require + [
118     "aiofiles",
119     "tox",
120     "black",
121     "flake8",
122     "bandit",
123     "towncrier",
124 ]
125 
126 all_require = dev_require + docs_require
127 
128 if strtobool(os.environ.get("SANIC_NO_UJSON", "no")):
129     print("Installing without uJSON")
130     requirements.remove(ujson)
131     tests_require.remove(ujson)
132 
133 # 'nt' means windows OS
134 if strtobool(os.environ.get("SANIC_NO_UVLOOP", "no")):
135     print("Installing without uvLoop")
136     requirements.remove(uvloop)
137     tests_require.remove(uvloop)
138 
139 extras_require = {
140     "test": tests_require,
141     "dev": dev_require,
142     "docs": docs_require,
143     "all": all_require,
144 }
145 
146 setup_kwargs["install_requires"] = requirements
147 setup_kwargs["tests_require"] = tests_require
148 setup_kwargs["extras_require"] = extras_require
149 setup_kwargs["cmdclass"] = {"test": PyTest}
150 setup(**setup_kwargs)
151
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/sanic/websocket.py b/sanic/websocket.py
--- a/sanic/websocket.py
+++ b/sanic/websocket.py
@@ -14,9 +14,13 @@
     ConnectionClosed,
     InvalidHandshake,
     WebSocketCommonProtocol,
-    handshake,
 )
 
+# Despite the "legacy" namespace, the primary maintainer of websockets
+# committed to maintaining backwards-compatibility until 2026 and will
+# consider extending it if sanic continues depending on this module.
+from websockets.legacy import handshake
+
 from sanic.exceptions import InvalidUsage
 from sanic.server import HttpProtocol
 
@@ -126,7 +130,9 @@
             ping_interval=self.websocket_ping_interval,
             ping_timeout=self.websocket_ping_timeout,
         )
-        # Following two lines are required for websockets 8.x
+        # we use WebSocketCommonProtocol because we don't want the handshake
+        # logic from WebSocketServerProtocol; however, we must tell it that
+        # we're running on the server side
         self.websocket.is_client = False
         self.websocket.side = "server"
         self.websocket.subprotocol = subprotocol
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -88,12 +88,12 @@
     uvloop,
     ujson,
     "aiofiles>=0.6.0",
-    "websockets>=8.1,<9.0",
+    "websockets>=9.0",
     "multidict>=5.0,<6.0",
 ]
 
 tests_require = [
-    "sanic-testing",
+    "sanic-testing>=0.6.0",
     "pytest==5.2.1",
     "multidict>=5.0,<6.0",
     "gunicorn==20.0.4",

{"golden_diff": "diff --git a/sanic/websocket.py b/sanic/websocket.py\n--- a/sanic/websocket.py\n+++ b/sanic/websocket.py\n@@ -14,9 +14,13 @@\n     ConnectionClosed,\n     InvalidHandshake,\n     WebSocketCommonProtocol,\n-    handshake,\n )\n \n+# Despite the \"legacy\" namespace, the primary maintainer of websockets\n+# committed to maintaining backwards-compatibility until 2026 and will\n+# consider extending it if sanic continues depending on this module.\n+from websockets.legacy import handshake\n+\n from sanic.exceptions import InvalidUsage\n from sanic.server import HttpProtocol\n \n@@ -126,7 +130,9 @@\n             ping_interval=self.websocket_ping_interval,\n             ping_timeout=self.websocket_ping_timeout,\n         )\n-        # Following two lines are required for websockets 8.x\n+        # we use WebSocketCommonProtocol because we don't want the handshake\n+        # logic from WebSocketServerProtocol; however, we must tell it that\n+        # we're running on the server side\n         self.websocket.is_client = False\n         self.websocket.side = \"server\"\n         self.websocket.subprotocol = subprotocol\ndiff --git a/setup.py b/setup.py\n--- a/setup.py\n+++ b/setup.py\n@@ -88,12 +88,12 @@\n     uvloop,\n     ujson,\n     \"aiofiles>=0.6.0\",\n-    \"websockets>=8.1,<9.0\",\n+    \"websockets>=9.0\",\n     \"multidict>=5.0,<6.0\",\n ]\n \n tests_require = [\n-    \"sanic-testing\",\n+    \"sanic-testing>=0.6.0\",\n     \"pytest==5.2.1\",\n     \"multidict>=5.0,<6.0\",\n     \"gunicorn==20.0.4\",\n", "issue": "Allow later websockets releases\n**Describe the bug**\r\n`websockets` is [pinned](https://github.com/sanic-org/sanic/blob/main/setup.py#L91\r\n). The latest `websockets` is 9.1 and this release is fixing a [authentication vulnerability](https://websockets.readthedocs.io/en/stable/changelog.html) which was introduced with 8.0.\r\n\r\n**Expected behavior**\r\nAllow to use `websockets>9`\r\n\r\n**Environment (please complete the following information):**\r\n - OS: probably all\r\n - Version: current\r\n\r\n**Additional context**\r\nn/a\n", "before_files": [{"content": "from typing import (\n    Any,\n    Awaitable,\n    Callable,\n    Dict,\n    List,\n    MutableMapping,\n    Optional,\n    Union,\n)\n\nfrom httptools import HttpParserUpgrade  # type: ignore\nfrom websockets import (  # type: ignore\n    ConnectionClosed,\n    InvalidHandshake,\n    WebSocketCommonProtocol,\n    handshake,\n)\n\nfrom sanic.exceptions import InvalidUsage\nfrom sanic.server import HttpProtocol\n\n\n__all__ = [\"ConnectionClosed\", \"WebSocketProtocol\", \"WebSocketConnection\"]\n\nASIMessage = MutableMapping[str, Any]\n\n\nclass WebSocketProtocol(HttpProtocol):\n    def __init__(\n        self,\n        *args,\n        websocket_timeout=10,\n        websocket_max_size=None,\n        websocket_max_queue=None,\n        websocket_read_limit=2 ** 16,\n        websocket_write_limit=2 ** 16,\n        websocket_ping_interval=20,\n        websocket_ping_timeout=20,\n        **kwargs\n    ):\n        super().__init__(*args, **kwargs)\n        self.websocket = None\n        # self.app = None\n        self.websocket_timeout = websocket_timeout\n        self.websocket_max_size = websocket_max_size\n        self.websocket_max_queue = websocket_max_queue\n        self.websocket_read_limit = websocket_read_limit\n        self.websocket_write_limit = websocket_write_limit\n        self.websocket_ping_interval = websocket_ping_interval\n        self.websocket_ping_timeout = websocket_ping_timeout\n\n    # timeouts make no sense for websocket routes\n    def request_timeout_callback(self):\n        if self.websocket is None:\n            super().request_timeout_callback()\n\n    def response_timeout_callback(self):\n        if self.websocket is None:\n            super().response_timeout_callback()\n\n    def keep_alive_timeout_callback(self):\n        if self.websocket is None:\n            super().keep_alive_timeout_callback()\n\n    def connection_lost(self, exc):\n        if self.websocket is not None:\n            self.websocket.connection_lost(exc)\n        super().connection_lost(exc)\n\n    def data_received(self, data):\n        if self.websocket is not None:\n            # pass the data to the websocket protocol\n            self.websocket.data_received(data)\n        else:\n            try:\n                super().data_received(data)\n            except HttpParserUpgrade:\n                # this is okay, it just indicates we've got an upgrade request\n                pass\n\n    def write_response(self, response):\n        if self.websocket is not None:\n            # websocket requests do not write a response\n            self.transport.close()\n        else:\n            super().write_response(response)\n\n    async def websocket_handshake(self, request, subprotocols=None):\n        # let the websockets package do the handshake with the client\n        headers = {}\n\n        try:\n            key = handshake.check_request(request.headers)\n            handshake.build_response(headers, key)\n        except InvalidHandshake:\n            raise InvalidUsage(\"Invalid websocket request\")\n\n        subprotocol = None\n        if subprotocols and \"Sec-Websocket-Protocol\" in request.headers:\n            # select a subprotocol\n            client_subprotocols = [\n                p.strip()\n                for p in request.headers[\"Sec-Websocket-Protocol\"].split(\",\")\n            ]\n            for p in client_subprotocols:\n                if p in subprotocols:\n                    subprotocol = p\n                    headers[\"Sec-Websocket-Protocol\"] = subprotocol\n                    break\n\n        # write the 101 response back to the client\n        rv = b\"HTTP/1.1 101 Switching Protocols\\r\\n\"\n        for k, v in headers.items():\n            rv += k.encode(\"utf-8\") + b\": \" + v.encode(\"utf-8\") + b\"\\r\\n\"\n        rv += b\"\\r\\n\"\n        request.transport.write(rv)\n\n        # hook up the websocket protocol\n        self.websocket = WebSocketCommonProtocol(\n            close_timeout=self.websocket_timeout,\n            max_size=self.websocket_max_size,\n            max_queue=self.websocket_max_queue,\n            read_limit=self.websocket_read_limit,\n            write_limit=self.websocket_write_limit,\n            ping_interval=self.websocket_ping_interval,\n            ping_timeout=self.websocket_ping_timeout,\n        )\n        # Following two lines are required for websockets 8.x\n        self.websocket.is_client = False\n        self.websocket.side = \"server\"\n        self.websocket.subprotocol = subprotocol\n        self.websocket.connection_made(request.transport)\n        self.websocket.connection_open()\n        return self.websocket\n\n\nclass WebSocketConnection:\n\n    # TODO\n    # - Implement ping/pong\n\n    def __init__(\n        self,\n        send: Callable[[ASIMessage], Awaitable[None]],\n        receive: Callable[[], Awaitable[ASIMessage]],\n        subprotocols: Optional[List[str]] = None,\n    ) -> None:\n        self._send = send\n        self._receive = receive\n        self.subprotocols = subprotocols or []\n\n    async def send(self, data: Union[str, bytes], *args, **kwargs) -> None:\n        message: Dict[str, Union[str, bytes]] = {\"type\": \"websocket.send\"}\n\n        if isinstance(data, bytes):\n            message.update({\"bytes\": data})\n        else:\n            message.update({\"text\": str(data)})\n\n        await self._send(message)\n\n    async def recv(self, *args, **kwargs) -> Optional[str]:\n        message = await self._receive()\n\n        if message[\"type\"] == \"websocket.receive\":\n            return message[\"text\"]\n        elif message[\"type\"] == \"websocket.disconnect\":\n            pass\n\n        return None\n\n    receive = recv\n\n    async def accept(self) -> None:\n        await self._send(\n            {\n                \"type\": \"websocket.accept\",\n                \"subprotocol\": \",\".join(list(self.subprotocols)),\n            }\n        )\n\n    async def close(self) -> None:\n        pass\n", "path": "sanic/websocket.py"}, {"content": "\"\"\"\nSanic\n\"\"\"\nimport codecs\nimport os\nimport re\nimport sys\n\nfrom distutils.util import strtobool\n\nfrom setuptools import find_packages, setup\nfrom setuptools.command.test import test as TestCommand\n\n\nclass PyTest(TestCommand):\n    \"\"\"\n    Provide a Test runner to be used from setup.py to run unit tests\n    \"\"\"\n\n    user_options = [(\"pytest-args=\", \"a\", \"Arguments to pass to pytest\")]\n\n    def initialize_options(self):\n        TestCommand.initialize_options(self)\n        self.pytest_args = \"\"\n\n    def run_tests(self):\n        import shlex\n\n        import pytest\n\n        errno = pytest.main(shlex.split(self.pytest_args))\n        sys.exit(errno)\n\n\ndef open_local(paths, mode=\"r\", encoding=\"utf8\"):\n    path = os.path.join(os.path.abspath(os.path.dirname(__file__)), *paths)\n\n    return codecs.open(path, mode, encoding)\n\n\nwith open_local([\"sanic\", \"__version__.py\"], encoding=\"latin1\") as fp:\n    try:\n        version = re.findall(\n            r\"^__version__ = \\\"([^']+)\\\"\\r?$\", fp.read(), re.M\n        )[0]\n    except IndexError:\n        raise RuntimeError(\"Unable to determine version.\")\n\nwith open_local([\"README.rst\"]) as rm:\n    long_description = rm.read()\n\nsetup_kwargs = {\n    \"name\": \"sanic\",\n    \"version\": version,\n    \"url\": \"http://github.com/sanic-org/sanic/\",\n    \"license\": \"MIT\",\n    \"author\": \"Sanic Community\",\n    \"author_email\": \"admhpkns@gmail.com\",\n    \"description\": (\n        \"A web server and web framework that's written to go fast. \"\n        \"Build fast. Run fast.\"\n    ),\n    \"long_description\": long_description,\n    \"packages\": find_packages(),\n    \"package_data\": {\"sanic\": [\"py.typed\"]},\n    \"platforms\": \"any\",\n    \"python_requires\": \">=3.7\",\n    \"classifiers\": [\n        \"Development Status :: 4 - Beta\",\n        \"Environment :: Web Environment\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\",\n    ],\n    \"entry_points\": {\"console_scripts\": [\"sanic = sanic.__main__:main\"]},\n}\n\nenv_dependency = (\n    '; sys_platform != \"win32\" ' 'and implementation_name == \"cpython\"'\n)\nujson = \"ujson>=1.35\" + env_dependency\nuvloop = \"uvloop>=0.5.3\" + env_dependency\n\nrequirements = [\n    \"sanic-routing>=0.6.0\",\n    \"httptools>=0.0.10\",\n    uvloop,\n    ujson,\n    \"aiofiles>=0.6.0\",\n    \"websockets>=8.1,<9.0\",\n    \"multidict>=5.0,<6.0\",\n]\n\ntests_require = [\n    \"sanic-testing\",\n    \"pytest==5.2.1\",\n    \"multidict>=5.0,<6.0\",\n    \"gunicorn==20.0.4\",\n    \"pytest-cov\",\n    \"beautifulsoup4\",\n    uvloop,\n    ujson,\n    \"pytest-sanic\",\n    \"pytest-sugar\",\n    \"pytest-benchmark\",\n]\n\ndocs_require = [\n    \"sphinx>=2.1.2\",\n    \"sphinx_rtd_theme\",\n    \"recommonmark>=0.5.0\",\n    \"docutils\",\n    \"pygments\",\n]\n\ndev_require = tests_require + [\n    \"aiofiles\",\n    \"tox\",\n    \"black\",\n    \"flake8\",\n    \"bandit\",\n    \"towncrier\",\n]\n\nall_require = dev_require + docs_require\n\nif strtobool(os.environ.get(\"SANIC_NO_UJSON\", \"no\")):\n    print(\"Installing without uJSON\")\n    requirements.remove(ujson)\n    tests_require.remove(ujson)\n\n# 'nt' means windows OS\nif strtobool(os.environ.get(\"SANIC_NO_UVLOOP\", \"no\")):\n    print(\"Installing without uvLoop\")\n    requirements.remove(uvloop)\n    tests_require.remove(uvloop)\n\nextras_require = {\n    \"test\": tests_require,\n    \"dev\": dev_require,\n    \"docs\": docs_require,\n    \"all\": all_require,\n}\n\nsetup_kwargs[\"install_requires\"] = requirements\nsetup_kwargs[\"tests_require\"] = tests_require\nsetup_kwargs[\"extras_require\"] = extras_require\nsetup_kwargs[\"cmdclass\"] = {\"test\": PyTest}\nsetup(**setup_kwargs)\n", "path": "setup.py"}], "after_files": [{"content": "from typing import (\n    Any,\n    Awaitable,\n    Callable,\n    Dict,\n    List,\n    MutableMapping,\n    Optional,\n    Union,\n)\n\nfrom httptools import HttpParserUpgrade  # type: ignore\nfrom websockets import (  # type: ignore\n    ConnectionClosed,\n    InvalidHandshake,\n    WebSocketCommonProtocol,\n)\n\n# Despite the \"legacy\" namespace, the primary maintainer of websockets\n# committed to maintaining backwards-compatibility until 2026 and will\n# consider extending it if sanic continues depending on this module.\nfrom websockets.legacy import handshake\n\nfrom sanic.exceptions import InvalidUsage\nfrom sanic.server import HttpProtocol\n\n\n__all__ = [\"ConnectionClosed\", \"WebSocketProtocol\", \"WebSocketConnection\"]\n\nASIMessage = MutableMapping[str, Any]\n\n\nclass WebSocketProtocol(HttpProtocol):\n    def __init__(\n        self,\n        *args,\n        websocket_timeout=10,\n        websocket_max_size=None,\n        websocket_max_queue=None,\n        websocket_read_limit=2 ** 16,\n        websocket_write_limit=2 ** 16,\n        websocket_ping_interval=20,\n        websocket_ping_timeout=20,\n        **kwargs\n    ):\n        super().__init__(*args, **kwargs)\n        self.websocket = None\n        # self.app = None\n        self.websocket_timeout = websocket_timeout\n        self.websocket_max_size = websocket_max_size\n        self.websocket_max_queue = websocket_max_queue\n        self.websocket_read_limit = websocket_read_limit\n        self.websocket_write_limit = websocket_write_limit\n        self.websocket_ping_interval = websocket_ping_interval\n        self.websocket_ping_timeout = websocket_ping_timeout\n\n    # timeouts make no sense for websocket routes\n    def request_timeout_callback(self):\n        if self.websocket is None:\n            super().request_timeout_callback()\n\n    def response_timeout_callback(self):\n        if self.websocket is None:\n            super().response_timeout_callback()\n\n    def keep_alive_timeout_callback(self):\n        if self.websocket is None:\n            super().keep_alive_timeout_callback()\n\n    def connection_lost(self, exc):\n        if self.websocket is not None:\n            self.websocket.connection_lost(exc)\n        super().connection_lost(exc)\n\n    def data_received(self, data):\n        if self.websocket is not None:\n            # pass the data to the websocket protocol\n            self.websocket.data_received(data)\n        else:\n            try:\n                super().data_received(data)\n            except HttpParserUpgrade:\n                # this is okay, it just indicates we've got an upgrade request\n                pass\n\n    def write_response(self, response):\n        if self.websocket is not None:\n            # websocket requests do not write a response\n            self.transport.close()\n        else:\n            super().write_response(response)\n\n    async def websocket_handshake(self, request, subprotocols=None):\n        # let the websockets package do the handshake with the client\n        headers = {}\n\n        try:\n            key = handshake.check_request(request.headers)\n            handshake.build_response(headers, key)\n        except InvalidHandshake:\n            raise InvalidUsage(\"Invalid websocket request\")\n\n        subprotocol = None\n        if subprotocols and \"Sec-Websocket-Protocol\" in request.headers:\n            # select a subprotocol\n            client_subprotocols = [\n                p.strip()\n                for p in request.headers[\"Sec-Websocket-Protocol\"].split(\",\")\n            ]\n            for p in client_subprotocols:\n                if p in subprotocols:\n                    subprotocol = p\n                    headers[\"Sec-Websocket-Protocol\"] = subprotocol\n                    break\n\n        # write the 101 response back to the client\n        rv = b\"HTTP/1.1 101 Switching Protocols\\r\\n\"\n        for k, v in headers.items():\n            rv += k.encode(\"utf-8\") + b\": \" + v.encode(\"utf-8\") + b\"\\r\\n\"\n        rv += b\"\\r\\n\"\n        request.transport.write(rv)\n\n        # hook up the websocket protocol\n        self.websocket = WebSocketCommonProtocol(\n            close_timeout=self.websocket_timeout,\n            max_size=self.websocket_max_size,\n            max_queue=self.websocket_max_queue,\n            read_limit=self.websocket_read_limit,\n            write_limit=self.websocket_write_limit,\n            ping_interval=self.websocket_ping_interval,\n            ping_timeout=self.websocket_ping_timeout,\n        )\n        # we use WebSocketCommonProtocol because we don't want the handshake\n        # logic from WebSocketServerProtocol; however, we must tell it that\n        # we're running on the server side\n        self.websocket.is_client = False\n        self.websocket.side = \"server\"\n        self.websocket.subprotocol = subprotocol\n        self.websocket.connection_made(request.transport)\n        self.websocket.connection_open()\n        return self.websocket\n\n\nclass WebSocketConnection:\n\n    # TODO\n    # - Implement ping/pong\n\n    def __init__(\n        self,\n        send: Callable[[ASIMessage], Awaitable[None]],\n        receive: Callable[[], Awaitable[ASIMessage]],\n        subprotocols: Optional[List[str]] = None,\n    ) -> None:\n        self._send = send\n        self._receive = receive\n        self.subprotocols = subprotocols or []\n\n    async def send(self, data: Union[str, bytes], *args, **kwargs) -> None:\n        message: Dict[str, Union[str, bytes]] = {\"type\": \"websocket.send\"}\n\n        if isinstance(data, bytes):\n            message.update({\"bytes\": data})\n        else:\n            message.update({\"text\": str(data)})\n\n        await self._send(message)\n\n    async def recv(self, *args, **kwargs) -> Optional[str]:\n        message = await self._receive()\n\n        if message[\"type\"] == \"websocket.receive\":\n            return message[\"text\"]\n        elif message[\"type\"] == \"websocket.disconnect\":\n            pass\n\n        return None\n\n    receive = recv\n\n    async def accept(self) -> None:\n        await self._send(\n            {\n                \"type\": \"websocket.accept\",\n                \"subprotocol\": \",\".join(list(self.subprotocols)),\n            }\n        )\n\n    async def close(self) -> None:\n        pass\n", "path": "sanic/websocket.py"}, {"content": "\"\"\"\nSanic\n\"\"\"\nimport codecs\nimport os\nimport re\nimport sys\n\nfrom distutils.util import strtobool\n\nfrom setuptools import find_packages, setup\nfrom setuptools.command.test import test as TestCommand\n\n\nclass PyTest(TestCommand):\n    \"\"\"\n    Provide a Test runner to be used from setup.py to run unit tests\n    \"\"\"\n\n    user_options = [(\"pytest-args=\", \"a\", \"Arguments to pass to pytest\")]\n\n    def initialize_options(self):\n        TestCommand.initialize_options(self)\n        self.pytest_args = \"\"\n\n    def run_tests(self):\n        import shlex\n\n        import pytest\n\n        errno = pytest.main(shlex.split(self.pytest_args))\n        sys.exit(errno)\n\n\ndef open_local(paths, mode=\"r\", encoding=\"utf8\"):\n    path = os.path.join(os.path.abspath(os.path.dirname(__file__)), *paths)\n\n    return codecs.open(path, mode, encoding)\n\n\nwith open_local([\"sanic\", \"__version__.py\"], encoding=\"latin1\") as fp:\n    try:\n        version = re.findall(\n            r\"^__version__ = \\\"([^']+)\\\"\\r?$\", fp.read(), re.M\n        )[0]\n    except IndexError:\n        raise RuntimeError(\"Unable to determine version.\")\n\nwith open_local([\"README.rst\"]) as rm:\n    long_description = rm.read()\n\nsetup_kwargs = {\n    \"name\": \"sanic\",\n    \"version\": version,\n    \"url\": \"http://github.com/sanic-org/sanic/\",\n    \"license\": \"MIT\",\n    \"author\": \"Sanic Community\",\n    \"author_email\": \"admhpkns@gmail.com\",\n    \"description\": (\n        \"A web server and web framework that's written to go fast. \"\n        \"Build fast. Run fast.\"\n    ),\n    \"long_description\": long_description,\n    \"packages\": find_packages(),\n    \"package_data\": {\"sanic\": [\"py.typed\"]},\n    \"platforms\": \"any\",\n    \"python_requires\": \">=3.7\",\n    \"classifiers\": [\n        \"Development Status :: 4 - Beta\",\n        \"Environment :: Web Environment\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\",\n    ],\n    \"entry_points\": {\"console_scripts\": [\"sanic = sanic.__main__:main\"]},\n}\n\nenv_dependency = (\n    '; sys_platform != \"win32\" ' 'and implementation_name == \"cpython\"'\n)\nujson = \"ujson>=1.35\" + env_dependency\nuvloop = \"uvloop>=0.5.3\" + env_dependency\n\nrequirements = [\n    \"sanic-routing>=0.6.0\",\n    \"httptools>=0.0.10\",\n    uvloop,\n    ujson,\n    \"aiofiles>=0.6.0\",\n    \"websockets>=9.0\",\n    \"multidict>=5.0,<6.0\",\n]\n\ntests_require = [\n    \"sanic-testing>=0.6.0\",\n    \"pytest==5.2.1\",\n    \"multidict>=5.0,<6.0\",\n    \"gunicorn==20.0.4\",\n    \"pytest-cov\",\n    \"beautifulsoup4\",\n    uvloop,\n    ujson,\n    \"pytest-sanic\",\n    \"pytest-sugar\",\n    \"pytest-benchmark\",\n]\n\ndocs_require = [\n    \"sphinx>=2.1.2\",\n    \"sphinx_rtd_theme\",\n    \"recommonmark>=0.5.0\",\n    \"docutils\",\n    \"pygments\",\n]\n\ndev_require = tests_require + [\n    \"aiofiles\",\n    \"tox\",\n    \"black\",\n    \"flake8\",\n    \"bandit\",\n    \"towncrier\",\n]\n\nall_require = dev_require + docs_require\n\nif strtobool(os.environ.get(\"SANIC_NO_UJSON\", \"no\")):\n    print(\"Installing without uJSON\")\n    requirements.remove(ujson)\n    tests_require.remove(ujson)\n\n# 'nt' means windows OS\nif strtobool(os.environ.get(\"SANIC_NO_UVLOOP\", \"no\")):\n    print(\"Installing without uvLoop\")\n    requirements.remove(uvloop)\n    tests_require.remove(uvloop)\n\nextras_require = {\n    \"test\": tests_require,\n    \"dev\": dev_require,\n    \"docs\": docs_require,\n    \"all\": all_require,\n}\n\nsetup_kwargs[\"install_requires\"] = requirements\nsetup_kwargs[\"tests_require\"] = tests_require\nsetup_kwargs[\"extras_require\"] = extras_require\nsetup_kwargs[\"cmdclass\"] = {\"test\": PyTest}\nsetup(**setup_kwargs)\n", "path": "setup.py"}]}