@push.rocks/smartproxy 19.6.2 → 19.6.7

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.
Files changed (52) hide show
  1. package/dist_ts/proxies/smart-proxy/connection-manager.d.ts +4 -7
  2. package/dist_ts/proxies/smart-proxy/connection-manager.js +22 -22
  3. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.d.ts +4 -3
  4. package/dist_ts/proxies/smart-proxy/http-proxy-bridge.js +9 -9
  5. package/dist_ts/proxies/smart-proxy/metrics-collector.d.ts +68 -56
  6. package/dist_ts/proxies/smart-proxy/metrics-collector.js +226 -176
  7. package/dist_ts/proxies/smart-proxy/models/interfaces.d.ts +5 -0
  8. package/dist_ts/proxies/smart-proxy/models/metrics-types.d.ts +94 -48
  9. package/dist_ts/proxies/smart-proxy/nftables-manager.d.ts +4 -4
  10. package/dist_ts/proxies/smart-proxy/nftables-manager.js +6 -6
  11. package/dist_ts/proxies/smart-proxy/port-manager.d.ts +4 -7
  12. package/dist_ts/proxies/smart-proxy/port-manager.js +6 -9
  13. package/dist_ts/proxies/smart-proxy/route-connection-handler.d.ts +4 -15
  14. package/dist_ts/proxies/smart-proxy/route-connection-handler.js +128 -128
  15. package/dist_ts/proxies/smart-proxy/security-manager.d.ts +3 -3
  16. package/dist_ts/proxies/smart-proxy/security-manager.js +9 -9
  17. package/dist_ts/proxies/smart-proxy/smart-proxy.d.ts +20 -13
  18. package/dist_ts/proxies/smart-proxy/smart-proxy.js +16 -13
  19. package/dist_ts/proxies/smart-proxy/throughput-tracker.d.ts +36 -0
  20. package/dist_ts/proxies/smart-proxy/throughput-tracker.js +117 -0
  21. package/dist_ts/proxies/smart-proxy/timeout-manager.d.ts +4 -3
  22. package/dist_ts/proxies/smart-proxy/timeout-manager.js +16 -16
  23. package/dist_ts/proxies/smart-proxy/tls-manager.d.ts +3 -3
  24. package/dist_ts/proxies/smart-proxy/tls-manager.js +12 -12
  25. package/package.json +8 -17
  26. package/readme.hints.md +0 -897
  27. package/readme.md +960 -54
  28. package/readme.plan.md +301 -562
  29. package/ts/proxies/smart-proxy/connection-manager.ts +23 -21
  30. package/ts/proxies/smart-proxy/http-proxy-bridge.ts +9 -8
  31. package/ts/proxies/smart-proxy/metrics-collector.ts +277 -189
  32. package/ts/proxies/smart-proxy/models/interfaces.ts +7 -0
  33. package/ts/proxies/smart-proxy/models/metrics-types.ts +93 -41
  34. package/ts/proxies/smart-proxy/nftables-manager.ts +5 -5
  35. package/ts/proxies/smart-proxy/port-manager.ts +6 -14
  36. package/ts/proxies/smart-proxy/route-connection-handler.ts +136 -136
  37. package/ts/proxies/smart-proxy/security-manager.ts +8 -8
  38. package/ts/proxies/smart-proxy/smart-proxy.ts +26 -35
  39. package/ts/proxies/smart-proxy/throughput-tracker.ts +144 -0
  40. package/ts/proxies/smart-proxy/timeout-manager.ts +16 -15
  41. package/ts/proxies/smart-proxy/tls-manager.ts +11 -11
  42. package/readme.connections.md +0 -724
  43. package/readme.delete.md +0 -187
  44. package/readme.memory-leaks-fixed.md +0 -45
  45. package/readme.metrics.md +0 -591
  46. package/readme.monitoring.md +0 -202
  47. package/readme.proxy-chain-summary.md +0 -112
  48. package/readme.proxy-protocol-example.md +0 -462
  49. package/readme.proxy-protocol.md +0 -415
  50. package/readme.routing.md +0 -341
  51. package/readme.websocket-keepalive-config.md +0 -140
  52. 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