django: a context bundle over the socket
The socket carried rpc but not ctx, so a socket transport could not fetch a context and every app needed an HTTP connection beside it. Dispatches through the same execute_context the HTTP view calls; that view's origin-side cache is a CDN concern with nothing in front of a socket, so it is not on this path. Both backends now answer subscribe/unsubscribe/message/rpc/ctx on one envelope. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ Protocol:
|
|||||||
|
|
||||||
# RPC calls (server functions)
|
# RPC calls (server functions)
|
||||||
{"action": "rpc", "id": "request-id", "fn": "function_name", "args": {...}}
|
{"action": "rpc", "id": "request-id", "fn": "function_name", "args": {...}}
|
||||||
|
{"action": "ctx", "id": "request-id", "context": "name", "params": {...}}
|
||||||
|
|
||||||
Server sends:
|
Server sends:
|
||||||
# Channel messages
|
# Channel messages
|
||||||
@@ -167,6 +168,8 @@ class DjangoReactConsumer(AsyncJsonWebsocketConsumer):
|
|||||||
await self._handle_message(content)
|
await self._handle_message(content)
|
||||||
elif action == "rpc":
|
elif action == "rpc":
|
||||||
await self._handle_rpc(content)
|
await self._handle_rpc(content)
|
||||||
|
elif action == "ctx":
|
||||||
|
await self._handle_ctx(content)
|
||||||
else:
|
else:
|
||||||
await self.send_json(
|
await self.send_json(
|
||||||
{
|
{
|
||||||
@@ -503,6 +506,61 @@ class DjangoReactConsumer(AsyncJsonWebsocketConsumer):
|
|||||||
|
|
||||||
await self.send_json({"id": request_id, "ok": True, "data": data})
|
await self.send_json({"id": request_id, "ok": True, "data": data})
|
||||||
|
|
||||||
|
async def _handle_ctx(self, content: dict):
|
||||||
|
"""
|
||||||
|
Handle a context-bundle fetch.
|
||||||
|
|
||||||
|
Protocol:
|
||||||
|
Request: {"action": "ctx", "id": "request-id", "context": "name", "params": {...}}
|
||||||
|
Response: {"id": "request-id", "ok": true, "data": {fn_name: result, ...}}
|
||||||
|
or: {"id": "request-id", "ok": false, "error": {...}}
|
||||||
|
|
||||||
|
Dispatches through the same execute_context the HTTP view calls. That view's
|
||||||
|
origin-side cache is a CDN concern with nothing in front of a socket, so it is not
|
||||||
|
part of this path.
|
||||||
|
"""
|
||||||
|
from mizan.client.executor import execute_context, FunctionError
|
||||||
|
|
||||||
|
request_id = content.get("id")
|
||||||
|
context_name = content.get("context")
|
||||||
|
|
||||||
|
if not request_id:
|
||||||
|
await self.send_json({"error": "ctx request missing 'id' field"})
|
||||||
|
return
|
||||||
|
|
||||||
|
if not context_name:
|
||||||
|
await self.send_json(
|
||||||
|
{
|
||||||
|
"id": request_id,
|
||||||
|
"ok": False,
|
||||||
|
"error": {"code": "BAD_REQUEST", "message": "Missing 'context' field"},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
ws_request = WebSocketRequest(
|
||||||
|
self.scope, channel_name=getattr(self, "channel_name", None)
|
||||||
|
)
|
||||||
|
result = await sync_to_async(execute_context, thread_sensitive=True)(
|
||||||
|
ws_request, context_name, content.get("params") or {}
|
||||||
|
)
|
||||||
|
|
||||||
|
if isinstance(result, FunctionError):
|
||||||
|
await self.send_json(
|
||||||
|
{
|
||||||
|
"id": request_id,
|
||||||
|
"ok": False,
|
||||||
|
"error": {
|
||||||
|
"code": result.code.value,
|
||||||
|
"message": result.message,
|
||||||
|
**({"details": result.details} if result.details else {}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
await self.send_json({"id": request_id, "ok": True, "data": result.data})
|
||||||
|
|
||||||
async def channel_message(self, event: dict):
|
async def channel_message(self, event: dict):
|
||||||
"""
|
"""
|
||||||
Handle messages broadcast to a group.
|
Handle messages broadcast to a group.
|
||||||
|
|||||||
Reference in New Issue
Block a user