@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.plan.md CHANGED
@@ -1,625 +1,364 @@
1
- # PROXY Protocol Implementation Plan
2
-
3
- ## ⚠️ CRITICAL: Implementation Order
4
-
5
- **Phase 1 (ProxyProtocolSocket/WrappedSocket) MUST be completed first!**
6
-
7
- The ProxyProtocolSocket class is the foundation that enables all PROXY protocol functionality. No protocol parsing or integration can happen until this wrapper class is fully implemented and tested.
8
-
9
- 1. **FIRST**: Implement ProxyProtocolSocket (the WrappedSocket)
10
- 2. **THEN**: Add PROXY protocol parser
11
- 3. **THEN**: Integrate with connection handlers
12
- 4. **FINALLY**: Add security and validation
1
+ # SmartProxy Metrics Improvement Plan
13
2
 
14
3
  ## Overview
15
- Implement PROXY protocol support in SmartProxy to preserve client IP information through proxy chains, solving the connection limit accumulation issue where inner proxies see all connections as coming from the outer proxy's IP.
16
4
 
17
- ## Problem Statement
18
- - In proxy chains, the inner proxy sees all connections from the outer proxy's IP
19
- - This causes the inner proxy to hit per-IP connection limits (default: 100)
20
- - Results in connection rejections while outer proxy accumulates connections
5
+ The current `getThroughputRate()` implementation calculates cumulative throughput over a 60-second window rather than providing an actual rate, making metrics misleading for monitoring systems. This plan outlines a comprehensive redesign of the metrics system to provide accurate, time-series based metrics suitable for production monitoring.
21
6
 
22
- ## Solution Design
7
+ ## 1. Core Issues with Current Implementation
23
8
 
24
- ### 1. Core Features
9
+ - **Cumulative vs Rate**: Current method accumulates all bytes from connections in the last minute rather than calculating actual throughput rate
10
+ - **No Time-Series Data**: Cannot track throughput changes over time
11
+ - **Inaccurate Estimates**: Attempting to estimate rates for older connections is fundamentally flawed
12
+ - **No Sliding Windows**: Cannot provide different time window views (1s, 10s, 60s, etc.)
13
+ - **Limited Granularity**: Only provides a single 60-second view
25
14
 
26
- #### 1.1 PROXY Protocol Parsing
27
- - Support PROXY protocol v1 (text format) initially
28
- - Parse incoming PROXY headers to extract:
29
- - Real client IP address
30
- - Real client port
31
- - Proxy IP address
32
- - Proxy port
33
- - Protocol (TCP4/TCP6)
15
+ ## 2. Proposed Architecture
34
16
 
35
- #### 1.2 PROXY Protocol Generation
36
- - Add ability to send PROXY protocol headers when forwarding connections
37
- - Configurable per route or target
38
-
39
- #### 1.3 Trusted Proxy IPs
40
- - New `proxyIPs` array in SmartProxy options
41
- - Auto-enable PROXY protocol acceptance for connections from these IPs
42
- - Reject PROXY protocol from untrusted sources (security)
43
-
44
- ### 2. Configuration Schema
17
+ ### A. Time-Series Throughput Tracking
45
18
 
