We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
WebSocket headers encoding problem
### Checklist

<!-- Please make sure you check all these items before submitting your bug report. -->

- [x] The bug is reproducible against the latest release and/or `master`.
- [x] There are no similar issues or pull requests to fix it yet.

### Describe the bug

<!-- A clear and concise description of what the bug is. -->
If there is a websocket connection with a non-ascii char in header, the server will explode. The problem seems to be from here:
https://github.com/encode/uvicorn/blob/9dc5a43209fe081ba3e74135189252bfddf75587/uvicorn/protocols/websockets/websockets_impl.py#L102
Also, I checked the [ASGI doc](https://buildmedia.readthedocs.org/media/pdf/asgi/latest/asgi.pdf) and in section 2.2.5 it states:
- These are byte strings of the exact byte sequences sent by the client/to be sent by the server. While
modern HTTP standards say that headers should be ASCII, older ones did not and allowed a wider range of
characters. Frameworks/applications should decode headers as they deem appropriate.

So, I think uvicorn should allow for non-ascii chars in header. It is definitely the only reason that we can't use uvicorn in production yet.


### To reproduce

<!-- Provide a *minimal* example with steps to reproduce the bug locally.
Run a websocket server. Then do a ws request with non-unicode header to it.
```bash
websocat -H "X-Test: Abraão" ws://localhost:5000/ws
```

NOTE: try to keep any external dependencies *at an absolute minimum* .
In other words, remove anything that doesn't make the bug go away.

-->

### Expected behavior

<!-- A clear and concise description of what you expected to happen. -->
Doesn't explode.

### Actual behavior

<!-- A clear and concise description of what actually happens. -->
Explodes with an exception.

### Debugging material

<!-- Any tracebacks, screenshots, etc. that can help understanding the problem.

NOTE:
- Please list tracebacks in full (don't truncate them).
- If relevant, consider turning on DEBUG or TRACE logs for additional details (see the Logging section on https://www.uvicorn.org/settings/ specifically the `log-level` flag).
- Consider using `<details>` to make tracebacks/logs collapsible if they're very large (see https://gist.github.com/ericclemmons/b146fe5da72ca1f706b2ef72a20ac39d).
-->

### Environment

- OS / Python / Uvicorn version: just run `uvicorn --version`
- The exact command you're running uvicorn with, all flags you passed included. If you run it with gunicorn please do the same. If there is a reverse-proxy involved and you cannot reproduce without it please give the minimal config of it to reproduce.

### Additional context

<!-- Any additional information that can help understanding the problem.

Eg. linked issues, or a description of what you were trying to achieve. -->

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `uvicorn/protocols/websockets/websockets_impl.py`
Content:
```
1 import asyncio
2 import http
3 import logging
4 from urllib.parse import unquote
5 
6 import websockets
7 from websockets.extensions.permessage_deflate import ServerPerMessageDeflateFactory
8 
9 from uvicorn.protocols.utils import get_local_addr, get_remote_addr, is_ssl
10 
11 
12 class Server:
13     closing = False
14 
15     def register(self, ws):
16         pass
17 
18     def unregister(self, ws):
19         pass
20 
21     def is_serving(self):
22         return not self.closing
23 
24 
25 class WebSocketProtocol(websockets.WebSocketServerProtocol):
26     def __init__(self, config, server_state, _loop=None):
27         if not config.loaded:
28             config.load()
29 
30         self.config = config
31         self.app = config.loaded_app
32         self.loop = _loop or asyncio.get_event_loop()
33         self.logger = logging.getLogger("uvicorn.error")
34         self.root_path = config.root_path
35 
36         # Shared server state
37         self.connections = server_state.connections
38         self.tasks = server_state.tasks
39 
40         # Connection state
41         self.transport = None
42         self.server = None
43         self.client = None
44         self.scheme = None
45 
46         # Connection events
47         self.scope = None
48         self.handshake_started_event = asyncio.Event()
49         self.handshake_completed_event = asyncio.Event()
50         self.closed_event = asyncio.Event()
51         self.initial_response = None
52         self.connect_sent = False
53         self.accepted_subprotocol = None
54         self.transfer_data_task = None
55 
56         self.ws_server = Server()
57 
58         super().__init__(
59             ws_handler=self.ws_handler,
60             ws_server=self.ws_server,
61             extensions=[ServerPerMessageDeflateFactory()],
62         )
63 
64     def connection_made(self, transport):
65         self.connections.add(self)
66         self.transport = transport
67         self.server = get_local_addr(transport)
68         self.client = get_remote_addr(transport)
69         self.scheme = "wss" if is_ssl(transport) else "ws"
70         super().connection_made(transport)
71 
72     def connection_lost(self, exc):
73         self.connections.remove(self)
74         self.handshake_completed_event.set()
75         super().connection_lost(exc)
76 
77     def shutdown(self):
78         self.ws_server.closing = True
79         self.transport.close()
80 
81     def on_task_complete(self, task):
82         self.tasks.discard(task)
83 
84     async def process_request(self, path, headers):
85         """
86         This hook is called to determine if the websocket should return
87         an HTTP response and close.
88 
89         Our behavior here is to start the ASGI application, and then wait
90         for either `accept` or `close` in order to determine if we should
91         close the connection.
92         """
93         path_portion, _, query_string = path.partition("?")
94 
95         websockets.handshake.check_request(headers)
96 
97         subprotocols = []
98         for header in headers.get_all("Sec-WebSocket-Protocol"):
99             subprotocols.extend([token.strip() for token in header.split(",")])
100 
101         asgi_headers = [
102             (name.encode("ascii"), value.encode("ascii"))
103             for name, value in headers.raw_items()
104         ]
105 
106         self.scope = {
107             "type": "websocket",
108             "asgi": {"version": self.config.asgi_version, "spec_version": "2.1"},
109             "scheme": self.scheme,
110             "server": self.server,
111             "client": self.client,
112             "root_path": self.root_path,
113             "path": unquote(path_portion),
114             "raw_path": path_portion,
115             "query_string": query_string.encode("ascii"),
116             "headers": asgi_headers,
117             "subprotocols": subprotocols,
118         }
119         task = self.loop.create_task(self.run_asgi())
120         task.add_done_callback(self.on_task_complete)
121         self.tasks.add(task)
122         await self.handshake_started_event.wait()
123         return self.initial_response
124 
125     def process_subprotocol(self, headers, available_subprotocols):
126         """
127         We override the standard 'process_subprotocol' behavior here so that
128         we return whatever subprotocol is sent in the 'accept' message.
129         """
130         return self.accepted_subprotocol
131 
132     def send_500_response(self):
133         msg = b"Internal Server Error"
134         content = [
135             b"HTTP/1.1 500 Internal Server Error\r\n"
136             b"content-type: text/plain; charset=utf-8\r\n",
137             b"content-length: " + str(len(msg)).encode("ascii") + b"\r\n",
138             b"connection: close\r\n",
139             b"\r\n",
140             msg,
141         ]
142         self.transport.write(b"".join(content))
143         # Allow handler task to terminate cleanly, as websockets doesn't cancel it by
144         # itself (see https://github.com/encode/uvicorn/issues/920)
145         self.handshake_started_event.set()
146 
147     async def ws_handler(self, protocol, path):
148         """
149         This is the main handler function for the 'websockets' implementation
150         to call into. We just wait for close then return, and instead allow
151         'send' and 'receive' events to drive the flow.
152         """
153         self.handshake_completed_event.set()
154         await self.closed_event.wait()
155 
156     async def run_asgi(self):
157         """
158         Wrapper around the ASGI callable, handling exceptions and unexpected
159         termination states.
160         """
161         try:
162             result = await self.app(self.scope, self.asgi_receive, self.asgi_send)
163         except BaseException as exc:
164             self.closed_event.set()
165             msg = "Exception in ASGI application\n"
166             self.logger.error(msg, exc_info=exc)
167             if not self.handshake_started_event.is_set():
168                 self.send_500_response()
169             else:
170                 await self.handshake_completed_event.wait()
171             self.transport.close()
172         else:
173             self.closed_event.set()
174             if not self.handshake_started_event.is_set():
175                 msg = "ASGI callable returned without sending handshake."
176                 self.logger.error(msg)
177                 self.send_500_response()
178                 self.transport.close()
179             elif result is not None:
180                 msg = "ASGI callable should return None, but returned '%s'."
181                 self.logger.error(msg, result)
182                 await self.handshake_completed_event.wait()
183                 self.transport.close()
184 
185     async def asgi_send(self, message):
186         message_type = message["type"]
187 
188         if not self.handshake_started_event.is_set():
189             if message_type == "websocket.accept":
190                 self.logger.info(
191                     '%s - "WebSocket %s" [accepted]',
192                     self.scope["client"],
193                     self.scope["root_path"] + self.scope["path"],
194                 )
195                 self.initial_response = None
196                 self.accepted_subprotocol = message.get("subprotocol")
197                 self.handshake_started_event.set()
198 
199             elif message_type == "websocket.close":
200                 self.logger.info(
201                     '%s - "WebSocket %s" 403',
202                     self.scope["client"],
203                     self.scope["root_path"] + self.scope["path"],
204                 )
205                 self.initial_response = (http.HTTPStatus.FORBIDDEN, [], b"")
206                 self.handshake_started_event.set()
207                 self.closed_event.set()
208 
209             else:
210                 msg = (
211                     "Expected ASGI message 'websocket.accept' or 'websocket.close', "
212                     "but got '%s'."
213                 )
214                 raise RuntimeError(msg % message_type)
215 
216         elif not self.closed_event.is_set():
217             await self.handshake_completed_event.wait()
218 
219             if message_type == "websocket.send":
220                 bytes_data = message.get("bytes")
221                 text_data = message.get("text")
222                 data = text_data if bytes_data is None else bytes_data
223                 await self.send(data)
224 
225             elif message_type == "websocket.close":
226                 code = message.get("code", 1000)
227                 reason = message.get("reason", "")
228                 await self.close(code, reason)
229                 self.closed_event.set()
230 
231             else:
232                 msg = (
233                     "Expected ASGI message 'websocket.send' or 'websocket.close',"
234                     " but got '%s'."
235                 )
236                 raise RuntimeError(msg % message_type)
237 
238         else:
239             msg = "Unexpected ASGI message '%s', after sending 'websocket.close'."
240             raise RuntimeError(msg % message_type)
241 
242     async def asgi_receive(self):
243         if not self.connect_sent:
244             self.connect_sent = True
245             return {"type": "websocket.connect"}
246 
247         await self.handshake_completed_event.wait()
248         try:
249             await self.ensure_open()
250             data = await self.recv()
251         except websockets.ConnectionClosed as exc:
252             return {"type": "websocket.disconnect", "code": exc.code}
253 
254         msg = {"type": "websocket.receive"}
255 
256         if isinstance(data, str):
257             msg["text"] = data
258         else:
259             msg["bytes"] = data
260 
261         return msg
262
```
--- 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/uvicorn/protocols/websockets/websockets_impl.py b/uvicorn/protocols/websockets/websockets_impl.py
--- a/uvicorn/protocols/websockets/websockets_impl.py
+++ b/uvicorn/protocols/websockets/websockets_impl.py
@@ -99,7 +99,7 @@
             subprotocols.extend([token.strip() for token in header.split(",")])
 
         asgi_headers = [
-            (name.encode("ascii"), value.encode("ascii"))
+            (name.encode("ascii"), value.encode("ascii", errors="surrogateescape"))
             for name, value in headers.raw_items()
         ]

{"golden_diff": "diff --git a/uvicorn/protocols/websockets/websockets_impl.py b/uvicorn/protocols/websockets/websockets_impl.py\n--- a/uvicorn/protocols/websockets/websockets_impl.py\n+++ b/uvicorn/protocols/websockets/websockets_impl.py\n@@ -99,7 +99,7 @@\n             subprotocols.extend([token.strip() for token in header.split(\",\")])\n \n         asgi_headers = [\n-            (name.encode(\"ascii\"), value.encode(\"ascii\"))\n+            (name.encode(\"ascii\"), value.encode(\"ascii\", errors=\"surrogateescape\"))\n             for name, value in headers.raw_items()\n         ]\n", "issue": "WebSocket headers encoding problem\n### Checklist\r\n\r\n<!-- Please make sure you check all these items before submitting your bug report. -->\r\n\r\n- [x] The bug is reproducible against the latest release and/or `master`.\r\n- [x] There are no similar issues or pull requests to fix it yet.\r\n\r\n### Describe the bug\r\n\r\n<!-- A clear and concise description of what the bug is. -->\r\nIf there is a websocket connection with a non-ascii char in header, the server will explode. The problem seems to be from here:\r\nhttps://github.com/encode/uvicorn/blob/9dc5a43209fe081ba3e74135189252bfddf75587/uvicorn/protocols/websockets/websockets_impl.py#L102\r\nAlso, I checked the [ASGI doc](https://buildmedia.readthedocs.org/media/pdf/asgi/latest/asgi.pdf) and in section 2.2.5 it states:\r\n- These are byte strings of the exact byte sequences sent by the client/to be sent by the server. While\r\nmodern HTTP standards say that headers should be ASCII, older ones did not and allowed a wider range of\r\ncharacters. Frameworks/applications should decode headers as they deem appropriate.\r\n\r\nSo, I think uvicorn should allow for non-ascii chars in header. It is definitely the only reason that we can't use uvicorn in production yet.\r\n\r\n\r\n### To reproduce\r\n\r\n<!-- Provide a *minimal* example with steps to reproduce the bug locally.\r\nRun a websocket server. Then do a ws request with non-unicode header to it.\r\n```bash\r\nwebsocat -H \"X-Test: Abra\u00e3o\" ws://localhost:5000/ws\r\n```\r\n\r\nNOTE: try to keep any external dependencies *at an absolute minimum* .\r\nIn other words, remove anything that doesn't make the bug go away.\r\n\r\n-->\r\n\r\n### Expected behavior\r\n\r\n<!-- A clear and concise description of what you expected to happen. -->\r\nDoesn't explode.\r\n\r\n### Actual behavior\r\n\r\n<!-- A clear and concise description of what actually happens. -->\r\nExplodes with an exception.\r\n\r\n### Debugging material\r\n\r\n<!-- Any tracebacks, screenshots, etc. that can help understanding the problem.\r\n\r\nNOTE:\r\n- Please list tracebacks in full (don't truncate them).\r\n- If relevant, consider turning on DEBUG or TRACE logs for additional details (see the Logging section on https://www.uvicorn.org/settings/ specifically the `log-level` flag).\r\n- Consider using `<details>` to make tracebacks/logs collapsible if they're very large (see https://gist.github.com/ericclemmons/b146fe5da72ca1f706b2ef72a20ac39d).\r\n-->\r\n\r\n### Environment\r\n\r\n- OS / Python / Uvicorn version: just run `uvicorn --version`\r\n- The exact command you're running uvicorn with, all flags you passed included. If you run it with gunicorn please do the same. If there is a reverse-proxy involved and you cannot reproduce without it please give the minimal config of it to reproduce.\r\n\r\n### Additional context\r\n\r\n<!-- Any additional information that can help understanding the problem.\r\n\r\nEg. linked issues, or a description of what you were trying to achieve. -->\n", "before_files": [{"content": "import asyncio\nimport http\nimport logging\nfrom urllib.parse import unquote\n\nimport websockets\nfrom websockets.extensions.permessage_deflate import ServerPerMessageDeflateFactory\n\nfrom uvicorn.protocols.utils import get_local_addr, get_remote_addr, is_ssl\n\n\nclass Server:\n    closing = False\n\n    def register(self, ws):\n        pass\n\n    def unregister(self, ws):\n        pass\n\n    def is_serving(self):\n        return not self.closing\n\n\nclass WebSocketProtocol(websockets.WebSocketServerProtocol):\n    def __init__(self, config, server_state, _loop=None):\n        if not config.loaded:\n            config.load()\n\n        self.config = config\n        self.app = config.loaded_app\n        self.loop = _loop or asyncio.get_event_loop()\n        self.logger = logging.getLogger(\"uvicorn.error\")\n        self.root_path = config.root_path\n\n        # Shared server state\n        self.connections = server_state.connections\n        self.tasks = server_state.tasks\n\n        # Connection state\n        self.transport = None\n        self.server = None\n        self.client = None\n        self.scheme = None\n\n        # Connection events\n        self.scope = None\n        self.handshake_started_event = asyncio.Event()\n        self.handshake_completed_event = asyncio.Event()\n        self.closed_event = asyncio.Event()\n        self.initial_response = None\n        self.connect_sent = False\n        self.accepted_subprotocol = None\n        self.transfer_data_task = None\n\n        self.ws_server = Server()\n\n        super().__init__(\n            ws_handler=self.ws_handler,\n            ws_server=self.ws_server,\n            extensions=[ServerPerMessageDeflateFactory()],\n        )\n\n    def connection_made(self, transport):\n        self.connections.add(self)\n        self.transport = transport\n        self.server = get_local_addr(transport)\n        self.client = get_remote_addr(transport)\n        self.scheme = \"wss\" if is_ssl(transport) else \"ws\"\n        super().connection_made(transport)\n\n    def connection_lost(self, exc):\n        self.connections.remove(self)\n        self.handshake_completed_event.set()\n        super().connection_lost(exc)\n\n    def shutdown(self):\n        self.ws_server.closing = True\n        self.transport.close()\n\n    def on_task_complete(self, task):\n        self.tasks.discard(task)\n\n    async def process_request(self, path, headers):\n        \"\"\"\n        This hook is called to determine if the websocket should return\n        an HTTP response and close.\n\n        Our behavior here is to start the ASGI application, and then wait\n        for either `accept` or `close` in order to determine if we should\n        close the connection.\n        \"\"\"\n        path_portion, _, query_string = path.partition(\"?\")\n\n        websockets.handshake.check_request(headers)\n\n        subprotocols = []\n        for header in headers.get_all(\"Sec-WebSocket-Protocol\"):\n            subprotocols.extend([token.strip() for token in header.split(\",\")])\n\n        asgi_headers = [\n            (name.encode(\"ascii\"), value.encode(\"ascii\"))\n            for name, value in headers.raw_items()\n        ]\n\n        self.scope = {\n            \"type\": \"websocket\",\n            \"asgi\": {\"version\": self.config.asgi_version, \"spec_version\": \"2.1\"},\n            \"scheme\": self.scheme,\n            \"server\": self.server,\n            \"client\": self.client,\n            \"root_path\": self.root_path,\n            \"path\": unquote(path_portion),\n            \"raw_path\": path_portion,\n            \"query_string\": query_string.encode(\"ascii\"),\n            \"headers\": asgi_headers,\n            \"subprotocols\": subprotocols,\n        }\n        task = self.loop.create_task(self.run_asgi())\n        task.add_done_callback(self.on_task_complete)\n        self.tasks.add(task)\n        await self.handshake_started_event.wait()\n        return self.initial_response\n\n    def process_subprotocol(self, headers, available_subprotocols):\n        \"\"\"\n        We override the standard 'process_subprotocol' behavior here so that\n        we return whatever subprotocol is sent in the 'accept' message.\n        \"\"\"\n        return self.accepted_subprotocol\n\n    def send_500_response(self):\n        msg = b\"Internal Server Error\"\n        content = [\n            b\"HTTP/1.1 500 Internal Server Error\\r\\n\"\n            b\"content-type: text/plain; charset=utf-8\\r\\n\",\n            b\"content-length: \" + str(len(msg)).encode(\"ascii\") + b\"\\r\\n\",\n            b\"connection: close\\r\\n\",\n            b\"\\r\\n\",\n            msg,\n        ]\n        self.transport.write(b\"\".join(content))\n        # Allow handler task to terminate cleanly, as websockets doesn't cancel it by\n        # itself (see https://github.com/encode/uvicorn/issues/920)\n        self.handshake_started_event.set()\n\n    async def ws_handler(self, protocol, path):\n        \"\"\"\n        This is the main handler function for the 'websockets' implementation\n        to call into. We just wait for close then return, and instead allow\n        'send' and 'receive' events to drive the flow.\n        \"\"\"\n        self.handshake_completed_event.set()\n        await self.closed_event.wait()\n\n    async def run_asgi(self):\n        \"\"\"\n        Wrapper around the ASGI callable, handling exceptions and unexpected\n        termination states.\n        \"\"\"\n        try:\n            result = await self.app(self.scope, self.asgi_receive, self.asgi_send)\n        except BaseException as exc:\n            self.closed_event.set()\n            msg = \"Exception in ASGI application\\n\"\n            self.logger.error(msg, exc_info=exc)\n            if not self.handshake_started_event.is_set():\n                self.send_500_response()\n            else:\n                await self.handshake_completed_event.wait()\n            self.transport.close()\n        else:\n            self.closed_event.set()\n            if not self.handshake_started_event.is_set():\n                msg = \"ASGI callable returned without sending handshake.\"\n                self.logger.error(msg)\n                self.send_500_response()\n                self.transport.close()\n            elif result is not None:\n                msg = \"ASGI callable should return None, but returned '%s'.\"\n                self.logger.error(msg, result)\n                await self.handshake_completed_event.wait()\n                self.transport.close()\n\n    async def asgi_send(self, message):\n        message_type = message[\"type\"]\n\n        if not self.handshake_started_event.is_set():\n            if message_type == \"websocket.accept\":\n                self.logger.info(\n                    '%s - \"WebSocket %s\" [accepted]',\n                    self.scope[\"client\"],\n                    self.scope[\"root_path\"] + self.scope[\"path\"],\n                )\n                self.initial_response = None\n                self.accepted_subprotocol = message.get(\"subprotocol\")\n                self.handshake_started_event.set()\n\n            elif message_type == \"websocket.close\":\n                self.logger.info(\n                    '%s - \"WebSocket %s\" 403',\n                    self.scope[\"client\"],\n                    self.scope[\"root_path\"] + self.scope[\"path\"],\n                )\n                self.initial_response = (http.HTTPStatus.FORBIDDEN, [], b\"\")\n                self.handshake_started_event.set()\n                self.closed_event.set()\n\n            else:\n                msg = (\n                    \"Expected ASGI message 'websocket.accept' or 'websocket.close', \"\n                    \"but got '%s'.\"\n                )\n                raise RuntimeError(msg % message_type)\n\n        elif not self.closed_event.is_set():\n            await self.handshake_completed_event.wait()\n\n            if message_type == \"websocket.send\":\n                bytes_data = message.get(\"bytes\")\n                text_data = message.get(\"text\")\n                data = text_data if bytes_data is None else bytes_data\n                await self.send(data)\n\n            elif message_type == \"websocket.close\":\n                code = message.get(\"code\", 1000)\n                reason = message.get(\"reason\", \"\")\n                await self.close(code, reason)\n                self.closed_event.set()\n\n            else:\n                msg = (\n                    \"Expected ASGI message 'websocket.send' or 'websocket.close',\"\n                    \" but got '%s'.\"\n                )\n                raise RuntimeError(msg % message_type)\n\n        else:\n            msg = \"Unexpected ASGI message '%s', after sending 'websocket.close'.\"\n            raise RuntimeError(msg % message_type)\n\n    async def asgi_receive(self):\n        if not self.connect_sent:\n            self.connect_sent = True\n            return {\"type\": \"websocket.connect\"}\n\n        await self.handshake_completed_event.wait()\n        try:\n            await self.ensure_open()\n            data = await self.recv()\n        except websockets.ConnectionClosed as exc:\n            return {\"type\": \"websocket.disconnect\", \"code\": exc.code}\n\n        msg = {\"type\": \"websocket.receive\"}\n\n        if isinstance(data, str):\n            msg[\"text\"] = data\n        else:\n            msg[\"bytes\"] = data\n\n        return msg\n", "path": "uvicorn/protocols/websockets/websockets_impl.py"}], "after_files": [{"content": "import asyncio\nimport http\nimport logging\nfrom urllib.parse import unquote\n\nimport websockets\nfrom websockets.extensions.permessage_deflate import ServerPerMessageDeflateFactory\n\nfrom uvicorn.protocols.utils import get_local_addr, get_remote_addr, is_ssl\n\n\nclass Server:\n    closing = False\n\n    def register(self, ws):\n        pass\n\n    def unregister(self, ws):\n        pass\n\n    def is_serving(self):\n        return not self.closing\n\n\nclass WebSocketProtocol(websockets.WebSocketServerProtocol):\n    def __init__(self, config, server_state, _loop=None):\n        if not config.loaded:\n            config.load()\n\n        self.config = config\n        self.app = config.loaded_app\n        self.loop = _loop or asyncio.get_event_loop()\n        self.logger = logging.getLogger(\"uvicorn.error\")\n        self.root_path = config.root_path\n\n        # Shared server state\n        self.connections = server_state.connections\n        self.tasks = server_state.tasks\n\n        # Connection state\n        self.transport = None\n        self.server = None\n        self.client = None\n        self.scheme = None\n\n        # Connection events\n        self.scope = None\n        self.handshake_started_event = asyncio.Event()\n        self.handshake_completed_event = asyncio.Event()\n        self.closed_event = asyncio.Event()\n        self.initial_response = None\n        self.connect_sent = False\n        self.accepted_subprotocol = None\n        self.transfer_data_task = None\n\n        self.ws_server = Server()\n\n        super().__init__(\n            ws_handler=self.ws_handler,\n            ws_server=self.ws_server,\n            extensions=[ServerPerMessageDeflateFactory()],\n        )\n\n    def connection_made(self, transport):\n        self.connections.add(self)\n        self.transport = transport\n        self.server = get_local_addr(transport)\n        self.client = get_remote_addr(transport)\n        self.scheme = \"wss\" if is_ssl(transport) else \"ws\"\n        super().connection_made(transport)\n\n    def connection_lost(self, exc):\n        self.connections.remove(self)\n        self.handshake_completed_event.set()\n        super().connection_lost(exc)\n\n    def shutdown(self):\n        self.ws_server.closing = True\n        self.transport.close()\n\n    def on_task_complete(self, task):\n        self.tasks.discard(task)\n\n    async def process_request(self, path, headers):\n        \"\"\"\n        This hook is called to determine if the websocket should return\n        an HTTP response and close.\n\n        Our behavior here is to start the ASGI application, and then wait\n        for either `accept` or `close` in order to determine if we should\n        close the connection.\n        \"\"\"\n        path_portion, _, query_string = path.partition(\"?\")\n\n        websockets.handshake.check_request(headers)\n\n        subprotocols = []\n        for header in headers.get_all(\"Sec-WebSocket-Protocol\"):\n            subprotocols.extend([token.strip() for token in header.split(\",\")])\n\n        asgi_headers = [\n            (name.encode(\"ascii\"), value.encode(\"ascii\", errors=\"surrogateescape\"))\n            for name, value in headers.raw_items()\n        ]\n\n        self.scope = {\n            \"type\": \"websocket\",\n            \"asgi\": {\"version\": self.config.asgi_version, \"spec_version\": \"2.1\"},\n            \"scheme\": self.scheme,\n            \"server\": self.server,\n            \"client\": self.client,\n            \"root_path\": self.root_path,\n            \"path\": unquote(path_portion),\n            \"raw_path\": path_portion,\n            \"query_string\": query_string.encode(\"ascii\"),\n            \"headers\": asgi_headers,\n            \"subprotocols\": subprotocols,\n        }\n        task = self.loop.create_task(self.run_asgi())\n        task.add_done_callback(self.on_task_complete)\n        self.tasks.add(task)\n        await self.handshake_started_event.wait()\n        return self.initial_response\n\n    def process_subprotocol(self, headers, available_subprotocols):\n        \"\"\"\n        We override the standard 'process_subprotocol' behavior here so that\n        we return whatever subprotocol is sent in the 'accept' message.\n        \"\"\"\n        return self.accepted_subprotocol\n\n    def send_500_response(self):\n        msg = b\"Internal Server Error\"\n        content = [\n            b\"HTTP/1.1 500 Internal Server Error\\r\\n\"\n            b\"content-type: text/plain; charset=utf-8\\r\\n\",\n            b\"content-length: \" + str(len(msg)).encode(\"ascii\") + b\"\\r\\n\",\n            b\"connection: close\\r\\n\",\n            b\"\\r\\n\",\n            msg,\n        ]\n        self.transport.write(b\"\".join(content))\n        # Allow handler task to terminate cleanly, as websockets doesn't cancel it by\n        # itself (see https://github.com/encode/uvicorn/issues/920)\n        self.handshake_started_event.set()\n\n    async def ws_handler(self, protocol, path):\n        \"\"\"\n        This is the main handler function for the 'websockets' implementation\n        to call into. We just wait for close then return, and instead allow\n        'send' and 'receive' events to drive the flow.\n        \"\"\"\n        self.handshake_completed_event.set()\n        await self.closed_event.wait()\n\n    async def run_asgi(self):\n        \"\"\"\n        Wrapper around the ASGI callable, handling exceptions and unexpected\n        termination states.\n        \"\"\"\n        try:\n            result = await self.app(self.scope, self.asgi_receive, self.asgi_send)\n        except BaseException as exc:\n            self.closed_event.set()\n            msg = \"Exception in ASGI application\\n\"\n            self.logger.error(msg, exc_info=exc)\n            if not self.handshake_started_event.is_set():\n                self.send_500_response()\n            else:\n                await self.handshake_completed_event.wait()\n            self.transport.close()\n        else:\n            self.closed_event.set()\n            if not self.handshake_started_event.is_set():\n                msg = \"ASGI callable returned without sending handshake.\"\n                self.logger.error(msg)\n                self.send_500_response()\n                self.transport.close()\n            elif result is not None:\n                msg = \"ASGI callable should return None, but returned '%s'.\"\n                self.logger.error(msg, result)\n                await self.handshake_completed_event.wait()\n                self.transport.close()\n\n    async def asgi_send(self, message):\n        message_type = message[\"type\"]\n\n        if not self.handshake_started_event.is_set():\n            if message_type == \"websocket.accept\":\n                self.logger.info(\n                    '%s - \"WebSocket %s\" [accepted]',\n                    self.scope[\"client\"],\n                    self.scope[\"root_path\"] + self.scope[\"path\"],\n                )\n                self.initial_response = None\n                self.accepted_subprotocol = message.get(\"subprotocol\")\n                self.handshake_started_event.set()\n\n            elif message_type == \"websocket.close\":\n                self.logger.info(\n                    '%s - \"WebSocket %s\" 403',\n                    self.scope[\"client\"],\n                    self.scope[\"root_path\"] + self.scope[\"path\"],\n                )\n                self.initial_response = (http.HTTPStatus.FORBIDDEN, [], b\"\")\n                self.handshake_started_event.set()\n                self.closed_event.set()\n\n            else:\n                msg = (\n                    \"Expected ASGI message 'websocket.accept' or 'websocket.close', \"\n                    \"but got '%s'.\"\n                )\n                raise RuntimeError(msg % message_type)\n\n        elif not self.closed_event.is_set():\n            await self.handshake_completed_event.wait()\n\n            if message_type == \"websocket.send\":\n                bytes_data = message.get(\"bytes\")\n                text_data = message.get(\"text\")\n                data = text_data if bytes_data is None else bytes_data\n                await self.send(data)\n\n            elif message_type == \"websocket.close\":\n                code = message.get(\"code\", 1000)\n                reason = message.get(\"reason\", \"\")\n                await self.close(code, reason)\n                self.closed_event.set()\n\n            else:\n                msg = (\n                    \"Expected ASGI message 'websocket.send' or 'websocket.close',\"\n                    \" but got '%s'.\"\n                )\n                raise RuntimeError(msg % message_type)\n\n        else:\n            msg = \"Unexpected ASGI message '%s', after sending 'websocket.close'.\"\n            raise RuntimeError(msg % message_type)\n\n    async def asgi_receive(self):\n        if not self.connect_sent:\n            self.connect_sent = True\n            return {\"type\": \"websocket.connect\"}\n\n        await self.handshake_completed_event.wait()\n        try:\n            await self.ensure_open()\n            data = await self.recv()\n        except websockets.ConnectionClosed as exc:\n            return {\"type\": \"websocket.disconnect\", \"code\": exc.code}\n\n        msg = {\"type\": \"websocket.receive\"}\n\n        if isinstance(data, str):\n            msg[\"text\"] = data\n        else:\n            msg[\"bytes\"] = data\n\n        return msg\n", "path": "uvicorn/protocols/websockets/websockets_impl.py"}]}