Skip to content

WebSocket-to-TCP Proxy ​

Connect browsers to native Cap'n Proto services (C++, etc.) via WebSocket.

Overview ​

Browsers can only use WebSocket, while native Cap'n Proto services use raw TCP. This proxy bridges the two:

Browser ──WebSocket──→ Proxy ──TCP──→ C++ Cap'n Proto Service

Quick Start ​

1. Start Your Cap'n Proto Service ​

For example, a C++ service on port 7000:

bash
./my-cpp-capnp-server --port 7000

2. Start the Proxy ​

bash
npx @naeemo/capnp proxy -p 9000 -t localhost:7000

Options:

  • -p, --ws-port: WebSocket server port (default: 8080)
  • -t, --target: Target TCP service (default: localhost:8081)
  • -d, --debug: Enable debug logging

3. Connect from Browser ​

typescript
import { RpcConnection, WebSocketTransport } from '@naeemo/capnp';
import { MyService } from './my-service.js';

// Connect via proxy
const transport = await WebSocketTransport.connect('ws://localhost:9000');
const connection = new RpcConnection(transport);

// Use the service as if directly connected
const service = await connection.bootstrap().getAs(MyService);
const result = await service.myMethod({ data: 'hello' });

Programmatic Usage ​

typescript
import { CapnpWebSocketProxy } from '@naeemo/capnp';

const proxy = new CapnpWebSocketProxy({
  wsPort: 9000,
  targetHost: 'localhost',
  targetPort: 7000,
  maxMessageSize: 16 * 1024 * 1024, // 16MB
  connectionTimeout: 30000, // 30 seconds
  debug: true,
});

// Monitor connections
proxy.on('connection', (conn) => {
  console.log('New connection');
});

proxy.on('disconnection', (conn) => {
  console.log('Connection closed');
  console.log('Stats:', conn.getStats());
});

// Shutdown
await proxy.close();

Architecture ​

Message Flow ​

  1. Browser → Proxy: WebSocket binary message
  2. Proxy → TCP Service: Raw bytes (no modification)
  3. TCP Service → Proxy: Raw bytes
  4. Proxy → Browser: WebSocket binary message

Protocol Compatibility ​

FeatureBrowserProxyTCP Service
TransportWebSocketBridgeTCP
FramingWebSocket framesPassthroughCap'n Proto messages
Binary✅✅✅
Full-duplex✅✅✅

Use Cases ​

Web Dashboard for Native Service ​

typescript
// Browser: Real-time metrics from C++ backend
const transport = await WebSocketTransport.connect('ws://proxy:9000');
const metrics = await connection.bootstrap().getAs(MetricsService);

// Stream real-time data
for await (const update of metrics.subscribe()) {
  updateChart(update);
}

Cross-Language RPC ​

typescript
// Browser (TypeScript) calling Python service
// Browser → Proxy → Python Cap'n Proto Server
const transport = await WebSocketTransport.connect('ws://proxy:9000');
const pythonService = await connection.bootstrap().getAs(PythonService);

Development & Debugging ​

bash
# Proxy local C++ service for browser dev tools
npx @naeemo/capnp proxy -p 9000 -t localhost:7000 --debug

Performance ​

MetricValue
Latency overhead~1-2ms
Throughput900+ MB/s
Concurrent connections1000+
Message size limitConfigurable (default 16MB)

Security Considerations ​

  1. CORS: Proxy accepts connections from any origin. Deploy behind a reverse proxy (nginx, etc.) for production.

  2. TLS: Use WSS (WebSocket Secure) in production:

    Browser ──WSS──→ nginx ──WS──→ Proxy ──TCP──→ Service
  3. Authentication: Implement auth at the application layer (Cap'n Proto capabilities) or use a token in the WebSocket URL.

Troubleshooting ​

Connection Refused ​

Check that the target TCP service is running:

bash
telnet localhost 7000

Large Messages Fail ​

Increase the max message size:

typescript
const proxy = new CapnpWebSocketProxy({
  wsPort: 9000,
  targetHost: 'localhost',
  targetPort: 7000,
  maxMessageSize: 64 * 1024 * 1024, // 64MB
});

Debug Mode ​

Enable verbose logging:

bash
npx @naeemo/capnp proxy -p 9000 -t localhost:7000 --debug

Example: Complete Setup ​

See examples/websocket-proxy for a complete working example with:

  • C++ Cap'n Proto server
  • WebSocket-to-TCP proxy
  • Browser client

API Reference ​

CapnpWebSocketProxy ​

Main proxy class.

Constructor ​

typescript
new CapnpWebSocketProxy(options: ProxyOptions)

Methods ​

  • getConnectionCount(): number - Active connections
  • getAllStats(): ConnectionStats[] - Stats for all connections
  • close(): Promise<void> - Graceful shutdown

Events ​

  • connection - New client connected
  • disconnection - Client disconnected
  • error - Error occurred

ProxyOptions ​

OptionTypeDefaultDescription
wsPortnumberrequiredWebSocket server port
targetHoststringrequiredTarget TCP host
targetPortnumberrequiredTarget TCP port
maxMessageSizenumber16MBMax WebSocket message size
connectionTimeoutnumber30000TCP connection timeout (ms)
debugbooleanfalseEnable debug logging

Released under the MIT License.