Files
smartproxy/readme.websocket-keepalive-config.md

140 lines
4.0 KiB
Markdown
Raw Normal View History

2025-06-09 22:13:46 +00:00
# WebSocket Keep-Alive Configuration Guide
## Quick Fix for SNI Passthrough WebSocket Disconnections
If your WebSocket connections are disconnecting every 30 seconds in SNI passthrough mode, here's the immediate solution:
### Option 1: Extended Keep-Alive Treatment (Recommended)
```typescript
const proxy = new SmartProxy({
// Extend timeout for keep-alive connections
keepAliveTreatment: 'extended',
keepAliveInactivityMultiplier: 10, // 10x the base timeout
inactivityTimeout: 14400000, // 4 hours base (40 hours with multiplier)
routes: [
{
name: 'websocket-passthrough',
match: {
ports: 443,
domains: ['ws.example.com', 'wss.example.com']
},
action: {
type: 'forward',
target: { host: 'backend', port: 443 },
tls: { mode: 'passthrough' }
}
}
]
});
```
### Option 2: Immortal Connections (Never Timeout)
```typescript
const proxy = new SmartProxy({
// Never timeout keep-alive connections
keepAliveTreatment: 'immortal',
routes: [
// ... same as above
]
});
```
### Option 3: Per-Route Security Settings
```typescript
const proxy = new SmartProxy({
routes: [
{
name: 'websocket-passthrough',
match: {
ports: 443,
domains: ['ws.example.com']
},
action: {
type: 'forward',
target: { host: 'backend', port: 443 },
tls: { mode: 'passthrough' }
},
security: {
// Disable connection limits for this route
maxConnections: 0, // 0 = unlimited
maxConnectionsPerIP: 0 // 0 = unlimited
}
}
]
});
```
## Understanding the Issue
### Why Connections Drop at 30 Seconds
1. **WebSocket Heartbeat**: The HTTP proxy's WebSocket handler sends ping frames every 30 seconds
2. **SNI Passthrough**: In passthrough mode, traffic is encrypted end-to-end
3. **Can't Inject Pings**: The proxy can't inject ping frames into encrypted traffic
4. **No Pong Response**: Client doesn't respond to pings that were never sent
5. **Connection Terminated**: After 30 seconds, connection is marked inactive and closed
### Why Grace Periods Were Too Short
- Half-zombie detection: 30 seconds (now 5 minutes for TLS)
- Stuck connection detection: 60 seconds (now 5 minutes for TLS)
- These were too aggressive for encrypted long-lived connections
## Long-Term Solution
The fix involves:
1. **Detecting SNI Passthrough**: Skip WebSocket heartbeat for passthrough connections
2. **Longer Grace Periods**: 5-minute grace for encrypted connections
3. **TCP Keep-Alive**: Rely on OS-level TCP keep-alive instead
4. **Route-Aware Timeouts**: Different timeout strategies per route type
## TCP Keep-Alive Configuration
For best results, also configure TCP keep-alive at the OS level:
### Linux
```bash
# /etc/sysctl.conf
net.ipv4.tcp_keepalive_time = 600 # Start probes after 10 minutes
net.ipv4.tcp_keepalive_intvl = 60 # Probe every minute
net.ipv4.tcp_keepalive_probes = 9 # Drop after 9 failed probes
```
### Node.js Socket Options
The proxy already enables TCP keep-alive on sockets:
- Keep-alive is enabled by default
- Initial delay can be configured via `keepAliveInitialDelay`
## Monitoring
Check your connections:
```typescript
const stats = proxy.getStats();
console.log('Active connections:', stats.getActiveConnections());
console.log('Connections by route:', stats.getConnectionsByRoute());
// Monitor long-lived connections
setInterval(() => {
const connections = proxy.connectionManager.getConnections();
for (const [id, conn] of connections) {
const age = Date.now() - conn.incomingStartTime;
if (age > 300000) { // 5+ minutes
console.log(`Long-lived connection: ${id}, age: ${age}ms, route: ${conn.routeName}`);
}
}
}, 60000);
```
## Summary
- **Immediate Fix**: Use `keepAliveTreatment: 'extended'` or `'immortal'`
- **Applied Fix**: Increased grace periods for TLS connections to 5 minutes
- **Best Practice**: Use SNI passthrough for WebSocket when you need end-to-end encryption
- **Alternative**: Use TLS termination if you need application-level WebSocket features