Skip to content

WebSocketManager Controller (DotNet DLL Worker)

A WorkManager worker written in C# that loads as a pre-compiled .NET DLL and controls WebSocket connections through the WebSocketManager gRPC service.

Topic: command.websocketmanagercontroller Group: websocketmanagercontroller MIME type: application/x-dotnet-dll

Architecture

Command Event (CloudEvent on command.websocketmanagercontroller)
Dapr Pubsub → WorkManager → DotNetDllEngine → Process(CloudEvent)
Parses command from CloudEvent.Data
Calls WSM gRPC methods via Virtufin.Api.Client
Returns response CloudEvent (topic: response.websocketmanagercontroller)

Building

cd dotnet
dotnet build Virtufin.Examples.WebSocketManagerController/Virtufin.Examples.WebSocketManagerController.csproj -c Release

dotnet publish produces just the worker's own DLL; the PackageWorker MSBuild target packages it into a worker.nupkg alongside a nuspec declaring the worker's PackageReferences as <dependencies> (not vendored). NuGetDependencyInstaller (virtufin-workmanager) resolves and downloads those declared dependencies itself at worker-load time.

Two assemblies never come from that download, regardless of what's declared:

Assembly Reason
CloudNative.CloudEvents.dll Aliased to the WorkManager's own already-loaded copy, for type identity
Virtufin.Worker.DevKit.dll Aliased to the WorkManager's own already-loaded copy (the IWorker base class DotNetDllEngine loads against)

Running

Quick start (one command)

# Build and package (worker.zip created automatically by PackageWorker target)
dotnet publish dotnet/Virtufin.Examples.WebSocketManagerController/ -c Release -o /tmp/out

# Deploy and test
WORKER_B64=$(base64 -i /tmp/out/worker.zip | tr -d '\n')
curl -X POST http://localhost:25001/v1/workers \
  -H "Content-Type: application/json" \
  -d "{
    \"code_source\": {\"content\": \"$WORKER_B64\"},
    \"mime_type\": \"application/x-dotnet-dll\",
    \"topic\": \"command.websocketmanagercontroller\",
    \"group\": \"websocketmanagercontroller\"
  }" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])" | xargs -I{} curl -X POST http://localhost:25001/v1/workers/{}/start

Manual deployment

# Build and package (worker.zip created automatically)
dotnet publish dotnet/Virtufin.Examples.WebSocketManagerController/ -c Release -o /tmp/out

# Deploy worker
WORKER_B64=$(base64 -i /tmp/out/worker.zip | tr -d '\n')
curl -X POST http://localhost:25001/v1/workers \
  -H "Content-Type: application/json" \
  -d "{
    \"code_source\": {\"content\": \"$WORKER_B64\"},
    \"mime_type\": \"application/x-dotnet-dll\",
    \"topic\": \"command.websocketmanagercontroller\",
    \"group\": \"websocketmanagercontroller\"
  }"

# Start worker (replace ID from create response)
curl -X POST http://localhost:25001/v1/workers/{id}/start

# Send command via pub/sub
curl -X POST "http://localhost:5001/v1/pubsub/command.websocketmanagercontroller" \
  -H "Content-Type: application/json" \
  -d "{\"data\":\"$(echo -n '{\\\"command\\\":\\\"list\\\"}' | base64)\"}"

Commands

Command Input Action
create {"url": "wss://stream.binance.com:9443/stream", "auto_reconnect": true, "host": "localhost:5002"} Create WebSocket connection
subscribe {"id": "<ws-id>", "params": ["btcusdt@depth@100ms"]} Subscribe to Binance streams
unsubscribe {"id": "<ws-id>", "params": ["btcusdt@aggTrade"]} Unsubscribe from streams
publish {"id": "<ws-id>", "topic": "btcusdt-depth"} Start publishing WS messages to Dapr topic
stop-publish {"id": "<ws-id>"} Stop publishing
list {} List all WebSocket connections
disconnect {"id": "<ws-id>"} Disconnect a WebSocket
stop {} Disconnect all WebSockets

Response

All commands return a response.websocketmanagercontroller CloudEvent:

{"command": "create", "success": true, "id": "connection-uuid", "message": "connected"}