46
19
  ```typescript
47
- interface ISmartProxyOptions {
48
- // ... existing options
49
-
50
- // List of trusted proxy IPs that can send PROXY protocol
51
- proxyIPs?: string[];
52
-
53
- // Global option to accept PROXY protocol (defaults based on proxyIPs)
54
- acceptProxyProtocol?: boolean;
55
-
56
- // Global option to send PROXY protocol to all targets
57
- sendProxyProtocol?: boolean;
20
+ interface IThroughputSample {
21
+ timestamp: number;
22
+ bytesIn: number;
23
+ bytesOut: number;
58
24
  }
59
25
 
60
- interface IRouteAction {
61
- // ... existing options
62
-
63
- // Send PROXY protocol to this specific target
64
- sendProxyProtocol?: boolean;
65
- }
66
- ```
67
-
68
- ### 3. Implementation Steps
69
-
70
- #### IMPORTANT: Phase 1 Must Be Completed First
71
- The `ProxyProtocolSocket` (WrappedSocket) is the foundation for all PROXY protocol functionality. This wrapper class must be implemented and integrated BEFORE any PROXY protocol parsing can begin.
72
-
73
- #### Phase 1: ProxyProtocolSocket (WrappedSocket) Foundation - ✅ COMPLETED (v19.5.19)
74
- This phase creates the socket wrapper infrastructure that all subsequent phases depend on.
75
-
76
- 1. **Create WrappedSocket class** in `ts/core/models/wrapped-socket.ts` ✅
77
- - Used JavaScript Proxy pattern instead of EventEmitter (avoids infinite loops)
78
- - Properties for real client IP and port
79
- - Transparent getters that return real or socket IP/port
80
- - All socket methods/properties delegated via Proxy
81
-
82
- 2. **Implement core wrapper functionality** ✅
83
- - Constructor accepts regular socket + optional metadata
84
- - `remoteAddress` getter returns real IP or falls back to socket IP
85
- - `remotePort` getter returns real port or falls back to socket port
86
- - `isFromTrustedProxy` property to check if it has real client info
87
- - `setProxyInfo()` method to update real client details
88
-
89
- 3. **Update ConnectionManager to handle wrapped sockets** ✅
90
- - Accept either `net.Socket` or `WrappedSocket`
91
- - Created `getUnderlyingSocket()` helper for socket utilities
92
- - All socket utility functions extract underlying socket
93
-
94
- 4. **Integration completed** ✅
95
- - All incoming sockets wrapped in RouteConnectionHandler
96
- - Socket forwarding verified working with wrapped sockets
97
- - Type safety maintained with index signature
98
-
99
- **Deliverables**: ✅ Working WrappedSocket that can wrap any socket and provide transparent access to client info.
100
-
101
- #### Phase 2: PROXY Protocol Parser - ✅ COMPLETED (v19.5.21)
102
- Only after WrappedSocket is working can we add protocol parsing.
103
-
104
- 1. ✅ Created `ProxyProtocolParser` class in `ts/core/utils/proxy-protocol.ts`
105
- 2. ✅ Implemented v1 text format parsing with full validation
106
- 3. ✅ Added comprehensive error handling and IP validation
107
- 4. ✅ Integrated parser to work WITH WrappedSocket in RouteConnectionHandler
108
-
109
- **Deliverables**: ✅ Working PROXY protocol v1 parser that validates headers, extracts client info, and handles both TCP4 and TCP6 protocols.
110
-
111
- #### Phase 3: Connection Handler Integration - ✅ COMPLETED (v19.5.21)
112
- 1. ✅ Modify `RouteConnectionHandler` to create WrappedSocket for all connections
113
- 2. ✅ Check if connection is from trusted proxy IP
114
- 3. ✅ If trusted, attempt to parse PROXY protocol header
115
- 4. ✅ Update wrapped socket with real client info
116
- 5. ✅ Continue normal connection handling with wrapped socket
117
-
118
- **Deliverables**: ✅ RouteConnectionHandler now parses PROXY protocol from trusted proxies and updates connection records with real client info.
119
-
120
- #### Phase 4: Outbound PROXY Protocol - ✅ COMPLETED (v19.5.21)
121
- 1. ✅ Add PROXY header generation in `setupDirectConnection`
122
- 2. ✅ Make it configurable per route via `sendProxyProtocol` option
123
- 3. ✅ Send header immediately after TCP connection
124
- 4. ✅ Added remotePort tracking to connection records
125
-
126
- **Deliverables**: ✅ SmartProxy can now send PROXY protocol headers to backend servers when configured, preserving client IP through proxy chains.
127
-
128
- #### Phase 5: Security & Validation - FINAL PHASE
129
- 1. Validate PROXY headers strictly
130
- 2. Reject malformed headers
131
- 3. Only accept from trusted IPs
132
- 4. Add rate limiting for PROXY protocol parsing
133
-
134
- ### 4. Design Decision: Socket Wrapper Architecture
135
-
136
- #### Option A: Minimal Single Socket Wrapper
137
- - **Scope**: Wraps individual sockets with metadata
138
- - **Use Case**: PROXY protocol support with minimal refactoring
139
- - **Pros**: Simple, low risk, easy migration
140
- - **Cons**: Still need separate connection management
141
-
142
- #### Option B: Comprehensive Connection Wrapper
143
- - **Scope**: Manages socket pairs (incoming + outgoing) with all utilities
144
- - **Use Case**: Complete connection lifecycle management
145
- - **Pros**:
146
- - Encapsulates all socket utilities (forwarding, cleanup, backpressure)
147
- - Single object represents entire connection
148
- - Cleaner API for connection handling
149
- - **Cons**:
150
- - Major architectural change
151
- - Higher implementation risk
152
- - More complex migration
153
-
154
- #### Recommendation
155
- Start with **Option A** (ProxyProtocolSocket) for immediate PROXY protocol support, then evaluate Option B based on:
156
- - Performance impact of additional abstraction
157
- - Code simplification benefits
158
- - Team comfort with architectural change
159
-
160
- ### 5. Code Implementation Details
161
-
162
- #### 5.1 ProxyProtocolSocket (WrappedSocket) - PHASE 1 IMPLEMENTATION
163
- This is the foundational wrapper class that MUST be implemented first. It wraps a regular socket and provides transparent access to the real client IP/port.
164
-
165
- ```typescript
166
- // ts/core/models/proxy-protocol-socket.ts
167
- import { EventEmitter } from 'events';
168
- import * as plugins from '../../../plugins.js';
169
-
170
- /**
171
- * ProxyProtocolSocket wraps a regular net.Socket to provide transparent access
172
- * to the real client IP and port when behind a proxy using PROXY protocol.
173
- *
174
- * This is the FOUNDATION for all PROXY protocol support and must be implemented
175
- * before any protocol parsing can occur.
176
- */
177
- export class ProxyProtocolSocket extends EventEmitter {
178
- private realClientIP?: string;
179
- private realClientPort?: number;
26
+ class ThroughputTracker {
27
+ private samples: IThroughputSample[] = [];
28
+ private readonly MAX_SAMPLES = 3600; // 1 hour at 1 sample/second
29
+ private lastSampleTime: number = 0;
30
+ private accumulatedBytesIn: number = 0;
31
+ private accumulatedBytesOut: number = 0;
180
32
 
181
- constructor(
182
- public readonly socket: plugins.net.Socket,
183
- realClientIP?: string,
184
- realClientPort?: number
185
- ) {
186
- super();
187
- this.realClientIP = realClientIP;
188
- this.realClientPort = realClientPort;
189
-
190
- // Forward all socket events
191
- this.forwardSocketEvents();
192
- }
193
-
194
- /**
195
- * Returns the real client IP if available, otherwise the socket's remote address
196
- */
197
- get remoteAddress(): string | undefined {
198
- return this.realClientIP || this.socket.remoteAddress;
199
- }
200
-
201
- /**
202
- * Returns the real client port if available, otherwise the socket's remote port
203
- */
204
- get remotePort(): number | undefined {
205
- return this.realClientPort || this.socket.remotePort;
206
- }
207
-
208
- /**
209
- * Indicates if this connection came through a trusted proxy
210
- */
211
- get isFromTrustedProxy(): boolean {
212
- return !!this.realClientIP;
33
+ // Called on every data transfer
34
+ public recordBytes(bytesIn: number, bytesOut: number): void {
35
+ this.accumulatedBytesIn += bytesIn;
36
+ this.accumulatedBytesOut += bytesOut;
213
37
  }
214
38
 
215
- /**
216
- * Updates the real client information (called after parsing PROXY protocol)
217
- */
218
- setProxyInfo(ip: string, port: number): void {
219
- this.realClientIP = ip;
220
- this.realClientPort = port;
221
- }
222
-
223
- // Pass-through all socket methods
224
- write(data: any, encoding?: any, callback?: any): boolean {
225
- return this.socket.write(data, encoding, callback);
226
- }
227
-
228
- end(data?: any, encoding?: any, callback?: any): this {
229
- this.socket.end(data, encoding, callback);
230
- return this;
231
- }
232
-
233
- destroy(error?: Error): this {
234
- this.socket.destroy(error);
235
- return this;
39
+ // Called periodically (every second)
40
+ public takeSample(): void {
41
+ const now = Date.now();
42
+
43
+ // Record accumulated bytes since last sample
44
+ this.samples.push({
45
+ timestamp: now,
46
+ bytesIn: this.accumulatedBytesIn,
47
+ bytesOut: this.accumulatedBytesOut
48
+ });
49
+
50
+ // Reset accumulators
51
+ this.accumulatedBytesIn = 0;
52
+ this.accumulatedBytesOut = 0;
53
+
54
+ // Trim old samples
55
+ const cutoff = now - 3600000; // 1 hour
56
+ this.samples = this.samples.filter(s => s.timestamp > cutoff);
236
57
  }
237
58
 
238
- // ... implement all other socket methods as pass-through
239
-
240
- /**
241
- * Forward all events from the underlying socket
242
- */
243
- private forwardSocketEvents(): void {
244
- const events = ['data', 'end', 'close', 'error', 'drain', 'timeout'];
245
- events.forEach(event => {
246
- this.socket.on(event, (...args) => {
247
- this.emit(event, ...args);
248
- });
249
- });
59
+ // Get rate over specified window
60
+ public getRate(windowSeconds: number): { bytesInPerSec: number; bytesOutPerSec: number } {
61
+ const now = Date.now();
62
+ const windowStart = now - (windowSeconds * 1000);
63
+
64
+ const relevantSamples = this.samples.filter(s => s.timestamp > windowStart);
65
+
66
+ if (relevantSamples.length === 0) {
67
+ return { bytesInPerSec: 0, bytesOutPerSec: 0 };
68
+ }
69
+
70
+ const totalBytesIn = relevantSamples.reduce((sum, s) => sum + s.bytesIn, 0);
71
+ const totalBytesOut = relevantSamples.reduce((sum, s) => sum + s.bytesOut, 0);
72
+
73
+ const actualWindow = (now - relevantSamples[0].timestamp) / 1000;
74
+
75
+ return {
76
+ bytesInPerSec: Math.round(totalBytesIn / actualWindow),
77
+ bytesOutPerSec: Math.round(totalBytesOut / actualWindow)
78
+ };
250
79
  }
251
80
  }
252
81
  ```
