59 lines
1.7 KiB
Python
59 lines
1.7 KiB
Python
|
|
"""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
|