| 177 | |
| 178 | |
| 179 | class ASGIWebsocketConnection: |
| 180 | def __init__(self, app: Quart, scope: WebsocketScope) -> None: |
| 181 | self.app = app |
| 182 | self.scope = scope |
| 183 | self.queue: asyncio.Queue = asyncio.Queue() |
| 184 | self._accepted = False |
| 185 | self._closed = False |
| 186 | |
| 187 | async def __call__( |
| 188 | self, receive: ASGIReceiveCallable, send: ASGISendCallable |
| 189 | ) -> None: |
| 190 | websocket = self._create_websocket_from_scope(send) |
| 191 | receiver_task = asyncio.ensure_future(self.handle_messages(receive)) |
| 192 | handler_task = asyncio.ensure_future(self.handle_websocket(websocket, send)) |
| 193 | done, pending = await asyncio.wait( |
| 194 | [handler_task, receiver_task], return_when=asyncio.FIRST_COMPLETED |
| 195 | ) |
| 196 | await cancel_tasks(pending) |
| 197 | raise_task_exceptions(done) |
| 198 | |
| 199 | async def handle_messages(self, receive: ASGIReceiveCallable) -> None: |
| 200 | while True: |
| 201 | event = await receive() |
| 202 | if event["type"] == "websocket.receive": |
| 203 | message = event.get("bytes") or event["text"] |
| 204 | await websocket_received.send_async(message) |
| 205 | await self.queue.put(message) |
| 206 | elif event["type"] == "websocket.disconnect": |
| 207 | return |
| 208 | |
| 209 | def _create_websocket_from_scope(self, send: ASGISendCallable) -> Websocket: |
| 210 | headers = Headers() |
| 211 | headers["Remote-Addr"] = (self.scope.get("client") or ["<local>"])[0] |
| 212 | for name, value in self.scope["headers"]: |
| 213 | headers.add(name.decode("latin1").title(), value.decode("latin1")) |
| 214 | |
| 215 | path = self.scope["path"] |
| 216 | path = path if path[0] == "/" else urlparse(path).path |
| 217 | root_path = self.scope.get("root_path", "") |
| 218 | if root_path != "": |
| 219 | try: |
| 220 | path = path.split(root_path, 1)[1] |
| 221 | path = " " if path == "" else path |
| 222 | except IndexError: |
| 223 | path = " " # Invalid in paths, hence will result in 404 |
| 224 | |
| 225 | return self.app.websocket_class( |
| 226 | path, |
| 227 | self.scope["query_string"], |
| 228 | self.scope["scheme"], |
| 229 | headers, |
| 230 | self.scope.get("root_path", ""), |
| 231 | self.scope.get("http_version", "1.1"), |
| 232 | list(self.scope.get("subprotocols", [])), |
| 233 | self.queue.get, |
| 234 | partial(self.send_data, send), |
| 235 | partial(self.accept_connection, send), |
| 236 | partial(self.close_connection, send), |
no outgoing calls
searching dependent graphs…