Overview
WebSocket transport multiplexes multiple HTTP-like requests over a single WebSocket connection, eliminating sub-request limits when using the SDK inside Cloudflare Workers or Durable Objects.Why WebSocket transport?
Cloudflare Workers and Durable Objects have sub-request limits:- Workers: 1,000 sub-requests per request
- Durable Objects: 1,000 sub-requests per Durable Object instance
- Using only 1 sub-request for the WebSocket upgrade
- Multiplexing unlimited operations over that single connection
- Maintaining request/response semantics for easy migration
Enabling WebSocket transport
Basic setup
Inside a Durable Object
How it works
Connection lifecycle
- Upgrade request: SDK sends HTTP upgrade request to container
- WebSocket established: Container accepts and maintains connection
- Request multiplexing: SDK sends JSON-encoded requests with unique IDs
- Response matching: Container sends responses with matching IDs
- Cleanup: Connection closes when sandbox is destroyed or explicitly disconnected
Request/response protocol
Requests and responses use a simple JSON protocol: Request format:Concurrent requests
Multiple requests can be in-flight simultaneously:Connection management
Automatic connection
Connections are established automatically on first use:Explicit connection control
For advanced use cases, manually control connections:Connection sharing
Multiple operations automatically share the same connection:Streaming over WebSocket
Streaming operations work seamlessly:Error handling
Connection errors
Handle connection failures gracefully:Request timeouts
Configure request-level timeouts:Connection loss
When the WebSocket closes unexpectedly, pending requests are rejected:Performance characteristics
Latency
WebSocket transport has similar latency to HTTP for individual requests:- First request: +10-30ms (connection establishment)
- Subsequent requests: Similar to HTTP (no additional overhead)
Throughput
WebSocket excels with many small requests:Memory usage
WebSocket maintains a connection with pending request tracking:- Per connection: ~2-5 KB base overhead
- Per pending request: ~200 bytes
- Stream buffers: ~16 KB per active stream
Comparison with HTTP transport
| Feature | HTTP Transport | WebSocket Transport |
|---|---|---|
| Sub-requests used | 1-3 per operation | 1 total (upgrade) |
| Connection overhead | None | Initial upgrade (~20ms) |
| Concurrent requests | Supported | Supported |
| Streaming | Server-Sent Events | WebSocket messages |
| Best for | Simple workflows | Complex workflows, DO context |
| Memory | Lower | Slightly higher |
When to use WebSocket transport
✅ Use WebSocket when:
- Running inside a Durable Object with many operations
- Executing complex workflows with 50+ SDK calls
- Approaching Worker sub-request limits
- Long-running processes with streaming output
- Batch operations across multiple files/commands
❌ Use HTTP when:
- Simple one-off operations (1-10 SDK calls)
- Running outside Workers (Node.js, browser)
- Debugging (easier to inspect HTTP traffic)
- Maximum compatibility (no WebSocket support needed)
Configuration options
Transport-specific options
Fallback to HTTP
Implement fallback logic for maximum reliability:Advanced patterns
Connection pooling
Reuse connections across multiple operations:Request prioritization
Implement priority queues over a single connection:Batch operations
Execute many operations efficiently:Debugging
Enable debug logging
Monitor connection state
Inspect message flow
Log request/response pairs for debugging:Troubleshooting
WebSocket upgrade fails
Verify the container supports WebSocket:Messages out of order
Responses may arrive out of order, but they’re matched by ID:Connection drops under load
Increase timeouts for high-concurrency scenarios:Memory leaks with streaming
Always consume or cancel streams:Implementation details
For contributors and advanced users:Transport abstraction
Both HTTP and WebSocket transports implementITransport:
packages/sandbox/src/clients/transport/types.ts
Connection establishment
WebSocket upgrade uses different mechanisms based on context:- Inside DO: Uses
stub.fetch()with upgrade headers - Browser/Node: Uses standard
new WebSocket(url)
Message encoding
All messages are JSON-encoded:Pending request tracking
The SDK maintains a map of pending requests:packages/sandbox/src/clients/transport/ws-transport.ts:40