Skip to content

Commit b3dc18f

Browse files
authored
docs: add WebSocket Handling section with Mermaid diagram (#23)
1 parent 4a1591a commit b3dc18f

File tree

1 file changed

+59
-2
lines changed

1 file changed

+59
-2
lines changed

README.md

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ A high-performance reverse-proxy for Solana JSON-RPC and WebSocket endpoints wit
88
- **Rate Limiting**: per-key RPS limits enforced atomically in Redis (INCR + EXPIRE Lua script).
99
- **Weighted Load Balancing**: distribute requests across backends by configurable weight; unhealthy backends are automatically excluded.
1010
- **Method-Based Routing**: pin specific RPC methods (e.g. `getSlot`) to designated backends.
11-
- **WebSocket Proxying**: separate WS server (HTTP port + 1) with the same auth, rate limiting, and weighted backend selection.
11+
- **WebSocket Proxying**: upgrade on the main HTTP port or a dedicated WS port (HTTP port + 1), with the same auth, rate limiting, and weighted backend selection.
1212
- **Health Checks**: background loop calls a configurable RPC method per backend; consecutive-failure / consecutive-success thresholds control status transitions.
1313
- **Prometheus Metrics**: `GET /metrics` exposes request counts, latencies, and backend health gauges.
1414
- **Admin CLI** (`rpc-admin`): create, list, inspect, and revoke API keys in Redis.
@@ -74,6 +74,62 @@ getSlot = "mainnet-primary"
7474
- `proxy.timeout_secs` must be > 0.
7575
- `method_routes` values must reference existing backend labels.
7676

77+
## WebSocket Handling
78+
79+
The proxy supports Solana WebSocket subscriptions (e.g. `accountSubscribe`, `logsSubscribe`) with the same authentication and load-balancing guarantees as HTTP.
80+
81+
### Connection Lifecycle
82+
83+
```mermaid
84+
sequenceDiagram
85+
participant Client
86+
participant Router
87+
participant Redis
88+
participant Backend
89+
90+
Client->>Router: GET / (Upgrade: websocket) + API Key
91+
Router->>Redis: Validate Key
92+
Redis-->>Router: OK (Owner info)
93+
Router->>Router: Select Healthy Backend
94+
Router->>Backend: Connect (WS Handshake)
95+
Backend-->>Router: 101 Switching Protocols
96+
Router-->>Client: 101 Switching Protocols
97+
98+
Note over Client,Backend: Bi-directional Message Pipe
99+
100+
Client->>Router: Message
101+
Router->>Backend: Forward
102+
Backend->>Router: Message
103+
Router->>Client: Forward
104+
```
105+
106+
1. **Upgrade** — Clients open a WebSocket to the main HTTP port (`GET /` with `Upgrade: websocket`) or the dedicated WS port (HTTP port + 1). Both accept `?api-key=` as a query parameter.
107+
2. **Authentication** — The API key is validated against Redis (same flow as HTTP: lookup, cache check, rate-limit enforcement). Failures return `401 Unauthorized` or `429 Too Many Requests` before the upgrade completes.
108+
3. **Backend Selection**`select_ws_backend()` picks a healthy backend that has a `ws_url` configured, using the same weighted-random algorithm as HTTP requests.
109+
4. **Bi-directional Piping** — After the upgrade, the proxy opens a second WebSocket to the chosen backend (via `tokio-tungstenite`). Two concurrent tasks forward frames in each direction (client ↔ backend). Text, Binary, Ping, and Pong frames are relayed transparently. When either side sends a Close frame or errors out, `tokio::select!` shuts down the other direction.
110+
5. **Cleanup** — On disconnect the active-connection gauge is decremented and the total session duration is recorded.
111+
112+
### Metrics
113+
114+
| Metric | Type | Labels | Description |
115+
|--------|------|--------|-------------|
116+
| `ws_connections_total` | Counter | `backend`, `owner`, `status` | Connection attempts (`connected`, `auth_failed`, `rate_limited`, `no_backend`, `backend_connect_failed`, `error`) |
117+
| `ws_active_connections` | Gauge | `backend`, `owner` | Currently open WebSocket sessions |
118+
| `ws_messages_total` | Counter | `backend`, `owner`, `direction` | Frames relayed (`client_to_backend` / `backend_to_client`) |
119+
| `ws_connection_duration_seconds` | Histogram | `backend`, `owner` | Session duration from upgrade to close |
120+
121+
### Configuration
122+
123+
Backends that should accept WebSocket traffic must include a `ws_url` field. Backends without `ws_url` are excluded from WebSocket routing but still serve HTTP requests.
124+
125+
```toml
126+
[[backends]]
127+
label = "mainnet-primary"
128+
url = "https://api.mainnet-beta.solana.com"
129+
ws_url = "wss://api.mainnet-beta.solana.com" # enables WS for this backend
130+
weight = 10
131+
```
132+
77133
## API Key Management CLI
78134

79135
```bash
@@ -103,10 +159,11 @@ Redis URL can be set via `--redis-url` flag or `REDIS_URL` env var (default `red
103159
| Endpoint | Method | Description |
104160
|----------|--------|-------------|
105161
| `/` | POST | Proxy JSON-RPC requests (requires `?api-key=`) |
162+
| `/` | GET (Upgrade) | WebSocket proxy on main port (requires `?api-key=`) |
106163
| `/*path` | POST | Proxy with subpath |
107164
| `/health` | GET | Backend health status (JSON) |
108165
| `/metrics` | GET | Prometheus metrics |
109-
| `ws://host:port+1/` | WS | WebSocket proxy (requires `?api-key=`) |
166+
| `ws://host:port+1/` | WS | Dedicated WebSocket port (requires `?api-key=`) |
110167

111168
## Testing
112169

0 commit comments

Comments
 (0)