253
82
 
254
- **KEY POINT**: This wrapper must be fully functional and tested BEFORE moving to Phase 2.
83
+ ### B. Connection-Level Byte Tracking
255
84
 
256
- #### 4.2 ProxyProtocolParser (new file)
257
85
  ```typescript
258
- // ts/core/utils/proxy-protocol.ts
259
- export class ProxyProtocolParser {
260
- static readonly PROXY_V1_SIGNATURE = 'PROXY ';
86
+ // In ConnectionRecord, add:
87
+ interface IConnectionRecord {
88
+ // ... existing fields ...
261
89
 
262
- static parse(chunk: Buffer): IProxyInfo | null {
263
- // Implementation
264
- }
90
+ // Byte counters with timestamps
91
+ bytesReceivedHistory: Array<{ timestamp: number; bytes: number }>;
92
+ bytesSentHistory: Array<{ timestamp: number; bytes: number }>;
265
93
 
266
- static generate(info: IProxyInfo): Buffer {
267
- // Implementation
268
- }
94
+ // For efficiency, could use circular buffer
95
+ lastBytesReceivedUpdate: number;
96
+ lastBytesSentUpdate: number;
269
97
  }
270
98
  ```
271
99
 
272
- #### 4.3 Connection Handler Updates
273
- ```typescript
274
- // In handleConnection method
275
- let wrappedSocket: ProxyProtocolSocket | plugins.net.Socket = socket;
100
+ ### C. Enhanced Metrics Interface
276
101
 
277
- // Wrap socket if from trusted proxy
278
- if (this.settings.proxyIPs?.includes(socket.remoteAddress)) {
279
- wrappedSocket = new ProxyProtocolSocket(socket);
102
+ ```typescript
103
+ interface IMetrics {
104
+ // Connection metrics
105
+ connections: {
106
+ active(): number;
107
+ total(): number;
108
+ byRoute(): Map<string, number>;
109
+ byIP(): Map<string, number>;
110
+ topIPs(limit?: number): Array<{ ip: string; count: number }>;
111
+ };
112
+
113
+ // Throughput metrics (bytes per second)
114
+ throughput: {
115
+ instant(): { in: number; out: number }; // Last 1 second
116
+ recent(): { in: number; out: number }; // Last 10 seconds
117
+ average(): { in: number; out: number }; // Last 60 seconds
118
+ custom(seconds: number): { in: number; out: number };
119
+ history(seconds: number): Array<{ timestamp: number; in: number; out: number }>;
120
+ byRoute(windowSeconds?: number): Map<string, { in: number; out: number }>;
121
+ byIP(windowSeconds?: number): Map<string, { in: number; out: number }>;
122
+ };
123
+
124
+ // Request metrics
125
+ requests: {
126
+ perSecond(): number;
127
+ perMinute(): number;
128
+ total(): number;
129
+ };
130
+
131
+ // Cumulative totals
132
+ totals: {
133
+ bytesIn(): number;
134
+ bytesOut(): number;
135
+ connections(): number;
136
+ };
137
+
138
+ // Performance metrics
139
+ percentiles: {
140
+ connectionDuration(): { p50: number; p95: number; p99: number };
141
+ bytesTransferred(): {
142
+ in: { p50: number; p95: number; p99: number };
143
+ out: { p50: number; p95: number; p99: number };
144
+ };
145
+ };
280
146
  }
