"""Async WebSocket ↔ guacd TCP bridge (Guacamole protocol relay).""" from __future__ import annotations import asyncio import logging from fastapi import WebSocket, WebSocketDisconnect logger = logging.getLogger("nexus.guacamole.tunnel") async def bridge_websocket_to_guacd( websocket: WebSocket, guacd_host: str, guacd_port: int, ) -> None: """Relay Guacamole instructions between browser and guacd.""" reader: asyncio.StreamReader writer: asyncio.StreamWriter reader, writer = await asyncio.open_connection(guacd_host, guacd_port) async def ws_to_guacd() -> None: try: while True: data = await websocket.receive_text() writer.write(data.encode("utf-8")) await writer.drain() except WebSocketDisconnect: pass except Exception as exc: logger.debug("RDP WS→guacd ended: %s", exc) async def guacd_to_ws() -> None: try: while True: chunk = await reader.read(8192) if not chunk: break await websocket.send_text(chunk.decode("utf-8", errors="replace")) except Exception as exc: logger.debug("RDP guacd→WS ended: %s", exc) ws_task = asyncio.create_task(ws_to_guacd()) guacd_task = asyncio.create_task(guacd_to_ws()) try: await asyncio.wait( [ws_task, guacd_task], return_when=asyncio.FIRST_COMPLETED, ) finally: for task in (ws_task, guacd_task): task.cancel() writer.close() try: await writer.wait_closed() except Exception: pass