19.6.2
This commit is contained in:
140
readme.websocket-keepalive-config.md
Normal file
140
readme.websocket-keepalive-config.md
Normal file
@ -0,0 +1,140 @@
|
||||
# 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
|
Reference in New Issue
Block a user