147
+ ```
281
148
 
282
- // Create connection record with wrapped socket
283
- const record = this.connectionManager.createConnection(wrappedSocket);
149
+ ## 3. Implementation Plan
150
+
151
+ ### Current Status
152
+ - **Phase 1**: ~90% complete (core functionality implemented, tests need fixing)
153
+ - **Phase 2**: ~60% complete (main features done, percentiles pending)
154
+ - **Phase 3**: ~40% complete (basic optimizations in place)
155
+ - **Phase 4**: 0% complete (export formats not started)
156
+
157
+ ### Phase 1: Core Throughput Tracking (Week 1)
158
+ - [x] Implement `ThroughputTracker` class
159
+ - [x] Integrate byte recording into socket data handlers
160
+ - [x] Add periodic sampling (1-second intervals)
161
+ - [x] Update `getThroughputRate()` to use time-series data (replaced with new clean API)
162
+ - [ ] Add unit tests for throughput tracking
163
+
164
+ ### Phase 2: Enhanced Metrics (Week 2)
165
+ - [x] Add configurable time windows (1s, 10s, 60s, 5m, etc.)
166
+ - [ ] Implement percentile calculations
167
+ - [x] Add route-specific and IP-specific throughput tracking
168
+ - [x] Create historical data access methods
169
+ - [ ] Add integration tests
170
+
171
+ ### Phase 3: Performance Optimization (Week 3)
172
+ - [x] Use circular buffers for efficiency
173
+ - [ ] Implement data aggregation for longer time windows
174
+ - [x] Add configurable retention periods
175
+ - [ ] Optimize memory usage
176
+ - [ ] Add performance benchmarks
177
+
178
+ ### Phase 4: Export Formats (Week 4)
179
+ - [ ] Add Prometheus metric format with proper metric types
180
+ - [ ] Add StatsD format support
181
+ - [ ] Add JSON export with metadata
182
+ - [ ] Create OpenMetrics compatibility
183
+ - [ ] Add documentation and examples
184
+
185
+ ## 4. Key Design Decisions
186
+
187
+ ### A. Sampling Strategy
188
+ - **1-second samples** for fine-grained data
189
+ - **Aggregate to 1-minute** for longer retention
190
+ - **Keep 1 hour** of second-level data
191
+ - **Keep 24 hours** of minute-level data
192
+
193
+ ### B. Memory Management
194
+ - **Circular buffers** for fixed memory usage
195
+ - **Configurable retention** periods
196
+ - **Lazy aggregation** for older data
197
+ - **Efficient data structures** (typed arrays for samples)
198
+
199
+ ### C. Performance Considerations
200
+ - **Batch updates** during high throughput
201
+ - **Debounced calculations** for expensive metrics
202
+ - **Cached results** with TTL
203
+ - **Worker thread** option for heavy calculations
204
+
205
+ ## 5. Configuration Options
284
206
 
285
- // In handleInitialData method
286
- if (wrappedSocket instanceof ProxyProtocolSocket) {
287
- const proxyInfo = await this.checkForProxyProtocol(chunk);
288
- if (proxyInfo) {
289
- wrappedSocket.setProxyInfo(proxyInfo.sourceIP, proxyInfo.sourcePort);
290
- // Continue with remaining data after PROXY header
291
- }
207
+ ```typescript
208
+ interface IMetricsConfig {
209
+ enabled: boolean;
210
+
211
+ // Sampling configuration
212
+ sampleIntervalMs: number; // Default: 1000 (1 second)
213
+ retentionSeconds: number; // Default: 3600 (1 hour)
214
+
215
+ // Performance tuning
216
+ enableDetailedTracking: boolean; // Per-connection byte history
217
+ enablePercentiles: boolean; // Calculate percentiles
218
+ cacheResultsMs: number; // Cache expensive calculations
219
+
220
+ // Export configuration
221
+ prometheusEnabled: boolean;
222
+ prometheusPath: string; // Default: /metrics
223
+ prometheusPrefix: string; // Default: smartproxy_
292
224
  }
