Download validation/app/websocket/manager.py from indrajit4533/voiceguard: direct link, hf CLI and curl.
- Browser
- Download file 5.38 kB
-
https://huggingface.co/indrajit4533/voiceguard/resolve/main/validation/app/websocket/manager.py
- Command line
-
hf download hf://indrajit4533/voiceguard/validation/app/websocket/manager.py
-
curl -L -o manager.py https://huggingface.co/indrajit4533/voiceguard/resolve/main/validation/app/websocket/manager.py
5.38 kB
| import asyncio | |
| import logging | |
| import time | |
| from typing import Dict, Set, Optional | |
| from fastapi import WebSocket | |
| from dataclasses import dataclass, field | |
| from ..config import settings | |
| from .protocol import ( | |
| MessageType, | |
| ReadyMessage, | |
| StatsMessage, | |
| ) | |
| class ConnectionInfo: | |
| websocket: WebSocket | |
| connected_at_ms: int | |
| last_active_ms: int = 0 | |
| chunks_received: int = 0 | |
| bytes_received: int = 0 | |
| client_info: dict = field(default_factory=dict) | |
| class ConnectionManager: | |
| """Manages WebSocket connections with per-connection isolation.""" | |
| def __init__(self, max_connections: int = settings.max_connections): | |
| self._connections: Dict[str, ConnectionInfo] = {} | |
| self._active_ids: Set[str] = set() | |
| self._max_connections = max_connections | |
| self._logger = logging.getLogger(__name__) | |
| self._lock = asyncio.Lock() | |
| async def connect(self, websocket: WebSocket, connection_id: str, **kwargs) -> None: | |
| """Accept connection and register it.""" | |
| async with self._lock: | |
| if len(self._connections) >= self._max_connections: | |
| await websocket.close(code=1013, reason="Too many connections") | |
| raise RuntimeError(f"Max connections ({self._max_connections}) reached") | |
| await websocket.accept() | |
| info = ConnectionInfo( | |
| websocket=websocket, | |
| connected_at_ms=int(time.time() * 1000), | |
| client_info=kwargs, | |
| ) | |
| self._connections[connection_id] = info | |
| self._active_ids.add(connection_id) | |
| self._logger.info(f"Connection established: {connection_id}") | |
| async def disconnect(self, connection_id: str) -> None: | |
| """Remove connection and cleanup resources.""" | |
| async with self._lock: | |
| self._connections.pop(connection_id, None) | |
| self._active_ids.discard(connection_id) | |
| self._logger.info(f"Connection closed: {connection_id}") | |
| def is_active(self, connection_id: str) -> bool: | |
| return connection_id in self._connections | |
| def get_connection(self, connection_id: str) -> Optional[ConnectionInfo]: | |
| return self._connections.get(connection_id) | |
| def touch(self, connection_id: str, n_chunks: int = 0, n_bytes: int = 0) -> None: | |
| """Update connection activity timestamps and counters.""" | |
| info = self._connections.get(connection_id) | |
| if info: | |
| info.last_active_ms = int(time.time() * 1000) | |
| info.chunks_received += n_chunks | |
| info.bytes_received += n_bytes | |
| async def send_text(self, connection_id: str, message: str) -> bool: | |
| """Send text message to a specific connection.""" | |
| info = self._connections.get(connection_id) | |
| if not info: | |
| return False | |
| try: | |
| await info.websocket.send_text(message) | |
| return True | |
| except Exception as e: | |
| self._logger.error(f"Failed to send to {connection_id}: {e}") | |
| return False | |
| async def send_json(self, connection_id: str, message: dict) -> bool: | |
| """Send JSON message to a specific connection.""" | |
| info = self._connections.get(connection_id) | |
| if not info: | |
| return False | |
| try: | |
| await info.websocket.send_json(message) | |
| return True | |
| except Exception as e: | |
| self._logger.error(f"Failed to send JSON to {connection_id}: {e}") | |
| return False | |
| async def broadcast_json(self, message: dict) -> int: | |
| """Broadcast message to all connections. Returns count of successes.""" | |
| successes = 0 | |
| for cid in list(self._connections.keys()): | |
| if await self.send_json(cid, message): | |
| successes += 1 | |
| return successes | |
| def get_active_connections(self) -> Dict[str, ConnectionInfo]: | |
| return dict(self._connections) | |
| def get_connection_count(self) -> int: | |
| return len(self._connections) | |
| def get_connection_summary(self) -> dict: | |
| """Get summary of all connections for monitoring.""" | |
| now = int(time.time() * 1000) | |
| return { | |
| cid: { | |
| "connected_at_ms": info.connected_at_ms, | |
| "uptime_ms": now - info.connected_at_ms, | |
| "last_active_ms": info.last_active_ms, | |
| "chunks_received": info.chunks_received, | |
| "bytes_received": info.bytes_received, | |
| } | |
| for cid, info in self._connections.items() | |
| } | |
| async def close_all(self) -> None: | |
| """Close all connections gracefully.""" | |
| for cid in list(self._connections.keys()): | |
| try: | |
| await self._connections[cid].websocket.close(code=1001, reason="Server shutting down") | |
| except Exception: | |
| pass | |
| self._connections.clear() | |
| self._active_ids.clear() | |
| async def session_cleanup(self) -> None: | |
| """Periodic cleanup of inactive connections.""" | |
| now = int(time.time() * 1000) | |
| for cid in list(self._connections.keys()): | |
| info = self._connections[cid] | |
| if now - info.last_active_ms > settings.ws_ping_timeout * 1000 * 2: | |
| self._logger.warning(f"Cleaning up inactive connection: {cid}") | |
| await self.disconnect(cid) |