|
| 1 | +--- |
| 2 | +title: WebSocket Transport |
| 3 | +pcx_content_type: concept |
| 4 | +sidebar: |
| 5 | + order: 8 |
| 6 | +description: Learn how WebSocket transport reduces sub-request usage by multiplexing SDK operations over a single persistent connection. |
| 7 | +--- |
| 8 | + |
| 9 | +WebSocket transport is an alternative communication method between the Sandbox SDK client and container runtime that reduces sub-request count by multiplexing multiple operations over a single WebSocket connection. |
| 10 | + |
| 11 | +## Why use WebSocket transport |
| 12 | + |
| 13 | +When using HTTP transport (the default), each SDK operation (like `exec()`, `readFile()`, `writeFile()`) creates a separate HTTP request. In Workers and Durable Objects, these requests count against [sub-request limits](/workers/platform/limits/#subrequests). |
| 14 | + |
| 15 | +WebSocket transport solves this by: |
| 16 | + |
| 17 | +- **Multiplexing requests** - Send many operations over one WebSocket connection |
| 18 | +- **Reducing sub-request count** - Only the initial WebSocket upgrade counts as a sub-request |
| 19 | +- **Maintaining compatibility** - Works with all existing SDK methods without code changes |
| 20 | + |
| 21 | +## When to use WebSocket transport |
| 22 | + |
| 23 | +Consider WebSocket transport when: |
| 24 | + |
| 25 | +- **High operation volume** - Your Worker performs many sandbox operations per request |
| 26 | +- **Near sub-request limits** - You are approaching Workers sub-request limits |
| 27 | +- **Long-lived interactions** - Your code performs sequences of operations on the same sandbox |
| 28 | + |
| 29 | +Continue using HTTP transport (default) when: |
| 30 | + |
| 31 | +- **Simple use cases** - You perform only a few operations per request |
| 32 | +- **No sub-request concerns** - Your Worker is well under sub-request limits |
| 33 | +- **Maximum compatibility** - You want to use the most battle-tested transport |
| 34 | + |
| 35 | +## How it works |
| 36 | + |
| 37 | +### Architecture |
| 38 | + |
| 39 | +```mermaid |
| 40 | +flowchart LR |
| 41 | + accTitle: WebSocket Transport Architecture |
| 42 | + accDescr: Diagram showing how multiple SDK operations multiplex over a single WebSocket connection |
| 43 | +
|
| 44 | + Worker["Worker (SDK Client)"] |
| 45 | + DO["Sandbox Durable Object"] |
| 46 | + Container["Container Runtime"] |
| 47 | +
|
| 48 | + Worker -->|"WebSocket Upgrade (1 sub-request)"| DO |
| 49 | + DO <-->|"HTTP to Container"| Container |
| 50 | +
|
| 51 | + Worker -.->|"exec() → WS message"| DO |
| 52 | + Worker -.->|"readFile() → WS message"| DO |
| 53 | + Worker -.->|"writeFile() → WS message"| DO |
| 54 | +
|
| 55 | + style Worker fill:#ffe8d1,stroke:#f6821f,stroke-width:2px |
| 56 | + style DO fill:#dce9f7,stroke:#1d8cf8,stroke-width:2px |
| 57 | + style Container fill:#d4f4e2,stroke:#17b26a,stroke-width:2px |
| 58 | +``` |
| 59 | + |
| 60 | +### Protocol |
| 61 | + |
| 62 | +The WebSocket transport implements a request-response protocol: |
| 63 | + |
| 64 | +1. **Client sends WSRequest** with unique ID, method, path, and body |
| 65 | +2. **Server sends WSResponse** with matching ID, status, and body |
| 66 | +3. **Streaming support** - For streaming operations, server sends multiple WSStreamChunk messages before final WSResponse |
| 67 | + |
| 68 | +All messages are JSON-encoded. The protocol maintains compatibility with existing HTTP-based handlers - the container runtime routes WebSocket messages through the same HTTP handlers used for direct HTTP requests. |
| 69 | + |
| 70 | +## Enable WebSocket transport |
| 71 | + |
| 72 | +WebSocket transport is automatically available in the SDK. Enable it by passing `useWebSocket: true` when creating a sandbox: |
| 73 | + |
| 74 | +```typescript |
| 75 | +import { getSandbox } from "@cloudflare/sandbox"; |
| 76 | + |
| 77 | +export default { |
| 78 | + async fetch(request: Request, env: Env): Promise<Response> { |
| 79 | + // Enable WebSocket transport |
| 80 | + const sandbox = getSandbox(env.Sandbox, "my-sandbox", { |
| 81 | + useWebSocket: true |
| 82 | + }); |
| 83 | + |
| 84 | + // All operations now use WebSocket transport |
| 85 | + await sandbox.exec("echo 'Hello via WebSocket'"); |
| 86 | + await sandbox.writeFile("/workspace/data.txt", "content"); |
| 87 | + const content = await sandbox.readFile("/workspace/data.txt"); |
| 88 | + |
| 89 | + // Only the initial WebSocket upgrade counted as a sub-request |
| 90 | + return Response.json({ success: true }); |
| 91 | + } |
| 92 | +}; |
| 93 | +``` |
| 94 | + |
| 95 | +## Performance characteristics |
| 96 | + |
| 97 | +### Connection lifecycle |
| 98 | + |
| 99 | +- **First operation** - Establishes WebSocket connection (counts as 1 sub-request) |
| 100 | +- **Subsequent operations** - Reuse existing connection (no additional sub-requests) |
| 101 | +- **Connection pooling** - The SDK maintains the connection for the lifetime of the sandbox instance |
| 102 | +- **Automatic reconnection** - If connection drops, SDK transparently reconnects |
| 103 | + |
| 104 | +### Latency considerations |
| 105 | + |
| 106 | +- **First request** - Slightly higher latency due to WebSocket upgrade handshake |
| 107 | +- **Subsequent requests** - Similar latency to HTTP transport |
| 108 | +- **Streaming operations** - WebSocket transport converts Server-Sent Events (SSE) to WebSocket messages |
| 109 | + |
| 110 | +### Best practices |
| 111 | + |
| 112 | +**Use WebSocket transport when:** |
| 113 | + |
| 114 | +```typescript |
| 115 | +// High operation count per request |
| 116 | +const sandbox = getSandbox(env.Sandbox, "batch-processor", { |
| 117 | + useWebSocket: true |
| 118 | +}); |
| 119 | + |
| 120 | +for (let i = 0; i < 100; i++) { |
| 121 | + await sandbox.exec(`process-item-${i}.sh`); |
| 122 | +} |
| 123 | +// Only 1 sub-request instead of 100 |
| 124 | +``` |
| 125 | + |
| 126 | +**Stick with HTTP transport when:** |
| 127 | + |
| 128 | +```typescript |
| 129 | +// Single operation per request |
| 130 | +const sandbox = getSandbox(env.Sandbox, "simple-exec"); |
| 131 | +await sandbox.exec("single-command.sh"); |
| 132 | +// 1 sub-request either way, HTTP is simpler |
| 133 | +``` |
| 134 | + |
| 135 | +## Implementation details |
| 136 | + |
| 137 | +### Message types |
| 138 | + |
| 139 | +The WebSocket protocol defines four message types: |
| 140 | + |
| 141 | +- **WSRequest** - Client to server request with method, path, and body |
| 142 | +- **WSResponse** - Server to client response with status and body |
| 143 | +- **WSStreamChunk** - Server to client streaming data chunk |
| 144 | +- **WSError** - Server to client error message |
| 145 | + |
| 146 | +### Streaming support |
| 147 | + |
| 148 | +For streaming operations like `execStream()`: |
| 149 | + |
| 150 | +1. Client sends WSRequest for streaming endpoint |
| 151 | +2. Server responds with multiple WSStreamChunk messages (converted from SSE format) |
| 152 | +3. Server sends final WSResponse when stream completes |
| 153 | + |
| 154 | +This maintains compatibility with existing streaming code while using WebSocket transport. |
| 155 | + |
| 156 | +### Error handling |
| 157 | + |
| 158 | +WebSocket transport handles errors at multiple levels: |
| 159 | + |
| 160 | +- **Connection errors** - Automatic reconnection with exponential backoff |
| 161 | +- **Request errors** - Returned as WSError messages with error codes |
| 162 | +- **Timeout handling** - Configurable timeouts for connection and requests |
| 163 | + |
| 164 | +## Compatibility |
| 165 | + |
| 166 | +WebSocket transport is fully compatible with: |
| 167 | + |
| 168 | +- All SDK methods (exec, files, processes, ports, etc.) |
| 169 | +- Streaming operations (execStream, readStream) |
| 170 | +- Session management |
| 171 | +- Preview URLs and port forwarding |
| 172 | +- Code Interpreter API |
| 173 | + |
| 174 | +No code changes are required to existing SDK usage - simply enable the option and all operations transparently use WebSocket transport. |
| 175 | + |
| 176 | +## Limitations |
| 177 | + |
| 178 | +- **Workers and Durable Objects only** - WebSocket transport is designed for environments with sub-request limits |
| 179 | +- **Not for external services** - This is for SDK-to-container communication only, not for [WebSocket connections to services inside sandboxes](/sandbox/guides/websocket-connections/) |
| 180 | +- **Durable Object WebSocket limits apply** - Standard Durable Object WebSocket limits and quotas apply |
| 181 | + |
| 182 | +## Related resources |
| 183 | + |
| 184 | +- [Architecture concept](/sandbox/concepts/architecture/) - Overview of SDK architecture |
| 185 | +- [Sandbox options](/sandbox/configuration/sandbox-options/) - Configuration options for sandboxes |
| 186 | +- [Workers limits](/workers/platform/limits/) - Workers sub-request limits and quotas |
| 187 | +- [WebSocket connections to sandboxes](/sandbox/guides/websocket-connections/) - Connect to WebSocket servers inside sandboxes |
0 commit comments