orchestrata_ultimate.cxp.ws_server
Reference › orchestrata_ultimate › cxp › ws_server
WSServer is a WebSocket server for CXP, an alternative to the cxpt direct transport that the rest of the system uses. No other module in the project imports it, and it is separate from the gateway's own WebSocket API. WSServerConfig holds host, port, maximum message size, and ping interval and timeout. CXPWebSocketHandler serves each connection: _process_message parses JSON text and dispatches on a type field. send_packet calls _handle_send_packet, subscribe calls _handle_subscribe, acknowledge calls _handle_ack, and ping gets a pong. An unknown type, invalid JSON or an exception produces an error message. WSServer wraps the handler: start begins serving with the configured limits, broadcast sends a packet to connected clients, stop shuts down, and serve_forever blocks. The matching client is ws_client. See cxp.
At a glance
| Source | src/orchestrata_ultimate/cxp/ws_server.py |
| Lines of code | 236 |
| Classes | 3 |
| Functions | 0 |
| Imports (internal) | none |
| Imported by | none |
| Tests that import it | tests/cxp/test_websocket.py |
Class diagram
Dependency diagram
No internal imports and no importers.
API
Classes
WSServerConfig (dataclass)
Decorators: dataclass · Line 30
Configuration for CXP WebSocket server.
| Attribute | Type | Default |
|---|---|---|
host |
str |
'0.0.0.0' |
port |
int |
8765 |
max_size |
int |
10 * 1024 * 1024 |
ping_interval |
int |
30 |
ping_timeout |
int |
10 |
CXPWebSocketHandler (dataclass)
Decorators: dataclass · Line 41
Handler for CXP WebSocket messages.
| Attribute | Type | Default |
|---|---|---|
packet_handler |
Callable[[dict[str, Any]], Any] | None |
None |
subscriptions |
dict[str, set[str]] |
field(default_factory=dict) |
| Method | Signature | Summary |
|---|---|---|
handle |
async handle(websocket: ServerConnection, path: str = '/') -> None |
Handle WebSocket connection. |
_process_message |
async _process_message(websocket: ServerConnection, message: str | bytes, client_id: str) -> None |
Process incoming WebSocket message. |
_handle_send_packet |
async _handle_send_packet(websocket: ServerConnection, data: dict) -> None |
Handle send_packet message. |
_handle_subscribe |
async _handle_subscribe(websocket: ServerConnection, data: dict, client_id: str) -> None |
Handle subscribe message. |
_handle_ack |
async _handle_ack(websocket: ServerConnection, data: dict) -> None |
Handle acknowledge message. |
WSServer
Line 182
CXP WebSocket Server.
| Method | Signature | Summary |
|---|---|---|
__init__ |
__init__(config: WSServerConfig | None = None, packet_handler: Callable[[dict[str, Any]], Any] | None = None) -> None |
|
start |
async start() -> None |
Start the WebSocket server. |
stop |
async stop() -> None |
Stop the WebSocket server. |
broadcast |
async broadcast(packet: dict[str, Any]) -> None |
Broadcast packet to all connected clients. |
serve_forever |
async serve_forever() -> None |
Run the server forever. |
Source
# mypy: ignore-errors
"""WebSocket Server for CXP Protocol.
Provides a WebSocket-based transport layer for CXP packets,
enabling real-time inter-Volk communication.
Usage:
server = WSServer(host="0.0.0.0", port=8765)
await server.start()
await server.serve_forever()
"""
import asyncio
import json
import logging
import uuid
from collections.abc import Callable
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any
import websockets
if TYPE_CHECKING:
from websockets.asyncio.server import Server, ServerConnection
logger = logging.getLogger(__name__)
@dataclass
class WSServerConfig:
"""Configuration for CXP WebSocket server."""
host: str = "0.0.0.0"
port: int = 8765
max_size: int = 10 * 1024 * 1024 # 10MB
ping_interval: int = 30
ping_timeout: int = 10
@dataclass
class CXPWebSocketHandler:
"""Handler for CXP WebSocket messages."""
packet_handler: Callable[[dict[str, Any]], Any] | None = None
subscriptions: dict[str, set[str]] = field(default_factory=dict)
async def handle(self, websocket: ServerConnection, path: str = "/") -> None:
"""Handle WebSocket connection."""
client_id = str(uuid.uuid4())
logger.info(
f"Client {client_id} connected from {websocket.remote_address} to path {path}"
)
try:
await websocket.send(
json.dumps(
{
"type": "connected",
"client_id": client_id,
}
)
)
async for message in websocket:
await self._process_message(websocket, message, client_id)
except websockets.exceptions.ConnectionClosed:
logger.info(f"Client {client_id} disconnected")
except Exception as e:
logger.error(f"Error handling client {client_id}: {e}")
finally:
if client_id in self.subscriptions:
del self.subscriptions[client_id]
async def _process_message(
self,
websocket: ServerConnection,
message: str | bytes,
client_id: str,
) -> None:
"""Process incoming WebSocket message."""
try:
data = json.loads(message)
msg_type = data.get("type")
if msg_type == "send_packet":
await self._handle_send_packet(websocket, data)
elif msg_type == "subscribe":
await self._handle_subscribe(websocket, data, client_id)
elif msg_type == "acknowledge":
await self._handle_ack(websocket, data)
elif msg_type == "ping":
await websocket.send(json.dumps({"type": "pong"}))
else:
await websocket.send(
json.dumps(
{
"type": "error",
"message": f"Unknown message type: {msg_type}",
}
)
)
except json.JSONDecodeError:
await websocket.send(
json.dumps(
{
"type": "error",
"message": "Invalid JSON",
}
)
)
except Exception as e:
await websocket.send(
json.dumps(
{
"type": "error",
"message": str(e),
}
)
)
async def _handle_send_packet(
self, websocket: ServerConnection, data: dict
) -> None:
"""Handle send_packet message."""
packet = data.get("packet", {})
if self.packet_handler:
handler_result = self.packet_handler(packet)
if asyncio.iscoroutine(handler_result) or hasattr(
handler_result, "__await__"
):
result = await handler_result
else:
result = handler_result
else:
result = {"success": True}
await websocket.send(
json.dumps(
{
"type": "packet_sent",
"message_id": packet.get("message_id"),
"success": result.get("success", True),
"error": result.get("error", ""),
}
)
)
async def _handle_subscribe(
self,
websocket: ServerConnection,
data: dict,
client_id: str,
) -> None:
"""Handle subscribe message."""
subjects = data.get("subjects", [])
self.subscriptions[client_id] = set(subjects)
await websocket.send(
json.dumps(
{
"type": "subscribed",
"subjects": subjects,
}
)
)
async def _handle_ack(self, websocket: ServerConnection, data: dict) -> None:
"""Handle acknowledge message."""
await websocket.send(
json.dumps(
{
"type": "acknowledged",
"message_id": data.get("message_id"),
}
)
)
class WSServer:
"""CXP WebSocket Server."""
def __init__(
self,
config: WSServerConfig | None = None,
packet_handler: Callable[[dict[str, Any]], Any] | None = None,
) -> None:
self._config = config or WSServerConfig()
self._handler = CXPWebSocketHandler(packet_handler=packet_handler)
self._server: Server | None = None
async def start(self) -> None:
"""Start the WebSocket server."""
async def handler_wrapper(websocket: ServerConnection, path: str = "/") -> None:
await self._handler.handle(websocket, path)
self._server = await websockets.serve(
handler_wrapper,
self._config.host,
self._config.port,
max_size=self._config.max_size,
ping_interval=self._config.ping_interval,
ping_timeout=self._config.ping_timeout,
)
logger.info(
f"CXP WebSocket server started on {self._config.host}:{self._config.port}"
)
async def stop(self) -> None:
"""Stop the WebSocket server."""
if self._server:
self._server.close()
await self._server.wait_closed()
logger.info("CXP WebSocket server stopped")
async def broadcast(self, packet: dict[str, Any]) -> None:
"""Broadcast packet to all connected clients."""
if not self._server:
return
message = json.dumps(
{
"type": "packet",
"packet": packet,
}
)
for ws in self._server.connections:
await ws.send(message)
async def serve_forever(self) -> None:
"""Run the server forever."""
await asyncio.Future()