@push.rocks/smartproxy 19.6.2 → 19.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -17
- package/readme.md +740 -0
- package/readme.connections.md +0 -724
- package/readme.delete.md +0 -187
- package/readme.hints.md +0 -897
- package/readme.memory-leaks-fixed.md +0 -45
- package/readme.metrics.md +0 -591
- package/readme.monitoring.md +0 -202
- package/readme.plan.md +0 -625
- package/readme.proxy-chain-summary.md +0 -112
- package/readme.proxy-protocol-example.md +0 -462
- package/readme.proxy-protocol.md +0 -415
- package/readme.routing.md +0 -341
- package/readme.websocket-keepalive-config.md +0 -140
- package/readme.websocket-keepalive-fix.md +0 -63
package/readme.routing.md
DELETED
|
@@ -1,341 +0,0 @@
|
|
|
1
|
-
# SmartProxy Routing Architecture Unification Plan
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
This document analyzes the current state of routing in SmartProxy, identifies redundancies and inconsistencies, and proposes a unified architecture.
|
|
5
|
-
|
|
6
|
-
## Current State Analysis
|
|
7
|
-
|
|
8
|
-
### 1. Multiple Route Manager Implementations
|
|
9
|
-
|
|
10
|
-
#### 1.1 Core SharedRouteManager (`ts/core/utils/route-manager.ts`)
|
|
11
|
-
- **Purpose**: Designed as a shared component for SmartProxy and NetworkProxy
|
|
12
|
-
- **Features**:
|
|
13
|
-
- Port mapping and expansion (e.g., `[80, 443]` → individual routes)
|
|
14
|
-
- Comprehensive route matching (domain, path, IP, headers, TLS)
|
|
15
|
-
- Route validation and conflict detection
|
|
16
|
-
- Event emitter for route changes
|
|
17
|
-
- Detailed logging support
|
|
18
|
-
- **Status**: Well-designed but underutilized
|
|
19
|
-
|
|
20
|
-
#### 1.2 SmartProxy RouteManager (`ts/proxies/smart-proxy/route-manager.ts`)
|
|
21
|
-
- **Purpose**: SmartProxy-specific route management
|
|
22
|
-
- **Issues**:
|
|
23
|
-
- 95% duplicate code from SharedRouteManager
|
|
24
|
-
- Only difference is using `ISmartProxyOptions` instead of generic interface
|
|
25
|
-
- Contains deprecated security methods
|
|
26
|
-
- Unnecessary code duplication
|
|
27
|
-
- **Status**: Should be removed in favor of SharedRouteManager
|
|
28
|
-
|
|
29
|
-
#### 1.3 HttpProxy Route Management (`ts/proxies/http-proxy/`)
|
|
30
|
-
- **Purpose**: HTTP-specific routing
|
|
31
|
-
- **Implementation**: Minimal, inline route matching
|
|
32
|
-
- **Status**: Could benefit from SharedRouteManager
|
|
33
|
-
|
|
34
|
-
### 2. Multiple Router Implementations
|
|
35
|
-
|
|
36
|
-
#### 2.1 ProxyRouter (`ts/routing/router/proxy-router.ts`)
|
|
37
|
-
- **Purpose**: Legacy compatibility with `IReverseProxyConfig`
|
|
38
|
-
- **Features**: Domain-based routing with path patterns
|
|
39
|
-
- **Used by**: HttpProxy for backward compatibility
|
|
40
|
-
|
|
41
|
-
#### 2.2 RouteRouter (`ts/routing/router/route-router.ts`)
|
|
42
|
-
- **Purpose**: Modern routing with `IRouteConfig`
|
|
43
|
-
- **Features**: Nearly identical to ProxyRouter
|
|
44
|
-
- **Issues**: Code duplication with ProxyRouter
|
|
45
|
-
|
|
46
|
-
### 3. Scattered Route Utilities
|
|
47
|
-
|
|
48
|
-
#### 3.1 Core route-utils (`ts/core/utils/route-utils.ts`)
|
|
49
|
-
- **Purpose**: Shared matching functions
|
|
50
|
-
- **Features**: Domain, path, IP, CIDR matching
|
|
51
|
-
- **Status**: Well-implemented, should be the single source
|
|
52
|
-
|
|
53
|
-
#### 3.2 SmartProxy route-utils (`ts/proxies/smart-proxy/utils/route-utils.ts`)
|
|
54
|
-
- **Purpose**: Route configuration utilities
|
|
55
|
-
- **Features**: Different scope - config merging, not pattern matching
|
|
56
|
-
- **Status**: Keep separate as it serves different purpose
|
|
57
|
-
|
|
58
|
-
### 4. Other Route-Related Files
|
|
59
|
-
- `route-patterns.ts`: Constants for route patterns
|
|
60
|
-
- `route-validators.ts`: Route configuration validation
|
|
61
|
-
- `route-helpers.ts`: Additional utilities
|
|
62
|
-
- `route-connection-handler.ts`: Connection routing logic
|
|
63
|
-
|
|
64
|
-
## Problems Identified
|
|
65
|
-
|
|
66
|
-
### 1. Code Duplication
|
|
67
|
-
- **SharedRouteManager vs SmartProxy RouteManager**: ~1000 lines of duplicate code
|
|
68
|
-
- **ProxyRouter vs RouteRouter**: ~500 lines of duplicate code
|
|
69
|
-
- **Matching logic**: Implemented in 4+ different places
|
|
70
|
-
|
|
71
|
-
### 2. Inconsistent Implementations
|
|
72
|
-
```typescript
|
|
73
|
-
// Example: Domain matching appears in multiple places
|
|
74
|
-
// 1. In route-utils.ts
|
|
75
|
-
export function matchDomain(pattern: string, hostname: string): boolean
|
|
76
|
-
|
|
77
|
-
// 2. In SmartProxy RouteManager
|
|
78
|
-
private matchDomain(domain: string, hostname: string): boolean
|
|
79
|
-
|
|
80
|
-
// 3. In ProxyRouter
|
|
81
|
-
private matchesHostname(configName: string, hostname: string): boolean
|
|
82
|
-
|
|
83
|
-
// 4. In RouteRouter
|
|
84
|
-
private matchDomain(pattern: string, hostname: string): boolean
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### 3. Unclear Separation of Concerns
|
|
88
|
-
- Route Managers handle both storage AND matching
|
|
89
|
-
- Routers also handle storage AND matching
|
|
90
|
-
- No clear boundaries between layers
|
|
91
|
-
|
|
92
|
-
### 4. Maintenance Burden
|
|
93
|
-
- Bug fixes need to be applied in multiple places
|
|
94
|
-
- New features must be implemented multiple times
|
|
95
|
-
- Testing effort multiplied
|
|
96
|
-
|
|
97
|
-
## Proposed Unified Architecture
|
|
98
|
-
|
|
99
|
-
### Layer 1: Core Routing Components
|
|
100
|
-
```
|
|
101
|
-
ts/core/routing/
|
|
102
|
-
├── types.ts # All route-related types
|
|
103
|
-
├── utils.ts # All matching logic (consolidated)
|
|
104
|
-
├── route-store.ts # Route storage and indexing
|
|
105
|
-
└── route-matcher.ts # Route matching engine
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Layer 2: Route Management
|
|
109
|
-
```
|
|
110
|
-
ts/core/routing/
|
|
111
|
-
└── route-manager.ts # Single RouteManager for all proxies
|
|
112
|
-
- Uses RouteStore for storage
|
|
113
|
-
- Uses RouteMatcher for matching
|
|
114
|
-
- Provides high-level API
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
### Layer 3: HTTP Routing
|
|
118
|
-
```
|
|
119
|
-
ts/routing/
|
|
120
|
-
└── http-router.ts # Single HTTP router implementation
|
|
121
|
-
- Uses RouteManager for route lookup
|
|
122
|
-
- Handles HTTP-specific concerns
|
|
123
|
-
- Legacy adapter built-in
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
### Layer 4: Proxy Integration
|
|
127
|
-
```
|
|
128
|
-
ts/proxies/
|
|
129
|
-
├── smart-proxy/
|
|
130
|
-
│ └── (uses core RouteManager directly)
|
|
131
|
-
├── http-proxy/
|
|
132
|
-
│ └── (uses core RouteManager + HttpRouter)
|
|
133
|
-
└── network-proxy/
|
|
134
|
-
└── (uses core RouteManager directly)
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## Implementation Plan
|
|
138
|
-
|
|
139
|
-
### Phase 1: Consolidate Matching Logic (Week 1)
|
|
140
|
-
1. **Audit all matching implementations**
|
|
141
|
-
- Document differences in behavior
|
|
142
|
-
- Identify the most comprehensive implementation
|
|
143
|
-
- Create test suite covering all edge cases
|
|
144
|
-
|
|
145
|
-
2. **Create unified matching module**
|
|
146
|
-
```typescript
|
|
147
|
-
// ts/core/routing/matchers.ts
|
|
148
|
-
export class DomainMatcher {
|
|
149
|
-
static match(pattern: string, hostname: string): boolean
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
export class PathMatcher {
|
|
153
|
-
static match(pattern: string, path: string): MatchResult
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export class IpMatcher {
|
|
157
|
-
static match(pattern: string, ip: string): boolean
|
|
158
|
-
static matchCidr(cidr: string, ip: string): boolean
|
|
159
|
-
}
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
3. **Update all components to use unified matchers**
|
|
163
|
-
- Replace local implementations
|
|
164
|
-
- Ensure backward compatibility
|
|
165
|
-
- Run comprehensive tests
|
|
166
|
-
|
|
167
|
-
### Phase 2: Unify Route Managers (Week 2)
|
|
168
|
-
1. **Enhance SharedRouteManager**
|
|
169
|
-
- Add any missing features from SmartProxy RouteManager
|
|
170
|
-
- Make it truly generic (no proxy-specific dependencies)
|
|
171
|
-
- Add adapter pattern for different options types
|
|
172
|
-
|
|
173
|
-
2. **Migrate SmartProxy to use SharedRouteManager**
|
|
174
|
-
```typescript
|
|
175
|
-
// Before
|
|
176
|
-
this.routeManager = new RouteManager(this.settings);
|
|
177
|
-
|
|
178
|
-
// After
|
|
179
|
-
this.routeManager = new SharedRouteManager({
|
|
180
|
-
logger: this.settings.logger,
|
|
181
|
-
enableDetailedLogging: this.settings.enableDetailedLogging
|
|
182
|
-
});
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
3. **Remove duplicate RouteManager**
|
|
186
|
-
- Delete `ts/proxies/smart-proxy/route-manager.ts`
|
|
187
|
-
- Update all imports
|
|
188
|
-
- Verify all tests pass
|
|
189
|
-
|
|
190
|
-
### Phase 3: Consolidate Routers (Week 3)
|
|
191
|
-
1. **Create unified HttpRouter**
|
|
192
|
-
```typescript
|
|
193
|
-
export class HttpRouter {
|
|
194
|
-
constructor(private routeManager: SharedRouteManager) {}
|
|
195
|
-
|
|
196
|
-
// Modern interface
|
|
197
|
-
route(req: IncomingMessage): RouteResult
|
|
198
|
-
|
|
199
|
-
// Legacy adapter
|
|
200
|
-
routeLegacy(config: IReverseProxyConfig): RouteResult
|
|
201
|
-
}
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
2. **Migrate HttpProxy**
|
|
205
|
-
- Replace both ProxyRouter and RouteRouter
|
|
206
|
-
- Use single HttpRouter with appropriate adapter
|
|
207
|
-
- Maintain backward compatibility
|
|
208
|
-
|
|
209
|
-
3. **Clean up legacy code**
|
|
210
|
-
- Mark old interfaces as deprecated
|
|
211
|
-
- Add migration guides
|
|
212
|
-
- Plan removal in next major version
|
|
213
|
-
|
|
214
|
-
### Phase 4: Architecture Cleanup (Week 4)
|
|
215
|
-
1. **Reorganize file structure**
|
|
216
|
-
```
|
|
217
|
-
ts/core/
|
|
218
|
-
├── routing/
|
|
219
|
-
│ ├── index.ts
|
|
220
|
-
│ ├── types.ts
|
|
221
|
-
│ ├── matchers/
|
|
222
|
-
│ │ ├── domain.ts
|
|
223
|
-
│ │ ├── path.ts
|
|
224
|
-
│ │ ├── ip.ts
|
|
225
|
-
│ │ └── index.ts
|
|
226
|
-
│ ├── route-store.ts
|
|
227
|
-
│ ├── route-matcher.ts
|
|
228
|
-
│ └── route-manager.ts
|
|
229
|
-
└── utils/
|
|
230
|
-
└── (remove route-specific utils)
|
|
231
|
-
```
|
|
232
|
-
|
|
233
|
-
2. **Update documentation**
|
|
234
|
-
- Architecture diagrams
|
|
235
|
-
- Migration guides
|
|
236
|
-
- API documentation
|
|
237
|
-
|
|
238
|
-
3. **Performance optimization**
|
|
239
|
-
- Add caching where beneficial
|
|
240
|
-
- Optimize hot paths
|
|
241
|
-
- Benchmark before/after
|
|
242
|
-
|
|
243
|
-
## Migration Strategy
|
|
244
|
-
|
|
245
|
-
### For SmartProxy RouteManager Users
|
|
246
|
-
```typescript
|
|
247
|
-
// Old way
|
|
248
|
-
import { RouteManager } from './route-manager.js';
|
|
249
|
-
const manager = new RouteManager(options);
|
|
250
|
-
|
|
251
|
-
// New way
|
|
252
|
-
import { SharedRouteManager as RouteManager } from '../core/utils/route-manager.js';
|
|
253
|
-
const manager = new RouteManager({
|
|
254
|
-
logger: options.logger,
|
|
255
|
-
enableDetailedLogging: options.enableDetailedLogging
|
|
256
|
-
});
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### For Router Users
|
|
260
|
-
```typescript
|
|
261
|
-
// Old way
|
|
262
|
-
const proxyRouter = new ProxyRouter();
|
|
263
|
-
const routeRouter = new RouteRouter();
|
|
264
|
-
|
|
265
|
-
// New way
|
|
266
|
-
const router = new HttpRouter(routeManager);
|
|
267
|
-
// Automatically handles both modern and legacy configs
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
## Success Metrics
|
|
271
|
-
|
|
272
|
-
1. **Code Reduction**
|
|
273
|
-
- Target: Remove ~1,500 lines of duplicate code
|
|
274
|
-
- Measure: Lines of code before/after
|
|
275
|
-
|
|
276
|
-
2. **Performance**
|
|
277
|
-
- Target: No regression in routing performance
|
|
278
|
-
- Measure: Benchmark route matching operations
|
|
279
|
-
|
|
280
|
-
3. **Maintainability**
|
|
281
|
-
- Target: Single implementation for each concept
|
|
282
|
-
- Measure: Time to implement new features
|
|
283
|
-
|
|
284
|
-
4. **Test Coverage**
|
|
285
|
-
- Target: 100% coverage of routing logic
|
|
286
|
-
- Measure: Coverage reports
|
|
287
|
-
|
|
288
|
-
## Risks and Mitigations
|
|
289
|
-
|
|
290
|
-
### Risk 1: Breaking Changes
|
|
291
|
-
- **Mitigation**: Extensive adapter patterns and backward compatibility layers
|
|
292
|
-
- **Testing**: Run all existing tests plus new integration tests
|
|
293
|
-
|
|
294
|
-
### Risk 2: Performance Regression
|
|
295
|
-
- **Mitigation**: Benchmark critical paths before changes
|
|
296
|
-
- **Testing**: Load testing with production-like scenarios
|
|
297
|
-
|
|
298
|
-
### Risk 3: Hidden Dependencies
|
|
299
|
-
- **Mitigation**: Careful code analysis and dependency mapping
|
|
300
|
-
- **Testing**: Integration tests across all proxy types
|
|
301
|
-
|
|
302
|
-
## Long-term Vision
|
|
303
|
-
|
|
304
|
-
### Future Enhancements
|
|
305
|
-
1. **Route Caching**: LRU cache for frequently accessed routes
|
|
306
|
-
2. **Route Indexing**: Trie-based indexing for faster domain matching
|
|
307
|
-
3. **Route Priorities**: Explicit priority system instead of specificity
|
|
308
|
-
4. **Dynamic Routes**: Support for runtime route modifications
|
|
309
|
-
5. **Route Templates**: Reusable route configurations
|
|
310
|
-
|
|
311
|
-
### API Evolution
|
|
312
|
-
```typescript
|
|
313
|
-
// Future unified routing API
|
|
314
|
-
const routingEngine = new RoutingEngine({
|
|
315
|
-
stores: [fileStore, dbStore, dynamicStore],
|
|
316
|
-
matchers: [domainMatcher, pathMatcher, customMatcher],
|
|
317
|
-
cache: new LRUCache({ max: 1000 }),
|
|
318
|
-
indexes: {
|
|
319
|
-
domain: new TrieIndex(),
|
|
320
|
-
path: new RadixTree()
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
// Simple, powerful API
|
|
325
|
-
const route = await routingEngine.findRoute({
|
|
326
|
-
domain: 'example.com',
|
|
327
|
-
path: '/api/v1/users',
|
|
328
|
-
ip: '192.168.1.1',
|
|
329
|
-
headers: { 'x-custom': 'value' }
|
|
330
|
-
});
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
## Conclusion
|
|
334
|
-
|
|
335
|
-
The current routing architecture has significant duplication and inconsistencies. By following this unification plan, we can:
|
|
336
|
-
1. Reduce code by ~30%
|
|
337
|
-
2. Improve maintainability
|
|
338
|
-
3. Ensure consistent behavior
|
|
339
|
-
4. Enable future enhancements
|
|
340
|
-
|
|
341
|
-
The phased approach minimizes risk while delivering incremental value. Each phase is independently valuable and can be deployed separately.
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
# WebSocket Keep-Alive Configuration Guide
|
|
2
|
-
|
|
3
|
-
## Quick Fix for SNI Passthrough WebSocket Disconnections
|
|
4
|
-
|
|
5
|
-
If your WebSocket connections are disconnecting every 30 seconds in SNI passthrough mode, here's the immediate solution:
|
|
6
|
-
|
|
7
|
-
### Option 1: Extended Keep-Alive Treatment (Recommended)
|
|
8
|
-
|
|
9
|
-
```typescript
|
|
10
|
-
const proxy = new SmartProxy({
|
|
11
|
-
// Extend timeout for keep-alive connections
|
|
12
|
-
keepAliveTreatment: 'extended',
|
|
13
|
-
keepAliveInactivityMultiplier: 10, // 10x the base timeout
|
|
14
|
-
inactivityTimeout: 14400000, // 4 hours base (40 hours with multiplier)
|
|
15
|
-
|
|
16
|
-
routes: [
|
|
17
|
-
{
|
|
18
|
-
name: 'websocket-passthrough',
|
|
19
|
-
match: {
|
|
20
|
-
ports: 443,
|
|
21
|
-
domains: ['ws.example.com', 'wss.example.com']
|
|
22
|
-
},
|
|
23
|
-
action: {
|
|
24
|
-
type: 'forward',
|
|
25
|
-
target: { host: 'backend', port: 443 },
|
|
26
|
-
tls: { mode: 'passthrough' }
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
]
|
|
30
|
-
});
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Option 2: Immortal Connections (Never Timeout)
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
const proxy = new SmartProxy({
|
|
37
|
-
// Never timeout keep-alive connections
|
|
38
|
-
keepAliveTreatment: 'immortal',
|
|
39
|
-
|
|
40
|
-
routes: [
|
|
41
|
-
// ... same as above
|
|
42
|
-
]
|
|
43
|
-
});
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### Option 3: Per-Route Security Settings
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
const proxy = new SmartProxy({
|
|
50
|
-
routes: [
|
|
51
|
-
{
|
|
52
|
-
name: 'websocket-passthrough',
|
|
53
|
-
match: {
|
|
54
|
-
ports: 443,
|
|
55
|
-
domains: ['ws.example.com']
|
|
56
|
-
},
|
|
57
|
-
action: {
|
|
58
|
-
type: 'forward',
|
|
59
|
-
target: { host: 'backend', port: 443 },
|
|
60
|
-
tls: { mode: 'passthrough' }
|
|
61
|
-
},
|
|
62
|
-
security: {
|
|
63
|
-
// Disable connection limits for this route
|
|
64
|
-
maxConnections: 0, // 0 = unlimited
|
|
65
|
-
maxConnectionsPerIP: 0 // 0 = unlimited
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
]
|
|
69
|
-
});
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Understanding the Issue
|
|
73
|
-
|
|
74
|
-
### Why Connections Drop at 30 Seconds
|
|
75
|
-
|
|
76
|
-
1. **WebSocket Heartbeat**: The HTTP proxy's WebSocket handler sends ping frames every 30 seconds
|
|
77
|
-
2. **SNI Passthrough**: In passthrough mode, traffic is encrypted end-to-end
|
|
78
|
-
3. **Can't Inject Pings**: The proxy can't inject ping frames into encrypted traffic
|
|
79
|
-
4. **No Pong Response**: Client doesn't respond to pings that were never sent
|
|
80
|
-
5. **Connection Terminated**: After 30 seconds, connection is marked inactive and closed
|
|
81
|
-
|
|
82
|
-
### Why Grace Periods Were Too Short
|
|
83
|
-
|
|
84
|
-
- Half-zombie detection: 30 seconds (now 5 minutes for TLS)
|
|
85
|
-
- Stuck connection detection: 60 seconds (now 5 minutes for TLS)
|
|
86
|
-
- These were too aggressive for encrypted long-lived connections
|
|
87
|
-
|
|
88
|
-
## Long-Term Solution
|
|
89
|
-
|
|
90
|
-
The fix involves:
|
|
91
|
-
|
|
92
|
-
1. **Detecting SNI Passthrough**: Skip WebSocket heartbeat for passthrough connections
|
|
93
|
-
2. **Longer Grace Periods**: 5-minute grace for encrypted connections
|
|
94
|
-
3. **TCP Keep-Alive**: Rely on OS-level TCP keep-alive instead
|
|
95
|
-
4. **Route-Aware Timeouts**: Different timeout strategies per route type
|
|
96
|
-
|
|
97
|
-
## TCP Keep-Alive Configuration
|
|
98
|
-
|
|
99
|
-
For best results, also configure TCP keep-alive at the OS level:
|
|
100
|
-
|
|
101
|
-
### Linux
|
|
102
|
-
```bash
|
|
103
|
-
# /etc/sysctl.conf
|
|
104
|
-
net.ipv4.tcp_keepalive_time = 600 # Start probes after 10 minutes
|
|
105
|
-
net.ipv4.tcp_keepalive_intvl = 60 # Probe every minute
|
|
106
|
-
net.ipv4.tcp_keepalive_probes = 9 # Drop after 9 failed probes
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Node.js Socket Options
|
|
110
|
-
The proxy already enables TCP keep-alive on sockets:
|
|
111
|
-
- Keep-alive is enabled by default
|
|
112
|
-
- Initial delay can be configured via `keepAliveInitialDelay`
|
|
113
|
-
|
|
114
|
-
## Monitoring
|
|
115
|
-
|
|
116
|
-
Check your connections:
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
const stats = proxy.getStats();
|
|
120
|
-
console.log('Active connections:', stats.getActiveConnections());
|
|
121
|
-
console.log('Connections by route:', stats.getConnectionsByRoute());
|
|
122
|
-
|
|
123
|
-
// Monitor long-lived connections
|
|
124
|
-
setInterval(() => {
|
|
125
|
-
const connections = proxy.connectionManager.getConnections();
|
|
126
|
-
for (const [id, conn] of connections) {
|
|
127
|
-
const age = Date.now() - conn.incomingStartTime;
|
|
128
|
-
if (age > 300000) { // 5+ minutes
|
|
129
|
-
console.log(`Long-lived connection: ${id}, age: ${age}ms, route: ${conn.routeName}`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}, 60000);
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## Summary
|
|
136
|
-
|
|
137
|
-
- **Immediate Fix**: Use `keepAliveTreatment: 'extended'` or `'immortal'`
|
|
138
|
-
- **Applied Fix**: Increased grace periods for TLS connections to 5 minutes
|
|
139
|
-
- **Best Practice**: Use SNI passthrough for WebSocket when you need end-to-end encryption
|
|
140
|
-
- **Alternative**: Use TLS termination if you need application-level WebSocket features
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# WebSocket Keep-Alive Fix for SNI Passthrough
|
|
2
|
-
|
|
3
|
-
## Problem
|
|
4
|
-
|
|
5
|
-
WebSocket connections in SNI passthrough mode are being disconnected every 30 seconds due to:
|
|
6
|
-
|
|
7
|
-
1. **WebSocket Heartbeat**: The HTTP proxy's WebSocket handler performs heartbeat checks every 30 seconds using ping/pong frames. In SNI passthrough mode, these frames can't be injected into the encrypted stream, causing connections to be marked as inactive and terminated.
|
|
8
|
-
|
|
9
|
-
2. **Half-Zombie Detection**: The connection manager's aggressive cleanup gives only 30 seconds grace period for connections where one socket is destroyed.
|
|
10
|
-
|
|
11
|
-
## Solution
|
|
12
|
-
|
|
13
|
-
For SNI passthrough connections:
|
|
14
|
-
1. Disable WebSocket-specific heartbeat checking (they're handled as raw TCP)
|
|
15
|
-
2. Rely on TCP keepalive settings instead
|
|
16
|
-
3. Increase grace period for encrypted connections
|
|
17
|
-
|
|
18
|
-
## Current Settings
|
|
19
|
-
|
|
20
|
-
- Default inactivity timeout: 4 hours (14400000 ms)
|
|
21
|
-
- Keep-alive multiplier for extended mode: 6x (24 hours)
|
|
22
|
-
- WebSocket heartbeat interval: 30 seconds (problem!)
|
|
23
|
-
- Half-zombie grace period: 30 seconds (too aggressive)
|
|
24
|
-
|
|
25
|
-
## Recommended Configuration
|
|
26
|
-
|
|
27
|
-
```typescript
|
|
28
|
-
const proxy = new SmartProxy({
|
|
29
|
-
// Increase grace period for connection cleanup
|
|
30
|
-
inactivityTimeout: 14400000, // 4 hours default
|
|
31
|
-
keepAliveTreatment: 'extended', // or 'immortal' for no timeout
|
|
32
|
-
keepAliveInactivityMultiplier: 10, // 40 hours for keepalive connections
|
|
33
|
-
|
|
34
|
-
// For routes with WebSocket over SNI passthrough
|
|
35
|
-
routes: [
|
|
36
|
-
{
|
|
37
|
-
name: 'websocket-passthrough',
|
|
38
|
-
match: { ports: 443, domains: 'ws.example.com' },
|
|
39
|
-
action: {
|
|
40
|
-
type: 'forward',
|
|
41
|
-
target: { host: 'backend', port: 443 },
|
|
42
|
-
tls: { mode: 'passthrough' },
|
|
43
|
-
// No WebSocket-specific config needed for passthrough
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
]
|
|
47
|
-
});
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Temporary Workaround
|
|
51
|
-
|
|
52
|
-
Until a fix is implemented, you can:
|
|
53
|
-
|
|
54
|
-
1. Use `keepAliveTreatment: 'immortal'` to disable timeout-based cleanup
|
|
55
|
-
2. Increase the half-zombie grace period
|
|
56
|
-
3. Use TCP keepalive at the OS level
|
|
57
|
-
|
|
58
|
-
## Proper Fix Implementation
|
|
59
|
-
|
|
60
|
-
1. Detect when a connection is SNI passthrough
|
|
61
|
-
2. Skip WebSocket heartbeat for passthrough connections
|
|
62
|
-
3. Increase grace period for encrypted connections
|
|
63
|
-
4. Rely on TCP keepalive instead of application-level ping/pong
|