293
225
  ```
294
226
 
295
- #### 4.4 Security Manager Updates
296
- - Accept socket or ProxyProtocolSocket
297
- - Use `socket.remoteAddress` getter for real client IP
298
- - Transparent handling of both socket types
227
+ ## 6. Example Usage
299
228
 
300
- ### 5. Configuration Examples
301
-
302
- #### Basic Setup (IMPLEMENTED ✅)
303
229
  ```typescript
304
- // Outer proxy - sends PROXY protocol
305
- const outerProxy = new SmartProxy({
306
- routes: [{
307
- name: 'to-inner-proxy',
308
- match: { ports: 443 },
309
- action: {
310
- type: 'forward',
311
- target: { host: '195.201.98.232', port: 443 },
312
- sendProxyProtocol: true // Enable for this route
313
- }
314
- }]
230
+ const proxy = new SmartProxy({
231
+ metrics: {
232
+ enabled: true,
233
+ sampleIntervalMs: 1000,
234
+ enableDetailedTracking: true
235
+ }
315
236
  });
316
237
 
317
- // Inner proxy - accepts PROXY protocol from outer proxy
318
- const innerProxy = new SmartProxy({
319
- proxyIPs: ['212.95.99.130'], // Outer proxy IP
320
- acceptProxyProtocol: true, // Optional - defaults to true when proxyIPs is set
321
- routes: [{
322
- name: 'to-backend',
323
- match: { ports: 443 },
324
- action: {
325
- type: 'forward',
326
- target: { host: '192.168.5.247', port: 443 }
327
- }
328
- }]
329
- });
330
- ```
238
+ // Get metrics instance
239
+ const metrics = proxy.getMetrics();
331
240
 
332
- ### 6. Testing Plan
241
+ // Connection metrics
242
+ console.log(`Active connections: ${metrics.connections.active()}`);
243
+ console.log(`Total connections: ${metrics.connections.total()}`);
333
244
 
334
- #### Unit Tests
335
- - PROXY protocol v1 parsing (valid/invalid formats)
336
- - Header generation
337
- - Trusted IP validation
338
- - Connection record updates
245
+ // Throughput metrics
246
+ const instant = metrics.throughput.instant();
247
+ console.log(`Current: ${instant.in} bytes/sec in, ${instant.out} bytes/sec out`);
339
248
 
340
- #### Integration Tests
341
- - Single proxy with PROXY protocol
342
- - Proxy chain with PROXY protocol
343
- - Security: reject from untrusted IPs
344
- - Performance: minimal overhead
345
- - Compatibility: works with TLS passthrough
249
+ const recent = metrics.throughput.recent(); // Last 10 seconds
250
+ const average = metrics.throughput.average(); // Last 60 seconds
346
251
 
347
- #### Test Scenarios
348
- 1. **Connection limit test**: Verify inner proxy sees real client IPs
349
- 2. **Security test**: Ensure PROXY protocol rejected from untrusted sources
350
- 3. **Compatibility test**: Verify no impact on non-PROXY connections
351
- 4. **Performance test**: Measure overhead of PROXY protocol parsing
252
+ // Custom time window
253
+ const custom = metrics.throughput.custom(30); // Last 30 seconds
352
254
 
353
- ### 7. Security Considerations
255
+ // Historical data for graphing
256
+ const history = metrics.throughput.history(300); // Last 5 minutes
257
+ history.forEach(point => {
258
+ console.log(`${new Date(point.timestamp)}: ${point.in} bytes/sec in, ${point.out} bytes/sec out`);
259
+ });
354
260
 
355
- 1. **IP Spoofing Prevention**
356
- - Only accept PROXY protocol from explicitly trusted IPs
357
- - Validate all header fields
358
- - Reject malformed headers immediately
261
+ // Top routes by throughput
262
+ const routeThroughput = metrics.throughput.byRoute(60);
263
+ routeThroughput.forEach((stats, route) => {
264
+ console.log(`Route ${route}: ${stats.in} bytes/sec in, ${stats.out} bytes/sec out`);
265
+ });
359
266
 
360
- 2. **Resource Protection**
361
- - Limit PROXY header size (107 bytes for v1)
362
- - Timeout for incomplete headers
363
- - Rate limit connection attempts
267
+ // Request metrics
268
+ console.log(`RPS: ${metrics.requests.perSecond()}`);
269
+ console.log(`RPM: ${metrics.requests.perMinute()}`);
364
270
 
365
- 3. **Logging**
366
- - Log all PROXY protocol acceptance/rejection
367
- - Include real client IP in all connection logs
271
+ // Totals
272
+ console.log(`Total bytes in: ${metrics.totals.bytesIn()}`);
273
+ console.log(`Total bytes out: ${metrics.totals.bytesOut()}`);
274
+ ```
368
275
 
369
- ### 8. Rollout Strategy
276
+ ## 7. Prometheus Export Example
370
277
 
371
- 1. **Phase 1**: Deploy parser and acceptance (backward compatible)
372
- 2. **Phase 2**: Enable between controlled proxy pairs
373
- 3. **Phase 3**: Monitor for issues and performance impact
374
- 4. **Phase 4**: Expand to all proxy chains
278
+ ```
279
+ # HELP smartproxy_throughput_bytes_per_second Current throughput in bytes per second
280
+ # TYPE smartproxy_throughput_bytes_per_second gauge
281
+ smartproxy_throughput_bytes_per_second{direction="in",window="1s"} 1234567
282
+ smartproxy_throughput_bytes_per_second{direction="out",window="1s"} 987654
283
+ smartproxy_throughput_bytes_per_second{direction="in",window="10s"} 1134567
284
+ smartproxy_throughput_bytes_per_second{direction="out",window="10s"} 887654
285
+
286
+ # HELP smartproxy_bytes_total Total bytes transferred
287
+ # TYPE smartproxy_bytes_total counter
288
+ smartproxy_bytes_total{direction="in"} 123456789
289
+ smartproxy_bytes_total{direction="out"} 98765432
290
+
291
+ # HELP smartproxy_active_connections Current number of active connections
292
+ # TYPE smartproxy_active_connections gauge
293
+ smartproxy_active_connections 42
294
+
295
+ # HELP smartproxy_connection_duration_seconds Connection duration in seconds
296
+ # TYPE smartproxy_connection_duration_seconds histogram
297
+ smartproxy_connection_duration_seconds_bucket{le="0.1"} 100
298
+ smartproxy_connection_duration_seconds_bucket{le="1"} 500
299
+ smartproxy_connection_duration_seconds_bucket{le="10"} 800
300
+ smartproxy_connection_duration_seconds_bucket{le="+Inf"} 850
301
+ smartproxy_connection_duration_seconds_sum 4250
302
+ smartproxy_connection_duration_seconds_count 850
303
+ ```
375
304
 
376
- ### 9. Success Metrics
305
+ ## 8. Migration Strategy
377
306
 
378
- - Inner proxy connection distribution matches outer proxy
379
- - No more connection limit rejections in proxy chains
380
- - Accurate client IP logging throughout the chain
381
- - No performance degradation (<1ms added latency)
307
+ ### Breaking Changes
308
+ - Completely replace the old metrics API with the new clean design
309
+ - Remove all `get*` prefixed methods in favor of grouped properties
310
+ - Use simple `{ in, out }` objects instead of verbose property names
311
+ - Provide clear migration guide in documentation
382
312
 
383
- ### 10. Future Enhancements
313
+ ### Implementation Approach
314
+ 1. ✅ Create new `ThroughputTracker` class for time-series data
315
+ 2. ✅ Implement new `IMetrics` interface with clean API
316
+ 3. ✅ Replace `MetricsCollector` implementation entirely
317
+ 4. ✅ Update all references to use new API
318
+ 5. ⚠️ Add comprehensive tests for accuracy validation (partial)
384
319
 
385
- - PROXY protocol v2 (binary format) support
386
- - TLV extensions for additional metadata
387
- - AWS VPC endpoint ID support
388
- - Custom metadata fields
320
+ ### Additional Refactoring Completed
321
+ - Refactored all SmartProxy components to use cleaner dependency pattern
322
+ - Components now receive only `SmartProxy` instance instead of individual dependencies
323
+ - Access to other components via `this.smartProxy.componentName`
324
+ - Significantly simplified constructor signatures across the codebase
389
325
 
390
- ## WrappedSocket Class Design
326
+ ## 9. Success Metrics
391
327
 
392
- ### Overview
393
- A WrappedSocket class has been evaluated and recommended to provide cleaner PROXY protocol integration and better socket management architecture.
328
+ - **Accuracy**: Throughput metrics accurate within 1% of actual
329
+ - **Performance**: < 1% CPU overhead for metrics collection
330
+ - **Memory**: < 10MB memory usage for 1 hour of data
331
+ - **Latency**: < 1ms to retrieve any metric
332
+ - **Reliability**: No metrics data loss under load
394
333
 
395
- ### Rationale for WrappedSocket
334
+ ## 10. Future Enhancements
396
335
 
397
- #### Current Challenges
398
- - Sockets handled directly as `net.Socket` instances throughout codebase
399
- - Metadata tracked separately in `IConnectionRecord` objects
400
- - Socket augmentation via TypeScript module augmentation for TLS properties
401
- - PROXY protocol would require modifying socket handling in multiple places
336
+ ### Phase 5: Advanced Analytics
337
+ - Anomaly detection for traffic patterns
338
+ - Predictive analytics for capacity planning
339
+ - Correlation analysis between routes
340
+ - Real-time alerting integration
402
341
 
403
- #### Benefits
404
- 1. **Clean PROXY Protocol Integration** - Parse and store real client IP/port without modifying existing socket handling
405
- 2. **Better Encapsulation** - Bundle socket + metadata + behavior together
406
- 3. **Type Safety** - No more module augmentation needed
407
- 4. **Future Extensibility** - Easy to add compression, metrics, etc.
408
- 5. **Simplified Testing** - Easier to mock and test socket behavior
342
+ ### Phase 6: Distributed Metrics
343
+ - Metrics aggregation across multiple proxies
344
+ - Distributed time-series storage
345
+ - Cross-proxy analytics
346
+ - Global dashboard support
409
347
 
410
- ### Implementation Strategy
348
+ ## 11. Risks and Mitigations
411
349
 
412
- #### Phase 1: Minimal ProxyProtocolSocket (Immediate)
413
- Create a minimal wrapper for PROXY protocol support:
350
+ ### Risk: Memory Usage
351
+ - **Mitigation**: Circular buffers and configurable retention
352
+ - **Monitoring**: Track memory usage per metric type
414
353
 
415
- ```typescript
416
- class ProxyProtocolSocket {
417
- constructor(
418
- public socket: net.Socket,
419
- public realClientIP?: string,
420
- public realClientPort?: number
421
- ) {}
422
-
423
- get remoteAddress(): string {
424
- return this.realClientIP || this.socket.remoteAddress || '';
425
- }
426
-
427
- get remotePort(): number {
428
- return this.realClientPort || this.socket.remotePort || 0;
429
- }
430
-
431
- get isFromTrustedProxy(): boolean {
432
- return !!this.realClientIP;
433
- }
434
- }
435
- ```
436
-
437
- Integration points:
438
- - Use in `RouteConnectionHandler` when receiving from trusted proxy IPs
439
- - Update `ConnectionManager` to accept wrapped sockets
440
- - Modify security checks to use `socket.remoteAddress` getter
441
-
442
- #### Phase 2: Connection-Aware WrappedSocket (Alternative Design)
443
- A more comprehensive design that manages both sides of a connection:
354
+ ### Risk: Performance Impact
355
+ - **Mitigation**: Efficient data structures and caching
356
+ - **Testing**: Load test with metrics enabled/disabled
444
357
 
445
- ```typescript
446
- // Option A: Single Socket Wrapper (simpler)
447
- class WrappedSocket extends EventEmitter {
448
- private socket: net.Socket;
449
- private connectionId: string;
450
- private metadata: ISocketMetadata;
451
-
452
- constructor(socket: net.Socket, metadata?: Partial<ISocketMetadata>) {
453
- super();
454
- this.socket = socket;
455
- this.connectionId = this.generateId();
456
- this.metadata = { ...defaultMetadata, ...metadata };
457
- this.setupHandlers();
458
- }
459
-
460
- // ... single socket management
461
- }
358
+ ### Risk: Data Accuracy
359
+ - **Mitigation**: Atomic operations and proper synchronization
360
+ - **Validation**: Compare with external monitoring tools
462
361
 
463
- // Option B: Connection Pair Wrapper (comprehensive)
464
- class WrappedConnection extends EventEmitter {
465
- private connectionId: string;
466
- private incoming: WrappedSocket;
467
- private outgoing?: WrappedSocket;
468
- private forwardingActive: boolean = false;
469
-
470
- constructor(incomingSocket: net.Socket) {
471
- super();
472
- this.connectionId = this.generateId();
473
- this.incoming = new WrappedSocket(incomingSocket);
474
- }
475
-
476
- // Connect to backend and set up forwarding
477
- async connectToBackend(target: ITarget): Promise<void> {
478
- const outgoingSocket = await this.createOutgoingConnection(target);
479
- this.outgoing = new WrappedSocket(outgoingSocket);
480
- await this.setupBidirectionalForwarding();
481
- }
482
-
483
- // Built-in forwarding logic from socket-utils
484
- private async setupBidirectionalForwarding(): Promise<void> {
485
- if (!this.outgoing) throw new Error('No outgoing socket');
486
-
487
- // Handle data forwarding with backpressure
488
- this.incoming.on('data', (chunk) => {
489
- this.outgoing!.write(chunk, () => {
490
- // Handle backpressure
491
- });
492
- });
493
-
494
- this.outgoing.on('data', (chunk) => {
495
- this.incoming.write(chunk, () => {
496
- // Handle backpressure
497
- });
498
- });
499
-
500
- // Handle connection lifecycle
501
- const cleanup = (reason: string) => {
502
- this.forwardingActive = false;
503
- this.incoming.destroy();
504
- this.outgoing?.destroy();
505
- this.emit('closed', reason);
506
- };
507
-
508
- this.incoming.once('close', () => cleanup('incoming_closed'));
509
- this.outgoing.once('close', () => cleanup('outgoing_closed'));
510
-
511
- this.forwardingActive = true;
512
- }
513
-
514
- // PROXY protocol support
515
- async handleProxyProtocol(trustedProxies: string[]): Promise<boolean> {
516
- if (trustedProxies.includes(this.incoming.socket.remoteAddress)) {
517
- const parsed = await this.incoming.parseProxyProtocol();
518
- if (parsed && this.outgoing) {
519
- // Forward PROXY protocol to backend if configured
520
- await this.outgoing.sendProxyProtocol(this.incoming.realClientIP);
521
- }
522
- return parsed;
523
- }
524
- return false;
525
- }
526
-
527
- // Consolidated metrics
528
- getMetrics(): IConnectionMetrics {
529
- return {
530
- connectionId: this.connectionId,
531
- duration: Date.now() - this.startTime,
532
- incoming: this.incoming.getMetrics(),
533
- outgoing: this.outgoing?.getMetrics(),
534
- totalBytes: this.getTotalBytes(),
535
- state: this.getConnectionState()
536
- };
537
- }
538
- }
539
- ```
540
-
541
- #### Phase 3: Full Migration (Long-term)
542
- - Replace all `net.Socket` usage with `WrappedSocket`
543
- - Remove socket augmentation from `socket-augmentation.ts`
544
- - Update all socket utilities to work with wrapped sockets
545
- - Standardize socket handling across all components
546
-
547
- ### Integration with PROXY Protocol
548
-
549
- The WrappedSocket class integrates seamlessly with PROXY protocol:
550
-
551
- 1. **Connection Acceptance**:
552
- ```typescript
553
- const wrappedSocket = new ProxyProtocolSocket(socket);
554
- if (this.isFromTrustedProxy(socket.remoteAddress)) {
555
- await wrappedSocket.parseProxyProtocol(this.settings.proxyIPs);
556
- }
557
- ```
558
-
559
- 2. **Security Checks**:
560
- ```typescript
561
- // Automatically uses real client IP if available
562
- const clientIP = wrappedSocket.remoteAddress;
563
- if (!this.securityManager.isIPAllowed(clientIP)) {
564
- wrappedSocket.destroy();
565
- }
566
- ```
567
-
568
- 3. **Connection Records**:
569
- ```typescript
570
- const record = this.connectionManager.createConnection(wrappedSocket);
571
- // ConnectionManager uses wrappedSocket.remoteAddress transparently
572
- ```
573
-
574
- ### Option B Example: How It Would Replace Current Architecture
575
-
576
- Instead of current approach with separate components:
577
- ```typescript
578
- // Current: Multiple separate components
579
- const record = connectionManager.createConnection(socket);
580
- const { cleanupClient, cleanupServer } = createIndependentSocketHandlers(
581
- clientSocket, serverSocket, onBothClosed
582
- );
583
- setupBidirectionalForwarding(clientSocket, serverSocket, handlers);
584
- ```
585
-
586
- Option B would consolidate everything:
587
- ```typescript
588
- // Option B: Single connection object
589
- const connection = new WrappedConnection(incomingSocket);
590
- await connection.handleProxyProtocol(trustedProxies);
591
- await connection.connectToBackend({ host: 'server', port: 443 });
592
- // Everything is handled internally - forwarding, cleanup, metrics
593
-
594
- connection.on('closed', (reason) => {
595
- logger.log('Connection closed', connection.getMetrics());
596
- });
597
- ```
362
+ ## Conclusion
598
363
 
599
- This would replace:
600
- - `IConnectionRecord` - absorbed into WrappedConnection
601
- - `socket-utils.ts` functions - methods on WrappedConnection
602
- - Separate incoming/outgoing tracking - unified in one object
603
- - Manual cleanup coordination - automatic lifecycle management
604
-
605
- Additional benefits with Option B:
606
- - **Connection Pooling Integration**: WrappedConnection could integrate with EnhancedConnectionPool for backend connections
607
- - **Unified Metrics**: Single point for all connection statistics
608
- - **Protocol Negotiation**: Handle PROXY, TLS, HTTP/2 upgrade in one place
609
- - **Resource Management**: Automatic cleanup with LifecycleComponent pattern
610
-
611
- ### Migration Path
612
-
613
- 1. **Week 1-2**: Implement minimal ProxyProtocolSocket (Option A)
614
- 2. **Week 3-4**: Test with PROXY protocol implementation
615
- 3. **Month 2**: Prototype WrappedConnection (Option B) if beneficial
616
- 4. **Month 3-6**: Gradual migration if Option B proves valuable
617
- 5. **Future**: Complete adoption in next major version
618
-
619
- ### Success Criteria
620
-
621
- - PROXY protocol works transparently with wrapped sockets
622
- - No performance regression (<0.1% overhead)
623
- - Simplified code in connection handlers
624
- - Better TypeScript type safety
625
- - Easier to add new socket-level features
364
+ This plan transforms SmartProxy's metrics from a basic cumulative system to a comprehensive, time-series based monitoring solution suitable for production environments. The phased approach ensures minimal disruption while delivering immediate value through accurate throughput measurements.